From 68f2d9fe5e3792cd44d93b829f227bac667bdb48 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 3 May 2023 15:16:39 +0800 Subject: [PATCH 001/391] [arch] add new example arch using subtile in I/O blocks; Updated documentation --- openfpga_flow/vpr_arch/README.md | 1 + .../k4_N4_tileable_IoSubtile_40nm.xml | 230 ++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml diff --git a/openfpga_flow/vpr_arch/README.md b/openfpga_flow/vpr_arch/README.md index f4f215fde..cbec4f286 100644 --- a/openfpga_flow/vpr_arch/README.md +++ b/openfpga_flow/vpr_arch/README.md @@ -21,6 +21,7 @@ Please reveal the following architecture features in the names to help quickly s - multi\_io\_capacity: If I/O capacity is different on each side of FPGAs. - reduced\_io: If I/Os only appear a certain or multiple sides of FPGAs - registerable\_io: If I/Os are registerable (can be either combinational or sequential) +- IoSubtile: If I/O block contains sub tiles (more compact with a higher density of I/Os) - CustomIoLoc: Use OpenFPGA's extended custom I/O location syntax - rstOnLut: The reset signal of CLB can feed LUT inputs through a local routing architecture - localClkGen: The clock signal of CLB can be generated by internal programmable resources diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml new file mode 100644 index 000000000..58204198e --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + fpga_input.inpad + fpga_input.inpad + fpga_input.inpad + fpga_input.inpad + + + + + + + + + + fpga_output.outpad + fpga_output.outpad + fpga_output.outpad + fpga_output.outpad + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 02a5057449de1dcd487c8e4defd3e17e6706e40f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 3 May 2023 15:20:49 +0800 Subject: [PATCH 002/391] [arch] add openfpga arch example using subtile; updated documentation --- openfpga_flow/openfpga_arch/README.md | 1 + .../k4_N4_40nm_IoSubtile_openfpga.xml | 206 ++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_openfpga.xml diff --git a/openfpga_flow/openfpga_arch/README.md b/openfpga_flow/openfpga_arch/README.md index cdd8db5f3..b23e837ec 100644 --- a/openfpga_flow/openfpga_arch/README.md +++ b/openfpga_flow/openfpga_arch/README.md @@ -27,6 +27,7 @@ Note that an OpenFPGA architecture can be applied to multiple VPR architecture f - local\_encoder: If local encoders are used in routing multiplexer design - spyio/spypad: If spy I/Os are used - registerable\_io: If I/Os are registerable (can be either combinational or sequential) +- IoSubtile: If I/O block contains sub tiles (more compact with a higher density of I/Os) - stdcell: If circuit designs are built with standard cells only - tree\_mux: If routing multiplexers are built with a tree-like structure - localClkGen: The clock signal of CLB can be generated by internal programmable resources diff --git a/openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_openfpga.xml b/openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_openfpga.xml new file mode 100644 index 000000000..bbe32024a --- /dev/null +++ b/openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_openfpga.xml @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + + + + + 10e-12 5e-12 5e-12 + + + 10e-12 5e-12 5e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a3f2ae3c33bfea43239c2d18a1e1e1ff50e1848f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 3 May 2023 15:23:47 +0800 Subject: [PATCH 003/391] [arch] format --- .../k4_N4_40nm_IoSubtile_openfpga.xml | 3 - .../k4_N4_tileable_IoSubtile_40nm.xml | 394 +++++++++--------- 2 files changed, 193 insertions(+), 204 deletions(-) diff --git a/openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_openfpga.xml b/openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_openfpga.xml index bbe32024a..6b17a175d 100644 --- a/openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_openfpga.xml @@ -185,13 +185,10 @@ - - - diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml index 58204198e..4dbf87b12 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml @@ -15,216 +15,208 @@ Authors: Jason Luu, Jeff Goeders, Vaughn Betz --> - - - - - - - - - - - - - - - - - - - - - - - fpga_input.inpad - fpga_input.inpad - fpga_input.inpad - fpga_input.inpad - - - - - - - - - - fpga_output.outpad - fpga_output.outpad - fpga_output.outpad - fpga_output.outpad - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1 1 1 1 1 - 1 1 1 1 - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + fpga_input.inpad + fpga_input.inpad + fpga_input.inpad + fpga_input.inpad + + + + + + + + + + fpga_output.outpad + fpga_output.outpad + fpga_output.outpad + fpga_output.outpad + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + 261e-12 261e-12 261e-12 261e-12 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + From df771cb33aa08264a8aa24bb57ecbc31bfdc175c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 3 May 2023 15:41:29 +0800 Subject: [PATCH 004/391] [test] add a new testcase for subtile and deploy it to basic regression test --- ...ile_openfpga.xml => k4_N4_40nm_IoSubtile_cc_openfpga.xml} | 0 openfpga_flow/regression_test_scripts/basic_reg_test.sh | 2 ++ .../tile_organization/tileable_io/config/task.conf | 5 ++--- 3 files changed, 4 insertions(+), 3 deletions(-) rename openfpga_flow/openfpga_arch/{k4_N4_40nm_IoSubtile_openfpga.xml => k4_N4_40nm_IoSubtile_cc_openfpga.xml} (100%) diff --git a/openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_openfpga.xml b/openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_cc_openfpga.xml similarity index 100% rename from openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_openfpga.xml rename to openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_cc_openfpga.xml diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index dd9e64a56..6b2d013cb 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -158,6 +158,8 @@ echo -e "Testing tiles with pins only on bottom and right sides"; run-task basic_tests/tile_organization/bottom_right_custom_pins $@ echo -e "Testing tiles with I/O in center grid"; run-task basic_tests/tile_organization/tileable_io $@ +echo -e "Testing tiles with I/O consisting of subtiles"; +run-task basic_tests/tile_organization/io_subtile $@ echo -e "Testing global port definition from tiles"; run-task basic_tests/global_tile_ports/global_tile_clock $@ diff --git a/openfpga_flow/tasks/basic_tests/tile_organization/tileable_io/config/task.conf b/openfpga_flow/tasks/basic_tests/tile_organization/tileable_io/config/task.conf index d2359135e..4e443688e 100644 --- a/openfpga_flow/tasks/basic_tests/tile_organization/tileable_io/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/tile_organization/tileable_io/config/task.conf @@ -17,12 +17,12 @@ fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga -openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_openfpga.xml +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_cc_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml openfpga_vpr_device_layout=2x2 [ARCHITECTURES] -arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileableIO_40nm.xml +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml [BENCHMARKS] bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v @@ -30,7 +30,6 @@ bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v [SYNTHESIS_PARAM] bench_read_verilog_options_common = -nolatches bench0_top = or2 -bench0_chan_width = 300 [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From 7bedc965ac814361ecd915874faa10078a2c6dc9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 3 May 2023 17:30:58 +0800 Subject: [PATCH 005/391] [core] supporting subtile --- openfpga/src/fabric/build_grid_modules.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 6773273f7..10fbd6704 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -1094,7 +1094,7 @@ static void build_physical_tile_module( * it as a mode under a */ for (const t_sub_tile& sub_tile : phy_block_type->sub_tiles) { - for (int iz = 0; iz < sub_tile.capacity.total(); ++iz) { + for (int iz = sub_tile.capacity.low; iz < sub_tile.capacity.high; ++iz) { VTR_ASSERT(1 == sub_tile.equivalent_sites.size()); t_logical_block_type_ptr lb_type = sub_tile.equivalent_sites[0]; /* Bypass empty pb_graph */ From 6c48c57421efc02783006b010bf47eddee758008 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 3 May 2023 21:23:52 +0800 Subject: [PATCH 006/391] [core] fixed some bugs in the subtile support --- .../build_grid_module_duplicated_pins.cpp | 83 ++++++++++--------- .../build_grid_module_duplicated_pins.h | 2 +- .../src/fabric/build_grid_module_utils.cpp | 3 +- openfpga/src/fabric/build_grid_module_utils.h | 1 + openfpga/src/fabric/build_grid_modules.cpp | 69 ++++++++------- 5 files changed, 81 insertions(+), 77 deletions(-) diff --git a/openfpga/src/fabric/build_grid_module_duplicated_pins.cpp b/openfpga/src/fabric/build_grid_module_duplicated_pins.cpp index 451500c0e..77f4024db 100644 --- a/openfpga/src/fabric/build_grid_module_duplicated_pins.cpp +++ b/openfpga/src/fabric/build_grid_module_duplicated_pins.cpp @@ -151,6 +151,7 @@ void add_grid_module_duplicated_pb_type_ports( static void add_grid_module_net_connect_duplicated_pb_graph_pin( ModuleManager& module_manager, const ModuleId& grid_module, const ModuleId& child_module, const size_t& child_instance, + const size_t& child_inst_subtile_index, const VprDeviceAnnotation& vpr_device_annotation, t_physical_tile_type_ptr grid_type_descriptor, t_pb_graph_pin* pb_graph_pin, const e_side& border_side, const e_pin2pin_interc_type& pin2pin_interc_type) { @@ -169,15 +170,18 @@ static void add_grid_module_net_connect_duplicated_pb_graph_pin( grid_pin_sides = {TOP, RIGHT, BOTTOM, LEFT}; } - /* num_pins/capacity = the number of pins that each type_descriptor has. - * Capacity defines the number of type_descriptors in each grid - * so the pin index at grid level = pin_index_in_type_descriptor - * + type_descriptor_index_in_capacity * - * num_pins_per_type_descriptor + /* Note that each grid may contain a number of sub tiles, each type of which + * may a different capacity and number of pins We need to find the start pin + * index for a given z offset (instance id), denotes the index of the first + * pin regarding the current instance. The variable 'pin_count_in_cluster' + * represent the pin index in the context of current instance only. With the + * information above, we can then calculate the absolute pin index at + * grid-level (considering all the sub tiles). */ - size_t grid_pin_index = pb_graph_pin->pin_count_in_cluster + - child_instance * grid_type_descriptor->num_pins / - grid_type_descriptor->capacity; + size_t grid_pin_index = + pb_graph_pin->pin_count_in_cluster + + vpr_device_annotation.physical_tile_z_to_start_pin_index( + grid_type_descriptor, child_inst_subtile_index); int pin_width = grid_type_descriptor->pin_width_offset[grid_pin_index]; int pin_height = grid_type_descriptor->pin_height_offset[grid_pin_index]; @@ -292,49 +296,48 @@ static void add_grid_module_net_connect_duplicated_pb_graph_pin( void add_grid_module_nets_connect_duplicated_pb_type_ports( ModuleManager& module_manager, const ModuleId& grid_module, const ModuleId& child_module, const size_t& child_instance, - const VprDeviceAnnotation& vpr_device_annotation, + const t_sub_tile& sub_tile, const VprDeviceAnnotation& vpr_device_annotation, t_physical_tile_type_ptr grid_type_descriptor, const e_side& border_side) { /* Ensure that we have a valid grid_type_descriptor */ VTR_ASSERT(false == is_empty_type(grid_type_descriptor)); /* FIXME: Currently support only 1 equivalent site! Should clarify this * limitation in documentation! */ - for (const t_sub_tile& sub_tile : grid_type_descriptor->sub_tiles) { - t_logical_block_type_ptr lb_type = sub_tile.equivalent_sites[0]; - t_pb_graph_node* top_pb_graph_node = lb_type->pb_graph_head; - VTR_ASSERT(nullptr != top_pb_graph_node); + t_logical_block_type_ptr lb_type = sub_tile.equivalent_sites[0]; + t_pb_graph_node* top_pb_graph_node = lb_type->pb_graph_head; + VTR_ASSERT(nullptr != top_pb_graph_node); + size_t child_inst_subtile_index = sub_tile.capacity.low + child_instance; - for (int iport = 0; iport < top_pb_graph_node->num_input_ports; ++iport) { - for (int ipin = 0; ipin < top_pb_graph_node->num_input_pins[iport]; - ++ipin) { - add_grid_module_net_connect_pb_graph_pin( - module_manager, grid_module, child_module, child_instance, - vpr_device_annotation, grid_type_descriptor, - &(top_pb_graph_node->input_pins[iport][ipin]), border_side, - INPUT2INPUT_INTERC); - } + for (int iport = 0; iport < top_pb_graph_node->num_input_ports; ++iport) { + for (int ipin = 0; ipin < top_pb_graph_node->num_input_pins[iport]; + ++ipin) { + add_grid_module_net_connect_pb_graph_pin( + module_manager, grid_module, child_module, child_instance, + child_inst_subtile_index, vpr_device_annotation, grid_type_descriptor, + &(top_pb_graph_node->input_pins[iport][ipin]), border_side, + INPUT2INPUT_INTERC); } + } - for (int iport = 0; iport < top_pb_graph_node->num_output_ports; ++iport) { - for (int ipin = 0; ipin < top_pb_graph_node->num_output_pins[iport]; - ++ipin) { - add_grid_module_net_connect_duplicated_pb_graph_pin( - module_manager, grid_module, child_module, child_instance, - vpr_device_annotation, grid_type_descriptor, - &(top_pb_graph_node->output_pins[iport][ipin]), border_side, - OUTPUT2OUTPUT_INTERC); - } + for (int iport = 0; iport < top_pb_graph_node->num_output_ports; ++iport) { + for (int ipin = 0; ipin < top_pb_graph_node->num_output_pins[iport]; + ++ipin) { + add_grid_module_net_connect_duplicated_pb_graph_pin( + module_manager, grid_module, child_module, child_instance, + child_inst_subtile_index, vpr_device_annotation, grid_type_descriptor, + &(top_pb_graph_node->output_pins[iport][ipin]), border_side, + OUTPUT2OUTPUT_INTERC); } + } - for (int iport = 0; iport < top_pb_graph_node->num_clock_ports; ++iport) { - for (int ipin = 0; ipin < top_pb_graph_node->num_clock_pins[iport]; - ++ipin) { - add_grid_module_net_connect_pb_graph_pin( - module_manager, grid_module, child_module, child_instance, - vpr_device_annotation, grid_type_descriptor, - &(top_pb_graph_node->clock_pins[iport][ipin]), border_side, - INPUT2INPUT_INTERC); - } + for (int iport = 0; iport < top_pb_graph_node->num_clock_ports; ++iport) { + for (int ipin = 0; ipin < top_pb_graph_node->num_clock_pins[iport]; + ++ipin) { + add_grid_module_net_connect_pb_graph_pin( + module_manager, grid_module, child_module, child_instance, + child_inst_subtile_index, vpr_device_annotation, grid_type_descriptor, + &(top_pb_graph_node->clock_pins[iport][ipin]), border_side, + INPUT2INPUT_INTERC); } } } diff --git a/openfpga/src/fabric/build_grid_module_duplicated_pins.h b/openfpga/src/fabric/build_grid_module_duplicated_pins.h index 82067f2d0..66e1a5634 100644 --- a/openfpga/src/fabric/build_grid_module_duplicated_pins.h +++ b/openfpga/src/fabric/build_grid_module_duplicated_pins.h @@ -24,7 +24,7 @@ void add_grid_module_duplicated_pb_type_ports( void add_grid_module_nets_connect_duplicated_pb_type_ports( ModuleManager& module_manager, const ModuleId& grid_module, const ModuleId& child_module, const size_t& child_instance, - const VprDeviceAnnotation& vpr_device_annotation, + const t_sub_tile& sub_tile, const VprDeviceAnnotation& vpr_device_annotation, t_physical_tile_type_ptr grid_type_descriptor, const e_side& border_side); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_grid_module_utils.cpp b/openfpga/src/fabric/build_grid_module_utils.cpp index 5a5f2c76e..c79262805 100644 --- a/openfpga/src/fabric/build_grid_module_utils.cpp +++ b/openfpga/src/fabric/build_grid_module_utils.cpp @@ -43,6 +43,7 @@ std::vector find_grid_module_pin_sides( void add_grid_module_net_connect_pb_graph_pin( ModuleManager& module_manager, const ModuleId& grid_module, const ModuleId& child_module, const size_t& child_instance, + const size_t& child_inst_subtile_index, const VprDeviceAnnotation& vpr_device_annotation, t_physical_tile_type_ptr grid_type_descriptor, t_pb_graph_pin* pb_graph_pin, const e_side& border_side, const e_pin2pin_interc_type& pin2pin_interc_type) { @@ -69,7 +70,7 @@ void add_grid_module_net_connect_pb_graph_pin( size_t grid_pin_index = pb_graph_pin->pin_count_in_cluster + vpr_device_annotation.physical_tile_z_to_start_pin_index( - grid_type_descriptor, child_instance); + grid_type_descriptor, child_inst_subtile_index); int pin_height = grid_type_descriptor->pin_height_offset[grid_pin_index]; int pin_width = grid_type_descriptor->pin_width_offset[grid_pin_index]; for (const e_side& side : grid_pin_sides) { diff --git a/openfpga/src/fabric/build_grid_module_utils.h b/openfpga/src/fabric/build_grid_module_utils.h index d93fc43ee..c1f0549dd 100644 --- a/openfpga/src/fabric/build_grid_module_utils.h +++ b/openfpga/src/fabric/build_grid_module_utils.h @@ -23,6 +23,7 @@ std::vector find_grid_module_pin_sides( void add_grid_module_net_connect_pb_graph_pin( ModuleManager& module_manager, const ModuleId& grid_module, const ModuleId& child_module, const size_t& child_instance, + const size_t& child_inst_subtile_index, const VprDeviceAnnotation& vpr_device_annotation, t_physical_tile_type_ptr grid_type_descriptor, t_pb_graph_pin* pb_graph_pin, const e_side& border_side, diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 10fbd6704..978501480 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -108,50 +108,49 @@ static void add_grid_module_pb_type_ports( static void add_grid_module_nets_connect_pb_type_ports( ModuleManager& module_manager, const ModuleId& grid_module, const ModuleId& child_module, const size_t& child_instance, - const VprDeviceAnnotation& vpr_device_annotation, + const t_sub_tile& sub_tile, const VprDeviceAnnotation& vpr_device_annotation, t_physical_tile_type_ptr grid_type_descriptor, const e_side& border_side) { /* Ensure that we have a valid grid_type_descriptor */ VTR_ASSERT(nullptr != grid_type_descriptor); /* FIXME: Currently support only 1 equivalent site! Should clarify this * limitation in documentation! */ - for (const t_sub_tile& sub_tile : grid_type_descriptor->sub_tiles) { - VTR_ASSERT(sub_tile.equivalent_sites.size() == 1); - t_logical_block_type_ptr lb_type = sub_tile.equivalent_sites[0]; - t_pb_graph_node* top_pb_graph_node = lb_type->pb_graph_head; - VTR_ASSERT(nullptr != top_pb_graph_node); + VTR_ASSERT(sub_tile.equivalent_sites.size() == 1); + t_logical_block_type_ptr lb_type = sub_tile.equivalent_sites[0]; + t_pb_graph_node* top_pb_graph_node = lb_type->pb_graph_head; + VTR_ASSERT(nullptr != top_pb_graph_node); + size_t child_inst_subtile_index = sub_tile.capacity.low + child_instance; - for (int iport = 0; iport < top_pb_graph_node->num_input_ports; ++iport) { - for (int ipin = 0; ipin < top_pb_graph_node->num_input_pins[iport]; - ++ipin) { - add_grid_module_net_connect_pb_graph_pin( - module_manager, grid_module, child_module, child_instance, - vpr_device_annotation, grid_type_descriptor, - &(top_pb_graph_node->input_pins[iport][ipin]), border_side, - INPUT2INPUT_INTERC); - } + for (int iport = 0; iport < top_pb_graph_node->num_input_ports; ++iport) { + for (int ipin = 0; ipin < top_pb_graph_node->num_input_pins[iport]; + ++ipin) { + add_grid_module_net_connect_pb_graph_pin( + module_manager, grid_module, child_module, child_instance, + child_inst_subtile_index, vpr_device_annotation, grid_type_descriptor, + &(top_pb_graph_node->input_pins[iport][ipin]), border_side, + INPUT2INPUT_INTERC); } + } - for (int iport = 0; iport < top_pb_graph_node->num_output_ports; ++iport) { - for (int ipin = 0; ipin < top_pb_graph_node->num_output_pins[iport]; - ++ipin) { - add_grid_module_net_connect_pb_graph_pin( - module_manager, grid_module, child_module, child_instance, - vpr_device_annotation, grid_type_descriptor, - &(top_pb_graph_node->output_pins[iport][ipin]), border_side, - OUTPUT2OUTPUT_INTERC); - } + for (int iport = 0; iport < top_pb_graph_node->num_output_ports; ++iport) { + for (int ipin = 0; ipin < top_pb_graph_node->num_output_pins[iport]; + ++ipin) { + add_grid_module_net_connect_pb_graph_pin( + module_manager, grid_module, child_module, child_instance, + child_inst_subtile_index, vpr_device_annotation, grid_type_descriptor, + &(top_pb_graph_node->output_pins[iport][ipin]), border_side, + OUTPUT2OUTPUT_INTERC); } + } - for (int iport = 0; iport < top_pb_graph_node->num_clock_ports; ++iport) { - for (int ipin = 0; ipin < top_pb_graph_node->num_clock_pins[iport]; - ++ipin) { - add_grid_module_net_connect_pb_graph_pin( - module_manager, grid_module, child_module, child_instance, - vpr_device_annotation, grid_type_descriptor, - &(top_pb_graph_node->clock_pins[iport][ipin]), border_side, - INPUT2INPUT_INTERC); - } + for (int iport = 0; iport < top_pb_graph_node->num_clock_ports; ++iport) { + for (int ipin = 0; ipin < top_pb_graph_node->num_clock_pins[iport]; + ++ipin) { + add_grid_module_net_connect_pb_graph_pin( + module_manager, grid_module, child_module, child_instance, + child_inst_subtile_index, vpr_device_annotation, grid_type_descriptor, + &(top_pb_graph_node->clock_pins[iport][ipin]), border_side, + INPUT2INPUT_INTERC); } } } @@ -1154,7 +1153,7 @@ static void build_physical_tile_module( for (const size_t& child_instance : module_manager.child_module_instances(grid_module, pb_module)) { add_grid_module_nets_connect_pb_type_ports( - module_manager, grid_module, pb_module, child_instance, + module_manager, grid_module, pb_module, child_instance, sub_tile, vpr_device_annotation, phy_block_type, border_side); } } @@ -1180,7 +1179,7 @@ static void build_physical_tile_module( for (const size_t& child_instance : module_manager.child_module_instances(grid_module, pb_module)) { add_grid_module_nets_connect_duplicated_pb_type_ports( - module_manager, grid_module, pb_module, child_instance, + module_manager, grid_module, pb_module, child_instance, sub_tile, vpr_device_annotation, phy_block_type, border_side); } } From cb0e6b9e173e8c081446c28d39fff04f22ea9d63 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 3 May 2023 21:46:35 +0800 Subject: [PATCH 007/391] [core] fixed a critical bug --- openfpga/src/fabric/build_grid_modules.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 978501480..7075bad63 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -1093,7 +1093,7 @@ static void build_physical_tile_module( * it as a mode under a */ for (const t_sub_tile& sub_tile : phy_block_type->sub_tiles) { - for (int iz = sub_tile.capacity.low; iz < sub_tile.capacity.high; ++iz) { + for (int iz = sub_tile.capacity.low; iz < sub_tile.capacity.high + 1; ++iz) { VTR_ASSERT(1 == sub_tile.equivalent_sites.size()); t_logical_block_type_ptr lb_type = sub_tile.equivalent_sites[0]; /* Bypass empty pb_graph */ From 8d02a6e600386830143caf1d34d9e60ef2c74de8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 3 May 2023 21:47:21 +0800 Subject: [PATCH 008/391] [test] now testcases are using proper arch --- .../io_subtile/config/task.conf | 37 +++++++++++++++++++ .../tileable_io/config/task.conf | 4 +- 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 openfpga_flow/tasks/basic_tests/tile_organization/io_subtile/config/task.conf diff --git a/openfpga_flow/tasks/basic_tests/tile_organization/io_subtile/config/task.conf b/openfpga_flow/tasks/basic_tests/tile_organization/io_subtile/config/task.conf new file mode 100644 index 000000000..feba1d962 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/tile_organization/io_subtile/config/task.conf @@ -0,0 +1,37 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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/fix_device_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout=2x2 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_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 +bench0_chan_width = 300 + +[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/tile_organization/tileable_io/config/task.conf b/openfpga_flow/tasks/basic_tests/tile_organization/tileable_io/config/task.conf index 4e443688e..291d214b4 100644 --- a/openfpga_flow/tasks/basic_tests/tile_organization/tileable_io/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/tile_organization/tileable_io/config/task.conf @@ -17,12 +17,12 @@ fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga -openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_cc_openfpga.xml +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_frame_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml openfpga_vpr_device_layout=2x2 [ARCHITECTURES] -arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileableIO_40nm.xml [BENCHMARKS] bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v From f89b7a82cf5a281d71a12997f4a84dc51651f078 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 3 May 2023 22:23:20 +0800 Subject: [PATCH 009/391] [arch] fixed a bug where the array size mismatch the layout name --- openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml index 4dbf87b12..857064fcd 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml @@ -82,7 +82,7 @@ - + From dab89322b339a2d5b141a68c426b1501bd51520a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 4 May 2023 09:51:05 +0800 Subject: [PATCH 010/391] [core] fixed the bug in I/O location map build-up when supporting subtiles --- .../src/fabric/build_fabric_io_location_map.cpp | 17 ++++++++++++++--- openfpga/src/fabric/build_grid_modules.cpp | 3 ++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/openfpga/src/fabric/build_fabric_io_location_map.cpp b/openfpga/src/fabric/build_fabric_io_location_map.cpp index 710ce4e5d..4b58f4c04 100644 --- a/openfpga/src/fabric/build_fabric_io_location_map.cpp +++ b/openfpga/src/fabric/build_fabric_io_location_map.cpp @@ -80,21 +80,32 @@ IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, module_manager.io_children(child).size()); for (size_t isubchild = 0; isubchild < module_manager.io_children(child).size(); ++isubchild) { + /* Note that we should use the subchild module when checking the GPIO + * ports. The child module is actually the grid-level I/O module, while + * the subchild module is the subtile inside grid-level I/O modules. Note + * that grid-level I/O module contains all the GPIO ports while the + * subtile may have part of it. For example, a grid I/O module may have 24 + * GPINs and 12 GPOUTs, while the first subtile only have 4 GPINs, and the + * second subtile only have 3 GPOUTs. Therefore, to accurately build the + * I/O location map downto subtile level, we need to check the subchild + * module here. + */ + ModuleId subchild = module_manager.io_children(child)[isubchild]; vtr::Point subchild_coord = module_manager.io_child_coordinates(child)[isubchild]; for (const ModuleManager::e_module_port_type& module_io_port_type : MODULE_IO_PORT_TYPES) { for (const ModulePortId& gpio_port_id : - module_manager.module_port_ids_by_type(child, + module_manager.module_port_ids_by_type(subchild, module_io_port_type)) { /* Only care mappable I/O */ if (false == - module_manager.port_is_mappable_io(child, gpio_port_id)) { + module_manager.port_is_mappable_io(subchild, gpio_port_id)) { continue; } const BasicPort& gpio_port = - module_manager.module_port(child, gpio_port_id); + module_manager.module_port(subchild, gpio_port_id); auto curr_io_index = io_counter.find(gpio_port.get_name()); /* Index always start from zero */ diff --git a/openfpga/src/fabric/build_grid_modules.cpp b/openfpga/src/fabric/build_grid_modules.cpp index 7075bad63..f724abd66 100644 --- a/openfpga/src/fabric/build_grid_modules.cpp +++ b/openfpga/src/fabric/build_grid_modules.cpp @@ -1093,7 +1093,8 @@ static void build_physical_tile_module( * it as a mode under a */ for (const t_sub_tile& sub_tile : phy_block_type->sub_tiles) { - for (int iz = sub_tile.capacity.low; iz < sub_tile.capacity.high + 1; ++iz) { + for (int iz = sub_tile.capacity.low; iz < sub_tile.capacity.high + 1; + ++iz) { VTR_ASSERT(1 == sub_tile.equivalent_sites.size()); t_logical_block_type_ptr lb_type = sub_tile.equivalent_sites[0]; /* Bypass empty pb_graph */ From 0e10727a5c51951ffa43de4d7c20a17b4712c51e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 4 May 2023 10:03:10 +0800 Subject: [PATCH 011/391] [doc] hotfix on sphinxcontrib-youtube package which causes readthedocs to fail --- docs/requirements.txt | 2 +- docs/source/conf.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index cd8e75de7..52fa9ade4 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -9,7 +9,7 @@ sphinxcontrib-bibtex<2.0.0 # Package required to embed youtube video -sphinxcontrib-yt +sphinxcontrib-youtube # Package required to convert SVG for latex building sphinxcontrib-svg2pdfconverter diff --git a/docs/source/conf.py b/docs/source/conf.py index 821dcb4b2..e33146101 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -26,7 +26,7 @@ import sphinx_rtd_theme # For bibtex support import sphinxcontrib.bibtex # For embedded youtube -import sphinxcontrib.yt +import sphinxcontrib.youtube # For converting SVG to PNG using rsvg import sphinxcontrib.rsvgconverter @@ -57,7 +57,7 @@ extensions = [ 'sphinx.ext.graphviz', 'sphinxcontrib.bibtex', 'sphinx.ext.autosectionlabel', - 'sphinxcontrib.yt', + 'sphinxcontrib.youtube', 'sphinxcontrib.rsvgconverter', ] From c67345eed31ed028e6a5739c62f4822f97403b71 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 4 May 2023 10:14:18 +0800 Subject: [PATCH 012/391] [doc] update readthedocs settings by using the latest build options and remove legacy syntax --- .readthedocs.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 5ff1814f4..5d03a8640 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -7,7 +7,9 @@ version: 2 # Specify docker image for building the doc build: - image: latest + os: ubuntu-22.04 + tools: + python: "3.7" # Build documentation in the docs/ directory with Sphinx sphinx: @@ -19,7 +21,8 @@ formats: all # Optionally set the version of Python and requirements required to build your docs python: - version: 3.7 system_packages: true install: - requirements: docs/requirements.txt + - method: pip + path: . From 266e6d87325e4fc9a09592f4e91f1023434a69d2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 4 May 2023 10:17:48 +0800 Subject: [PATCH 013/391] [doc] try to fix the bug on bibtex package --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 52fa9ade4..359b7099b 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -6,7 +6,7 @@ #recommonmark #Handle references in bibtex format -sphinxcontrib-bibtex<2.0.0 +sphinxcontrib-bibtex # Package required to embed youtube video sphinxcontrib-youtube From fd37136322e0f5262bcd7f555f083915a0dcf502 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 4 May 2023 10:20:01 +0800 Subject: [PATCH 014/391] [doc] remove invalid syntax --- .readthedocs.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 5d03a8640..793cafce9 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -24,5 +24,3 @@ python: system_packages: true install: - requirements: docs/requirements.txt - - method: pip - path: . From d8575295e72f189ee5a14ef61db91825d36641dd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 5 May 2023 00:02:33 +0000 Subject: [PATCH 015/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 24b435dac..3ee96a6a9 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.971 +1.2.989 From e4b167824e9b3ac5dcfdcc18c4883f66a184ff26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 May 2023 06:58:48 +0000 Subject: [PATCH 016/391] Bump yosys-plugins from `6b69bc5` to `a08c529` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `6b69bc5` to `a08c529`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/6b69bc5ed8eafbaca6f143062dbe0c486a8a37b3...a08c529a86a525fc4278221a5599a2e783c8105e) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index 6b69bc5ed..a08c529a8 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit 6b69bc5ed8eafbaca6f143062dbe0c486a8a37b3 +Subproject commit a08c529a86a525fc4278221a5599a2e783c8105e From 885cad4c79b9f351a0602f4923ca49555ae32ed8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 May 2023 06:58:51 +0000 Subject: [PATCH 017/391] Bump yosys from `51dd029` to `226a224` Bumps [yosys](https://github.com/YosysHQ/yosys) from `51dd029` to `226a224`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/51dd0290241c521f5498f71f4fd4fb0598d83a76...226a224640b3dc7e357be7a7e0c3ae6b97b1b6ba) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 51dd02902..226a22464 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 51dd0290241c521f5498f71f4fd4fb0598d83a76 +Subproject commit 226a224640b3dc7e357be7a7e0c3ae6b97b1b6ba From 249caaa7ff3c30164a586ceaf2f806cee6da1a26 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 10 May 2023 13:24:01 +0800 Subject: [PATCH 018/391] [script] serialize make all depedent targets to avoid ``make all`` bugs --- .github/workflows/build.yml | 35 +++++------------------------------ Makefile | 2 +- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4017fcaf5..e0cd7bc71 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -127,15 +127,10 @@ jobs: - uses: hendrikmuhs/ccache-action@v1 - - name: checkout submodules - shell: bash - run: | - make checkout - - name: Build shell: bash run: | - make compile BUILD_TYPE=$BUILD_TYPE + make all BUILD_TYPE=$BUILD_TYPE # Check the cache size and see if it is over the limit - name: Check ccache size @@ -216,15 +211,10 @@ jobs: - uses: hendrikmuhs/ccache-action@v1 - - name: checkout submodules - shell: bash - run: | - make checkout - - name: Build shell: bash run: | - make compile BUILD_TYPE=$BUILD_TYPE CMAKE_FLAGS="${{ matrix.config.cmake_flags }}" + make all BUILD_TYPE=$BUILD_TYPE CMAKE_FLAGS="${{ matrix.config.cmake_flags }}" ubuntu_support: needs: change_detect @@ -262,15 +252,10 @@ jobs: - uses: hendrikmuhs/ccache-action@v1 - - name: checkout submodules - shell: bash - run: | - make checkout - - name: Build shell: bash run: | - make compile BUILD_TYPE=$BUILD_TYPE + make all BUILD_TYPE=$BUILD_TYPE debug_build: needs: change_detect @@ -315,15 +300,10 @@ jobs: - uses: hendrikmuhs/ccache-action@v1 - - name: checkout submodules - shell: bash - run: | - make checkout - - name: Build shell: bash run: | - make compile BUILD_TYPE=${{ matrix.config.build_type }} -j ${{ matrix.config.cores }} + make all BUILD_TYPE=${{ matrix.config.build_type }} -j ${{ matrix.config.cores }} - name: Quick Test shell: bash @@ -371,15 +351,10 @@ jobs: - uses: hendrikmuhs/ccache-action@v1 - - name: checkout submodules - shell: bash - run: | - make checkout - - name: Build shell: bash run: | - make compile BUILD_TYPE=${{ matrix.config.build_type }} -j ${{ matrix.config.cores }} CMAKE_FLAGS="-DOPENFPGA_ENABLE_STRICT_COMPILE=ON" + make all BUILD_TYPE=${{ matrix.config.build_type }} -j ${{ matrix.config.cores }} CMAKE_FLAGS="-DOPENFPGA_ENABLE_STRICT_COMPILE=ON" - name: Quick Test shell: bash diff --git a/Makefile b/Makefile index db960eb5b..d06031cf9 100644 --- a/Makefile +++ b/Makefile @@ -79,7 +79,7 @@ list_cmake_targets: prebuild # Show the targets available to be built, which can be specified through ``CMAKE_GOALS`` when compile cd ${BUILD_DIR} && make help && cd - -all: checkout compile +all: checkout | compile # A shortcut command to run checkout and compile in serial format-cpp: From 196ba1fe7f93a4080f035e1862df6b9f65355739 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 10 May 2023 13:48:43 +0800 Subject: [PATCH 019/391] [script] debugging --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index d06031cf9..e9b5d808f 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,7 @@ prebuild: echo "cd ${BUILD_DIR} && ${CMAKE_COMMAND} ${CMAKE_FLAGS} ${SOURCE_DIR}" && \ cd ${BUILD_DIR} && ${CMAKE_COMMAND} ${CMAKE_FLAGS} ${SOURCE_DIR} -compile: prebuild +compile: | prebuild # Compile the code base. By default, all the targets will be compiled # Following options are available # .. option:: CMAKE_GOALS @@ -75,11 +75,11 @@ compile: prebuild echo "Building target(s): ${CMAKE_GOALS}" @+${MAKE} -C ${BUILD_DIR} ${CMAKE_GOALS} -list_cmake_targets: prebuild +list_cmake_targets: | prebuild # Show the targets available to be built, which can be specified through ``CMAKE_GOALS`` when compile cd ${BUILD_DIR} && make help && cd - -all: checkout | compile +all: | checkout compile # A shortcut command to run checkout and compile in serial format-cpp: From a570dfc454d21b523298726eeff1a9eb7ee4e16f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 May 2023 06:58:57 +0000 Subject: [PATCH 020/391] Bump yosys from `226a224` to `d82bae3` Bumps [yosys](https://github.com/YosysHQ/yosys) from `226a224` to `d82bae3`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/226a224640b3dc7e357be7a7e0c3ae6b97b1b6ba...d82bae32bee63d4a521e5cb081359aa5a35213f1) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 226a22464..d82bae32b 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 226a224640b3dc7e357be7a7e0c3ae6b97b1b6ba +Subproject commit d82bae32bee63d4a521e5cb081359aa5a35213f1 From 83218044f5a61081fd80e8242969efae63c75e99 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 10 May 2023 16:05:24 +0800 Subject: [PATCH 021/391] [script] debugging --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e9b5d808f..abb6e0760 100644 --- a/Makefile +++ b/Makefile @@ -79,8 +79,9 @@ list_cmake_targets: | prebuild # Show the targets available to be built, which can be specified through ``CMAKE_GOALS`` when compile cd ${BUILD_DIR} && make help && cd - -all: | checkout compile +all: checkout # A shortcut command to run checkout and compile in serial + @+${MAKE} compile format-cpp: # Format all the C/C++ files under this project, excluding submodules From b12961d5bf92046ebd814fade94c09e4797fbecd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 May 2023 00:02:27 +0000 Subject: [PATCH 022/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 3ee96a6a9..ba7ddebbf 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.989 +1.2.999 From 4ef7e2da52525075481e4ca9c886fce5b7e78462 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 May 2023 06:59:22 +0000 Subject: [PATCH 023/391] Bump yosys-plugins from `a08c529` to `730146f` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `a08c529` to `730146f`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/a08c529a86a525fc4278221a5599a2e783c8105e...730146f49eeae9021d31fae0f39b1e3568879b44) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index a08c529a8..730146f49 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit a08c529a86a525fc4278221a5599a2e783c8105e +Subproject commit 730146f49eeae9021d31fae0f39b1e3568879b44 From b1bc2067781a70ebc547332bff07be162a6198b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 May 2023 06:59:28 +0000 Subject: [PATCH 024/391] Bump yosys from `d82bae3` to `147cceb` Bumps [yosys](https://github.com/YosysHQ/yosys) from `d82bae3` to `147cceb`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/d82bae32bee63d4a521e5cb081359aa5a35213f1...147cceb516552f2f9f989508bcdd57ae04621254) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index d82bae32b..147cceb51 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit d82bae32bee63d4a521e5cb081359aa5a35213f1 +Subproject commit 147cceb516552f2f9f989508bcdd57ae04621254 From 0c9dbcb4caf2f6196fa344bb713e8ede6554cac8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 04:39:43 +0000 Subject: [PATCH 025/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index ba7ddebbf..08508c1ec 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.999 +1.2.1007 From e86602f2c640699d9185e761d03f34902ade66a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 07:01:20 +0000 Subject: [PATCH 026/391] Bump yosys-plugins from `730146f` to `38a8a5d` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `730146f` to `38a8a5d`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/730146f49eeae9021d31fae0f39b1e3568879b44...38a8a5dd5a7bb880e1dbcbcb14e97d6e9917da3d) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index 730146f49..38a8a5dd5 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit 730146f49eeae9021d31fae0f39b1e3568879b44 +Subproject commit 38a8a5dd5a7bb880e1dbcbcb14e97d6e9917da3d From 36dae833b287bd309f4ca171100658af94da437a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 07:01:25 +0000 Subject: [PATCH 027/391] Bump yosys from `147cceb` to `cdeef54` Bumps [yosys](https://github.com/YosysHQ/yosys) from `147cceb` to `cdeef54`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/147cceb516552f2f9f989508bcdd57ae04621254...cdeef5481caf87ae4fe6b5f0effbf0a251a6bbda) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 147cceb51..cdeef5481 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 147cceb516552f2f9f989508bcdd57ae04621254 +Subproject commit cdeef5481caf87ae4fe6b5f0effbf0a251a6bbda From 4cb39f8bf2bbf550cee2bd4e12ff88a7fa1f33eb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 17:32:29 +0000 Subject: [PATCH 028/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 08508c1ec..460fdae92 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1007 +1.2.1013 From a6977d9e5b48c488297509cc10652f33769673dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 06:59:26 +0000 Subject: [PATCH 029/391] Bump yosys from `cdeef54` to `4f3d1be` Bumps [yosys](https://github.com/YosysHQ/yosys) from `cdeef54` to `4f3d1be`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/cdeef5481caf87ae4fe6b5f0effbf0a251a6bbda...4f3d1be96a54cc37a1cf5847299044717daf93fc) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index cdeef5481..4f3d1be96 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit cdeef5481caf87ae4fe6b5f0effbf0a251a6bbda +Subproject commit 4f3d1be96a54cc37a1cf5847299044717daf93fc From 5027af61280dbdd6b3960a4e1e2efc0df004eceb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 00:02:20 +0000 Subject: [PATCH 030/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 460fdae92..da6311061 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1013 +1.2.1017 From 1bcf015b1aef2beff6414a1ca78c7074f9e444d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 07:00:09 +0000 Subject: [PATCH 031/391] Bump yosys from `4f3d1be` to `57c9eb7` Bumps [yosys](https://github.com/YosysHQ/yosys) from `4f3d1be` to `57c9eb7`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/4f3d1be96a54cc37a1cf5847299044717daf93fc...57c9eb70feb54cff112d095d2153b0d032bdbf18) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 4f3d1be96..57c9eb70f 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 4f3d1be96a54cc37a1cf5847299044717daf93fc +Subproject commit 57c9eb70feb54cff112d095d2153b0d032bdbf18 From e20eb84d8363bb99c5462adb74433288cf2e1a7a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 25 May 2023 00:02:38 +0000 Subject: [PATCH 032/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index da6311061..c26189bf0 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1017 +1.2.1021 From 8d7429fc2b69975281cc0ebbc2b32918b2af09be Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 25 May 2023 12:58:12 -0700 Subject: [PATCH 033/391] [core] adding the new command 'write_mock_fpga_wrapper' --- .../base/openfpga_verilog_command_template.h | 76 +++++++++++++++++++ openfpga/src/base/openfpga_verilog_template.h | 55 ++++++++++++++ 2 files changed, 131 insertions(+) diff --git a/openfpga/src/base/openfpga_verilog_command_template.h b/openfpga/src/base/openfpga_verilog_command_template.h index 266afc6d8..e61938008 100644 --- a/openfpga/src/base/openfpga_verilog_command_template.h +++ b/openfpga/src/base/openfpga_verilog_command_template.h @@ -251,6 +251,71 @@ ShellCommandId add_write_preconfigured_fabric_wrapper_command_template( return shell_cmd_id; } +/******************************************************************** + * - add a command to shell environment: write mock fpga wrapper + * - add associated options + * - add command dependency + *******************************************************************/ +template +ShellCommandId add_write_mock_fpga_wrapper_command_template( + openfpga::Shell& shell, const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds, const bool& hidden) { + Command shell_cmd("write_mock_fpga_wrapper"); + + /* add an option '--file' in short '-f'*/ + CommandOptionId output_opt = shell_cmd.add_option( + "file", true, "specify the output directory for hdl netlists"); + shell_cmd.set_option_short_name(output_opt, "f"); + shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING); + + /* add an option '--pin_constraints_file in short '-pcf' */ + CommandOptionId pcf_opt = + shell_cmd.add_option("pin_constraints_file", false, + "specify the file path to the pin constraints"); + shell_cmd.set_option_short_name(pcf_opt, "pcf"); + shell_cmd.set_option_require_value(pcf_opt, openfpga::OPT_STRING); + + /* add an option '--bus_group_file in short '-bgf' */ + CommandOptionId bgf_opt = shell_cmd.add_option( + "bus_group_file", false, "specify the file path to the group pins to bus"); + shell_cmd.set_option_short_name(bgf_opt, "bgf"); + shell_cmd.set_option_require_value(bgf_opt, openfpga::OPT_STRING); + + /* add an option '--explicit_port_mapping' */ + shell_cmd.add_option("explicit_port_mapping", false, + "use explicit port mapping in verilog netlists"); + + /* Add an option '--default_net_type' */ + CommandOptionId default_net_type_opt = shell_cmd.add_option( + "default_net_type", false, + "Set the default net type for Verilog netlists. Default value is 'none'"); + shell_cmd.set_option_require_value(default_net_type_opt, + openfpga::OPT_STRING); + + /* add an option '--explicit_port_mapping' */ + shell_cmd.add_option("explicit_port_mapping", false, + "use explicit port mapping in verilog netlists"); + + /* Add an option '--no_time_stamp' */ + shell_cmd.add_option("no_time_stamp", false, + "Do not print a time stamp in the output files"); + + /* add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "enable verbose output"); + + /* add command to the shell */ + ShellCommandId shell_cmd_id = shell.add_command( + shell_cmd, "generate a wrapper of a mock fpga fabric mapped with applications", hidden); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function( + shell_cmd_id, write_mock_fpga_wrapper_template); + + /* add command dependency to the shell */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + /******************************************************************** * - Add a command to Shell environment: write preconfigured testbench * - Add associated options @@ -435,6 +500,17 @@ void add_verilog_command_templates(openfpga::Shell& shell, shell, openfpga_verilog_cmd_class, preconfig_wrapper_dependent_cmds, hidden); + /******************************** + * Command 'write_mock_fpga_wrapper' + */ + /* The command 'write_mock_fpga_wrapper' should NOT be executed + * before 'build_fabric' */ + std::vector write_mock_fpga_wrapper_dependent_cmds; + write_mock_fpga_wrapper_dependent_cmds.push_back(build_fabric_cmd_id); + add_write_mock_fpga_wrapper_command_template( + shell, openfpga_verilog_cmd_class, write_mock_fpga_wrapper_dependent_cmds, + hidden); + /******************************** * Command 'write_preconfigured_testbench' */ diff --git a/openfpga/src/base/openfpga_verilog_template.h b/openfpga/src/base/openfpga_verilog_template.h index 3f042ddcc..107952c5d 100644 --- a/openfpga/src/base/openfpga_verilog_template.h +++ b/openfpga/src/base/openfpga_verilog_template.h @@ -207,6 +207,61 @@ int write_preconfigured_fabric_wrapper_template( openfpga_ctx.arch().config_protocol, options); } +/******************************************************************** + * A wrapper function to call the mock fpga wrapper generator of + *FPGA-Verilog + *******************************************************************/ +template +int write_mock_fpga_wrapper_template( + const T& openfpga_ctx, const Command& cmd, + const CommandContext& cmd_context) { + CommandOptionId opt_output_dir = cmd.option("file"); + CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); + CommandOptionId opt_bgf = cmd.option("bus_group_file"); + CommandOptionId opt_explicit_port_mapping = + cmd.option("explicit_port_mapping"); + CommandOptionId opt_default_net_type = cmd.option("default_net_type"); + CommandOptionId opt_no_time_stamp = cmd.option("no_time_stamp"); + CommandOptionId opt_verbose = cmd.option("verbose"); + + /* This is an intermediate data structure which is designed to modularize the + * FPGA-Verilog Keep it independent from any other outside data structures + */ + VerilogTestbenchOption options; + options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); + options.set_explicit_port_mapping( + cmd_context.option_enable(cmd, opt_explicit_port_mapping)); + options.set_time_stamp(!cmd_context.option_enable(cmd, opt_no_time_stamp)); + options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); + + if (true == cmd_context.option_enable(cmd, opt_default_net_type)) { + options.set_default_net_type( + cmd_context.option_value(cmd, opt_default_net_type)); + } + + /* If pin constraints are enabled by command options, read the file */ + PinConstraints pin_constraints; + if (true == cmd_context.option_enable(cmd, opt_pcf)) { + pin_constraints = + read_xml_pin_constraints(cmd_context.option_value(cmd, opt_pcf).c_str()); + } + + /* If bug group file are enabled by command options, read the file */ + BusGroup bus_group; + if (true == cmd_context.option_enable(cmd, opt_bgf)) { + bus_group = + read_xml_bus_group(cmd_context.option_value(cmd, opt_bgf).c_str()); + } + + return fpga_verilog_preconfigured_fabric_wrapper( + openfpga_ctx.module_graph(), openfpga_ctx.bitstream_manager(), + g_vpr_ctx.atom(), g_vpr_ctx.placement(), pin_constraints, bus_group, + openfpga_ctx.io_location_map(), openfpga_ctx.fabric_global_port_info(), + openfpga_ctx.vpr_netlist_annotation(), openfpga_ctx.arch().circuit_lib, + openfpga_ctx.arch().config_protocol, options); +} + + /******************************************************************** * A wrapper function to call the preconfigured testbench generator of *FPGA-Verilog From d7837b8eeb823cd5d7d1d6647fefe4fcd98e85c7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 25 May 2023 15:01:10 -0700 Subject: [PATCH 034/391] [doc] add documentation about mock fpga wrapper --- .../figures/mock_fpga_wrapper.png | Bin 0 -> 76095 bytes docs/source/manual/fpga_verilog/index.rst | 2 + .../manual/fpga_verilog/mock_fpga_wrapper.rst | 25 +++++++++++++ docs/source/manual/fpga_verilog/testbench.rst | 10 ++--- .../fpga_verilog_commands.rst | 35 ++++++++++++++++++ 5 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 docs/source/manual/fpga_verilog/figures/mock_fpga_wrapper.png create mode 100644 docs/source/manual/fpga_verilog/mock_fpga_wrapper.rst diff --git a/docs/source/manual/fpga_verilog/figures/mock_fpga_wrapper.png b/docs/source/manual/fpga_verilog/figures/mock_fpga_wrapper.png new file mode 100644 index 0000000000000000000000000000000000000000..68e8745a180e1b04bd4e946f71d9d623ce361374 GIT binary patch literal 76095 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 ULDk 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 xT}jN;n||s@CkF6880Ej{&%iMz6~ or -f + + The output directory for the netlists. We suggest the use of same output directory as fabric Verilog netlists. For example, ``--file /temp/testbench`` + + .. option:: --pin_constraints_file or -pcf + + Specify the *Pin Constraints File* (PCF) if you want to custom stimulus in testbenches. For example, ``-pin_constraints_file pin_constraints.xml`` + Strongly recommend for multi-clock simulations. See detailed file format about :ref:`file_format_pin_constraints_file`. + + .. option:: --bus_group_file or -bgf + + Specify the *Bus Group File* (BGF) if you want to group pins to buses. For example, ``-bgf bus_group.xml`` + Strongly recommend when input HDL contains bus ports. See detailed file format about :ref:`file_format_bus_group_file`. + + .. option:: --explicit_port_mapping + + Use explicit port mapping when writing the Verilog netlists + + .. option:: --default_net_type + + Specify the default net type for the Verilog netlists. Currently, supported types are ``none`` and ``wire``. Default value: ``none``. + + .. option:: --no_time_stamp + + Do not print time stamp in Verilog netlists + + .. option:: --verbose + + Show verbose log + write_preconfigured_testbench ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From ab263aa5b17e30a0e8e7a7fd2cffa6f9dfa45ff5 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 25 May 2023 15:02:03 -0700 Subject: [PATCH 035/391] [core] code format --- openfpga/src/base/openfpga_verilog_command_template.h | 8 +++++--- openfpga/src/base/openfpga_verilog_template.h | 6 ++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openfpga/src/base/openfpga_verilog_command_template.h b/openfpga/src/base/openfpga_verilog_command_template.h index e61938008..c1b7e6c64 100644 --- a/openfpga/src/base/openfpga_verilog_command_template.h +++ b/openfpga/src/base/openfpga_verilog_command_template.h @@ -305,10 +305,12 @@ ShellCommandId add_write_mock_fpga_wrapper_command_template( /* add command to the shell */ ShellCommandId shell_cmd_id = shell.add_command( - shell_cmd, "generate a wrapper of a mock fpga fabric mapped with applications", hidden); + shell_cmd, + "generate a wrapper of a mock fpga fabric mapped with applications", + hidden); shell.set_command_class(shell_cmd_id, cmd_class_id); - shell.set_command_execute_function( - shell_cmd_id, write_mock_fpga_wrapper_template); + shell.set_command_execute_function(shell_cmd_id, + write_mock_fpga_wrapper_template); /* add command dependency to the shell */ shell.set_command_dependency(shell_cmd_id, dependent_cmds); diff --git a/openfpga/src/base/openfpga_verilog_template.h b/openfpga/src/base/openfpga_verilog_template.h index 107952c5d..cc88dcf3f 100644 --- a/openfpga/src/base/openfpga_verilog_template.h +++ b/openfpga/src/base/openfpga_verilog_template.h @@ -212,9 +212,8 @@ int write_preconfigured_fabric_wrapper_template( *FPGA-Verilog *******************************************************************/ template -int write_mock_fpga_wrapper_template( - const T& openfpga_ctx, const Command& cmd, - const CommandContext& cmd_context) { +int write_mock_fpga_wrapper_template(const T& openfpga_ctx, const Command& cmd, + const CommandContext& cmd_context) { CommandOptionId opt_output_dir = cmd.option("file"); CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); CommandOptionId opt_bgf = cmd.option("bus_group_file"); @@ -261,7 +260,6 @@ int write_mock_fpga_wrapper_template( openfpga_ctx.arch().config_protocol, options); } - /******************************************************************** * A wrapper function to call the preconfigured testbench generator of *FPGA-Verilog From affe5c5d1e6b9ab04aa4d78abce052ab7d5f6c77 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 25 May 2023 18:50:47 -0700 Subject: [PATCH 036/391] [core] developing mock wrapper generator --- .../verilog_mock_fpga_wrapper.cpp | 359 ++++++++++++++++++ .../fpga_verilog/verilog_mock_fpga_wrapper.h | 42 ++ 2 files changed, 401 insertions(+) create mode 100644 openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.h diff --git a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp new file mode 100644 index 000000000..c3f5d1941 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp @@ -0,0 +1,359 @@ +/******************************************************************** + * This file includes functions that are used to generate + * a Verilog module of a pre-configured FPGA fabric + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "command_exit_codes.h" +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from openfpgautil library */ +#include "bitstream_manager_utils.h" +#include "openfpga_atom_netlist_utils.h" +#include "openfpga_digest.h" +#include "openfpga_naming.h" +#include "openfpga_port.h" +#include "openfpga_reserved_words.h" +#include "verilog_constants.h" +#include "verilog_mock_fpga_wrapper.h" +#include "verilog_testbench_utils.h" +#include "verilog_writer_utils.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Local variables used only in this file + *******************************************************************/ +constexpr const char* BENCHMARK_PORT_POSTFIX = "_bench"; +constexpr const char* BENCHMARK_INSTANCE_NAME = "MAPPED_DESIGN"; + +/******************************************************************** + * This function adds stimuli to I/Os of FPGA fabric + * 1. For mapped I/Os, this function will wire them to the input ports + * of the pre-configured FPGA top module + * 2. For unmapped I/Os, this function will assign a constant value + * by default + *******************************************************************/ +static +void print_verilog_mock_fpga_wrapper_connect_ios( + std::fstream& fp, const ModuleManager& module_manager, + const ModuleId& top_module, const AtomContext& atom_ctx, + const PlacementContext& place_ctx, const IoLocationMap& io_location_map, + const VprNetlistAnnotation& netlist_annotation, const BusGroup& bus_group, + const std::string& net_name_postfix, + const std::string& io_input_port_name_postfix, + const std::string& io_output_port_name_postfix, + const std::vector& clock_port_names, + const size_t& unused_io_value) { + /* Validate the file stream */ + valid_file_stream(fp); + + /* Only mappable i/o ports can be considered */ + std::vector module_io_ports; + for (const ModuleManager::e_module_port_type& module_io_port_type : + MODULE_IO_PORT_TYPES) { + for (const ModulePortId& gpio_port_id : + module_manager.module_port_ids_by_type(top_module, + module_io_port_type)) { + /* Only care mappable I/O */ + if (false == + module_manager.port_is_mappable_io(top_module, gpio_port_id)) { + continue; + } + module_io_ports.push_back(gpio_port_id); + } + } + + /* Keep tracking which I/Os have been used */ + std::map> io_used; + for (const ModulePortId& module_io_port_id : module_io_ports) { + const BasicPort& module_io_port = + module_manager.module_port(top_module, module_io_port_id); + io_used[module_io_port_id] = + std::vector(module_io_port.get_width(), false); + } + + /* Type mapping between VPR block and Module port */ + std::map + atom_block_type_to_module_port_type; + atom_block_type_to_module_port_type[AtomBlockType::INPAD] = + ModuleManager::MODULE_GPIN_PORT; + atom_block_type_to_module_port_type[AtomBlockType::OUTPAD] = + ModuleManager::MODULE_GPOUT_PORT; + + /* See if this I/O should be wired to a benchmark input/output */ + /* Add signals from blif benchmark and short-wire them to FPGA I/O PADs + * This brings convenience to checking functionality + */ + print_verilog_comment( + fp, std::string("----- Link FPGA I/Os to Benchmark I/Os -----")); + + for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) { + /* Bypass non-I/O atom blocks ! */ + if ((AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk)) && + (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk))) { + continue; + } + + /* If there is a GPIO port, use it directly + * Otherwise, should find a GPIN for INPAD + * or should find a GPOUT for OUTPAD + */ + std::pair mapped_module_io_info = + std::make_pair(ModulePortId::INVALID(), -1); + for (const ModulePortId& module_io_port_id : module_io_ports) { + const BasicPort& module_io_port = + module_manager.module_port(top_module, module_io_port_id); + + /* Find the index of the mapped GPIO in top-level FPGA fabric */ + size_t temp_io_index = io_location_map.io_index( + place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.x, + place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.y, + place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.sub_tile, + module_io_port.get_name()); + + /* Bypass invalid index (not mapped to this GPIO port) */ + if (size_t(-1) == temp_io_index) { + continue; + } + + /* If the port is an GPIO port, just use it */ + if (ModuleManager::MODULE_GPIO_PORT == + module_manager.port_type(top_module, module_io_port_id)) { + mapped_module_io_info = + std::make_pair(module_io_port_id, temp_io_index); + break; + } + + /* If this is an INPAD, we can use an GPIN port (if available) */ + if (atom_block_type_to_module_port_type[atom_ctx.nlist.block_type( + atom_blk)] == + module_manager.port_type(top_module, module_io_port_id)) { + mapped_module_io_info = + std::make_pair(module_io_port_id, temp_io_index); + break; + } + } + + /* We must find a valid one */ + VTR_ASSERT(true == module_manager.valid_module_port_id( + top_module, mapped_module_io_info.first)); + VTR_ASSERT(size_t(-1) != mapped_module_io_info.second); + + /* Ensure that IO index is in range */ + BasicPort module_mapped_io_port = + module_manager.module_port(top_module, mapped_module_io_info.first); + size_t io_index = mapped_module_io_info.second; + + /* Set the port pin index */ + VTR_ASSERT(io_index < module_mapped_io_port.get_width()); + module_mapped_io_port.set_name(module_mapped_io_port.get_name() + + net_name_postfix); + module_mapped_io_port.set_width(io_index, io_index); + + /* The block may be renamed as it contains special characters which violate + * Verilog syntax */ + std::string block_name = atom_ctx.nlist.block_name(atom_blk); + if (true == netlist_annotation.is_block_renamed(atom_blk)) { + block_name = netlist_annotation.block_name(atom_blk); + } + /* Note that VPR added a prefix to the name of output blocks + * We can remove this when specified through input argument + */ + if (AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk)) { + block_name = remove_atom_block_name_prefix(block_name); + } + + /* Create the port for benchmark I/O, due to BLIF benchmark, each I/O always + * has a size of 1 In addition, the input and output ports may have + * different postfix in naming due to verification context! Here, we give + * full customization on naming + */ + BasicPort benchmark_io_port; + + /* If this benchmark pin belongs to any bus group, use the bus pin instead + */ + BusGroupId bus_id = bus_group.find_pin_bus(block_name); + BusPinId bus_pin_id = bus_group.find_pin(block_name); + if (bus_id) { + block_name = bus_group.bus_port(bus_id).get_name(); + VTR_ASSERT_SAFE(bus_pin_id); + benchmark_io_port.set_width(bus_group.pin_index(bus_pin_id), + bus_group.pin_index(bus_pin_id)); + } else { + benchmark_io_port.set_width(1); + } + + if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) { + /* If the port is a clock, do not add a postfix */ + if (clock_port_names.end() != std::find(clock_port_names.begin(), + clock_port_names.end(), + block_name)) { + benchmark_io_port.set_name(block_name); + } else { + benchmark_io_port.set_name( + std::string(block_name + io_input_port_name_postfix)); + } + print_verilog_comment( + fp, std::string("----- Blif Benchmark input " + block_name + + " is mapped to FPGA IOPAD " + + module_mapped_io_port.get_name() + "[" + + std::to_string(io_index) + "] -----")); + print_verilog_wire_connection(fp, benchmark_io_port, + module_mapped_io_port, + false); + } else { + VTR_ASSERT(AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk)); + benchmark_io_port.set_name( + std::string(block_name + io_output_port_name_postfix)); + print_verilog_comment( + fp, std::string("----- Blif Benchmark output " + block_name + + " is mapped to FPGA IOPAD " + + module_mapped_io_port.get_name() + "[" + + std::to_string(io_index) + "] -----")); + print_verilog_wire_connection(fp, module_mapped_io_port, benchmark_io_port, false); + } + + /* Mark this I/O has been used/wired */ + io_used[mapped_module_io_info.first][io_index] = true; + + /* Add an empty line as a splitter */ + fp << std::endl; + } + + /* Wire the unused iopads to a constant */ + print_verilog_comment( + fp, std::string("----- Wire unused FPGA I/Os to constants -----")); + for (const ModulePortId& module_io_port_id : module_io_ports) { + for (size_t io_index = 0; io_index < io_used[module_io_port_id].size(); + ++io_index) { + /* Bypass used iopads */ + if (true == io_used[module_io_port_id][io_index]) { + continue; + } + + /* Bypass unused output pads */ + if (ModuleManager::MODULE_GPOUT_PORT != + module_manager.port_type(top_module, module_io_port_id)) { + continue; + } + + /* Wire to a contant */ + BasicPort module_unused_io_port = + module_manager.module_port(top_module, module_io_port_id); + /* Set the port pin index */ + module_unused_io_port.set_name(module_unused_io_port.get_name() + + net_name_postfix); + module_unused_io_port.set_width(io_index, io_index); + + std::vector default_values(module_unused_io_port.get_width(), + unused_io_value); + print_verilog_wire_constant_values(fp, module_unused_io_port, + default_values); + } + + /* Add an empty line as a splitter */ + fp << std::endl; + } +} + +/******************************************************************** + * Top-level function to generate a Verilog module of + * a mock FPGA wrapper which contains an benchmark instance. + * + * Mock FPGA wrapper + * +-------------------------------------------- + * | + * | Benchmark instance + * | +-------------------------------+ + * | | | + * fpga_clock----->|--------->|benchmark_clock | + * | | | + * fpga_inputs---->|--------->|benchmark_inputs | + * | | | + * fpga_outputs<---|<---------|benchmark_output | + * | | | + * | +-------------------------------+ + * | + * +------------------------------------------- + * + * Note: we do NOT put this module in the module manager. + * Because, it is not a standard module, where we just wrap an instance of application HDL (supposed to be implemented on FPGA). + *******************************************************************/ +int print_verilog_mock_fpga_wrapper( + const ModuleManager &module_manager, + const BitstreamManager &bitstream_manager, + const ConfigProtocol &config_protocol, const CircuitLibrary &circuit_lib, + const FabricGlobalPortInfo &global_ports, const AtomContext &atom_ctx, + const PlacementContext &place_ctx, const PinConstraints &pin_constraints, + const BusGroup &bus_group, const IoLocationMap &io_location_map, + const VprNetlistAnnotation &netlist_annotation, + const std::string &circuit_name, const std::string &verilog_fname, + const VerilogTestbenchOption &options) { + std::string timer_message = + std::string( + "Write mock FPGA wrapper in Verilog format for design '") + + circuit_name + std::string("'"); + + int status = CMD_EXEC_SUCCESS; + + /* Start time count */ + vtr::ScopedStartFinishTimer timer(timer_message); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + check_file_stream(verilog_fname.c_str(), fp); + + /* Generate a brief description on the Verilog file*/ + std::string title = + std::string("Verilog netlist for mock FPGA fabric by design: ") + + circuit_name; + print_verilog_file_header(fp, title, options.time_stamp()); + + print_verilog_default_net_type_declaration(fp, options.default_net_type()); + + /* Find the top_module */ + ModuleId top_module = + module_manager.find_module(generate_fpga_top_module_name()); + VTR_ASSERT(true == module_manager.valid_module_id(top_module)); + + /* Print module declaration */ + print_verilog_module_declaration(fp, module_manager, top_module, + default_net_type); + + /* Find clock ports in benchmark */ + std::vector benchmark_clock_port_names = + find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation); + + /* Instanciate application HDL module */ + print_verilog_testbench_benchmark_instance( + fp, circuit_name, std::string(BENCHMARK_INSTANCE_NAME), + std::string(), std::string(), std::string(), + std::string(BENCHMARK_PORT_POSTFIX), benchmark_clock_port_names, atom_ctx, + netlist_annotation, pin_constraints, bus_group, options.explicit_port_mapping()); + + /* Connect I/Os to benchmark I/Os or constant driver */ + print_verilog_mock_fpga_wrapper_connect_ios( + fp, module_manager, top_module, atom_ctx, place_ctx, io_location_map, + netlist_annotation, bus_group, + std::string(BENCHMARK_PORT_POSTFIX), std::string(), + std::string(), std::vector(), + (size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE); + + /* Testbench ends*/ + print_verilog_module_end(fp, title); + + /* Close the file stream */ + fp.close(); + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.h b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.h new file mode 100644 index 000000000..503fd679f --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.h @@ -0,0 +1,42 @@ +#ifndef VERILOG_MOCK_FPGA_WRAPPER_H +#define VERILOG_MOCK_FPGA_WRAPPER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include + +#include "bitstream_manager.h" +#include "bus_group.h" +#include "circuit_library.h" +#include "config_protocol.h" +#include "fabric_global_port_info.h" +#include "io_location_map.h" +#include "module_manager.h" +#include "pin_constraints.h" +#include "verilog_testbench_options.h" +#include "vpr_context.h" +#include "vpr_netlist_annotation.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_verilog_mock_fpga_wrapper( + const ModuleManager& module_manager, + const BitstreamManager& bitstream_manager, + const ConfigProtocol& config_protocol, const CircuitLibrary& circuit_lib, + const FabricGlobalPortInfo& global_ports, const AtomContext& atom_ctx, + const PlacementContext& place_ctx, const PinConstraints& pin_constraints, + const BusGroup& bus_group, const IoLocationMap& io_location_map, + const VprNetlistAnnotation& netlist_annotation, + const std::string& circuit_name, const std::string& verilog_fname, + const VerilogTestbenchOption& options); + +} /* end namespace openfpga */ + +#endif From 45e25e4152dd5a2e00a6d43e1c0e0295c2d00ed9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 25 May 2023 19:50:39 -0700 Subject: [PATCH 037/391] [core] hooking up API with command --- openfpga/src/base/openfpga_verilog_template.h | 3 +- openfpga/src/fpga_verilog/verilog_api.cpp | 41 ++++++++++++++++ openfpga/src/fpga_verilog/verilog_api.h | 10 ++++ .../verilog_mock_fpga_wrapper.cpp | 49 +++++++++---------- 4 files changed, 77 insertions(+), 26 deletions(-) diff --git a/openfpga/src/base/openfpga_verilog_template.h b/openfpga/src/base/openfpga_verilog_template.h index cc88dcf3f..99d8df2ad 100644 --- a/openfpga/src/base/openfpga_verilog_template.h +++ b/openfpga/src/base/openfpga_verilog_template.h @@ -12,6 +12,7 @@ #include "read_xml_bus_group.h" #include "read_xml_pin_constraints.h" #include "verilog_api.h" +#include "verilog_mock_fpga_wrapper.h" #include "vtr_log.h" #include "vtr_time.h" @@ -252,7 +253,7 @@ int write_mock_fpga_wrapper_template(const T& openfpga_ctx, const Command& cmd, read_xml_bus_group(cmd_context.option_value(cmd, opt_bgf).c_str()); } - return fpga_verilog_preconfigured_fabric_wrapper( + return fpga_verilog_mock_fpga_wrapper( openfpga_ctx.module_graph(), openfpga_ctx.bitstream_manager(), g_vpr_ctx.atom(), g_vpr_ctx.placement(), pin_constraints, bus_group, openfpga_ctx.io_location_map(), openfpga_ctx.fabric_global_port_info(), diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 3fa929afc..aaea119cb 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -12,11 +12,13 @@ /* Headers from openfpgautil library */ #include "device_rr_gsb.h" #include "openfpga_digest.h" +#include "openfpga_naming.h" #include "openfpga_reserved_words.h" #include "verilog_auxiliary_netlists.h" #include "verilog_constants.h" #include "verilog_formal_random_top_testbench.h" #include "verilog_grid.h" +#include "verilog_mock_fpga_wrapper.h" #include "verilog_preconfig_top_module.h" #include "verilog_routing.h" #include "verilog_simulation_info_writer.h" @@ -222,6 +224,45 @@ int fpga_verilog_preconfigured_fabric_wrapper( return status; } +/******************************************************************** + * A top-level function of FPGA-Verilog which focuses on a wrapper module, + * which encapsulate the application HDL into a mock FPGA module + ********************************************************************/ +int fpga_verilog_mock_fpga_wrapper( + const ModuleManager &module_manager, + const BitstreamManager &bitstream_manager, const AtomContext &atom_ctx, + const PlacementContext &place_ctx, const PinConstraints &pin_constraints, + const BusGroup &bus_group, const IoLocationMap &io_location_map, + const FabricGlobalPortInfo &fabric_global_port_info, + const VprNetlistAnnotation &netlist_annotation, + const CircuitLibrary &circuit_lib, const ConfigProtocol &config_protocol, + const VerilogTestbenchOption &options) { + vtr::ScopedStartFinishTimer timer( + "Write a wrapper module to mock a mapped FPGA fabric\n"); + + std::string src_dir_path = format_dir_path(options.output_directory()); + + std::string netlist_name = atom_ctx.nlist.netlist_name(); + + int status = CMD_EXEC_SUCCESS; + + /* Create directories */ + create_directory(src_dir_path); + + /* Generate wrapper module for FPGA fabric (mapped by the input benchmark and + * pre-configured testbench for verification */ + std::string netlist_file_path = + src_dir_path + + generate_fpga_top_netlist_name(std::string(VERILOG_NETLIST_FILE_POSTFIX)); + status = print_verilog_mock_fpga_wrapper( + module_manager, bitstream_manager, config_protocol, circuit_lib, + fabric_global_port_info, atom_ctx, place_ctx, pin_constraints, bus_group, + io_location_map, netlist_annotation, netlist_name, netlist_file_path, + options); + + return status; +} + /******************************************************************** * A top-level function of FPGA-Verilog which focuses on fabric Verilog *generation This function will generate diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index 79ce6759d..c680a6251 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -68,6 +68,16 @@ int fpga_verilog_preconfigured_fabric_wrapper( const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, const VerilogTestbenchOption& options); +int fpga_verilog_mock_fpga_wrapper( + const ModuleManager& module_manager, + const BitstreamManager& bitstream_manager, const AtomContext& atom_ctx, + const PlacementContext& place_ctx, const PinConstraints& pin_constraints, + const BusGroup& bus_group, const IoLocationMap& io_location_map, + const FabricGlobalPortInfo& fabric_global_port_info, + const VprNetlistAnnotation& netlist_annotation, + const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, + const VerilogTestbenchOption& options); + int fpga_verilog_preconfigured_testbench( const ModuleManager& module_manager, const AtomContext& atom_ctx, const PinConstraints& pin_constraints, const BusGroup& bus_group, diff --git a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp index c3f5d1941..253321543 100644 --- a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp +++ b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp @@ -12,6 +12,7 @@ /* Headers from openfpgautil library */ #include "bitstream_manager_utils.h" +#include "module_manager_utils.h" #include "openfpga_atom_netlist_utils.h" #include "openfpga_digest.h" #include "openfpga_naming.h" @@ -38,8 +39,7 @@ constexpr const char* BENCHMARK_INSTANCE_NAME = "MAPPED_DESIGN"; * 2. For unmapped I/Os, this function will assign a constant value * by default *******************************************************************/ -static -void print_verilog_mock_fpga_wrapper_connect_ios( +static void print_verilog_mock_fpga_wrapper_connect_ios( std::fstream& fp, const ModuleManager& module_manager, const ModuleId& top_module, const AtomContext& atom_ctx, const PlacementContext& place_ctx, const IoLocationMap& io_location_map, @@ -204,8 +204,7 @@ void print_verilog_mock_fpga_wrapper_connect_ios( module_mapped_io_port.get_name() + "[" + std::to_string(io_index) + "] -----")); print_verilog_wire_connection(fp, benchmark_io_port, - module_mapped_io_port, - false); + module_mapped_io_port, false); } else { VTR_ASSERT(AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk)); benchmark_io_port.set_name( @@ -215,7 +214,8 @@ void print_verilog_mock_fpga_wrapper_connect_ios( " is mapped to FPGA IOPAD " + module_mapped_io_port.get_name() + "[" + std::to_string(io_index) + "] -----")); - print_verilog_wire_connection(fp, module_mapped_io_port, benchmark_io_port, false); + print_verilog_wire_connection(fp, module_mapped_io_port, + benchmark_io_port, false); } /* Mark this I/O has been used/wired */ @@ -282,21 +282,21 @@ void print_verilog_mock_fpga_wrapper_connect_ios( * +------------------------------------------- * * Note: we do NOT put this module in the module manager. - * Because, it is not a standard module, where we just wrap an instance of application HDL (supposed to be implemented on FPGA). + * Because, it is not a standard module, where we just wrap an instance of + *application HDL (supposed to be implemented on FPGA). *******************************************************************/ int print_verilog_mock_fpga_wrapper( - const ModuleManager &module_manager, - const BitstreamManager &bitstream_manager, - const ConfigProtocol &config_protocol, const CircuitLibrary &circuit_lib, - const FabricGlobalPortInfo &global_ports, const AtomContext &atom_ctx, - const PlacementContext &place_ctx, const PinConstraints &pin_constraints, - const BusGroup &bus_group, const IoLocationMap &io_location_map, - const VprNetlistAnnotation &netlist_annotation, - const std::string &circuit_name, const std::string &verilog_fname, - const VerilogTestbenchOption &options) { + const ModuleManager& module_manager, + const BitstreamManager& bitstream_manager, + const ConfigProtocol& config_protocol, const CircuitLibrary& circuit_lib, + const FabricGlobalPortInfo& global_ports, const AtomContext& atom_ctx, + const PlacementContext& place_ctx, const PinConstraints& pin_constraints, + const BusGroup& bus_group, const IoLocationMap& io_location_map, + const VprNetlistAnnotation& netlist_annotation, + const std::string& circuit_name, const std::string& verilog_fname, + const VerilogTestbenchOption& options) { std::string timer_message = - std::string( - "Write mock FPGA wrapper in Verilog format for design '") + + std::string("Write mock FPGA wrapper in Verilog format for design '") + circuit_name + std::string("'"); int status = CMD_EXEC_SUCCESS; @@ -326,7 +326,7 @@ int print_verilog_mock_fpga_wrapper( /* Print module declaration */ print_verilog_module_declaration(fp, module_manager, top_module, - default_net_type); + options.default_net_type()); /* Find clock ports in benchmark */ std::vector benchmark_clock_port_names = @@ -334,17 +334,16 @@ int print_verilog_mock_fpga_wrapper( /* Instanciate application HDL module */ print_verilog_testbench_benchmark_instance( - fp, circuit_name, std::string(BENCHMARK_INSTANCE_NAME), - std::string(), std::string(), std::string(), - std::string(BENCHMARK_PORT_POSTFIX), benchmark_clock_port_names, atom_ctx, - netlist_annotation, pin_constraints, bus_group, options.explicit_port_mapping()); + fp, circuit_name, std::string(BENCHMARK_INSTANCE_NAME), std::string(), + std::string(), std::string(), std::string(BENCHMARK_PORT_POSTFIX), + benchmark_clock_port_names, atom_ctx, netlist_annotation, pin_constraints, + bus_group, options.explicit_port_mapping()); /* Connect I/Os to benchmark I/Os or constant driver */ print_verilog_mock_fpga_wrapper_connect_ios( fp, module_manager, top_module, atom_ctx, place_ctx, io_location_map, - netlist_annotation, bus_group, - std::string(BENCHMARK_PORT_POSTFIX), std::string(), - std::string(), std::vector(), + netlist_annotation, bus_group, std::string(BENCHMARK_PORT_POSTFIX), + std::string(), std::string(), std::vector(), (size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE); /* Testbench ends*/ From 7da7d03db598de2d2a7112d3e3f4f6bccc450a78 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 25 May 2023 19:59:14 -0700 Subject: [PATCH 038/391] [script] add example script for mock wrapper --- .../mock_wrapper_example_script.openfpga | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga diff --git a/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga new file mode 100644 index 000000000..b84f8f0ed --- /dev/null +++ b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_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 #--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_mock_fpga_wrapper --file ./SRC --explicit_port_mapping --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 --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping + +# Write the SDC files for PnR backend +# - Turn on every options here +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 From 11832ad22c1b47e3416be60827a764523ec1784d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 25 May 2023 20:02:10 -0700 Subject: [PATCH 039/391] [test] add a new testcase to validate mock wrapper --- .../config/task.conf | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf new file mode 100644 index 000000000..bc60035f3 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf @@ -0,0 +1,41 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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/mock_wrapper_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 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2 + +bench1_top = or2 + +bench2_top = and2_latch + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= +vpr_fpga_verilog_formal_verification_top_netlist= From 812553e13d3f3e50c0de3b66069b5f49e6f0d517 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 25 May 2023 20:17:23 -0700 Subject: [PATCH 040/391] [test] adding more test cases --- .../regression_test_scripts/basic_reg_test.sh | 5 +++ .../config/task.conf | 0 .../config/task.conf | 41 +++++++++++++++++++ .../mock_wrapper_pcf/config/task.conf | 41 +++++++++++++++++++ 4 files changed, 87 insertions(+) rename openfpga_flow/tasks/basic_tests/mock_wrapper/{mock_wrapper_explicit_port_mapping => mock_wrapper_bgf}/config/task.conf (100%) create mode 100644 openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index 6b2d013cb..d9be3f249 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -115,6 +115,11 @@ run-task basic_tests/fabric_key/load_external_key_qlbanksr_multi_chain_fpga $@ # TODO: This feature is temporarily out of test due to the emergency in delivering netlists for multi-chain shift-register memory bank #run-task basic_tests/fabric_key/load_external_key_multi_region_qlbanksr_fpga $@ +echo -e "Testing mock wrapper" +run-task basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping $@ +run-task basic_tests/mock_wrapper/mock_wrapper_pcf $@ +run-task basic_tests/mock_wrapper/mock_wrapper_bgf $@ + echo -e "Testing K4 series FPGA"; echo -e "Testing K4N4 with facturable LUTs"; run-task basic_tests/k4_series/k4n4_frac_lut $@ diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf similarity index 100% rename from openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf rename to openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf new file mode 100644 index 000000000..bc60035f3 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf @@ -0,0 +1,41 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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/mock_wrapper_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 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2 + +bench1_top = or2 + +bench2_top = and2_latch + +[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/mock_wrapper/mock_wrapper_pcf/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf new file mode 100644 index 000000000..bc60035f3 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf @@ -0,0 +1,41 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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/mock_wrapper_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 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2 + +bench1_top = or2 + +bench2_top = and2_latch + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= +vpr_fpga_verilog_formal_verification_top_netlist= From 7fbe567d4c189778d71481904a4586f50a26569a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 25 May 2023 20:24:02 -0700 Subject: [PATCH 041/391] [test] add more testcases --- .../mock_wrapper_example_script.openfpga | 2 +- .../basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf | 3 +++ .../mock_wrapper_implicit_port_mapping/config/task.conf | 3 +++ .../basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf | 3 +++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga index b84f8f0ed..0fc318d93 100644 --- a/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga @@ -44,7 +44,7 @@ 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_mock_fpga_wrapper --file ./SRC --explicit_port_mapping --verbose +write_mock_fpga_wrapper --file ./SRC ${OPENFPGA_MOCK_WRAPPER_OPTIONS} ${OPENFPGA_MOCK_WRAPPER_BGF} ${OPENFPGA_MOCK_WRAPPER_PCF} # Write the Verilog testbench for FPGA fabric # - We suggest the use of same output directory as fabric Verilog netlists diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf index bc60035f3..1fd36c598 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf @@ -19,6 +19,9 @@ fpga_flow=yosys_vpr openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/mock_wrapper_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_mock_wrapper_options= +openfpga_mock_wrapper_bgf= +openfpga_mock_wrapper_pcf= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf index bc60035f3..1fd36c598 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf @@ -19,6 +19,9 @@ fpga_flow=yosys_vpr openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/mock_wrapper_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_mock_wrapper_options= +openfpga_mock_wrapper_bgf= +openfpga_mock_wrapper_pcf= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf index bc60035f3..6487d74c5 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf @@ -19,6 +19,9 @@ fpga_flow=yosys_vpr openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/mock_wrapper_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_mock_wrapper_options=--explicit_port_mapping +openfpga_mock_wrapper_bgf= +openfpga_mock_wrapper_pcf= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From 5a652c10e74d2efce70751fc21f0e49570d0d457 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 May 2023 06:59:06 +0000 Subject: [PATCH 042/391] Bump yosys from `57c9eb7` to `8596c5c` Bumps [yosys](https://github.com/YosysHQ/yosys) from `57c9eb7` to `8596c5c`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/57c9eb70feb54cff112d095d2153b0d032bdbf18...8596c5ce4915d1727d93df45fc58047f08886b41) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 57c9eb70f..8596c5ce4 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 57c9eb70feb54cff112d095d2153b0d032bdbf18 +Subproject commit 8596c5ce4915d1727d93df45fc58047f08886b41 From e9848c5728e6ddea728f6d35678be54e68a126ab Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 26 May 2023 10:24:21 -0700 Subject: [PATCH 043/391] [core] typo --- .../src/fpga_verilog/verilog_mock_fpga_wrapper.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp index 253321543..12151a99d 100644 --- a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp +++ b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp @@ -29,8 +29,8 @@ namespace openfpga { /******************************************************************** * Local variables used only in this file *******************************************************************/ -constexpr const char* BENCHMARK_PORT_POSTFIX = "_bench"; -constexpr const char* BENCHMARK_INSTANCE_NAME = "MAPPED_DESIGN"; +constexpr const char* APPINST_PORT_POSTFIX = "_bench"; +constexpr const char* APP_INSTANCE_NAME = "MAPPED_DESIGN"; /******************************************************************** * This function adds stimuli to I/Os of FPGA fabric @@ -334,16 +334,16 @@ int print_verilog_mock_fpga_wrapper( /* Instanciate application HDL module */ print_verilog_testbench_benchmark_instance( - fp, circuit_name, std::string(BENCHMARK_INSTANCE_NAME), std::string(), - std::string(), std::string(), std::string(BENCHMARK_PORT_POSTFIX), + fp, circuit_name, std::string(APP_INSTANCE_NAME), std::string(), + std::string(), std::string(), std::string(APPINST_PORT_POSTFIX), benchmark_clock_port_names, atom_ctx, netlist_annotation, pin_constraints, bus_group, options.explicit_port_mapping()); /* Connect I/Os to benchmark I/Os or constant driver */ print_verilog_mock_fpga_wrapper_connect_ios( fp, module_manager, top_module, atom_ctx, place_ctx, io_location_map, - netlist_annotation, bus_group, std::string(BENCHMARK_PORT_POSTFIX), - std::string(), std::string(), std::vector(), + netlist_annotation, bus_group, std::string(), + std::string(APPINST_PORT_POSTFIX), std::string(APPINST_PORT_POSTFIX), std::vector(), (size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE); /* Testbench ends*/ From f7afbfa0bd017adb6070e5c24477ba4cf5752a09 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 26 May 2023 12:26:30 -0700 Subject: [PATCH 044/391] [core] fixed some bugs --- openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp index 12151a99d..8aaab807b 100644 --- a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp +++ b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp @@ -317,8 +317,6 @@ int print_verilog_mock_fpga_wrapper( circuit_name; print_verilog_file_header(fp, title, options.time_stamp()); - print_verilog_default_net_type_declaration(fp, options.default_net_type()); - /* Find the top_module */ ModuleId top_module = module_manager.find_module(generate_fpga_top_module_name()); @@ -335,7 +333,7 @@ int print_verilog_mock_fpga_wrapper( /* Instanciate application HDL module */ print_verilog_testbench_benchmark_instance( fp, circuit_name, std::string(APP_INSTANCE_NAME), std::string(), - std::string(), std::string(), std::string(APPINST_PORT_POSTFIX), + std::string(), std::string(APPINST_PORT_POSTFIX), std::string(APPINST_PORT_POSTFIX), benchmark_clock_port_names, atom_ctx, netlist_annotation, pin_constraints, bus_group, options.explicit_port_mapping()); From 517c80aa22e3ec9ef6fd4574a70197adcea0fd32 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 27 May 2023 00:02:16 +0000 Subject: [PATCH 045/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index c26189bf0..1f34657a2 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1021 +1.2.1025 From 788b1495dd62ab4c663f6e8a7d3f74a5573bf36a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 26 May 2023 17:31:07 -0700 Subject: [PATCH 046/391] [core] split a big function to 4 sub functions so that we can efficiently reuse for mock wrapper --- .../verilog_mock_fpga_wrapper.cpp | 20 +++- .../fpga_verilog/verilog_testbench_utils.cpp | 100 +++++++++++++++--- .../fpga_verilog/verilog_testbench_utils.h | 23 ++++ 3 files changed, 126 insertions(+), 17 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp index 8aaab807b..9fa67eaff 100644 --- a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp +++ b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp @@ -330,19 +330,29 @@ int print_verilog_mock_fpga_wrapper( std::vector benchmark_clock_port_names = find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation); + /* Print local wires */ + print_verilog_testbench_shared_input_ports( + fp, module_manager, global_ports, pin_constraints, atom_ctx, + netlist_annotation, benchmark_clock_port_names, + std::string(APPINST_PORT_POSTFIX), false); + + print_verilog_testbench_shared_benchmark_output_ports( + fp, atom_ctx, netlist_annotation, std::string(APPINST_PORT_POSTFIX)); + /* Instanciate application HDL module */ print_verilog_testbench_benchmark_instance( fp, circuit_name, std::string(APP_INSTANCE_NAME), std::string(), - std::string(), std::string(APPINST_PORT_POSTFIX), std::string(APPINST_PORT_POSTFIX), - benchmark_clock_port_names, atom_ctx, netlist_annotation, pin_constraints, - bus_group, options.explicit_port_mapping()); + std::string(), std::string(APPINST_PORT_POSTFIX), + std::string(APPINST_PORT_POSTFIX), benchmark_clock_port_names, atom_ctx, + netlist_annotation, pin_constraints, bus_group, + options.explicit_port_mapping()); /* Connect I/Os to benchmark I/Os or constant driver */ print_verilog_mock_fpga_wrapper_connect_ios( fp, module_manager, top_module, atom_ctx, place_ctx, io_location_map, netlist_annotation, bus_group, std::string(), - std::string(APPINST_PORT_POSTFIX), std::string(APPINST_PORT_POSTFIX), std::vector(), - (size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE); + std::string(APPINST_PORT_POSTFIX), std::string(APPINST_PORT_POSTFIX), + std::vector(), (size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE); /* Testbench ends*/ print_verilog_module_end(fp, title); diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp index 8c91e50d0..565ff927d 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp @@ -914,20 +914,15 @@ void print_verilog_testbench_random_stimuli( * which are * 1. the shared input ports (registers) to drive both * FPGA fabric and benchmark instance - * 2. the output ports (wires) for both FPGA fabric and benchmark instance - * 3. the checking flag ports to evaluate if outputs matches under the * same input vectors *******************************************************************/ -void print_verilog_testbench_shared_ports( +void print_verilog_testbench_shared_input_ports( std::fstream& fp, const ModuleManager& module_manager, const FabricGlobalPortInfo& global_ports, const PinConstraints& pin_constraints, const AtomContext& atom_ctx, const VprNetlistAnnotation& netlist_annotation, const std::vector& clock_port_names, - const std::string& shared_input_port_postfix, - const std::string& benchmark_output_port_postfix, - const std::string& fpga_output_port_postfix, - const std::string& check_flag_port_postfix, const bool& no_self_checking) { + const std::string& shared_input_port_postfix, const bool& use_reg_port) { /* Validate the file stream */ valid_file_stream(fp); @@ -959,8 +954,13 @@ void print_verilog_testbench_shared_ports( if (false == port_is_fabric_global_reset_port(global_ports, module_manager, pin_constraints.net_pin(block_name))) { - fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, input_port) << ";" - << std::endl; + if (use_reg_port) { + fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, input_port) << ";" + << std::endl; + } else { + fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, input_port) + << ";" << std::endl; + } } else { fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, input_port) << ";" << std::endl; @@ -969,6 +969,19 @@ void print_verilog_testbench_shared_ports( /* Add an empty line as splitter */ fp << std::endl; +} + +/******************************************************************** + * Print Verilog declaration of shared ports appear in testbenches + * which are + * 2. the output ports (wires) for FPGA fabric + *******************************************************************/ +void print_verilog_testbench_shared_fpga_output_ports( + std::fstream& fp, const AtomContext& atom_ctx, + const VprNetlistAnnotation& netlist_annotation, + const std::string& fpga_output_port_postfix) { + /* Validate the file stream */ + valid_file_stream(fp); /* Instantiate wires for FPGA fabric outputs */ print_verilog_comment(fp, std::string("----- FPGA fabric outputs -------")); @@ -996,10 +1009,19 @@ void print_verilog_testbench_shared_ports( /* Add an empty line as splitter */ fp << std::endl; +} - if (no_self_checking) { - return; - } +/******************************************************************** + * Print Verilog declaration of shared ports appear in testbenches + * which are + * 2. the output ports (wires) for benchmark instance + *******************************************************************/ +void print_verilog_testbench_shared_benchmark_output_ports( + std::fstream& fp, const AtomContext& atom_ctx, + const VprNetlistAnnotation& netlist_annotation, + const std::string& benchmark_output_port_postfix) { + /* Validate the file stream */ + valid_file_stream(fp); /* Instantiate wire for benchmark output */ print_verilog_comment(fp, std::string("----- Benchmark outputs -------")); @@ -1026,6 +1048,23 @@ void print_verilog_testbench_shared_ports( /* Add an empty line as splitter */ fp << std::endl; +} + +/******************************************************************** + * Print Verilog declaration of shared ports appear in testbenches + * which are + * 1. the shared input ports (registers) to drive both + * FPGA fabric and benchmark instance + * 2. the output ports (wires) for both FPGA fabric and benchmark instance + * 3. the checking flag ports to evaluate if outputs matches under the + * same input vectors + *******************************************************************/ +void print_verilog_testbench_shared_check_flags( + std::fstream& fp, const AtomContext& atom_ctx, + const VprNetlistAnnotation& netlist_annotation, + const std::string& check_flag_port_postfix) { + /* Validate the file stream */ + valid_file_stream(fp); /* Instantiate register for output comparison */ print_verilog_comment( @@ -1054,6 +1093,43 @@ void print_verilog_testbench_shared_ports( fp << std::endl; } +/******************************************************************** + * Print Verilog declaration of shared ports appear in testbenches + * which are + * 1. the shared input ports (registers) to drive both + * FPGA fabric and benchmark instance + * 2. the output ports (wires) for both FPGA fabric and benchmark instance + * 3. the checking flag ports to evaluate if outputs matches under the + * same input vectors + *******************************************************************/ +void print_verilog_testbench_shared_ports( + std::fstream& fp, const ModuleManager& module_manager, + const FabricGlobalPortInfo& global_ports, + const PinConstraints& pin_constraints, const AtomContext& atom_ctx, + const VprNetlistAnnotation& netlist_annotation, + const std::vector& clock_port_names, + const std::string& shared_input_port_postfix, + const std::string& benchmark_output_port_postfix, + const std::string& fpga_output_port_postfix, + const std::string& check_flag_port_postfix, const bool& no_self_checking) { + print_verilog_testbench_shared_input_ports( + fp, module_manager, global_ports, pin_constraints, atom_ctx, + netlist_annotation, clock_port_names, shared_input_port_postfix, true); + + print_verilog_testbench_shared_fpga_output_ports( + fp, atom_ctx, netlist_annotation, fpga_output_port_postfix); + + if (no_self_checking) { + return; + } + + print_verilog_testbench_shared_benchmark_output_ports( + fp, atom_ctx, netlist_annotation, benchmark_output_port_postfix); + + print_verilog_testbench_shared_check_flags(fp, atom_ctx, netlist_annotation, + check_flag_port_postfix); +} + /******************************************************************** * Print signal initialization which * deposit initial values for the input ports of primitive circuit models diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.h b/openfpga/src/fpga_verilog/verilog_testbench_utils.h index 2aedd18e3..e945683d6 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.h +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.h @@ -90,6 +90,29 @@ void print_verilog_testbench_random_stimuli( const std::string& check_flag_port_postfix, const std::vector& clock_ports, const bool& no_self_checking); +void print_verilog_testbench_shared_input_ports( + std::fstream& fp, const ModuleManager& module_manager, + const FabricGlobalPortInfo& global_ports, + const PinConstraints& pin_constraints, const AtomContext& atom_ctx, + const VprNetlistAnnotation& netlist_annotation, + const std::vector& clock_port_names, + const std::string& shared_input_port_postfix, const bool& use_reg_port); + +void print_verilog_testbench_shared_fpga_output_ports( + std::fstream& fp, const AtomContext& atom_ctx, + const VprNetlistAnnotation& netlist_annotation, + const std::string& fpga_output_port_postfix); + +void print_verilog_testbench_shared_benchmark_output_ports( + std::fstream& fp, const AtomContext& atom_ctx, + const VprNetlistAnnotation& netlist_annotation, + const std::string& benchmark_output_port_postfix); + +void print_verilog_testbench_shared_check_flags( + std::fstream& fp, const AtomContext& atom_ctx, + const VprNetlistAnnotation& netlist_annotation, + const std::string& check_flag_port_postfix); + void print_verilog_testbench_shared_ports( std::fstream& fp, const ModuleManager& module_manager, const FabricGlobalPortInfo& global_ports, From a9e5e1af89f420945d19726cc00d2c5053e5456c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 26 May 2023 18:49:57 -0700 Subject: [PATCH 047/391] [core] now fabric netlist include mock wrapper --- .../base/openfpga_verilog_command_template.h | 5 +++ openfpga/src/base/openfpga_verilog_template.h | 12 +++--- openfpga/src/fpga_verilog/verilog_api.cpp | 33 +++++++++++---- openfpga/src/fpga_verilog/verilog_api.h | 4 +- .../verilog_auxiliary_netlists.cpp | 42 +++++++++++++++++++ .../fpga_verilog/verilog_auxiliary_netlists.h | 4 ++ .../verilog_mock_fpga_wrapper.cpp | 10 ++--- .../fpga_verilog/verilog_mock_fpga_wrapper.h | 10 ++--- 8 files changed, 91 insertions(+), 29 deletions(-) diff --git a/openfpga/src/base/openfpga_verilog_command_template.h b/openfpga/src/base/openfpga_verilog_command_template.h index c1b7e6c64..010b73802 100644 --- a/openfpga/src/base/openfpga_verilog_command_template.h +++ b/openfpga/src/base/openfpga_verilog_command_template.h @@ -281,6 +281,11 @@ ShellCommandId add_write_mock_fpga_wrapper_command_template( shell_cmd.set_option_short_name(bgf_opt, "bgf"); shell_cmd.set_option_require_value(bgf_opt, openfpga::OPT_STRING); + /* Add an option '--use_relative_path' */ + shell_cmd.add_option( + "use_relative_path", false, + "Force to use relative path in netlists when including other netlists"); + /* add an option '--explicit_port_mapping' */ shell_cmd.add_option("explicit_port_mapping", false, "use explicit port mapping in verilog netlists"); diff --git a/openfpga/src/base/openfpga_verilog_template.h b/openfpga/src/base/openfpga_verilog_template.h index 99d8df2ad..a25ce81c1 100644 --- a/openfpga/src/base/openfpga_verilog_template.h +++ b/openfpga/src/base/openfpga_verilog_template.h @@ -220,6 +220,7 @@ int write_mock_fpga_wrapper_template(const T& openfpga_ctx, const Command& cmd, CommandOptionId opt_bgf = cmd.option("bus_group_file"); CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); + CommandOptionId opt_use_relative_path = cmd.option("use_relative_path"); CommandOptionId opt_default_net_type = cmd.option("default_net_type"); CommandOptionId opt_no_time_stamp = cmd.option("no_time_stamp"); CommandOptionId opt_verbose = cmd.option("verbose"); @@ -231,6 +232,8 @@ int write_mock_fpga_wrapper_template(const T& openfpga_ctx, const Command& cmd, options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); options.set_explicit_port_mapping( cmd_context.option_enable(cmd, opt_explicit_port_mapping)); + options.set_use_relative_path( + cmd_context.option_enable(cmd, opt_use_relative_path)); options.set_time_stamp(!cmd_context.option_enable(cmd, opt_no_time_stamp)); options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); @@ -254,11 +257,10 @@ int write_mock_fpga_wrapper_template(const T& openfpga_ctx, const Command& cmd, } return fpga_verilog_mock_fpga_wrapper( - openfpga_ctx.module_graph(), openfpga_ctx.bitstream_manager(), - g_vpr_ctx.atom(), g_vpr_ctx.placement(), pin_constraints, bus_group, - openfpga_ctx.io_location_map(), openfpga_ctx.fabric_global_port_info(), - openfpga_ctx.vpr_netlist_annotation(), openfpga_ctx.arch().circuit_lib, - openfpga_ctx.arch().config_protocol, options); + openfpga_ctx.module_graph(), g_vpr_ctx.atom(), g_vpr_ctx.placement(), + pin_constraints, bus_group, openfpga_ctx.io_location_map(), + openfpga_ctx.fabric_global_port_info(), + openfpga_ctx.vpr_netlist_annotation(), options); } /******************************************************************** diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index aaea119cb..4bc5e6ced 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -229,13 +229,11 @@ int fpga_verilog_preconfigured_fabric_wrapper( * which encapsulate the application HDL into a mock FPGA module ********************************************************************/ int fpga_verilog_mock_fpga_wrapper( - const ModuleManager &module_manager, - const BitstreamManager &bitstream_manager, const AtomContext &atom_ctx, + const ModuleManager &module_manager, const AtomContext &atom_ctx, const PlacementContext &place_ctx, const PinConstraints &pin_constraints, const BusGroup &bus_group, const IoLocationMap &io_location_map, const FabricGlobalPortInfo &fabric_global_port_info, const VprNetlistAnnotation &netlist_annotation, - const CircuitLibrary &circuit_lib, const ConfigProtocol &config_protocol, const VerilogTestbenchOption &options) { vtr::ScopedStartFinishTimer timer( "Write a wrapper module to mock a mapped FPGA fabric\n"); @@ -246,19 +244,36 @@ int fpga_verilog_mock_fpga_wrapper( int status = CMD_EXEC_SUCCESS; + NetlistManager netlist_manager; + /* Create directories */ create_directory(src_dir_path); /* Generate wrapper module for FPGA fabric (mapped by the input benchmark and * pre-configured testbench for verification */ - std::string netlist_file_path = - src_dir_path + + std::string netlist_file_name = generate_fpga_top_netlist_name(std::string(VERILOG_NETLIST_FILE_POSTFIX)); + std::string netlist_file_path = src_dir_path + netlist_file_name; status = print_verilog_mock_fpga_wrapper( - module_manager, bitstream_manager, config_protocol, circuit_lib, - fabric_global_port_info, atom_ctx, place_ctx, pin_constraints, bus_group, - io_location_map, netlist_annotation, netlist_name, netlist_file_path, - options); + module_manager, fabric_global_port_info, atom_ctx, place_ctx, + pin_constraints, bus_group, io_location_map, netlist_annotation, + netlist_name, netlist_file_path, options); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = NetlistId::INVALID(); + if (options.use_relative_path()) { + nlist_id = netlist_manager.add_netlist(netlist_file_name); + } else { + nlist_id = netlist_manager.add_netlist(netlist_file_path); + } + VTR_ASSERT(nlist_id); + netlist_manager.set_netlist_type(nlist_id, + NetlistManager::TOP_MODULE_NETLIST); + + /* Generate an netlist including all the fabric-related netlists */ + print_verilog_mock_fabric_include_netlist(netlist_manager, src_dir_path, + options.use_relative_path(), + options.time_stamp()); return status; } diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index c680a6251..914e58d70 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -69,13 +69,11 @@ int fpga_verilog_preconfigured_fabric_wrapper( const VerilogTestbenchOption& options); int fpga_verilog_mock_fpga_wrapper( - const ModuleManager& module_manager, - const BitstreamManager& bitstream_manager, const AtomContext& atom_ctx, + const ModuleManager& module_manager, const AtomContext& atom_ctx, const PlacementContext& place_ctx, const PinConstraints& pin_constraints, const BusGroup& bus_group, const IoLocationMap& io_location_map, const FabricGlobalPortInfo& fabric_global_port_info, const VprNetlistAnnotation& netlist_annotation, - const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, const VerilogTestbenchOption& options); int fpga_verilog_preconfigured_testbench( diff --git a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp index aa600ddba..a19ee9446 100644 --- a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp +++ b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp @@ -23,6 +23,48 @@ namespace openfpga { * Local constant variables *******************************************************************/ +/******************************************************************** + * Print a file that includes all the fabric netlists + * that have been generated and user-defined. + * This does NOT include any testbenches! + * Some netlists are open to compile under specific preprocessing flags + *******************************************************************/ +void print_verilog_mock_fabric_include_netlist( + const NetlistManager& netlist_manager, const std::string& src_dir_path, + const bool& use_relative_path, const bool& include_time_stamp) { + /* If we force the use of relative path, the src dir path should NOT be + * included in any output */ + std::string src_dir = src_dir_path; + if (use_relative_path) { + src_dir.clear(); + } + std::string verilog_fpath = + src_dir_path + std::string(FABRIC_INCLUDE_VERILOG_NETLIST_FILE_NAME); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fpath, std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + check_file_stream(verilog_fpath.c_str(), fp); + + /* Print the title */ + print_verilog_file_header(fp, std::string("Mock Fabric Netlist Summary"), + include_time_stamp); + + /* Include FPGA top module */ + print_verilog_comment( + fp, std::string("------ Include fabric top-level netlists -----")); + for (const NetlistId& nlist_id : + netlist_manager.netlists_by_type(NetlistManager::TOP_MODULE_NETLIST)) { + print_verilog_include_netlist(fp, netlist_manager.netlist_name(nlist_id)); + } + fp << std::endl; + + /* Close the file stream */ + fp.close(); +} + /******************************************************************** * Print a file that includes all the fabric netlists * that have been generated and user-defined. diff --git a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h index 553c8c3a4..493f2c4af 100644 --- a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h +++ b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h @@ -18,6 +18,10 @@ /* begin namespace openfpga */ namespace openfpga { +void print_verilog_mock_fabric_include_netlist( + const NetlistManager& netlist_manager, const std::string& src_dir_path, + const bool& use_relative_path, const bool& include_time_stamp); + void print_verilog_fabric_include_netlist(const NetlistManager& netlist_manager, const std::string& src_dir_path, const CircuitLibrary& circuit_lib, diff --git a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp index 9fa67eaff..6389ca5d6 100644 --- a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp +++ b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp @@ -286,12 +286,10 @@ static void print_verilog_mock_fpga_wrapper_connect_ios( *application HDL (supposed to be implemented on FPGA). *******************************************************************/ int print_verilog_mock_fpga_wrapper( - const ModuleManager& module_manager, - const BitstreamManager& bitstream_manager, - const ConfigProtocol& config_protocol, const CircuitLibrary& circuit_lib, - const FabricGlobalPortInfo& global_ports, const AtomContext& atom_ctx, - const PlacementContext& place_ctx, const PinConstraints& pin_constraints, - const BusGroup& bus_group, const IoLocationMap& io_location_map, + const ModuleManager& module_manager, const FabricGlobalPortInfo& global_ports, + const AtomContext& atom_ctx, const PlacementContext& place_ctx, + const PinConstraints& pin_constraints, const BusGroup& bus_group, + const IoLocationMap& io_location_map, const VprNetlistAnnotation& netlist_annotation, const std::string& circuit_name, const std::string& verilog_fname, const VerilogTestbenchOption& options) { diff --git a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.h b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.h index 503fd679f..62f2f5d71 100644 --- a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.h +++ b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.h @@ -27,12 +27,10 @@ namespace openfpga { int print_verilog_mock_fpga_wrapper( - const ModuleManager& module_manager, - const BitstreamManager& bitstream_manager, - const ConfigProtocol& config_protocol, const CircuitLibrary& circuit_lib, - const FabricGlobalPortInfo& global_ports, const AtomContext& atom_ctx, - const PlacementContext& place_ctx, const PinConstraints& pin_constraints, - const BusGroup& bus_group, const IoLocationMap& io_location_map, + const ModuleManager& module_manager, const FabricGlobalPortInfo& global_ports, + const AtomContext& atom_ctx, const PlacementContext& place_ctx, + const PinConstraints& pin_constraints, const BusGroup& bus_group, + const IoLocationMap& io_location_map, const VprNetlistAnnotation& netlist_annotation, const std::string& circuit_name, const std::string& verilog_fname, const VerilogTestbenchOption& options); From 77be0539669d271b16998c6015021f746e0ace34 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 26 May 2023 18:50:54 -0700 Subject: [PATCH 048/391] [test] mock wrapper does not need bitstream forcing --- .../openfpga_shell_scripts/mock_wrapper_example_script.openfpga | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga index 0fc318d93..ae90178a7 100644 --- a/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga @@ -52,7 +52,7 @@ write_mock_fpga_wrapper --file ./SRC ${OPENFPGA_MOCK_WRAPPER_OPTIONS} ${OPENFPGA # - 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 --explicit_port_mapping +write_preconfigured_fabric_wrapper --embed_bitstream none --file ./SRC --explicit_port_mapping write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Write the SDC files for PnR backend From 0abc5af1a97eef6449e367411008b0c570b3173c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 26 May 2023 20:44:04 -0700 Subject: [PATCH 049/391] [core] fixed the bug supporting global nets --- .../verilog_mock_fpga_wrapper.cpp | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp index 6389ca5d6..5d5acb535 100644 --- a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp +++ b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp @@ -12,6 +12,7 @@ /* Headers from openfpgautil library */ #include "bitstream_manager_utils.h" +#include "fabric_global_port_info_utils.h" #include "module_manager_utils.h" #include "openfpga_atom_netlist_utils.h" #include "openfpga_digest.h" @@ -43,6 +44,8 @@ static void print_verilog_mock_fpga_wrapper_connect_ios( std::fstream& fp, const ModuleManager& module_manager, const ModuleId& top_module, const AtomContext& atom_ctx, const PlacementContext& place_ctx, const IoLocationMap& io_location_map, + const PinConstraints& pin_constraints, + const FabricGlobalPortInfo& global_ports, const VprNetlistAnnotation& netlist_annotation, const BusGroup& bus_group, const std::string& net_name_postfix, const std::string& io_input_port_name_postfix, @@ -189,15 +192,21 @@ static void print_verilog_mock_fpga_wrapper_connect_ios( } if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) { - /* If the port is a clock, do not add a postfix */ + /* If the port is a clock, skip it */ if (clock_port_names.end() != std::find(clock_port_names.begin(), clock_port_names.end(), block_name)) { - benchmark_io_port.set_name(block_name); - } else { - benchmark_io_port.set_name( - std::string(block_name + io_input_port_name_postfix)); + continue; } + /* For global ports, use wires; otherwise, use registers*/ + if (true == port_is_fabric_global_reset_port( + global_ports, module_manager, + pin_constraints.net_pin(block_name))) { + continue; + } + + benchmark_io_port.set_name( + std::string(block_name + io_input_port_name_postfix)); print_verilog_comment( fp, std::string("----- Blif Benchmark input " + block_name + " is mapped to FPGA IOPAD " + @@ -348,9 +357,9 @@ int print_verilog_mock_fpga_wrapper( /* Connect I/Os to benchmark I/Os or constant driver */ print_verilog_mock_fpga_wrapper_connect_ios( fp, module_manager, top_module, atom_ctx, place_ctx, io_location_map, - netlist_annotation, bus_group, std::string(), + pin_constraints, global_ports, netlist_annotation, bus_group, std::string(), std::string(APPINST_PORT_POSTFIX), std::string(APPINST_PORT_POSTFIX), - std::vector(), (size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE); + benchmark_clock_port_names, (size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE); /* Testbench ends*/ print_verilog_module_end(fp, title); From 205e9aa67ba59c1f07308c973b47c073648c713a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 26 May 2023 20:55:52 -0700 Subject: [PATCH 050/391] [test] add a new test case --- .../config/task.conf | 44 +++++++++++++++++++ .../config/counter8_bus_group.xml | 12 +++++ .../config/pin_constraints_reset.xml | 7 +++ .../mock_wrapper_pcf/config/task.conf | 28 +++++++----- 4 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/counter8_bus_group.xml create mode 100644 openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/pin_constraints_reset.xml diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf new file mode 100644 index 000000000..6487d74c5 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf @@ -0,0 +1,44 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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/mock_wrapper_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_mock_wrapper_options=--explicit_port_mapping +openfpga_mock_wrapper_bgf= +openfpga_mock_wrapper_pcf= + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2 + +bench1_top = or2 + +bench2_top = and2_latch + +[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/mock_wrapper/mock_wrapper_pcf/config/counter8_bus_group.xml b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/counter8_bus_group.xml new file mode 100644 index 000000000..a0fd22f77 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/counter8_bus_group.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/pin_constraints_reset.xml b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/pin_constraints_reset.xml new file mode 100644 index 000000000..abcf209f6 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/pin_constraints_reset.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf index 6487d74c5..5685d3ca4 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf @@ -9,7 +9,7 @@ [GENERAL] run_engine=openfpga_shell power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml -power_analysis = true +power_analysis = false spice_output=false verilog_output=true timeout_each_job = 20*60 @@ -17,27 +17,33 @@ fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/mock_wrapper_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_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml openfpga_mock_wrapper_options=--explicit_port_mapping openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= [ARCHITECTURES] -arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v -bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v -bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counters/counter_8bit_async_reset/counter.v [SYNTHESIS_PARAM] +# Yosys script parameters +bench_yosys_cell_sim_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_cell_sim.v +bench_yosys_dff_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_dff_map.v +bench_yosys_bram_map_rules_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_bram.txt +bench_yosys_bram_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_bram_map.v +bench_yosys_dsp_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_dsp_map.v +bench_yosys_dsp_map_parameters_common=-D DSP_A_MAXWIDTH=36 -D DSP_B_MAXWIDTH=36 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_NAME=mult_36x36 bench_read_verilog_options_common = -nolatches -bench0_top = and2 +bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_dff_flow.ys +bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys -bench1_top = or2 - -bench2_top = and2_latch +bench0_top = counter +bench0_openfpga_mock_wrapper_pcf=${PATH:TASK_DIR}/config/pin_constraints_reset.xml +bench0_openfpga_mock_wrapper_bgf=${PATH:TASK_DIR}/config/counter8_bus_group.xml [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From e1feebc96d24919bfe0b6565b00730519e3f8382 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 26 May 2023 21:54:08 -0700 Subject: [PATCH 051/391] [core] fixing bugs on pcf and bgf support for mock efpga wrapper --- .../verilog_mock_fpga_wrapper.cpp | 137 ++++++++++++++++-- .../mock_wrapper_example_script.openfpga | 4 +- .../regression_test_scripts/basic_reg_test.sh | 1 + .../mock_wrapper_pcf/config/task.conf | 4 +- 4 files changed, 130 insertions(+), 16 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp index 5d5acb535..0c3642092 100644 --- a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp +++ b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp @@ -178,18 +178,7 @@ static void print_verilog_mock_fpga_wrapper_connect_ios( */ BasicPort benchmark_io_port; - /* If this benchmark pin belongs to any bus group, use the bus pin instead - */ - BusGroupId bus_id = bus_group.find_pin_bus(block_name); - BusPinId bus_pin_id = bus_group.find_pin(block_name); - if (bus_id) { - block_name = bus_group.bus_port(bus_id).get_name(); - VTR_ASSERT_SAFE(bus_pin_id); - benchmark_io_port.set_width(bus_group.pin_index(bus_pin_id), - bus_group.pin_index(bus_pin_id)); - } else { - benchmark_io_port.set_width(1); - } + benchmark_io_port.set_width(1); if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) { /* If the port is a clock, skip it */ @@ -270,6 +259,121 @@ static void print_verilog_mock_fpga_wrapper_connect_ios( } } +/******************************************************************** + * Connect global ports of FPGA top module to constants except: + * 1. operating clock, which should be wired to the clock port of + * this pre-configured FPGA top module + *******************************************************************/ +static int print_verilog_mock_fpga_wrapper_connect_global_ports( + std::fstream &fp, const ModuleManager &module_manager, + const ModuleId &top_module, const PinConstraints &pin_constraints, + const FabricGlobalPortInfo &fabric_global_ports, + const std::vector &benchmark_clock_port_names) { + /* Validate the file stream */ + valid_file_stream(fp); + + print_verilog_comment( + fp, + std::string("----- Begin Connect Global ports to FPGA top-level interface -----")); + + for (const FabricGlobalPortId &global_port_id : + fabric_global_ports.global_ports()) { + ModulePortId module_global_port_id = + fabric_global_ports.global_module_port(global_port_id); + VTR_ASSERT(ModuleManager::MODULE_GLOBAL_PORT == + module_manager.port_type(top_module, module_global_port_id)); + BasicPort module_global_port = + module_manager.module_port(top_module, module_global_port_id); + /* Now, for operating clock port, we should wire it to the clock of + * benchmark! */ + if ((true == fabric_global_ports.global_port_is_clock(global_port_id)) && + (false == fabric_global_ports.global_port_is_prog(global_port_id))) { + /* Wiring to each pin of the global port: benchmark clock is always 1-bit + */ + for (size_t pin_id = 0; pin_id < module_global_port.pins().size(); + ++pin_id) { + BasicPort module_clock_pin( + module_global_port.get_name(), + module_global_port.pins()[pin_id], module_global_port.pins()[pin_id]); + + /* If the clock port name is in the pin constraints, we should wire it + * to the constrained pin */ + std::string constrained_net_name = pin_constraints.pin_net(BasicPort( + module_global_port.get_name(), module_global_port.pins()[pin_id], + module_global_port.pins()[pin_id])); + + /* If constrained to an open net or there is no clock in the benchmark, + * we assign it to a default value */ + if ((true == pin_constraints.unmapped_net(constrained_net_name)) || + (true == benchmark_clock_port_names.empty())) { + continue; + } + + std::string clock_name_to_connect; + if (!pin_constraints.unconstrained_net(constrained_net_name)) { + clock_name_to_connect = constrained_net_name; + } else { + /* Otherwise, we must have a clear one-to-one clock net + * corresponding!!! */ + if (benchmark_clock_port_names.size() != + module_global_port.get_width()) { + VTR_LOG_ERROR( + "Unable to map %lu benchmark clocks to %lu clock pins of " + "FPGA!\nRequire clear pin constraints!\n", + benchmark_clock_port_names.size(), + module_global_port.get_width()); + return CMD_EXEC_FATAL_ERROR; + } + clock_name_to_connect = benchmark_clock_port_names[pin_id]; + } + + BasicPort benchmark_clock_pin(clock_name_to_connect, 1); + print_verilog_wire_connection(fp, benchmark_clock_pin, module_clock_pin, + false); + } + /* Finish, go to the next */ + continue; + } + + /* For other ports, give an default value */ + for (size_t pin_id = 0; pin_id < module_global_port.pins().size(); + ++pin_id) { + BasicPort module_global_pin(module_global_port.get_name(), + module_global_port.pins()[pin_id], + module_global_port.pins()[pin_id]); + + /* If the global port name is in the pin constraints, we should wire it to + * the constrained pin */ + std::string constrained_net_name = + pin_constraints.pin_net(module_global_pin) + std::string(APPINST_PORT_POSTFIX); + + module_global_pin.set_name( + module_global_port.get_name()); + + /* - If constrained to a given net in the benchmark, we connect the global + * pin to the net + * - If constrained to an open net in the benchmark, we assign it to a + * default value + */ + if ((false == pin_constraints.unconstrained_net(constrained_net_name)) && + (false == pin_constraints.unmapped_net(constrained_net_name))) { + BasicPort benchmark_pin(constrained_net_name, 1); + print_verilog_wire_connection(fp, benchmark_pin, module_global_pin, + false); + } + } + } + + print_verilog_comment( + fp, std::string("----- End Connect Global ports to FPGA top-level interface -----")); + + /* Add an empty line as a splitter */ + fp << std::endl; + + return CMD_EXEC_SUCCESS; +} + + /******************************************************************** * Top-level function to generate a Verilog module of * a mock FPGA wrapper which contains an benchmark instance. @@ -354,6 +458,15 @@ int print_verilog_mock_fpga_wrapper( netlist_annotation, pin_constraints, bus_group, options.explicit_port_mapping()); + /* Connect FPGA top module global ports to constant or benchmark global + * signals! */ + status = print_verilog_mock_fpga_wrapper_connect_global_ports( + fp, module_manager, top_module, pin_constraints, global_ports, + benchmark_clock_port_names); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + /* Connect I/Os to benchmark I/Os or constant driver */ print_verilog_mock_fpga_wrapper_connect_ios( fp, module_manager, top_module, atom_ctx, place_ctx, io_location_map, diff --git a/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga index ae90178a7..636ac6e9d 100644 --- a/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga @@ -1,6 +1,6 @@ # Run VPR for the 'and' design #--write_rr_graph example_rr_graph.xml -vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling ideal # Read OpenFPGA architecture definition read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} @@ -10,7 +10,7 @@ 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 +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 diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index d9be3f249..489120590 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -116,6 +116,7 @@ run-task basic_tests/fabric_key/load_external_key_qlbanksr_multi_chain_fpga $@ #run-task basic_tests/fabric_key/load_external_key_multi_region_qlbanksr_fpga $@ echo -e "Testing mock wrapper" +run-task basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping $@ run-task basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping $@ run-task basic_tests/mock_wrapper/mock_wrapper_pcf $@ run-task basic_tests/mock_wrapper/mock_wrapper_bgf $@ diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf index 5685d3ca4..93e225201 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf @@ -42,8 +42,8 @@ bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_df bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys bench0_top = counter -bench0_openfpga_mock_wrapper_pcf=${PATH:TASK_DIR}/config/pin_constraints_reset.xml -bench0_openfpga_mock_wrapper_bgf=${PATH:TASK_DIR}/config/counter8_bus_group.xml +bench0_openfpga_mock_wrapper_pcf=-pcf ${PATH:TASK_DIR}/config/pin_constraints_reset.xml +bench0_openfpga_mock_wrapper_bgf=-bgf ${PATH:TASK_DIR}/config/counter8_bus_group.xml [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From b6c90eb99a9ed344948fa3cb0918ec5e9b888020 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 27 May 2023 12:13:16 -0700 Subject: [PATCH 052/391] [core] fixed several bugs which causes bgf and pcf support in mock wrapper failed --- .../verilog_formal_random_top_testbench.cpp | 7 +-- .../verilog_mock_fpga_wrapper.cpp | 43 +++++++++++-------- .../fpga_verilog/verilog_testbench_utils.cpp | 17 ++++++-- .../fpga_verilog/verilog_testbench_utils.h | 6 ++- .../fpga_verilog/verilog_top_testbench.cpp | 2 +- .../mock_wrapper_example_script.openfpga | 4 +- 6 files changed, 48 insertions(+), 31 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.cpp index c72eb6786..629d1759b 100644 --- a/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_formal_random_top_testbench.cpp @@ -116,8 +116,9 @@ static void print_verilog_top_random_testbench_benchmark_instance( print_verilog_testbench_benchmark_instance( fp, reference_verilog_top_name, std::string(BENCHMARK_INSTANCE_NAME), std::string(), std::string(), std::string(), - std::string(BENCHMARK_PORT_POSTFIX), std::vector(), atom_ctx, - netlist_annotation, pin_constraints, bus_group, explicit_port_mapping); + std::string(BENCHMARK_PORT_POSTFIX), std::vector(), false, + atom_ctx, netlist_annotation, pin_constraints, bus_group, + explicit_port_mapping); print_verilog_comment( fp, std::string("----- End reference Benchmark Instanication -------")); @@ -147,7 +148,7 @@ static void print_verilog_random_testbench_fpga_instance( std::string(FORMAL_VERIFICATION_TOP_MODULE_POSTFIX)), std::string(FPGA_INSTANCE_NAME), std::string(), std::string(), std::string(), std::string(FPGA_PORT_POSTFIX), std::vector(), - atom_ctx, netlist_annotation, pin_constraints, bus_group, + false, atom_ctx, netlist_annotation, pin_constraints, bus_group, explicit_port_mapping); print_verilog_comment( diff --git a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp index 0c3642092..a28b9ffaa 100644 --- a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp +++ b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp @@ -46,7 +46,7 @@ static void print_verilog_mock_fpga_wrapper_connect_ios( const PlacementContext& place_ctx, const IoLocationMap& io_location_map, const PinConstraints& pin_constraints, const FabricGlobalPortInfo& global_ports, - const VprNetlistAnnotation& netlist_annotation, const BusGroup& bus_group, + const VprNetlistAnnotation& netlist_annotation, const std::string& net_name_postfix, const std::string& io_input_port_name_postfix, const std::string& io_output_port_name_postfix, @@ -265,18 +265,19 @@ static void print_verilog_mock_fpga_wrapper_connect_ios( * this pre-configured FPGA top module *******************************************************************/ static int print_verilog_mock_fpga_wrapper_connect_global_ports( - std::fstream &fp, const ModuleManager &module_manager, - const ModuleId &top_module, const PinConstraints &pin_constraints, - const FabricGlobalPortInfo &fabric_global_ports, - const std::vector &benchmark_clock_port_names) { + std::fstream& fp, const ModuleManager& module_manager, + const ModuleId& top_module, const PinConstraints& pin_constraints, + const FabricGlobalPortInfo& fabric_global_ports, + const std::vector& benchmark_clock_port_names) { /* Validate the file stream */ valid_file_stream(fp); print_verilog_comment( fp, - std::string("----- Begin Connect Global ports to FPGA top-level interface -----")); + std::string( + "----- Begin Connect Global ports to FPGA top-level interface -----")); - for (const FabricGlobalPortId &global_port_id : + for (const FabricGlobalPortId& global_port_id : fabric_global_ports.global_ports()) { ModulePortId module_global_port_id = fabric_global_ports.global_module_port(global_port_id); @@ -292,9 +293,9 @@ static int print_verilog_mock_fpga_wrapper_connect_global_ports( */ for (size_t pin_id = 0; pin_id < module_global_port.pins().size(); ++pin_id) { - BasicPort module_clock_pin( - module_global_port.get_name(), - module_global_port.pins()[pin_id], module_global_port.pins()[pin_id]); + BasicPort module_clock_pin(module_global_port.get_name(), + module_global_port.pins()[pin_id], + module_global_port.pins()[pin_id]); /* If the clock port name is in the pin constraints, we should wire it * to the constrained pin */ @@ -326,6 +327,7 @@ static int print_verilog_mock_fpga_wrapper_connect_global_ports( } clock_name_to_connect = benchmark_clock_port_names[pin_id]; } + clock_name_to_connect += std::string(APPINST_PORT_POSTFIX); BasicPort benchmark_clock_pin(clock_name_to_connect, 1); print_verilog_wire_connection(fp, benchmark_clock_pin, module_clock_pin, @@ -345,10 +347,13 @@ static int print_verilog_mock_fpga_wrapper_connect_global_ports( /* If the global port name is in the pin constraints, we should wire it to * the constrained pin */ std::string constrained_net_name = - pin_constraints.pin_net(module_global_pin) + std::string(APPINST_PORT_POSTFIX); + pin_constraints.pin_net(module_global_pin); + if (constrained_net_name.empty()) { + continue; + } + constrained_net_name += std::string(APPINST_PORT_POSTFIX); - module_global_pin.set_name( - module_global_port.get_name()); + module_global_pin.set_name(module_global_port.get_name()); /* - If constrained to a given net in the benchmark, we connect the global * pin to the net @@ -365,7 +370,8 @@ static int print_verilog_mock_fpga_wrapper_connect_global_ports( } print_verilog_comment( - fp, std::string("----- End Connect Global ports to FPGA top-level interface -----")); + fp, std::string( + "----- End Connect Global ports to FPGA top-level interface -----")); /* Add an empty line as a splitter */ fp << std::endl; @@ -373,7 +379,6 @@ static int print_verilog_mock_fpga_wrapper_connect_global_ports( return CMD_EXEC_SUCCESS; } - /******************************************************************** * Top-level function to generate a Verilog module of * a mock FPGA wrapper which contains an benchmark instance. @@ -444,7 +449,7 @@ int print_verilog_mock_fpga_wrapper( /* Print local wires */ print_verilog_testbench_shared_input_ports( fp, module_manager, global_ports, pin_constraints, atom_ctx, - netlist_annotation, benchmark_clock_port_names, + netlist_annotation, benchmark_clock_port_names, true, std::string(APPINST_PORT_POSTFIX), false); print_verilog_testbench_shared_benchmark_output_ports( @@ -454,8 +459,8 @@ int print_verilog_mock_fpga_wrapper( print_verilog_testbench_benchmark_instance( fp, circuit_name, std::string(APP_INSTANCE_NAME), std::string(), std::string(), std::string(APPINST_PORT_POSTFIX), - std::string(APPINST_PORT_POSTFIX), benchmark_clock_port_names, atom_ctx, - netlist_annotation, pin_constraints, bus_group, + std::string(APPINST_PORT_POSTFIX), benchmark_clock_port_names, true, + atom_ctx, netlist_annotation, pin_constraints, bus_group, options.explicit_port_mapping()); /* Connect FPGA top module global ports to constant or benchmark global @@ -470,7 +475,7 @@ int print_verilog_mock_fpga_wrapper( /* Connect I/Os to benchmark I/Os or constant driver */ print_verilog_mock_fpga_wrapper_connect_ios( fp, module_manager, top_module, atom_ctx, place_ctx, io_location_map, - pin_constraints, global_ports, netlist_annotation, bus_group, std::string(), + pin_constraints, global_ports, netlist_annotation, std::string(), std::string(APPINST_PORT_POSTFIX), std::string(APPINST_PORT_POSTFIX), benchmark_clock_port_names, (size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE); diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp index 565ff927d..09c900b3b 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp @@ -78,7 +78,8 @@ void print_verilog_testbench_benchmark_instance( const std::string& module_input_port_postfix, const std::string& module_output_port_postfix, const std::string& input_port_postfix, const std::string& output_port_postfix, - const std::vector& clock_port_names, const AtomContext& atom_ctx, + const std::vector& clock_port_names, + const bool& include_clock_port_postfix, const AtomContext& atom_ctx, const VprNetlistAnnotation& netlist_annotation, const PinConstraints& pin_constraints, const BusGroup& bus_group, const bool& use_explicit_port_map) { @@ -183,6 +184,8 @@ void print_verilog_testbench_benchmark_instance( clock_port_names.end(), port_names[iport])) { fp << input_port_postfix; + } else if (include_clock_port_postfix) { + fp << input_port_postfix; } pin_counter++; @@ -206,6 +209,8 @@ void print_verilog_testbench_benchmark_instance( clock_port_names.end(), port_names[iport])) { fp << input_port_postfix; + } else if (include_clock_port_postfix) { + fp << input_port_postfix; } } @@ -922,7 +927,8 @@ void print_verilog_testbench_shared_input_ports( const PinConstraints& pin_constraints, const AtomContext& atom_ctx, const VprNetlistAnnotation& netlist_annotation, const std::vector& clock_port_names, - const std::string& shared_input_port_postfix, const bool& use_reg_port) { + const bool& include_clock_ports, const std::string& shared_input_port_postfix, + const bool& use_reg_port) { /* Validate the file stream */ valid_file_stream(fp); @@ -945,7 +951,9 @@ void print_verilog_testbench_shared_input_ports( if (clock_port_names.end() != std::find(clock_port_names.begin(), clock_port_names.end(), block_name)) { - continue; + if (!include_clock_ports) { + continue; + } } /* Each logical block assumes a single-width port */ @@ -1114,7 +1122,8 @@ void print_verilog_testbench_shared_ports( const std::string& check_flag_port_postfix, const bool& no_self_checking) { print_verilog_testbench_shared_input_ports( fp, module_manager, global_ports, pin_constraints, atom_ctx, - netlist_annotation, clock_port_names, shared_input_port_postfix, true); + netlist_annotation, clock_port_names, false, shared_input_port_postfix, + true); print_verilog_testbench_shared_fpga_output_ports( fp, atom_ctx, netlist_annotation, fpga_output_port_postfix); diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.h b/openfpga/src/fpga_verilog/verilog_testbench_utils.h index e945683d6..44850bb41 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.h +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.h @@ -38,7 +38,8 @@ void print_verilog_testbench_benchmark_instance( const std::string& module_input_port_postfix, const std::string& module_output_port_postfix, const std::string& input_port_postfix, const std::string& output_port_postfix, - const std::vector& clock_port_names, const AtomContext& atom_ctx, + const std::vector& clock_port_names, + const bool& include_clock_port_postfix, const AtomContext& atom_ctx, const VprNetlistAnnotation& netlist_annotation, const PinConstraints& pin_constraints, const BusGroup& bus_group, const bool& use_explicit_port_map); @@ -96,7 +97,8 @@ void print_verilog_testbench_shared_input_ports( const PinConstraints& pin_constraints, const AtomContext& atom_ctx, const VprNetlistAnnotation& netlist_annotation, const std::vector& clock_port_names, - const std::string& shared_input_port_postfix, const bool& use_reg_port); + const bool& include_clock_ports, const std::string& shared_input_port_postfix, + const bool& use_reg_port); void print_verilog_testbench_shared_fpga_output_ports( std::fstream& fp, const AtomContext& atom_ctx, diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index b98a44007..02b77ad58 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -1146,7 +1146,7 @@ static void print_verilog_top_testbench_benchmark_instance( std::string(TOP_TESTBENCH_REFERENCE_INSTANCE_NAME), std::string(), std::string(), std::string(TOP_TESTBENCH_SHARED_INPUT_POSTFIX), std::string(TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX), clock_port_names, - atom_ctx, netlist_annotation, pin_constraints, bus_group, + false, atom_ctx, netlist_annotation, pin_constraints, bus_group, explicit_port_mapping); print_verilog_comment( diff --git a/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga index 636ac6e9d..90eae6d4e 100644 --- a/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga @@ -52,8 +52,8 @@ write_mock_fpga_wrapper --file ./SRC ${OPENFPGA_MOCK_WRAPPER_OPTIONS} ${OPENFPGA # - 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 none --file ./SRC --explicit_port_mapping -write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping +write_preconfigured_fabric_wrapper --embed_bitstream none --file ./SRC --explicit_port_mapping ${OPENFPGA_MOCK_WRAPPER_BGF} ${OPENFPGA_MOCK_WRAPPER_PCF} +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping ${OPENFPGA_MOCK_WRAPPER_BGF} ${OPENFPGA_MOCK_WRAPPER_PCF} # Write the SDC files for PnR backend # - Turn on every options here From 89f184e77954bd3ed790a4394c5617d88c73cc68 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 27 May 2023 12:19:28 -0700 Subject: [PATCH 053/391] [test] fixed a few bugs --- .../mock_wrapper/mock_wrapper_bgf/config/task.conf | 4 ++-- .../mock_wrapper_explicit_port_mapping/config/task.conf | 4 ++-- .../mock_wrapper_implicit_port_mapping/config/task.conf | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf index 1fd36c598..87008c7f5 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf @@ -9,7 +9,7 @@ [GENERAL] run_engine=openfpga_shell power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml -power_analysis = true +power_analysis = false spice_output=false verilog_output=true timeout_each_job = 20*60 @@ -18,7 +18,7 @@ fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/mock_wrapper_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_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml openfpga_mock_wrapper_options= openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf index 6487d74c5..1aa6907ce 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf @@ -9,7 +9,7 @@ [GENERAL] run_engine=openfpga_shell power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml -power_analysis = true +power_analysis = false spice_output=false verilog_output=true timeout_each_job = 20*60 @@ -18,7 +18,7 @@ fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/mock_wrapper_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_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml openfpga_mock_wrapper_options=--explicit_port_mapping openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf index 1fd36c598..87008c7f5 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf @@ -9,7 +9,7 @@ [GENERAL] run_engine=openfpga_shell power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml -power_analysis = true +power_analysis = false spice_output=false verilog_output=true timeout_each_job = 20*60 @@ -18,7 +18,7 @@ fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/mock_wrapper_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_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml openfpga_mock_wrapper_options= openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= From b3471f2703242fdacbd030a9117ee8d8edc6ecca Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 27 May 2023 12:34:10 -0700 Subject: [PATCH 054/391] [test] swap test name --- .../config/counter8_bus_group.xml | 0 .../config/pin_constraints_reset.xml | 0 .../mock_wrapper_bgf/config/task.conf | 26 ++++++++++++------- .../mock_wrapper_pcf/config/task.conf | 26 +++++++------------ 4 files changed, 26 insertions(+), 26 deletions(-) rename openfpga_flow/tasks/basic_tests/mock_wrapper/{mock_wrapper_pcf => mock_wrapper_bgf}/config/counter8_bus_group.xml (100%) rename openfpga_flow/tasks/basic_tests/mock_wrapper/{mock_wrapper_pcf => mock_wrapper_bgf}/config/pin_constraints_reset.xml (100%) diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/counter8_bus_group.xml b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/counter8_bus_group.xml similarity index 100% rename from openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/counter8_bus_group.xml rename to openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/counter8_bus_group.xml diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/pin_constraints_reset.xml b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/pin_constraints_reset.xml similarity index 100% rename from openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/pin_constraints_reset.xml rename to openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/pin_constraints_reset.xml diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf index 87008c7f5..93e225201 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf @@ -17,27 +17,33 @@ fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga -openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_openfpga.xml +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml -openfpga_mock_wrapper_options= +openfpga_mock_wrapper_options=--explicit_port_mapping openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= [ARCHITECTURES] -arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v -bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v -bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counters/counter_8bit_async_reset/counter.v [SYNTHESIS_PARAM] +# Yosys script parameters +bench_yosys_cell_sim_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_cell_sim.v +bench_yosys_dff_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_dff_map.v +bench_yosys_bram_map_rules_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_bram.txt +bench_yosys_bram_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_bram_map.v +bench_yosys_dsp_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_dsp_map.v +bench_yosys_dsp_map_parameters_common=-D DSP_A_MAXWIDTH=36 -D DSP_B_MAXWIDTH=36 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_NAME=mult_36x36 bench_read_verilog_options_common = -nolatches -bench0_top = and2 +bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_dff_flow.ys +bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys -bench1_top = or2 - -bench2_top = and2_latch +bench0_top = counter +bench0_openfpga_mock_wrapper_pcf=-pcf ${PATH:TASK_DIR}/config/pin_constraints_reset.xml +bench0_openfpga_mock_wrapper_bgf=-bgf ${PATH:TASK_DIR}/config/counter8_bus_group.xml [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf index 93e225201..87008c7f5 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf @@ -17,33 +17,27 @@ fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga -openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml +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/fixed_sim_openfpga.xml -openfpga_mock_wrapper_options=--explicit_port_mapping +openfpga_mock_wrapper_options= openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= [ARCHITECTURES] -arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counters/counter_8bit_async_reset/counter.v +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v [SYNTHESIS_PARAM] -# Yosys script parameters -bench_yosys_cell_sim_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_cell_sim.v -bench_yosys_dff_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_dff_map.v -bench_yosys_bram_map_rules_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_bram.txt -bench_yosys_bram_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_bram_map.v -bench_yosys_dsp_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_dsp_map.v -bench_yosys_dsp_map_parameters_common=-D DSP_A_MAXWIDTH=36 -D DSP_B_MAXWIDTH=36 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_NAME=mult_36x36 bench_read_verilog_options_common = -nolatches -bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_dff_flow.ys -bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys +bench0_top = and2 -bench0_top = counter -bench0_openfpga_mock_wrapper_pcf=-pcf ${PATH:TASK_DIR}/config/pin_constraints_reset.xml -bench0_openfpga_mock_wrapper_bgf=-bgf ${PATH:TASK_DIR}/config/counter8_bus_group.xml +bench1_top = or2 + +bench2_top = and2_latch [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From 27b8007d1b67cceafa4f84ed91e74d45dbed3c99 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 27 May 2023 12:45:29 -0700 Subject: [PATCH 055/391] [test] rework pcf support testcase for mock wrapper --- .../mock_wrapper_example_script.openfpga | 2 +- .../mock_wrapper_bgf/config/task.conf | 1 + .../config/task.conf | 1 + .../config/task.conf | 1 + .../config/and2_latch_pin_constraints.xml | 11 +++++++++++ .../config/repack_pin_constraints.xml | 14 ++++++++++++++ .../mock_wrapper_pcf/config/task.conf | 17 +++++++---------- 7 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/and2_latch_pin_constraints.xml create mode 100644 openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/repack_pin_constraints.xml diff --git a/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga index 90eae6d4e..feed9d0e7 100644 --- a/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga @@ -30,7 +30,7 @@ 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 +repack ${OPENFPGA_REPACK_DESIGN_CONSTRAINTS} #--verbose # Build the bitstream # - Output the fabric-independent bitstream to a file diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf index 93e225201..1b5e4a371 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf @@ -19,6 +19,7 @@ fpga_flow=yosys_vpr openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml +openfpga_repack_design_constraints= openfpga_mock_wrapper_options=--explicit_port_mapping openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf index 1aa6907ce..ce8a09b7f 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf @@ -19,6 +19,7 @@ fpga_flow=yosys_vpr openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/mock_wrapper_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/fixed_sim_openfpga.xml +openfpga_repack_design_constraints= openfpga_mock_wrapper_options=--explicit_port_mapping openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf index 87008c7f5..6e6d87b9f 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf @@ -19,6 +19,7 @@ fpga_flow=yosys_vpr openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/mock_wrapper_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/fixed_sim_openfpga.xml +openfpga_repack_design_constraints= openfpga_mock_wrapper_options= openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/and2_latch_pin_constraints.xml b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/and2_latch_pin_constraints.xml new file mode 100644 index 000000000..e6949c9d4 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/and2_latch_pin_constraints.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/repack_pin_constraints.xml b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/repack_pin_constraints.xml new file mode 100644 index 000000000..d695cf85e --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/repack_pin_constraints.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf index 87008c7f5..f1370d2fe 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf @@ -17,27 +17,24 @@ fpga_flow=yosys_vpr [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/mock_wrapper_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/fixed_sim_openfpga.xml +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_GlobalTile4Clk_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_4clock_sim_openfpga.xml +openfpga_repack_design_constraints=--design_constraints ${PATH:TASK_DIR}/config/repack_pin_constraints.xml openfpga_mock_wrapper_options= openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= [ARCHITECTURES] -arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_GlobalTile4Clk_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v -bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v -bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch_2clock/and2_latch_2clock.v [SYNTHESIS_PARAM] bench_read_verilog_options_common = -nolatches -bench0_top = and2 -bench1_top = or2 - -bench2_top = and2_latch +bench1_top = and2_latch_2clock +bench1_openfpga_pin_constraints=--design_constraints ${PATH:TASK_DIR}/config/and2_latch_pin_constraints.xml [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From 31b16ba9d7cfb7c419232fe90084b14dfdfb1b13 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 27 May 2023 12:47:57 -0700 Subject: [PATCH 056/391] [test] fixed a few bugs --- .../mock_wrapper/mock_wrapper_pcf/config/task.conf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf index f1370d2fe..3352a51bf 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf @@ -33,8 +33,10 @@ bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch [SYNTHESIS_PARAM] bench_read_verilog_options_common = -nolatches -bench1_top = and2_latch_2clock -bench1_openfpga_pin_constraints=--design_constraints ${PATH:TASK_DIR}/config/and2_latch_pin_constraints.xml +bench0_top = and2_latch_2clock +bench0_openfpga_pin_constraints=--design_constraints ${PATH:TASK_DIR}/config/and2_latch_pin_constraints.xml +bench0_openfpga_mock_wrapper_pcf=-pcf ${PATH:TASK_DIR}/config/and2_latch_pin_constraints.xml +bench0_openfpga_mock_wrapper_bgf= [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From ff4e0a827fae1a45bfb624e267e429a12ee085fe Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 27 May 2023 13:30:50 -0700 Subject: [PATCH 057/391] [doc] add missing option for mock efpga wrapper command --- .../openfpga_commands/fpga_verilog_commands.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst index 513f9ac07..b085df0bf 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst @@ -189,6 +189,10 @@ write_mock_fpga_wrapper Use explicit port mapping when writing the Verilog netlists + .. option:: --use_relative_path + + Force to use relative path in netlists when including other netlists. By default, this is off, which means that netlists use absolute paths when including other netlists + .. option:: --default_net_type Specify the default net type for the Verilog netlists. Currently, supported types are ``none`` and ``wire``. Default value: ``none``. From 32fcb81cd2b1a35af2531c5316366fd3cd41698d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 29 May 2023 00:02:25 +0000 Subject: [PATCH 058/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 1f34657a2..09830a174 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1025 +1.2.1053 From d8f2a89e4ddbb0f8742cac8998077cbcf5025514 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 May 2023 06:59:46 +0000 Subject: [PATCH 059/391] Bump yosys from `8596c5c` to `fb7af09` Bumps [yosys](https://github.com/YosysHQ/yosys) from `8596c5c` to `fb7af09`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/8596c5ce4915d1727d93df45fc58047f08886b41...fb7af093a88daf9fa20fb7c45877686f5516808b) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 8596c5ce4..fb7af093a 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 8596c5ce4915d1727d93df45fc58047f08886b41 +Subproject commit fb7af093a88daf9fa20fb7c45877686f5516808b From 24313964da9fd9e5f0c0ffd9ca62d3a6eee0a85c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 May 2023 06:59:29 +0000 Subject: [PATCH 060/391] Bump yosys from `fb7af09` to `43b807f` Bumps [yosys](https://github.com/YosysHQ/yosys) from `fb7af09` to `43b807f`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/fb7af093a88daf9fa20fb7c45877686f5516808b...43b807fe6fc9a124f2c6330e1522f0ceedbd1f4e) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index fb7af093a..43b807fe6 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit fb7af093a88daf9fa20fb7c45877686f5516808b +Subproject commit 43b807fe6fc9a124f2c6330e1522f0ceedbd1f4e From 652c418379e613787e47fa1c518c35d602f3977a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 31 May 2023 00:02:49 +0000 Subject: [PATCH 061/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 09830a174..0b9dc04d8 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1053 +1.2.1057 From 100fc6b95caff81dcf09aab7b8740ec6b053a4b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jun 2023 07:01:12 +0000 Subject: [PATCH 062/391] Bump yosys-plugins from `38a8a5d` to `4a05db9` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `38a8a5d` to `4a05db9`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/38a8a5dd5a7bb880e1dbcbcb14e97d6e9917da3d...4a05db9778b3ac45789e682940e0be73aaa1eacf) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index 38a8a5dd5..4a05db977 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit 38a8a5dd5a7bb880e1dbcbcb14e97d6e9917da3d +Subproject commit 4a05db9778b3ac45789e682940e0be73aaa1eacf From 0c671f189127f39a35d0c76b4edeb8d958dd260b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 00:02:30 +0000 Subject: [PATCH 063/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 0b9dc04d8..12fb4c3cf 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1057 +1.2.1063 From 34bee80758f1f7451da4b4e818f513785069d756 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 06:59:21 +0000 Subject: [PATCH 064/391] Bump yosys-plugins from `4a05db9` to `56f957c` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `4a05db9` to `56f957c`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/4a05db9778b3ac45789e682940e0be73aaa1eacf...56f957caa573658015cc4256cd9ebf2a0cc70a19) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index 4a05db977..56f957caa 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit 4a05db9778b3ac45789e682940e0be73aaa1eacf +Subproject commit 56f957caa573658015cc4256cd9ebf2a0cc70a19 From 5d94f275f31f075aca2ec422ea3d2b0e49db169f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 06:59:26 +0000 Subject: [PATCH 065/391] Bump yosys from `43b807f` to `88c849d` Bumps [yosys](https://github.com/YosysHQ/yosys) from `43b807f` to `88c849d`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/43b807fe6fc9a124f2c6330e1522f0ceedbd1f4e...88c849d112f4f8cf20617bbc98eac055956269c2) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 43b807fe6..88c849d11 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 43b807fe6fc9a124f2c6330e1522f0ceedbd1f4e +Subproject commit 88c849d112f4f8cf20617bbc98eac055956269c2 From 047a076ec9c36de3fb71d65641126307b081f292 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 00:02:18 +0000 Subject: [PATCH 066/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 12fb4c3cf..ee64a2b38 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1063 +1.2.1069 From 87fad178d4ae6cf40c7c04bbb6c4f5f882cbd287 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 06:58:48 +0000 Subject: [PATCH 067/391] Bump yosys-plugins from `56f957c` to `8edc027` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `56f957c` to `8edc027`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/56f957caa573658015cc4256cd9ebf2a0cc70a19...8edc027cdc484f86c5ac8698c696c74d04e6f92e) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index 56f957caa..8edc027cd 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit 56f957caa573658015cc4256cd9ebf2a0cc70a19 +Subproject commit 8edc027cdc484f86c5ac8698c696c74d04e6f92e From 0ccf4a6974f7394e0f4bf374d0c6231e3e9b992b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 06:58:50 +0000 Subject: [PATCH 068/391] Bump yosys from `88c849d` to `73badec` Bumps [yosys](https://github.com/YosysHQ/yosys) from `88c849d` to `73badec`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/88c849d112f4f8cf20617bbc98eac055956269c2...73badeccefc5dedd2681ae5bcbc2893d92be2354) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 88c849d11..73badecce 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 88c849d112f4f8cf20617bbc98eac055956269c2 +Subproject commit 73badeccefc5dedd2681ae5bcbc2893d92be2354 From 4d9e0c41ad72cde87620addc98f73b0267b5518c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Jun 2023 06:59:26 +0000 Subject: [PATCH 069/391] Bump yosys from `73badec` to `5813809` Bumps [yosys](https://github.com/YosysHQ/yosys) from `73badec` to `5813809`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/73badeccefc5dedd2681ae5bcbc2893d92be2354...5813809ad9afbe1c38f65c6aae7c3441d7614d0b) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 73badecce..5813809ad 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 73badeccefc5dedd2681ae5bcbc2893d92be2354 +Subproject commit 5813809ad9afbe1c38f65c6aae7c3441d7614d0b From 46640d39654fb622be0933a136e7982636cbacf3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 7 Jun 2023 17:00:01 -0700 Subject: [PATCH 070/391] [lib] update vtr to latest version --- vtr-verilog-to-routing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtr-verilog-to-routing b/vtr-verilog-to-routing index 90ee6e663..863989a74 160000 --- a/vtr-verilog-to-routing +++ b/vtr-verilog-to-routing @@ -1 +1 @@ -Subproject commit 90ee6e663a0e021604dff4552f5b7f9744cedd74 +Subproject commit 863989a74104ab6a8fb5db45e6d05e03de9fc80f From 327f7f4daba390d549df320ffc60b0e11538a3d0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 7 Jun 2023 18:54:48 -0700 Subject: [PATCH 071/391] [core] now adapt to latest API of DeviceGrid --- .../src/annotation/append_clock_rr_graph.cpp | 2 +- .../annotation/vpr_placement_annotation.cpp | 2 +- openfpga/src/base/openfpga_pb_pin_fixup.cpp | 7 +-- .../fabric/build_fabric_io_location_map.cpp | 15 +++--- .../src/fabric/build_routing_module_utils.cpp | 6 +-- openfpga/src/fabric/build_top_module.cpp | 43 ++++++++--------- .../fabric/build_top_module_connection.cpp | 26 +++++----- .../src/fabric/build_top_module_directs.cpp | 8 ++-- .../src/fabric/build_top_module_memory.cpp | 6 +-- .../src/fabric/build_top_module_utils.cpp | 5 +- .../fpga_bitstream/build_grid_bitstream.cpp | 14 +++--- .../src/fpga_sdc/analysis_sdc_grid_writer.cpp | 6 +-- .../src/tile_direct/build_tile_direct.cpp | 47 +++++++++---------- .../utils/openfpga_physical_tile_utils.cpp | 4 +- 14 files changed, 97 insertions(+), 94 deletions(-) diff --git a/openfpga/src/annotation/append_clock_rr_graph.cpp b/openfpga/src/annotation/append_clock_rr_graph.cpp index e59f19398..96fe420b3 100644 --- a/openfpga/src/annotation/append_clock_rr_graph.cpp +++ b/openfpga/src/annotation/append_clock_rr_graph.cpp @@ -396,7 +396,7 @@ static void try_find_and_add_clock_track2ipin_node( const e_side& pin_side, const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree, const ClockTreePinId& clk_pin) { t_physical_tile_type_ptr grid_type = - grids[grid_coord.x()][grid_coord.y()].type; + grids.get_physical_type(grid_coord.x(), grid_coord.y()); for (std::string tap_pin_name : clk_ntwk.tree_flatten_taps(clk_tree, clk_pin)) { /* tap pin name could be 'io[5:5].a2f[0]' */ diff --git a/openfpga/src/annotation/vpr_placement_annotation.cpp b/openfpga/src/annotation/vpr_placement_annotation.cpp index 9d7e73836..efdcb0ea6 100644 --- a/openfpga/src/annotation/vpr_placement_annotation.cpp +++ b/openfpga/src/annotation/vpr_placement_annotation.cpp @@ -32,7 +32,7 @@ void VprPlacementAnnotation::init_mapped_blocks(const DeviceGrid& grids) { for (size_t x = 0; x < grids.width(); ++x) { for (size_t y = 0; y < grids.height(); ++y) { /* Deposit invalid ids and we will fill later */ - blocks_[x][y].resize(grids[x][y].type->capacity, + blocks_[x][y].resize(grids.get_physical_type(x, y)->capacity, ClusterBlockId::INVALID()); } } diff --git a/openfpga/src/base/openfpga_pb_pin_fixup.cpp b/openfpga/src/base/openfpga_pb_pin_fixup.cpp index 0e55f8f9d..949b3dd9c 100644 --- a/openfpga/src/base/openfpga_pb_pin_fixup.cpp +++ b/openfpga/src/base/openfpga_pb_pin_fixup.cpp @@ -39,7 +39,7 @@ static void update_cluster_pin_with_post_routing_results( const e_side& border_side, const size_t& z, const bool& verbose) { /* Handle each pin */ auto logical_block = clustering_ctx.clb_nlist.block_type(blk_id); - auto physical_tile = device_ctx.grid[grid_coord.x()][grid_coord.y()].type; + auto physical_tile = device_ctx.grid.get_physical_type(grid_coord.x(), grid_coord.y()); for (int j = 0; j < logical_block->pb_type->num_pins; j++) { /* Get the ptc num for the pin in rr_graph, we need t consider the z offset @@ -195,7 +195,7 @@ void update_pb_pin_with_post_routing_results( for (size_t x = 1; x < device_ctx.grid.width() - 1; ++x) { for (size_t y = 1; y < device_ctx.grid.height() - 1; ++y) { /* Bypass the EMPTY tiles */ - if (true == is_empty_type(device_ctx.grid[x][y].type)) { + if (true == is_empty_type(device_ctx.grid.get_physical_type(x, y))) { continue; } /* Get the mapped blocks to this grid */ @@ -222,9 +222,10 @@ void update_pb_pin_with_post_routing_results( for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coord : io_coordinates[io_side]) { + t_physical_tile_type_ptr phy_tile_type = device_ctx.grid.get_physical_type(io_coord.x(), io_coord.y()); /* Bypass EMPTY grid */ if (true == - is_empty_type(device_ctx.grid[io_coord.x()][io_coord.y()].type)) { + is_empty_type(phy_tile_type)) { continue; } /* Get the mapped blocks to this grid */ diff --git a/openfpga/src/fabric/build_fabric_io_location_map.cpp b/openfpga/src/fabric/build_fabric_io_location_map.cpp index 4b58f4c04..b84fd333a 100644 --- a/openfpga/src/fabric/build_fabric_io_location_map.cpp +++ b/openfpga/src/fabric/build_fabric_io_location_map.cpp @@ -50,15 +50,16 @@ IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, ModuleId child = module_manager.io_children(top_module)[ichild]; vtr::Point coord = module_manager.io_child_coordinates(top_module)[ichild]; + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(coord.x(), coord.y()); /* Bypass EMPTY grid */ - if (true == is_empty_type(grids[coord.x()][coord.y()].type)) { + if (true == is_empty_type(phy_tile_type)) { continue; } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids[coord.x()][coord.y()].width_offset) || - (0 < grids[coord.x()][coord.y()].height_offset)) { + if ((0 < grids.get_width_offset(coord.x(), coord.y())) || + (0 < grids.get_height_offset(coord.x(), coord.y()))) { continue; } @@ -69,14 +70,14 @@ IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, /* MUST DO: register in io location mapping! * I/O location mapping is a critical look-up for testbench generators */ - if (size_t(grids[coord.x()][coord.y()].type->capacity) != + if (size_t(phy_tile_type->capacity) != module_manager.io_children(child).size()) { VTR_LOG("%s[%ld][%ld] capacity: %d while io_child number is %d", - grids[coord.x()][coord.y()].type->name, coord.x(), coord.y(), - grids[coord.x()][coord.y()].type->capacity, + phy_tile_type->name, coord.x(), coord.y(), + phy_tile_type->capacity, module_manager.io_children(child).size()); } - VTR_ASSERT(size_t(grids[coord.x()][coord.y()].type->capacity) == + VTR_ASSERT(size_t(phy_tile_type->capacity) == module_manager.io_children(child).size()); for (size_t isubchild = 0; isubchild < module_manager.io_children(child).size(); ++isubchild) { diff --git a/openfpga/src/fabric/build_routing_module_utils.cpp b/openfpga/src/fabric/build_routing_module_utils.cpp index 9ecef274d..83b167a73 100644 --- a/openfpga/src/fabric/build_routing_module_utils.cpp +++ b/openfpga/src/fabric/build_routing_module_utils.cpp @@ -77,8 +77,7 @@ std::string generate_sb_module_grid_port_name( int pin_id = rr_graph.node_pin_num(rr_node); e_side pin_side = get_rr_graph_single_node_side(rr_graph, rr_node); t_physical_tile_type_ptr physical_tile = - vpr_device_grid[rr_graph.node_xlow(rr_node)][rr_graph.node_ylow(rr_node)] - .type; + vpr_device_grid.get_physical_type(rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node)); int pin_width_offset = physical_tile->pin_width_offset[pin_id]; int pin_height_offset = physical_tile->pin_height_offset[pin_id]; BasicPort pin_info = @@ -112,8 +111,7 @@ std::string generate_cb_module_grid_port_name( int pin_id = rr_graph.node_pin_num(rr_node); e_side pin_side = get_rr_graph_single_node_side(rr_graph, rr_node); t_physical_tile_type_ptr physical_tile = - vpr_device_grid[rr_graph.node_xlow(rr_node)][rr_graph.node_ylow(rr_node)] - .type; + vpr_device_grid.get_physical_type(rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node)); int pin_width_offset = physical_tile->pin_width_offset[pin_id]; int pin_height_offset = physical_tile->pin_height_offset[pin_id]; BasicPort pin_info = diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index 3de1dc176..cfe9ac7c9 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -108,22 +108,23 @@ static vtr::Matrix add_top_module_grid_instances( for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); /* Bypass EMPTY grid */ if (true == - is_empty_type(grids[io_coordinate.x()][io_coordinate.y()].type)) { + is_empty_type(phy_tile_type)) { continue; } /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids[io_coordinate.x()][io_coordinate.y()].width_offset) || - (0 < grids[io_coordinate.x()][io_coordinate.y()].height_offset)) { + if ((0 < grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || + (0 < grids.get_height_offset(io_coordinate.x(),io_coordinate.y()))) { /* Find the root of this grid, the instance id should be valid. * We just copy it here */ vtr::Point root_grid_coord( io_coordinate.x() - - grids[io_coordinate.x()][io_coordinate.y()].width_offset, + grids.get_width_offset(io_coordinate.x(), io_coordinate.y()), io_coordinate.y() - - grids[io_coordinate.x()][io_coordinate.y()].height_offset); + grids.get_height_offset(io_coordinate.x(), io_coordinate.y())); VTR_ASSERT(size_t(-1) != grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = @@ -135,7 +136,7 @@ static vtr::Matrix add_top_module_grid_instances( grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = add_top_module_grid_instance( module_manager, top_module, - grids[io_coordinate.x()][io_coordinate.y()].type, io_side, + phy_tile_type, io_side, io_coordinate); } } @@ -148,18 +149,19 @@ static vtr::Matrix add_top_module_grid_instances( */ for (size_t ix = 1; ix < grids.width() - 1; ++ix) { for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(ix, iy); /* Bypass EMPTY grid */ - if (true == is_empty_type(grids[ix][iy].type)) { + if (true == is_empty_type(phy_tile_type)) { continue; } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids[ix][iy].width_offset) || - (0 < grids[ix][iy].height_offset)) { + if ((0 < grids.get_width_offset(ix, iy)) || + (0 < grids.get_height_offset(ix, iy))) { /* Find the root of this grid, the instance id should be valid. * We just copy it here */ - vtr::Point root_grid_coord(ix - grids[ix][iy].width_offset, - iy - grids[ix][iy].height_offset); + vtr::Point root_grid_coord(ix - grids.get_width_offset(ix, iy), + iy - grids.get_height_offset(ix, iy)); VTR_ASSERT(size_t(-1) != grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); grid_instance_ids[ix][iy] = @@ -169,7 +171,7 @@ static vtr::Matrix add_top_module_grid_instances( /* Add a grid module to top_module*/ vtr::Point grid_coord(ix, iy); grid_instance_ids[ix][iy] = add_top_module_grid_instance( - module_manager, top_module, grids[ix][iy].type, NUM_SIDES, grid_coord); + module_manager, top_module, phy_tile_type, NUM_SIDES, grid_coord); } } @@ -324,18 +326,17 @@ static void add_top_module_io_children( for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coord : io_coordinates[io_side]) { + t_physical_tile_type_ptr grid_type = grids.get_physical_type(io_coord.x(), io_coord.y()); /* Bypass EMPTY grid */ - if (true == is_empty_type(grids[io_coord.x()][io_coord.y()].type)) { + if (true == is_empty_type(grid_type)) { continue; } /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids[io_coord.x()][io_coord.y()].width_offset) || - (0 < grids[io_coord.x()][io_coord.y()].height_offset)) { + if ((0 < grids.get_width_offset(io_coord.x(), io_coord.y())) || + (0 < grids.get_height_offset(io_coord.x(), io_coord.y()))) { continue; } /* Find the module name for this type of grid */ - t_physical_tile_type_ptr grid_type = - grids[io_coord.x()][io_coord.y()].type; std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); std::string grid_module_name = generate_grid_block_module_name( grid_module_name_prefix, std::string(grid_type->name), @@ -393,17 +394,17 @@ static void add_top_module_io_children( /* Now walk through the coordinates */ for (vtr::Point coord : coords) { + t_physical_tile_type_ptr grid_type = grids.get_physical_type(coord.x(), coord.y()); /* Bypass EMPTY grid */ - if (true == is_empty_type(grids[coord.x()][coord.y()].type)) { + if (true == is_empty_type(grid_type)) { continue; } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids[coord.x()][coord.y()].width_offset) || - (0 < grids[coord.x()][coord.y()].height_offset)) { + if ((0 < grids.get_width_offset(coord.x(), coord.y())) || + (0 < grids.get_height_offset(coord.x(), coord.y()))) { continue; } /* Find the module name for this type of grid */ - t_physical_tile_type_ptr grid_type = grids[coord.x()][coord.y()].type; std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); std::string grid_module_name = generate_grid_block_module_name( grid_module_name_prefix, std::string(grid_type->name), diff --git a/openfpga/src/fabric/build_top_module_connection.cpp b/openfpga/src/fabric/build_top_module_connection.cpp index d2284d318..15dbd04f3 100644 --- a/openfpga/src/fabric/build_top_module_connection.cpp +++ b/openfpga/src/fabric/build_top_module_connection.cpp @@ -127,7 +127,7 @@ static void add_top_module_nets_connect_grids_and_sb( rr_gsb.get_opin_node(side_manager.get_side(), inode)); t_physical_tile_type_ptr grid_type_descriptor = - grids[grid_coordinate.x()][grid_coordinate.y()].type; + grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); size_t src_grid_pin_width = grid_type_descriptor->pin_width_offset[src_grid_pin_index]; size_t src_grid_pin_height = @@ -301,7 +301,7 @@ static void add_top_module_nets_connect_grids_and_sb_with_duplicated_pins( rr_gsb.get_opin_node(side_manager.get_side(), inode)); t_physical_tile_type_ptr grid_type_descriptor = - grids[grid_coordinate.x()][grid_coordinate.y()].type; + grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); size_t src_grid_pin_width = grid_type_descriptor->pin_width_offset[src_grid_pin_index]; size_t src_grid_pin_height = @@ -520,7 +520,7 @@ static void add_top_module_nets_connect_grids_and_cb( size_t sink_grid_pin_index = rr_graph.node_pin_num(instance_ipin_node); t_physical_tile_type_ptr grid_type_descriptor = - grids[grid_coordinate.x()][grid_coordinate.y()].type; + grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); size_t sink_grid_pin_width = grid_type_descriptor->pin_width_offset[sink_grid_pin_index]; size_t sink_grid_pin_height = @@ -859,7 +859,7 @@ static int build_top_module_global_net_for_given_grid_module( const vtr::Point& grid_coordinate, const e_side& border_side, const vtr::Matrix& grid_instance_ids) { t_physical_tile_type_ptr physical_tile = - grids[grid_coordinate.x()][grid_coordinate.y()].type; + grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); /* Find the module name for this type of grid */ std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); std::string grid_module_name = generate_grid_block_module_name( @@ -1038,18 +1038,19 @@ static int build_top_module_global_net_from_grid_modules( /* Spot the port from child modules from core grids */ for (size_t ix = start_coord.x(); ix < end_coord.x(); ++ix) { for (size_t iy = start_coord.y(); iy < end_coord.y(); ++iy) { + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(ix, iy); /* Bypass EMPTY tiles */ - if (true == is_empty_type(grids[ix][iy].type)) { + if (true == is_empty_type(phy_tile_type)) { continue; } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids[ix][iy].width_offset) || - (0 < grids[ix][iy].height_offset)) { + if ((0 < grids.get_width_offset(ix, iy)) || + (0 < grids.get_height_offset(ix, iy))) { continue; } /* Bypass the tiles whose names do not match */ - if (std::string(grids[ix][iy].type->name) != tile_name) { + if (std::string(phy_tile_type->name) != tile_name) { continue; } @@ -1067,21 +1068,22 @@ static int build_top_module_global_net_from_grid_modules( /* Walk through all the grids on the perimeter, which are I/O grids */ for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); /* Bypass EMPTY grid */ if (true == - is_empty_type(grids[io_coordinate.x()][io_coordinate.y()].type)) { + is_empty_type(phy_tile_type)) { continue; } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids[io_coordinate.x()][io_coordinate.y()].width_offset) || - (0 < grids[io_coordinate.x()][io_coordinate.y()].height_offset)) { + if ((0 < grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || + (0 < grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { continue; } /* Bypass the tiles whose names do not match */ if (std::string( - grids[io_coordinate.x()][io_coordinate.y()].type->name) != + phy_tile_type->name) != tile_name) { continue; } diff --git a/openfpga/src/fabric/build_top_module_directs.cpp b/openfpga/src/fabric/build_top_module_directs.cpp index 9b0179493..6af69cdb2 100644 --- a/openfpga/src/fabric/build_top_module_directs.cpp +++ b/openfpga/src/fabric/build_top_module_directs.cpp @@ -47,7 +47,7 @@ static void add_module_nets_tile_direct_connection( vtr::Point src_clb_coord = tile_direct.from_tile_coordinate(tile_direct_id); t_physical_tile_type_ptr src_grid_type = - grids[src_clb_coord.x()][src_clb_coord.y()].type; + grids.get_physical_type(src_clb_coord.x(), src_clb_coord.y()); e_side src_grid_border_side = find_grid_border_side(device_size, src_clb_coord); std::string src_module_name_prefix(GRID_MODULE_NAME_PREFIX); @@ -64,7 +64,7 @@ static void add_module_nets_tile_direct_connection( vtr::Point des_clb_coord = tile_direct.to_tile_coordinate(tile_direct_id); t_physical_tile_type_ptr sink_grid_type = - grids[des_clb_coord.x()][des_clb_coord.y()].type; + grids.get_physical_type(des_clb_coord.x(), des_clb_coord.y()); e_side sink_grid_border_side = find_grid_border_side(device_size, des_clb_coord); std::string sink_module_name_prefix(GRID_MODULE_NAME_PREFIX); @@ -114,7 +114,7 @@ static void add_module_nets_tile_direct_connection( size_t src_tile_pin = tile_direct.from_tile_pin(tile_direct_id); t_physical_tile_type_ptr src_grid_type_descriptor = - grids[src_clb_coord.x()][src_clb_coord.y()].type; + grids.get_physical_type(src_clb_coord.x(), src_clb_coord.y()); size_t src_pin_width = src_grid_type_descriptor->pin_width_offset[src_tile_pin]; size_t src_pin_height = @@ -148,7 +148,7 @@ static void add_module_nets_tile_direct_connection( size_t sink_tile_pin = tile_direct.to_tile_pin(tile_direct_id); t_physical_tile_type_ptr sink_grid_type_descriptor = - grids[des_clb_coord.x()][des_clb_coord.y()].type; + grids.get_physical_type(des_clb_coord.x(), des_clb_coord.y()); size_t sink_pin_width = sink_grid_type_descriptor->pin_width_offset[src_tile_pin]; size_t sink_pin_height = diff --git a/openfpga/src/fabric/build_top_module_memory.cpp b/openfpga/src/fabric/build_top_module_memory.cpp index 9b61e0a55..74a8713a1 100644 --- a/openfpga/src/fabric/build_top_module_memory.cpp +++ b/openfpga/src/fabric/build_top_module_memory.cpp @@ -191,7 +191,7 @@ static void organize_top_module_tile_memory_modules( /* Find the module name for this type of grid */ t_physical_tile_type_ptr grid_type = - grids[tile_coord.x()][tile_coord.y()].type; + grids.get_physical_type(tile_coord.x(), tile_coord.y()); /* Skip EMPTY Grid */ if (true == is_empty_type(grid_type)) { @@ -199,8 +199,8 @@ static void organize_top_module_tile_memory_modules( } /* Skip width > 1 or height > 1 Grid, which should already been processed when * offset=0 */ - if ((0 < grids[tile_coord.x()][tile_coord.y()].width_offset) || - (0 < grids[tile_coord.x()][tile_coord.y()].height_offset)) { + if ((0 < grids.get_width_offset(tile_coord.x(), tile_coord.y())) || + (0 < grids.get_height_offset(tile_coord.x(), tile_coord.y()))) { return; } diff --git a/openfpga/src/fabric/build_top_module_utils.cpp b/openfpga/src/fabric/build_top_module_utils.cpp index c4405623a..f9c4f53ac 100644 --- a/openfpga/src/fabric/build_top_module_utils.cpp +++ b/openfpga/src/fabric/build_top_module_utils.cpp @@ -28,10 +28,11 @@ std::string generate_grid_block_module_name_in_top_module( /* Determine if the grid locates at the border */ vtr::Point device_size(grids.width(), grids.height()); e_side border_side = find_grid_border_side(device_size, grid_coord); + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(grid_coord.x(), grid_coord.y()); return generate_grid_block_module_name( - prefix, std::string(grids[grid_coord.x()][grid_coord.y()].type->name), - is_io_type(grids[grid_coord.x()][grid_coord.y()].type), border_side); + prefix, std::string(phy_tile_type->name), + is_io_type(phy_tile_type), border_side); } /******************************************************************** diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index 52f07e4db..4a3945c00 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -736,7 +736,7 @@ static void build_physical_block_bitstream( const vtr::Point& grid_coord, const e_side& border_side) { /* Create a block for the grid in bitstream manager */ t_physical_tile_type_ptr grid_type = - grids[grid_coord.x()][grid_coord.y()].type; + grids.get_physical_type(grid_coord.x(), grid_coord.y()); std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); /* Early exit if this parent module has no configurable child modules */ @@ -831,12 +831,12 @@ void build_grid_bitstream( for (size_t ix = 1; ix < grids.width() - 1; ++ix) { for (size_t iy = 1; iy < grids.height() - 1; ++iy) { /* Bypass EMPTY grid */ - if (true == is_empty_type(grids[ix][iy].type)) { + if (true == is_empty_type(grids.get_physical_type(ix, iy))) { continue; } /* Skip width > 1 or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids[ix][iy].width_offset) || - (0 < grids[ix][iy].height_offset)) { + if ((0 < grids.get_width_offset(ix, iy)) || + (0 < grids.get_height_offset(ix, iy))) { continue; } /* Add a grid module to top_module*/ @@ -860,12 +860,12 @@ void build_grid_bitstream( for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { /* Bypass EMPTY grid */ if (true == - is_empty_type(grids[io_coordinate.x()][io_coordinate.y()].type)) { + is_empty_type(grids.get_physical_type(io_coordinate.x(), io_coordinate.y()))) { continue; } /* Skip height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids[io_coordinate.x()][io_coordinate.y()].width_offset) || - (0 < grids[io_coordinate.x()][io_coordinate.y()].height_offset)) { + if ((0 < grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || + (0 < grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { continue; } build_physical_block_bitstream( diff --git a/openfpga/src/fpga_sdc/analysis_sdc_grid_writer.cpp b/openfpga/src/fpga_sdc/analysis_sdc_grid_writer.cpp index 1aeeef090..94bda6b5b 100644 --- a/openfpga/src/fpga_sdc/analysis_sdc_grid_writer.cpp +++ b/openfpga/src/fpga_sdc/analysis_sdc_grid_writer.cpp @@ -604,14 +604,14 @@ static void print_analysis_sdc_disable_unused_grid( valid_file_stream(fp); t_physical_tile_type_ptr grid_type = - grids[grid_coordinate.x()][grid_coordinate.y()].type; + grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); /* Bypass conditions for grids : * 1. EMPTY type, which is by nature unused * 2. Offset > 0, which has already been processed when offset = 0 */ if ((true == is_empty_type(grid_type)) || - (0 < grids[grid_coordinate.x()][grid_coordinate.y()].width_offset) || - (0 < grids[grid_coordinate.x()][grid_coordinate.y()].height_offset)) { + (0 < grids.get_width_offset(grid_coordinate.x(), grid_coordinate.y())) || + (0 < grids.get_height_offset(grid_coordinate.x(), grid_coordinate.y()))) { return; } diff --git a/openfpga/src/tile_direct/build_tile_direct.cpp b/openfpga/src/tile_direct/build_tile_direct.cpp index 7ea380d8c..c3dd24c36 100644 --- a/openfpga/src/tile_direct/build_tile_direct.cpp +++ b/openfpga/src/tile_direct/build_tile_direct.cpp @@ -168,7 +168,7 @@ static vtr::Point find_grid_coordinate_given_type( continue; } if (wanted_grid_type_name == - std::string(grids[coord.x()][coord.y()].type->name)) { + std::string(grids.get_physical_type(coord.x(), coord.y())->name)) { return coord; } } @@ -401,13 +401,14 @@ static void build_inner_column_row_tile_direct( /* Walk through the device fabric and find the grid that fit the source */ for (size_t x = 0; x < device_ctx.grid.width(); ++x) { for (size_t y = 0; y < device_ctx.grid.height(); ++y) { + t_physical_tile_type_ptr from_phy_tile_type = device_ctx.grid.get_physical_type(x, y); /* Bypass empty grid */ - if (true == is_empty_type(device_ctx.grid[x][y].type)) { + if (true == is_empty_type(from_phy_tile_type)) { continue; } /* Bypass the grid that does not fit the from_tile name */ - if (from_tile_name != std::string(device_ctx.grid[x][y].type->name)) { + if (from_tile_name != std::string(from_phy_tile_type->name)) { continue; } @@ -418,8 +419,8 @@ static void build_inner_column_row_tile_direct( for (const e_side& from_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ std::vector from_pins = find_physical_tile_pin_id( - device_ctx.grid[x][y].type, device_ctx.grid[x][y].width_offset, - device_ctx.grid[x][y].height_offset, from_tile_port, from_side); + from_phy_tile_type, device_ctx.grid.get_width_offset(x, y), + device_ctx.grid.get_height_offset(x, y), from_tile_port, from_side); /* If nothing found, we can continue */ if (0 == from_pins.size()) { continue; @@ -435,10 +436,10 @@ static void build_inner_column_row_tile_direct( continue; } + t_physical_tile_type_ptr to_phy_tile_type = device_ctx.grid.get_physical_type(to_grid_coord.x(), to_grid_coord.y()); /* Bypass the grid that does not fit the from_tile name */ if (to_tile_name != - std::string(device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()] - .type->name)) { + std::string(to_phy_tile_type->name)) { continue; } @@ -449,9 +450,9 @@ static void build_inner_column_row_tile_direct( for (const e_side& to_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ std::vector to_pins = find_physical_tile_pin_id( - device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].type, - device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].width_offset, - device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].height_offset, + to_phy_tile_type, + device_ctx.grid.get_width_offset(to_grid_coord.x(), to_grid_coord.y()), + device_ctx.grid.get_height_offset(to_grid_coord.x(), to_grid_coord.y()), to_tile_port, to_side); /* If nothing found, we can continue */ if (0 == to_pins.size()) { @@ -590,11 +591,9 @@ static void build_inter_column_row_tile_direct( for (const e_side& from_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ std::vector from_pins = find_physical_tile_pin_id( - device_ctx.grid[from_grid_coord.x()][from_grid_coord.y()].type, - device_ctx.grid[from_grid_coord.x()][from_grid_coord.y()] - .width_offset, - device_ctx.grid[from_grid_coord.x()][from_grid_coord.y()] - .height_offset, + device_ctx.grid.get_physical_type(from_grid_coord.x(), from_grid_coord.y()), + device_ctx.grid.get_width_offset(from_grid_coord.x(), from_grid_coord.y()), + device_ctx.grid.get_height_offset(from_grid_coord.x(), from_grid_coord.y()), from_tile_port, from_side); /* If nothing found, we can continue */ if (0 == from_pins.size()) { @@ -620,9 +619,9 @@ static void build_inter_column_row_tile_direct( for (const e_side& to_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ std::vector to_pins = find_physical_tile_pin_id( - device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].type, - device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].width_offset, - device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].height_offset, + device_ctx.grid.get_physical_type(to_grid_coord.x(), to_grid_coord.y()), + device_ctx.grid.get_width_offset(to_grid_coord.x(), to_grid_coord.y()), + device_ctx.grid.get_height_offset(to_grid_coord.x(), to_grid_coord.y()), to_tile_port, to_side); /* If nothing found, we can continue */ if (0 == to_pins.size()) { @@ -699,9 +698,9 @@ static void build_inter_column_row_tile_direct( for (const e_side& from_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ std::vector from_pins = find_physical_tile_pin_id( - device_ctx.grid[from_grid_coord.x()][from_grid_coord.y()].type, - device_ctx.grid[from_grid_coord.x()][from_grid_coord.y()].width_offset, - device_ctx.grid[from_grid_coord.x()][from_grid_coord.y()].height_offset, + device_ctx.grid.get_physical_type(from_grid_coord.x(), from_grid_coord.y()), + device_ctx.grid.get_width_offset(from_grid_coord.x(), from_grid_coord.y()), + device_ctx.grid.get_height_offset(from_grid_coord.x(), from_grid_coord.y()), from_tile_port, from_side); /* If nothing found, we can continue */ if (0 == from_pins.size()) { @@ -727,9 +726,9 @@ static void build_inter_column_row_tile_direct( for (const e_side& to_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ std::vector to_pins = find_physical_tile_pin_id( - device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].type, - device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].width_offset, - device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].height_offset, + device_ctx.grid.get_physical_type(to_grid_coord.x(), to_grid_coord.y()), + device_ctx.grid.get_width_offset(to_grid_coord.x(), to_grid_coord.y()), + device_ctx.grid.get_height_offset(to_grid_coord.x(), to_grid_coord.y()), to_tile_port, to_side); /* If nothing found, we can continue */ if (0 == to_pins.size()) { diff --git a/openfpga/src/utils/openfpga_physical_tile_utils.cpp b/openfpga/src/utils/openfpga_physical_tile_utils.cpp index c4272ab09..7ab62538a 100644 --- a/openfpga/src/utils/openfpga_physical_tile_utils.cpp +++ b/openfpga/src/utils/openfpga_physical_tile_utils.cpp @@ -89,7 +89,7 @@ std::set find_physical_io_tile_located_sides( for (size_t ix = 1; ix < grids.width() - 1; ++ix) { for (size_t iy = 1; iy < grids.height() - 1; ++iy) { /* If located in center, we add a NUM_SIDES and finish */ - if (physical_tile == grids[ix][iy].type) { + if (physical_tile == grids.get_physical_type(ix, iy)) { io_sides.insert(NUM_SIDES); center_io = true; break; @@ -108,7 +108,7 @@ std::set find_physical_io_tile_located_sides( for (const e_side& fpga_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[fpga_side]) { /* If located in center, we add a NUM_SIDES and finish */ - if (physical_tile == grids[io_coordinate.x()][io_coordinate.y()].type) { + if (physical_tile == grids.get_physical_type(io_coordinate.x(), io_coordinate.y())) { io_sides.insert(fpga_side); break; } From ee59bdb675d02220ee02e3daff20e9df73108557 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 7 Jun 2023 18:55:34 -0700 Subject: [PATCH 072/391] [core] code format --- openfpga/src/base/openfpga_pb_pin_fixup.cpp | 9 ++- .../fabric/build_fabric_io_location_map.cpp | 3 +- .../src/fabric/build_routing_module_utils.cpp | 8 +- openfpga/src/fabric/build_top_module.cpp | 25 ++++--- .../fabric/build_top_module_connection.cpp | 19 ++--- .../src/fabric/build_top_module_utils.cpp | 7 +- .../fpga_bitstream/build_grid_bitstream.cpp | 4 +- .../src/tile_direct/build_tile_direct.cpp | 74 +++++++++++-------- .../utils/openfpga_physical_tile_utils.cpp | 3 +- 9 files changed, 87 insertions(+), 65 deletions(-) diff --git a/openfpga/src/base/openfpga_pb_pin_fixup.cpp b/openfpga/src/base/openfpga_pb_pin_fixup.cpp index 949b3dd9c..952a495d1 100644 --- a/openfpga/src/base/openfpga_pb_pin_fixup.cpp +++ b/openfpga/src/base/openfpga_pb_pin_fixup.cpp @@ -39,7 +39,8 @@ static void update_cluster_pin_with_post_routing_results( const e_side& border_side, const size_t& z, const bool& verbose) { /* Handle each pin */ auto logical_block = clustering_ctx.clb_nlist.block_type(blk_id); - auto physical_tile = device_ctx.grid.get_physical_type(grid_coord.x(), grid_coord.y()); + auto physical_tile = + device_ctx.grid.get_physical_type(grid_coord.x(), grid_coord.y()); for (int j = 0; j < logical_block->pb_type->num_pins; j++) { /* Get the ptc num for the pin in rr_graph, we need t consider the z offset @@ -222,10 +223,10 @@ void update_pb_pin_with_post_routing_results( for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coord : io_coordinates[io_side]) { - t_physical_tile_type_ptr phy_tile_type = device_ctx.grid.get_physical_type(io_coord.x(), io_coord.y()); + t_physical_tile_type_ptr phy_tile_type = + device_ctx.grid.get_physical_type(io_coord.x(), io_coord.y()); /* Bypass EMPTY grid */ - if (true == - is_empty_type(phy_tile_type)) { + if (true == is_empty_type(phy_tile_type)) { continue; } /* Get the mapped blocks to this grid */ diff --git a/openfpga/src/fabric/build_fabric_io_location_map.cpp b/openfpga/src/fabric/build_fabric_io_location_map.cpp index b84fd333a..f099e546c 100644 --- a/openfpga/src/fabric/build_fabric_io_location_map.cpp +++ b/openfpga/src/fabric/build_fabric_io_location_map.cpp @@ -50,7 +50,8 @@ IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, ModuleId child = module_manager.io_children(top_module)[ichild]; vtr::Point coord = module_manager.io_child_coordinates(top_module)[ichild]; - t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(coord.x(), coord.y()); + t_physical_tile_type_ptr phy_tile_type = + grids.get_physical_type(coord.x(), coord.y()); /* Bypass EMPTY grid */ if (true == is_empty_type(phy_tile_type)) { diff --git a/openfpga/src/fabric/build_routing_module_utils.cpp b/openfpga/src/fabric/build_routing_module_utils.cpp index 83b167a73..64a1472ea 100644 --- a/openfpga/src/fabric/build_routing_module_utils.cpp +++ b/openfpga/src/fabric/build_routing_module_utils.cpp @@ -76,8 +76,8 @@ std::string generate_sb_module_grid_port_name( /* Collect the attributes of the rr_node required to generate the port name */ int pin_id = rr_graph.node_pin_num(rr_node); e_side pin_side = get_rr_graph_single_node_side(rr_graph, rr_node); - t_physical_tile_type_ptr physical_tile = - vpr_device_grid.get_physical_type(rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node)); + t_physical_tile_type_ptr physical_tile = vpr_device_grid.get_physical_type( + rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node)); int pin_width_offset = physical_tile->pin_width_offset[pin_id]; int pin_height_offset = physical_tile->pin_height_offset[pin_id]; BasicPort pin_info = @@ -110,8 +110,8 @@ std::string generate_cb_module_grid_port_name( /* Collect the attributes of the rr_node required to generate the port name */ int pin_id = rr_graph.node_pin_num(rr_node); e_side pin_side = get_rr_graph_single_node_side(rr_graph, rr_node); - t_physical_tile_type_ptr physical_tile = - vpr_device_grid.get_physical_type(rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node)); + t_physical_tile_type_ptr physical_tile = vpr_device_grid.get_physical_type( + rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node)); int pin_width_offset = physical_tile->pin_width_offset[pin_id]; int pin_height_offset = physical_tile->pin_height_offset[pin_id]; BasicPort pin_info = diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index cfe9ac7c9..395ca26f2 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -108,15 +108,15 @@ static vtr::Matrix add_top_module_grid_instances( for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { - t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); + t_physical_tile_type_ptr phy_tile_type = + grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); /* Bypass EMPTY grid */ - if (true == - is_empty_type(phy_tile_type)) { + if (true == is_empty_type(phy_tile_type)) { continue; } /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ if ((0 < grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || - (0 < grids.get_height_offset(io_coordinate.x(),io_coordinate.y()))) { + (0 < grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { /* Find the root of this grid, the instance id should be valid. * We just copy it here */ @@ -134,10 +134,8 @@ static vtr::Matrix add_top_module_grid_instances( /* Add a grid module to top_module*/ grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = - add_top_module_grid_instance( - module_manager, top_module, - phy_tile_type, io_side, - io_coordinate); + add_top_module_grid_instance(module_manager, top_module, phy_tile_type, + io_side, io_coordinate); } } @@ -160,8 +158,9 @@ static vtr::Matrix add_top_module_grid_instances( /* Find the root of this grid, the instance id should be valid. * We just copy it here */ - vtr::Point root_grid_coord(ix - grids.get_width_offset(ix, iy), - iy - grids.get_height_offset(ix, iy)); + vtr::Point root_grid_coord( + ix - grids.get_width_offset(ix, iy), + iy - grids.get_height_offset(ix, iy)); VTR_ASSERT(size_t(-1) != grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); grid_instance_ids[ix][iy] = @@ -326,7 +325,8 @@ static void add_top_module_io_children( for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coord : io_coordinates[io_side]) { - t_physical_tile_type_ptr grid_type = grids.get_physical_type(io_coord.x(), io_coord.y()); + t_physical_tile_type_ptr grid_type = + grids.get_physical_type(io_coord.x(), io_coord.y()); /* Bypass EMPTY grid */ if (true == is_empty_type(grid_type)) { continue; @@ -394,7 +394,8 @@ static void add_top_module_io_children( /* Now walk through the coordinates */ for (vtr::Point coord : coords) { - t_physical_tile_type_ptr grid_type = grids.get_physical_type(coord.x(), coord.y()); + t_physical_tile_type_ptr grid_type = + grids.get_physical_type(coord.x(), coord.y()); /* Bypass EMPTY grid */ if (true == is_empty_type(grid_type)) { continue; diff --git a/openfpga/src/fabric/build_top_module_connection.cpp b/openfpga/src/fabric/build_top_module_connection.cpp index 15dbd04f3..de4402c51 100644 --- a/openfpga/src/fabric/build_top_module_connection.cpp +++ b/openfpga/src/fabric/build_top_module_connection.cpp @@ -1038,7 +1038,8 @@ static int build_top_module_global_net_from_grid_modules( /* Spot the port from child modules from core grids */ for (size_t ix = start_coord.x(); ix < end_coord.x(); ++ix) { for (size_t iy = start_coord.y(); iy < end_coord.y(); ++iy) { - t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(ix, iy); + t_physical_tile_type_ptr phy_tile_type = + grids.get_physical_type(ix, iy); /* Bypass EMPTY tiles */ if (true == is_empty_type(phy_tile_type)) { continue; @@ -1068,23 +1069,23 @@ static int build_top_module_global_net_from_grid_modules( /* Walk through all the grids on the perimeter, which are I/O grids */ for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { - t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); + t_physical_tile_type_ptr phy_tile_type = + grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); /* Bypass EMPTY grid */ - if (true == - is_empty_type(phy_tile_type)) { + if (true == is_empty_type(phy_tile_type)) { continue; } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || - (0 < grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { + if ((0 < + grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || + (0 < + grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { continue; } /* Bypass the tiles whose names do not match */ - if (std::string( - phy_tile_type->name) != - tile_name) { + if (std::string(phy_tile_type->name) != tile_name) { continue; } diff --git a/openfpga/src/fabric/build_top_module_utils.cpp b/openfpga/src/fabric/build_top_module_utils.cpp index f9c4f53ac..b75c2bafc 100644 --- a/openfpga/src/fabric/build_top_module_utils.cpp +++ b/openfpga/src/fabric/build_top_module_utils.cpp @@ -28,11 +28,12 @@ std::string generate_grid_block_module_name_in_top_module( /* Determine if the grid locates at the border */ vtr::Point device_size(grids.width(), grids.height()); e_side border_side = find_grid_border_side(device_size, grid_coord); - t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(grid_coord.x(), grid_coord.y()); + t_physical_tile_type_ptr phy_tile_type = + grids.get_physical_type(grid_coord.x(), grid_coord.y()); return generate_grid_block_module_name( - prefix, std::string(phy_tile_type->name), - is_io_type(phy_tile_type), border_side); + prefix, std::string(phy_tile_type->name), is_io_type(phy_tile_type), + border_side); } /******************************************************************** diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index 4a3945c00..35a6556dc 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -859,8 +859,8 @@ void build_grid_bitstream( for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { /* Bypass EMPTY grid */ - if (true == - is_empty_type(grids.get_physical_type(io_coordinate.x(), io_coordinate.y()))) { + if (true == is_empty_type(grids.get_physical_type(io_coordinate.x(), + io_coordinate.y()))) { continue; } /* Skip height > 1 tiles (mostly heterogeneous blocks) */ diff --git a/openfpga/src/tile_direct/build_tile_direct.cpp b/openfpga/src/tile_direct/build_tile_direct.cpp index c3dd24c36..e2c46cf12 100644 --- a/openfpga/src/tile_direct/build_tile_direct.cpp +++ b/openfpga/src/tile_direct/build_tile_direct.cpp @@ -401,7 +401,8 @@ static void build_inner_column_row_tile_direct( /* Walk through the device fabric and find the grid that fit the source */ for (size_t x = 0; x < device_ctx.grid.width(); ++x) { for (size_t y = 0; y < device_ctx.grid.height(); ++y) { - t_physical_tile_type_ptr from_phy_tile_type = device_ctx.grid.get_physical_type(x, y); + t_physical_tile_type_ptr from_phy_tile_type = + device_ctx.grid.get_physical_type(x, y); /* Bypass empty grid */ if (true == is_empty_type(from_phy_tile_type)) { continue; @@ -436,10 +437,11 @@ static void build_inner_column_row_tile_direct( continue; } - t_physical_tile_type_ptr to_phy_tile_type = device_ctx.grid.get_physical_type(to_grid_coord.x(), to_grid_coord.y()); + t_physical_tile_type_ptr to_phy_tile_type = + device_ctx.grid.get_physical_type(to_grid_coord.x(), + to_grid_coord.y()); /* Bypass the grid that does not fit the from_tile name */ - if (to_tile_name != - std::string(to_phy_tile_type->name)) { + if (to_tile_name != std::string(to_phy_tile_type->name)) { continue; } @@ -449,11 +451,13 @@ static void build_inner_column_row_tile_direct( */ for (const e_side& to_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ - std::vector to_pins = find_physical_tile_pin_id( - to_phy_tile_type, - device_ctx.grid.get_width_offset(to_grid_coord.x(), to_grid_coord.y()), - device_ctx.grid.get_height_offset(to_grid_coord.x(), to_grid_coord.y()), - to_tile_port, to_side); + std::vector to_pins = + find_physical_tile_pin_id(to_phy_tile_type, + device_ctx.grid.get_width_offset( + to_grid_coord.x(), to_grid_coord.y()), + device_ctx.grid.get_height_offset( + to_grid_coord.x(), to_grid_coord.y()), + to_tile_port, to_side); /* If nothing found, we can continue */ if (0 == to_pins.size()) { continue; @@ -590,11 +594,14 @@ static void build_inter_column_row_tile_direct( */ for (const e_side& from_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ - std::vector from_pins = find_physical_tile_pin_id( - device_ctx.grid.get_physical_type(from_grid_coord.x(), from_grid_coord.y()), - device_ctx.grid.get_width_offset(from_grid_coord.x(), from_grid_coord.y()), - device_ctx.grid.get_height_offset(from_grid_coord.x(), from_grid_coord.y()), - from_tile_port, from_side); + std::vector from_pins = + find_physical_tile_pin_id(device_ctx.grid.get_physical_type( + from_grid_coord.x(), from_grid_coord.y()), + device_ctx.grid.get_width_offset( + from_grid_coord.x(), from_grid_coord.y()), + device_ctx.grid.get_height_offset( + from_grid_coord.x(), from_grid_coord.y()), + from_tile_port, from_side); /* If nothing found, we can continue */ if (0 == from_pins.size()) { continue; @@ -618,11 +625,14 @@ static void build_inter_column_row_tile_direct( */ for (const e_side& to_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ - std::vector to_pins = find_physical_tile_pin_id( - device_ctx.grid.get_physical_type(to_grid_coord.x(), to_grid_coord.y()), - device_ctx.grid.get_width_offset(to_grid_coord.x(), to_grid_coord.y()), - device_ctx.grid.get_height_offset(to_grid_coord.x(), to_grid_coord.y()), - to_tile_port, to_side); + std::vector to_pins = + find_physical_tile_pin_id(device_ctx.grid.get_physical_type( + to_grid_coord.x(), to_grid_coord.y()), + device_ctx.grid.get_width_offset( + to_grid_coord.x(), to_grid_coord.y()), + device_ctx.grid.get_height_offset( + to_grid_coord.x(), to_grid_coord.y()), + to_tile_port, to_side); /* If nothing found, we can continue */ if (0 == to_pins.size()) { continue; @@ -697,11 +707,14 @@ static void build_inter_column_row_tile_direct( */ for (const e_side& from_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ - std::vector from_pins = find_physical_tile_pin_id( - device_ctx.grid.get_physical_type(from_grid_coord.x(), from_grid_coord.y()), - device_ctx.grid.get_width_offset(from_grid_coord.x(), from_grid_coord.y()), - device_ctx.grid.get_height_offset(from_grid_coord.x(), from_grid_coord.y()), - from_tile_port, from_side); + std::vector from_pins = + find_physical_tile_pin_id(device_ctx.grid.get_physical_type( + from_grid_coord.x(), from_grid_coord.y()), + device_ctx.grid.get_width_offset( + from_grid_coord.x(), from_grid_coord.y()), + device_ctx.grid.get_height_offset( + from_grid_coord.x(), from_grid_coord.y()), + from_tile_port, from_side); /* If nothing found, we can continue */ if (0 == from_pins.size()) { continue; @@ -725,11 +738,14 @@ static void build_inter_column_row_tile_direct( */ for (const e_side& to_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ - std::vector to_pins = find_physical_tile_pin_id( - device_ctx.grid.get_physical_type(to_grid_coord.x(), to_grid_coord.y()), - device_ctx.grid.get_width_offset(to_grid_coord.x(), to_grid_coord.y()), - device_ctx.grid.get_height_offset(to_grid_coord.x(), to_grid_coord.y()), - to_tile_port, to_side); + std::vector to_pins = + find_physical_tile_pin_id(device_ctx.grid.get_physical_type( + to_grid_coord.x(), to_grid_coord.y()), + device_ctx.grid.get_width_offset( + to_grid_coord.x(), to_grid_coord.y()), + device_ctx.grid.get_height_offset( + to_grid_coord.x(), to_grid_coord.y()), + to_tile_port, to_side); /* If nothing found, we can continue */ if (0 == to_pins.size()) { continue; diff --git a/openfpga/src/utils/openfpga_physical_tile_utils.cpp b/openfpga/src/utils/openfpga_physical_tile_utils.cpp index 7ab62538a..2e599eb1b 100644 --- a/openfpga/src/utils/openfpga_physical_tile_utils.cpp +++ b/openfpga/src/utils/openfpga_physical_tile_utils.cpp @@ -108,7 +108,8 @@ std::set find_physical_io_tile_located_sides( for (const e_side& fpga_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[fpga_side]) { /* If located in center, we add a NUM_SIDES and finish */ - if (physical_tile == grids.get_physical_type(io_coordinate.x(), io_coordinate.y())) { + if (physical_tile == + grids.get_physical_type(io_coordinate.x(), io_coordinate.y())) { io_sides.insert(fpga_side); break; } From 293493898910a021059ca308e618c302c60a4f3c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 7 Jun 2023 20:31:57 -0700 Subject: [PATCH 073/391] [lib] update vtr --- vtr-verilog-to-routing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtr-verilog-to-routing b/vtr-verilog-to-routing index 863989a74..0963c2047 160000 --- a/vtr-verilog-to-routing +++ b/vtr-verilog-to-routing @@ -1 +1 @@ -Subproject commit 863989a74104ab6a8fb5db45e6d05e03de9fc80f +Subproject commit 0963c20472e178d6562e40d90727870342035f67 From f7cf2344ae5310b80b197d03bd850d068b11dccf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Jun 2023 06:59:18 +0000 Subject: [PATCH 074/391] Bump yosys-plugins from `8edc027` to `6689e20` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `8edc027` to `6689e20`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/8edc027cdc484f86c5ac8698c696c74d04e6f92e...6689e20e5fdb0b8d6f494acd65b08fd4646edc0e) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index 8edc027cd..6689e20e5 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit 8edc027cdc484f86c5ac8698c696c74d04e6f92e +Subproject commit 6689e20e5fdb0b8d6f494acd65b08fd4646edc0e From ac31a20376bd1efac47ec6a0417418ac45fefc1c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 8 Jun 2023 13:44:22 -0700 Subject: [PATCH 075/391] [test] now bypass clock routing in default example --- .../write_full_testbench_example_script.openfpga | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga index 9eec95b23..20ff5dfe9 100644 --- a/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga @@ -1,6 +1,6 @@ # Run VPR for the 'and' design #--write_rr_graph example_rr_graph.xml -vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route ${OPENFPGA_VPR_DEVICE_LAYOUT} +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling ideal ${OPENFPGA_VPR_DEVICE_LAYOUT} # Read OpenFPGA architecture definition read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} From 1ef8eed589a29afc4bae1e60d31d6d1941cdb162 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 8 Jun 2023 15:38:15 -0700 Subject: [PATCH 076/391] [test] update no time stamp golden outputs --- .../and2_formal_random_top_tb.v | 4 +- .../and2_fpga_top_analysis.sdc | 182 +-- .../and2_top_formal_verification.v | 98 +- .../fabric_bitstream.bit | 54 +- .../fabric_bitstream.xml | 64 +- .../fabric_independent_bitstream.xml | 270 ++-- .../global_ports.sdc | 2 +- .../pin_mapping.xml | 6 +- .../and2_formal_random_top_tb.v | 4 +- .../and2_fpga_top_analysis.sdc | 1117 ++++++++--------- .../and2_top_formal_verification.v | 118 +- .../fabric_bitstream.bit | 78 +- .../fabric_bitstream.xml | 116 +- .../fabric_independent_bitstream.xml | 422 +++---- .../global_ports.sdc | 2 +- .../pin_mapping.xml | 6 +- 16 files changed, 1289 insertions(+), 1254 deletions(-) diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/and2_formal_random_top_tb.v b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/and2_formal_random_top_tb.v index f82c458e3..9445b3195 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/and2_formal_random_top_tb.v +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/and2_formal_random_top_tb.v @@ -50,7 +50,7 @@ module and2_top_formal_verification_random_tb; initial begin clk[0] <= 1'b0; while(1) begin - #0.7626540661 + #0.4628907144 clk[0] <= !clk[0]; end end @@ -109,7 +109,7 @@ initial begin $timeformat(-9, 2, "ns", 20); $display("Simulation start"); // ----- Can be changed by the user for his/her need ------- - #10.6771574 + #6.480470181 if(nb_error == 0) begin $display("Simulation Succeed"); end else begin diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/and2_fpga_top_analysis.sdc b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/and2_fpga_top_analysis.sdc index acf0f91b8..fa1a05891 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/and2_fpga_top_analysis.sdc +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/and2_fpga_top_analysis.sdc @@ -9,20 +9,19 @@ ################################################## # Create clock ################################################## -create_clock clk[0] -period 1.525308102e-09 -waveform {0 7.62654051e-10} +create_clock clk[0] -period 9.25781396e-10 -waveform {0 4.62890698e-10} ################################################## # Create input and output delays for used I/Os ################################################## -set_input_delay -clock clk[0] -max 1.525308102e-09 gfpga_pad_GPIO_PAD[27] -set_input_delay -clock clk[0] -max 1.525308102e-09 gfpga_pad_GPIO_PAD[15] -set_output_delay -clock clk[0] -max 1.525308102e-09 gfpga_pad_GPIO_PAD[12] +set_input_delay -clock clk[0] -max 9.25781396e-10 gfpga_pad_GPIO_PAD[11] +set_input_delay -clock clk[0] -max 9.25781396e-10 gfpga_pad_GPIO_PAD[14] +set_output_delay -clock clk[0] -max 9.25781396e-10 gfpga_pad_GPIO_PAD[1] ################################################## # Disable timing for unused I/Os ################################################## set_disable_timing gfpga_pad_GPIO_PAD[0] -set_disable_timing gfpga_pad_GPIO_PAD[1] set_disable_timing gfpga_pad_GPIO_PAD[2] set_disable_timing gfpga_pad_GPIO_PAD[3] set_disable_timing gfpga_pad_GPIO_PAD[4] @@ -32,9 +31,9 @@ set_disable_timing gfpga_pad_GPIO_PAD[7] set_disable_timing gfpga_pad_GPIO_PAD[8] set_disable_timing gfpga_pad_GPIO_PAD[9] set_disable_timing gfpga_pad_GPIO_PAD[10] -set_disable_timing gfpga_pad_GPIO_PAD[11] +set_disable_timing gfpga_pad_GPIO_PAD[12] set_disable_timing gfpga_pad_GPIO_PAD[13] -set_disable_timing gfpga_pad_GPIO_PAD[14] +set_disable_timing gfpga_pad_GPIO_PAD[15] set_disable_timing gfpga_pad_GPIO_PAD[16] set_disable_timing gfpga_pad_GPIO_PAD[17] set_disable_timing gfpga_pad_GPIO_PAD[18] @@ -46,6 +45,7 @@ set_disable_timing gfpga_pad_GPIO_PAD[23] set_disable_timing gfpga_pad_GPIO_PAD[24] set_disable_timing gfpga_pad_GPIO_PAD[25] set_disable_timing gfpga_pad_GPIO_PAD[26] +set_disable_timing gfpga_pad_GPIO_PAD[27] set_disable_timing gfpga_pad_GPIO_PAD[28] set_disable_timing gfpga_pad_GPIO_PAD[29] set_disable_timing gfpga_pad_GPIO_PAD[30] @@ -156,9 +156,11 @@ set_disable_timing cbx_1__0_/chanx_left_in[7] set_disable_timing cbx_1__0_/chanx_right_in[7] set_disable_timing cbx_1__0_/chanx_left_in[8] set_disable_timing cbx_1__0_/chanx_right_in[8] +set_disable_timing cbx_1__0_/chanx_left_in[9] set_disable_timing cbx_1__0_/chanx_right_in[9] set_disable_timing cbx_1__0_/chanx_left_in[10] set_disable_timing cbx_1__0_/chanx_right_in[10] +set_disable_timing cbx_1__0_/chanx_left_in[11] set_disable_timing cbx_1__0_/chanx_right_in[11] set_disable_timing cbx_1__0_/chanx_left_in[12] set_disable_timing cbx_1__0_/chanx_right_in[12] @@ -180,9 +182,11 @@ set_disable_timing cbx_1__0_/chanx_left_out[7] set_disable_timing cbx_1__0_/chanx_right_out[7] set_disable_timing cbx_1__0_/chanx_left_out[8] set_disable_timing cbx_1__0_/chanx_right_out[8] +set_disable_timing cbx_1__0_/chanx_left_out[9] set_disable_timing cbx_1__0_/chanx_right_out[9] set_disable_timing cbx_1__0_/chanx_left_out[10] set_disable_timing cbx_1__0_/chanx_right_out[10] +set_disable_timing cbx_1__0_/chanx_left_out[11] set_disable_timing cbx_1__0_/chanx_right_out[11] set_disable_timing cbx_1__0_/chanx_left_out[12] set_disable_timing cbx_1__0_/chanx_right_out[12] @@ -272,6 +276,7 @@ set_disable_timing cbx_1__1_/chanx_left_in[1] set_disable_timing cbx_1__1_/chanx_left_in[2] set_disable_timing cbx_1__1_/chanx_right_in[2] set_disable_timing cbx_1__1_/chanx_left_in[3] +set_disable_timing cbx_1__1_/chanx_right_in[3] set_disable_timing cbx_1__1_/chanx_left_in[4] set_disable_timing cbx_1__1_/chanx_right_in[4] set_disable_timing cbx_1__1_/chanx_left_in[5] @@ -280,6 +285,7 @@ set_disable_timing cbx_1__1_/chanx_left_in[6] set_disable_timing cbx_1__1_/chanx_right_in[6] set_disable_timing cbx_1__1_/chanx_left_in[7] set_disable_timing cbx_1__1_/chanx_right_in[7] +set_disable_timing cbx_1__1_/chanx_left_in[8] set_disable_timing cbx_1__1_/chanx_right_in[8] set_disable_timing cbx_1__1_/chanx_left_in[9] set_disable_timing cbx_1__1_/chanx_right_in[9] @@ -295,6 +301,7 @@ set_disable_timing cbx_1__1_/chanx_left_out[1] set_disable_timing cbx_1__1_/chanx_left_out[2] set_disable_timing cbx_1__1_/chanx_right_out[2] set_disable_timing cbx_1__1_/chanx_left_out[3] +set_disable_timing cbx_1__1_/chanx_right_out[3] set_disable_timing cbx_1__1_/chanx_left_out[4] set_disable_timing cbx_1__1_/chanx_right_out[4] set_disable_timing cbx_1__1_/chanx_left_out[5] @@ -303,6 +310,7 @@ set_disable_timing cbx_1__1_/chanx_left_out[6] set_disable_timing cbx_1__1_/chanx_right_out[6] set_disable_timing cbx_1__1_/chanx_left_out[7] set_disable_timing cbx_1__1_/chanx_right_out[7] +set_disable_timing cbx_1__1_/chanx_left_out[8] set_disable_timing cbx_1__1_/chanx_right_out[8] set_disable_timing cbx_1__1_/chanx_left_out[9] set_disable_timing cbx_1__1_/chanx_right_out[9] @@ -313,13 +321,13 @@ set_disable_timing cbx_1__1_/chanx_right_out[11] set_disable_timing cbx_1__1_/chanx_left_out[12] set_disable_timing cbx_1__1_/chanx_right_out[12] set_disable_timing cbx_1__1_/top_grid_bottom_width_0_height_0_subtile_0__pin_outpad_0_[0] -set_disable_timing cbx_1__1_/top_grid_bottom_width_0_height_0_subtile_1__pin_outpad_0_[0] set_disable_timing cbx_1__1_/top_grid_bottom_width_0_height_0_subtile_2__pin_outpad_0_[0] set_disable_timing cbx_1__1_/top_grid_bottom_width_0_height_0_subtile_3__pin_outpad_0_[0] set_disable_timing cbx_1__1_/top_grid_bottom_width_0_height_0_subtile_4__pin_outpad_0_[0] set_disable_timing cbx_1__1_/top_grid_bottom_width_0_height_0_subtile_5__pin_outpad_0_[0] set_disable_timing cbx_1__1_/top_grid_bottom_width_0_height_0_subtile_6__pin_outpad_0_[0] set_disable_timing cbx_1__1_/top_grid_bottom_width_0_height_0_subtile_7__pin_outpad_0_[0] +set_disable_timing cbx_1__1_/bottom_grid_top_width_0_height_0_subtile_0__pin_I_0_[0] set_disable_timing cbx_1__1_/bottom_grid_top_width_0_height_0_subtile_0__pin_I_4_[0] set_disable_timing cbx_1__1_/bottom_grid_top_width_0_height_0_subtile_0__pin_I_8_[0] set_disable_timing cbx_1__1_/mux_bottom_ipin_0/in[1] @@ -331,7 +339,6 @@ set_disable_timing cbx_1__1_/mux_bottom_ipin_7/in[0] set_disable_timing cbx_1__1_/mux_bottom_ipin_1/in[3] set_disable_timing cbx_1__1_/mux_bottom_ipin_2/in[1] set_disable_timing cbx_1__1_/mux_top_ipin_0/in[1] -set_disable_timing cbx_1__1_/mux_bottom_ipin_1/in[2] set_disable_timing cbx_1__1_/mux_bottom_ipin_2/in[0] set_disable_timing cbx_1__1_/mux_top_ipin_0/in[0] set_disable_timing cbx_1__1_/mux_bottom_ipin_2/in[3] @@ -367,6 +374,7 @@ set_disable_timing cbx_1__1_/mux_bottom_ipin_1/in[4] set_disable_timing cbx_1__1_/mux_bottom_ipin_7/in[4] set_disable_timing cbx_1__1_/mux_top_ipin_0/in[2] set_disable_timing cbx_1__1_/mux_bottom_ipin_2/in[5] +set_disable_timing cbx_1__1_/mux_top_ipin_0/in[5] set_disable_timing cbx_1__1_/mux_top_ipin_1/in[3] set_disable_timing cbx_1__1_/mux_bottom_ipin_2/in[4] set_disable_timing cbx_1__1_/mux_top_ipin_0/in[4] @@ -396,6 +404,7 @@ set_disable_timing cby_0__1_/chany_bottom_in[1] set_disable_timing cby_0__1_/chany_top_in[1] set_disable_timing cby_0__1_/chany_bottom_in[2] set_disable_timing cby_0__1_/chany_top_in[2] +set_disable_timing cby_0__1_/chany_bottom_in[3] set_disable_timing cby_0__1_/chany_top_in[3] set_disable_timing cby_0__1_/chany_bottom_in[4] set_disable_timing cby_0__1_/chany_top_in[4] @@ -406,9 +415,11 @@ set_disable_timing cby_0__1_/chany_top_in[6] set_disable_timing cby_0__1_/chany_bottom_in[7] set_disable_timing cby_0__1_/chany_top_in[7] set_disable_timing cby_0__1_/chany_bottom_in[8] +set_disable_timing cby_0__1_/chany_top_in[8] set_disable_timing cby_0__1_/chany_bottom_in[9] set_disable_timing cby_0__1_/chany_top_in[9] set_disable_timing cby_0__1_/chany_bottom_in[10] +set_disable_timing cby_0__1_/chany_top_in[10] set_disable_timing cby_0__1_/chany_bottom_in[11] set_disable_timing cby_0__1_/chany_top_in[11] set_disable_timing cby_0__1_/chany_bottom_in[12] @@ -419,6 +430,7 @@ set_disable_timing cby_0__1_/chany_bottom_out[1] set_disable_timing cby_0__1_/chany_top_out[1] set_disable_timing cby_0__1_/chany_bottom_out[2] set_disable_timing cby_0__1_/chany_top_out[2] +set_disable_timing cby_0__1_/chany_bottom_out[3] set_disable_timing cby_0__1_/chany_top_out[3] set_disable_timing cby_0__1_/chany_bottom_out[4] set_disable_timing cby_0__1_/chany_top_out[4] @@ -429,9 +441,11 @@ set_disable_timing cby_0__1_/chany_top_out[6] set_disable_timing cby_0__1_/chany_bottom_out[7] set_disable_timing cby_0__1_/chany_top_out[7] set_disable_timing cby_0__1_/chany_bottom_out[8] +set_disable_timing cby_0__1_/chany_top_out[8] set_disable_timing cby_0__1_/chany_bottom_out[9] set_disable_timing cby_0__1_/chany_top_out[9] set_disable_timing cby_0__1_/chany_bottom_out[10] +set_disable_timing cby_0__1_/chany_top_out[10] set_disable_timing cby_0__1_/chany_bottom_out[11] set_disable_timing cby_0__1_/chany_top_out[11] set_disable_timing cby_0__1_/chany_bottom_out[12] @@ -510,44 +524,46 @@ set_disable_timing cby_0__1_/mux_right_ipin_4/in[4] # Disable timing for Connection block cby_1__1_ ################################################## set_disable_timing cby_1__1_/chany_top_in[0] +set_disable_timing cby_1__1_/chany_bottom_in[1] set_disable_timing cby_1__1_/chany_top_in[1] +set_disable_timing cby_1__1_/chany_bottom_in[2] set_disable_timing cby_1__1_/chany_top_in[2] set_disable_timing cby_1__1_/chany_bottom_in[3] set_disable_timing cby_1__1_/chany_top_in[3] +set_disable_timing cby_1__1_/chany_bottom_in[4] set_disable_timing cby_1__1_/chany_top_in[4] set_disable_timing cby_1__1_/chany_bottom_in[5] set_disable_timing cby_1__1_/chany_top_in[5] set_disable_timing cby_1__1_/chany_bottom_in[6] set_disable_timing cby_1__1_/chany_top_in[6] -set_disable_timing cby_1__1_/chany_bottom_in[7] set_disable_timing cby_1__1_/chany_top_in[7] set_disable_timing cby_1__1_/chany_bottom_in[8] set_disable_timing cby_1__1_/chany_top_in[8] set_disable_timing cby_1__1_/chany_bottom_in[9] set_disable_timing cby_1__1_/chany_top_in[9] -set_disable_timing cby_1__1_/chany_bottom_in[10] set_disable_timing cby_1__1_/chany_top_in[10] set_disable_timing cby_1__1_/chany_bottom_in[11] set_disable_timing cby_1__1_/chany_top_in[11] set_disable_timing cby_1__1_/chany_bottom_in[12] set_disable_timing cby_1__1_/chany_top_in[12] set_disable_timing cby_1__1_/chany_top_out[0] +set_disable_timing cby_1__1_/chany_bottom_out[1] set_disable_timing cby_1__1_/chany_top_out[1] +set_disable_timing cby_1__1_/chany_bottom_out[2] set_disable_timing cby_1__1_/chany_top_out[2] set_disable_timing cby_1__1_/chany_bottom_out[3] set_disable_timing cby_1__1_/chany_top_out[3] +set_disable_timing cby_1__1_/chany_bottom_out[4] set_disable_timing cby_1__1_/chany_top_out[4] set_disable_timing cby_1__1_/chany_bottom_out[5] set_disable_timing cby_1__1_/chany_top_out[5] set_disable_timing cby_1__1_/chany_bottom_out[6] set_disable_timing cby_1__1_/chany_top_out[6] -set_disable_timing cby_1__1_/chany_bottom_out[7] set_disable_timing cby_1__1_/chany_top_out[7] set_disable_timing cby_1__1_/chany_bottom_out[8] set_disable_timing cby_1__1_/chany_top_out[8] set_disable_timing cby_1__1_/chany_bottom_out[9] set_disable_timing cby_1__1_/chany_top_out[9] -set_disable_timing cby_1__1_/chany_bottom_out[10] set_disable_timing cby_1__1_/chany_top_out[10] set_disable_timing cby_1__1_/chany_bottom_out[11] set_disable_timing cby_1__1_/chany_top_out[11] @@ -557,11 +573,11 @@ set_disable_timing cby_1__1_/right_grid_left_width_0_height_0_subtile_0__pin_out set_disable_timing cby_1__1_/right_grid_left_width_0_height_0_subtile_1__pin_outpad_0_[0] set_disable_timing cby_1__1_/right_grid_left_width_0_height_0_subtile_2__pin_outpad_0_[0] set_disable_timing cby_1__1_/right_grid_left_width_0_height_0_subtile_3__pin_outpad_0_[0] +set_disable_timing cby_1__1_/right_grid_left_width_0_height_0_subtile_4__pin_outpad_0_[0] set_disable_timing cby_1__1_/right_grid_left_width_0_height_0_subtile_5__pin_outpad_0_[0] set_disable_timing cby_1__1_/right_grid_left_width_0_height_0_subtile_6__pin_outpad_0_[0] set_disable_timing cby_1__1_/right_grid_left_width_0_height_0_subtile_7__pin_outpad_0_[0] set_disable_timing cby_1__1_/left_grid_right_width_0_height_0_subtile_0__pin_I_5_[0] -set_disable_timing cby_1__1_/left_grid_right_width_0_height_0_subtile_0__pin_I_9_[0] set_disable_timing cby_1__1_/mux_left_ipin_0/in[1] set_disable_timing cby_1__1_/mux_left_ipin_1/in[1] set_disable_timing cby_1__1_/mux_left_ipin_7/in[1] @@ -570,6 +586,7 @@ set_disable_timing cby_1__1_/mux_left_ipin_1/in[0] set_disable_timing cby_1__1_/mux_left_ipin_7/in[0] set_disable_timing cby_1__1_/mux_left_ipin_1/in[3] set_disable_timing cby_1__1_/mux_left_ipin_2/in[1] +set_disable_timing cby_1__1_/mux_right_ipin_0/in[1] set_disable_timing cby_1__1_/mux_left_ipin_1/in[2] set_disable_timing cby_1__1_/mux_left_ipin_2/in[0] set_disable_timing cby_1__1_/mux_right_ipin_0/in[0] @@ -585,6 +602,7 @@ set_disable_timing cby_1__1_/mux_right_ipin_2/in[1] set_disable_timing cby_1__1_/mux_left_ipin_3/in[2] set_disable_timing cby_1__1_/mux_left_ipin_4/in[0] set_disable_timing cby_1__1_/mux_right_ipin_2/in[0] +set_disable_timing cby_1__1_/mux_left_ipin_4/in[3] set_disable_timing cby_1__1_/mux_left_ipin_5/in[1] set_disable_timing cby_1__1_/mux_left_ipin_4/in[2] set_disable_timing cby_1__1_/mux_left_ipin_5/in[0] @@ -600,7 +618,6 @@ set_disable_timing cby_1__1_/mux_left_ipin_6/in[2] set_disable_timing cby_1__1_/mux_left_ipin_7/in[2] set_disable_timing cby_1__1_/mux_left_ipin_1/in[5] set_disable_timing cby_1__1_/mux_left_ipin_7/in[5] -set_disable_timing cby_1__1_/mux_right_ipin_0/in[3] set_disable_timing cby_1__1_/mux_left_ipin_1/in[4] set_disable_timing cby_1__1_/mux_left_ipin_7/in[4] set_disable_timing cby_1__1_/mux_right_ipin_0/in[2] @@ -617,7 +634,6 @@ set_disable_timing cby_1__1_/mux_left_ipin_3/in[4] set_disable_timing cby_1__1_/mux_right_ipin_1/in[4] set_disable_timing cby_1__1_/mux_right_ipin_2/in[2] set_disable_timing cby_1__1_/mux_left_ipin_4/in[5] -set_disable_timing cby_1__1_/mux_right_ipin_2/in[5] set_disable_timing cby_1__1_/mux_left_ipin_4/in[4] set_disable_timing cby_1__1_/mux_right_ipin_2/in[4] set_disable_timing cby_1__1_/mux_left_ipin_5/in[5] @@ -635,6 +651,7 @@ set_disable_timing sb_0__0_/chany_top_out[1] set_disable_timing sb_0__0_/chany_top_in[1] set_disable_timing sb_0__0_/chany_top_out[2] set_disable_timing sb_0__0_/chany_top_in[2] +set_disable_timing sb_0__0_/chany_top_out[3] set_disable_timing sb_0__0_/chany_top_in[3] set_disable_timing sb_0__0_/chany_top_out[4] set_disable_timing sb_0__0_/chany_top_in[4] @@ -645,9 +662,11 @@ set_disable_timing sb_0__0_/chany_top_in[6] set_disable_timing sb_0__0_/chany_top_out[7] set_disable_timing sb_0__0_/chany_top_in[7] set_disable_timing sb_0__0_/chany_top_out[8] +set_disable_timing sb_0__0_/chany_top_in[8] set_disable_timing sb_0__0_/chany_top_out[9] set_disable_timing sb_0__0_/chany_top_in[9] set_disable_timing sb_0__0_/chany_top_out[10] +set_disable_timing sb_0__0_/chany_top_in[10] set_disable_timing sb_0__0_/chany_top_out[11] set_disable_timing sb_0__0_/chany_top_in[11] set_disable_timing sb_0__0_/chany_top_out[12] @@ -670,15 +689,18 @@ set_disable_timing sb_0__0_/chanx_right_out[7] set_disable_timing sb_0__0_/chanx_right_in[7] set_disable_timing sb_0__0_/chanx_right_out[8] set_disable_timing sb_0__0_/chanx_right_in[8] +set_disable_timing sb_0__0_/chanx_right_out[9] set_disable_timing sb_0__0_/chanx_right_in[9] set_disable_timing sb_0__0_/chanx_right_out[10] set_disable_timing sb_0__0_/chanx_right_in[10] +set_disable_timing sb_0__0_/chanx_right_out[11] set_disable_timing sb_0__0_/chanx_right_in[11] set_disable_timing sb_0__0_/chanx_right_out[12] set_disable_timing sb_0__0_/chanx_right_in[12] set_disable_timing sb_0__0_/top_left_grid_right_width_0_height_0_subtile_0__pin_inpad_0_[0] set_disable_timing sb_0__0_/top_left_grid_right_width_0_height_0_subtile_1__pin_inpad_0_[0] set_disable_timing sb_0__0_/top_left_grid_right_width_0_height_0_subtile_2__pin_inpad_0_[0] +set_disable_timing sb_0__0_/top_left_grid_right_width_0_height_0_subtile_3__pin_inpad_0_[0] set_disable_timing sb_0__0_/top_left_grid_right_width_0_height_0_subtile_4__pin_inpad_0_[0] set_disable_timing sb_0__0_/top_left_grid_right_width_0_height_0_subtile_5__pin_inpad_0_[0] set_disable_timing sb_0__0_/top_left_grid_right_width_0_height_0_subtile_6__pin_inpad_0_[0] @@ -703,6 +725,7 @@ set_disable_timing sb_0__0_/mux_top_track_2/in[1] set_disable_timing sb_0__0_/mux_top_track_4/in[0] set_disable_timing sb_0__0_/mux_top_track_16/in[0] set_disable_timing sb_0__0_/mux_top_track_4/in[1] +set_disable_timing sb_0__0_/mux_top_track_6/in[0] set_disable_timing sb_0__0_/mux_top_track_18/in[0] set_disable_timing sb_0__0_/mux_top_track_6/in[1] set_disable_timing sb_0__0_/mux_top_track_8/in[0] @@ -752,7 +775,9 @@ set_disable_timing sb_0__0_/mux_right_track_10/in[0] set_disable_timing sb_0__0_/mux_right_track_12/in[0] set_disable_timing sb_0__0_/mux_right_track_14/in[0] set_disable_timing sb_0__0_/mux_right_track_16/in[0] +set_disable_timing sb_0__0_/mux_right_track_18/in[0] set_disable_timing sb_0__0_/mux_right_track_20/in[0] +set_disable_timing sb_0__0_/mux_right_track_22/in[0] set_disable_timing sb_0__0_/mux_right_track_24/in[0] set_disable_timing sb_0__0_/mux_right_track_0/in[0] set_disable_timing sb_0__0_/mux_top_track_24/in[2] @@ -777,6 +802,7 @@ set_disable_timing sb_0__1_/chanx_right_out[1] set_disable_timing sb_0__1_/chanx_right_out[2] set_disable_timing sb_0__1_/chanx_right_in[2] set_disable_timing sb_0__1_/chanx_right_out[3] +set_disable_timing sb_0__1_/chanx_right_in[3] set_disable_timing sb_0__1_/chanx_right_out[4] set_disable_timing sb_0__1_/chanx_right_in[4] set_disable_timing sb_0__1_/chanx_right_out[5] @@ -785,6 +811,7 @@ set_disable_timing sb_0__1_/chanx_right_out[6] set_disable_timing sb_0__1_/chanx_right_in[6] set_disable_timing sb_0__1_/chanx_right_out[7] set_disable_timing sb_0__1_/chanx_right_in[7] +set_disable_timing sb_0__1_/chanx_right_out[8] set_disable_timing sb_0__1_/chanx_right_in[8] set_disable_timing sb_0__1_/chanx_right_out[9] set_disable_timing sb_0__1_/chanx_right_in[9] @@ -800,6 +827,7 @@ set_disable_timing sb_0__1_/chany_bottom_in[1] set_disable_timing sb_0__1_/chany_bottom_out[1] set_disable_timing sb_0__1_/chany_bottom_in[2] set_disable_timing sb_0__1_/chany_bottom_out[2] +set_disable_timing sb_0__1_/chany_bottom_in[3] set_disable_timing sb_0__1_/chany_bottom_out[3] set_disable_timing sb_0__1_/chany_bottom_in[4] set_disable_timing sb_0__1_/chany_bottom_out[4] @@ -810,9 +838,11 @@ set_disable_timing sb_0__1_/chany_bottom_out[6] set_disable_timing sb_0__1_/chany_bottom_in[7] set_disable_timing sb_0__1_/chany_bottom_out[7] set_disable_timing sb_0__1_/chany_bottom_in[8] +set_disable_timing sb_0__1_/chany_bottom_out[8] set_disable_timing sb_0__1_/chany_bottom_in[9] set_disable_timing sb_0__1_/chany_bottom_out[9] set_disable_timing sb_0__1_/chany_bottom_in[10] +set_disable_timing sb_0__1_/chany_bottom_out[10] set_disable_timing sb_0__1_/chany_bottom_in[11] set_disable_timing sb_0__1_/chany_bottom_out[11] set_disable_timing sb_0__1_/chany_bottom_in[12] @@ -830,6 +860,7 @@ set_disable_timing sb_0__1_/bottom_right_grid_left_width_0_height_0_subtile_0__p set_disable_timing sb_0__1_/bottom_left_grid_right_width_0_height_0_subtile_0__pin_inpad_0_[0] set_disable_timing sb_0__1_/bottom_left_grid_right_width_0_height_0_subtile_1__pin_inpad_0_[0] set_disable_timing sb_0__1_/bottom_left_grid_right_width_0_height_0_subtile_2__pin_inpad_0_[0] +set_disable_timing sb_0__1_/bottom_left_grid_right_width_0_height_0_subtile_3__pin_inpad_0_[0] set_disable_timing sb_0__1_/bottom_left_grid_right_width_0_height_0_subtile_4__pin_inpad_0_[0] set_disable_timing sb_0__1_/bottom_left_grid_right_width_0_height_0_subtile_5__pin_inpad_0_[0] set_disable_timing sb_0__1_/bottom_left_grid_right_width_0_height_0_subtile_6__pin_inpad_0_[0] @@ -885,7 +916,9 @@ set_disable_timing sb_0__1_/mux_bottom_track_3/in[3] set_disable_timing sb_0__1_/mux_bottom_track_15/in[3] set_disable_timing sb_0__1_/mux_bottom_track_17/in[2] set_disable_timing sb_0__1_/mux_bottom_track_23/in[0] +set_disable_timing sb_0__1_/mux_bottom_track_21/in[0] set_disable_timing sb_0__1_/mux_bottom_track_19/in[0] +set_disable_timing sb_0__1_/mux_bottom_track_17/in[0] set_disable_timing sb_0__1_/mux_bottom_track_15/in[0] set_disable_timing sb_0__1_/mux_bottom_track_13/in[0] set_disable_timing sb_0__1_/mux_bottom_track_11/in[0] @@ -898,6 +931,7 @@ set_disable_timing sb_0__1_/mux_bottom_track_25/in[0] set_disable_timing sb_0__1_/mux_right_track_22/in[1] set_disable_timing sb_0__1_/mux_right_track_20/in[1] set_disable_timing sb_0__1_/mux_right_track_18/in[1] +set_disable_timing sb_0__1_/mux_right_track_16/in[2] set_disable_timing sb_0__1_/mux_right_track_14/in[2] set_disable_timing sb_0__1_/mux_right_track_12/in[3] set_disable_timing sb_0__1_/mux_right_track_10/in[2] @@ -911,22 +945,23 @@ set_disable_timing sb_0__1_/mux_right_track_24/in[2] # Disable timing for Switch block sb_1__0_ ################################################## set_disable_timing sb_1__0_/chany_top_in[0] +set_disable_timing sb_1__0_/chany_top_out[1] set_disable_timing sb_1__0_/chany_top_in[1] +set_disable_timing sb_1__0_/chany_top_out[2] set_disable_timing sb_1__0_/chany_top_in[2] set_disable_timing sb_1__0_/chany_top_out[3] set_disable_timing sb_1__0_/chany_top_in[3] +set_disable_timing sb_1__0_/chany_top_out[4] set_disable_timing sb_1__0_/chany_top_in[4] set_disable_timing sb_1__0_/chany_top_out[5] set_disable_timing sb_1__0_/chany_top_in[5] set_disable_timing sb_1__0_/chany_top_out[6] set_disable_timing sb_1__0_/chany_top_in[6] -set_disable_timing sb_1__0_/chany_top_out[7] set_disable_timing sb_1__0_/chany_top_in[7] set_disable_timing sb_1__0_/chany_top_out[8] set_disable_timing sb_1__0_/chany_top_in[8] set_disable_timing sb_1__0_/chany_top_out[9] set_disable_timing sb_1__0_/chany_top_in[9] -set_disable_timing sb_1__0_/chany_top_out[10] set_disable_timing sb_1__0_/chany_top_in[10] set_disable_timing sb_1__0_/chany_top_out[11] set_disable_timing sb_1__0_/chany_top_in[11] @@ -950,19 +985,20 @@ set_disable_timing sb_1__0_/chanx_left_in[7] set_disable_timing sb_1__0_/chanx_left_out[7] set_disable_timing sb_1__0_/chanx_left_in[8] set_disable_timing sb_1__0_/chanx_left_out[8] +set_disable_timing sb_1__0_/chanx_left_in[9] set_disable_timing sb_1__0_/chanx_left_out[9] set_disable_timing sb_1__0_/chanx_left_in[10] set_disable_timing sb_1__0_/chanx_left_out[10] +set_disable_timing sb_1__0_/chanx_left_in[11] set_disable_timing sb_1__0_/chanx_left_out[11] set_disable_timing sb_1__0_/chanx_left_in[12] set_disable_timing sb_1__0_/chanx_left_out[12] set_disable_timing sb_1__0_/top_right_grid_left_width_0_height_0_subtile_0__pin_inpad_0_[0] set_disable_timing sb_1__0_/top_right_grid_left_width_0_height_0_subtile_1__pin_inpad_0_[0] set_disable_timing sb_1__0_/top_right_grid_left_width_0_height_0_subtile_2__pin_inpad_0_[0] -set_disable_timing sb_1__0_/top_right_grid_left_width_0_height_0_subtile_3__pin_inpad_0_[0] set_disable_timing sb_1__0_/top_right_grid_left_width_0_height_0_subtile_4__pin_inpad_0_[0] set_disable_timing sb_1__0_/top_right_grid_left_width_0_height_0_subtile_5__pin_inpad_0_[0] -set_disable_timing sb_1__0_/top_right_grid_left_width_0_height_0_subtile_6__pin_inpad_0_[0] +set_disable_timing sb_1__0_/top_right_grid_left_width_0_height_0_subtile_7__pin_inpad_0_[0] set_disable_timing sb_1__0_/left_top_grid_bottom_width_0_height_0_subtile_0__pin_O_0_[0] set_disable_timing sb_1__0_/left_bottom_grid_top_width_0_height_0_subtile_0__pin_inpad_0_[0] set_disable_timing sb_1__0_/left_bottom_grid_top_width_0_height_0_subtile_1__pin_inpad_0_[0] @@ -983,7 +1019,6 @@ set_disable_timing sb_1__0_/mux_top_track_6/in[0] set_disable_timing sb_1__0_/mux_top_track_18/in[0] set_disable_timing sb_1__0_/mux_top_track_6/in[1] set_disable_timing sb_1__0_/mux_top_track_8/in[0] -set_disable_timing sb_1__0_/mux_top_track_20/in[0] set_disable_timing sb_1__0_/mux_top_track_8/in[1] set_disable_timing sb_1__0_/mux_top_track_10/in[0] set_disable_timing sb_1__0_/mux_top_track_22/in[0] @@ -992,7 +1027,7 @@ set_disable_timing sb_1__0_/mux_top_track_12/in[0] set_disable_timing sb_1__0_/mux_top_track_24/in[0] set_disable_timing sb_1__0_/mux_top_track_0/in[2] set_disable_timing sb_1__0_/mux_top_track_12/in[1] -set_disable_timing sb_1__0_/mux_top_track_14/in[1] +set_disable_timing sb_1__0_/mux_top_track_2/in[2] set_disable_timing sb_1__0_/mux_top_track_14/in[2] set_disable_timing sb_1__0_/mux_top_track_16/in[1] set_disable_timing sb_1__0_/mux_left_track_1/in[1] @@ -1044,28 +1079,31 @@ set_disable_timing sb_1__0_/mux_top_track_16/in[2] set_disable_timing sb_1__0_/mux_top_track_14/in[3] set_disable_timing sb_1__0_/mux_top_track_12/in[2] set_disable_timing sb_1__0_/mux_top_track_10/in[2] +set_disable_timing sb_1__0_/mux_top_track_8/in[2] set_disable_timing sb_1__0_/mux_top_track_6/in[2] +set_disable_timing sb_1__0_/mux_top_track_4/in[2] set_disable_timing sb_1__0_/mux_top_track_2/in[3] ################################################## # Disable timing for Switch block sb_1__1_ ################################################## set_disable_timing sb_1__1_/chany_bottom_out[0] +set_disable_timing sb_1__1_/chany_bottom_in[1] set_disable_timing sb_1__1_/chany_bottom_out[1] +set_disable_timing sb_1__1_/chany_bottom_in[2] set_disable_timing sb_1__1_/chany_bottom_out[2] set_disable_timing sb_1__1_/chany_bottom_in[3] set_disable_timing sb_1__1_/chany_bottom_out[3] +set_disable_timing sb_1__1_/chany_bottom_in[4] set_disable_timing sb_1__1_/chany_bottom_out[4] set_disable_timing sb_1__1_/chany_bottom_in[5] set_disable_timing sb_1__1_/chany_bottom_out[5] set_disable_timing sb_1__1_/chany_bottom_in[6] set_disable_timing sb_1__1_/chany_bottom_out[6] -set_disable_timing sb_1__1_/chany_bottom_in[7] set_disable_timing sb_1__1_/chany_bottom_out[7] set_disable_timing sb_1__1_/chany_bottom_in[8] set_disable_timing sb_1__1_/chany_bottom_out[8] set_disable_timing sb_1__1_/chany_bottom_in[9] set_disable_timing sb_1__1_/chany_bottom_out[9] -set_disable_timing sb_1__1_/chany_bottom_in[10] set_disable_timing sb_1__1_/chany_bottom_out[10] set_disable_timing sb_1__1_/chany_bottom_in[11] set_disable_timing sb_1__1_/chany_bottom_out[11] @@ -1077,6 +1115,7 @@ set_disable_timing sb_1__1_/chanx_left_in[1] set_disable_timing sb_1__1_/chanx_left_in[2] set_disable_timing sb_1__1_/chanx_left_out[2] set_disable_timing sb_1__1_/chanx_left_in[3] +set_disable_timing sb_1__1_/chanx_left_out[3] set_disable_timing sb_1__1_/chanx_left_in[4] set_disable_timing sb_1__1_/chanx_left_out[4] set_disable_timing sb_1__1_/chanx_left_in[5] @@ -1085,6 +1124,7 @@ set_disable_timing sb_1__1_/chanx_left_in[6] set_disable_timing sb_1__1_/chanx_left_out[6] set_disable_timing sb_1__1_/chanx_left_in[7] set_disable_timing sb_1__1_/chanx_left_out[7] +set_disable_timing sb_1__1_/chanx_left_in[8] set_disable_timing sb_1__1_/chanx_left_out[8] set_disable_timing sb_1__1_/chanx_left_in[9] set_disable_timing sb_1__1_/chanx_left_out[9] @@ -1097,10 +1137,9 @@ set_disable_timing sb_1__1_/chanx_left_out[12] set_disable_timing sb_1__1_/bottom_right_grid_left_width_0_height_0_subtile_0__pin_inpad_0_[0] set_disable_timing sb_1__1_/bottom_right_grid_left_width_0_height_0_subtile_1__pin_inpad_0_[0] set_disable_timing sb_1__1_/bottom_right_grid_left_width_0_height_0_subtile_2__pin_inpad_0_[0] -set_disable_timing sb_1__1_/bottom_right_grid_left_width_0_height_0_subtile_3__pin_inpad_0_[0] set_disable_timing sb_1__1_/bottom_right_grid_left_width_0_height_0_subtile_4__pin_inpad_0_[0] set_disable_timing sb_1__1_/bottom_right_grid_left_width_0_height_0_subtile_5__pin_inpad_0_[0] -set_disable_timing sb_1__1_/bottom_right_grid_left_width_0_height_0_subtile_6__pin_inpad_0_[0] +set_disable_timing sb_1__1_/bottom_right_grid_left_width_0_height_0_subtile_7__pin_inpad_0_[0] set_disable_timing sb_1__1_/left_top_grid_bottom_width_0_height_0_subtile_0__pin_inpad_0_[0] set_disable_timing sb_1__1_/left_top_grid_bottom_width_0_height_0_subtile_1__pin_inpad_0_[0] set_disable_timing sb_1__1_/left_top_grid_bottom_width_0_height_0_subtile_2__pin_inpad_0_[0] @@ -1161,6 +1200,7 @@ set_disable_timing sb_1__1_/mux_left_track_13/in[3] set_disable_timing sb_1__1_/mux_left_track_15/in[2] set_disable_timing sb_1__1_/mux_left_track_17/in[2] set_disable_timing sb_1__1_/mux_left_track_5/in[0] +set_disable_timing sb_1__1_/mux_left_track_7/in[0] set_disable_timing sb_1__1_/mux_left_track_9/in[0] set_disable_timing sb_1__1_/mux_left_track_11/in[0] set_disable_timing sb_1__1_/mux_left_track_13/in[0] @@ -1193,6 +1233,7 @@ set_disable_timing sb_1__1_/mux_bottom_track_23/in[1] ####################################### # Disable unused pins for pb_graph_node clb[0] ####################################### +set_disable_timing grid_clb_1__1_/logical_tile_clb_mode_clb__0/clb_I[0] set_disable_timing grid_clb_1__1_/logical_tile_clb_mode_clb__0/clb_I[2] set_disable_timing grid_clb_1__1_/logical_tile_clb_mode_clb__0/clb_I[3] set_disable_timing grid_clb_1__1_/logical_tile_clb_mode_clb__0/clb_I[4] @@ -1200,7 +1241,6 @@ set_disable_timing grid_clb_1__1_/logical_tile_clb_mode_clb__0/clb_I[5] set_disable_timing grid_clb_1__1_/logical_tile_clb_mode_clb__0/clb_I[6] set_disable_timing grid_clb_1__1_/logical_tile_clb_mode_clb__0/clb_I[7] set_disable_timing grid_clb_1__1_/logical_tile_clb_mode_clb__0/clb_I[8] -set_disable_timing grid_clb_1__1_/logical_tile_clb_mode_clb__0/clb_I[9] set_disable_timing grid_clb_1__1_/logical_tile_clb_mode_clb__0/clb_O[0] set_disable_timing grid_clb_1__1_/logical_tile_clb_mode_clb__0/clb_O[1] set_disable_timing grid_clb_1__1_/logical_tile_clb_mode_clb__0/clb_O[2] @@ -1426,16 +1466,20 @@ set_disable_timing grid_io_top_1__2_/logical_tile_io_mode_io__0/* ####################################### set_disable_timing grid_io_top_1__2_/logical_tile_io_mode_io__0/logical_tile_io_mode_physical__iopad_0/* ####################################### -# Disable Timing for unused grid[1][2][1] +# Disable Timing for unused resources in grid[1][2][1] ####################################### ####################################### -# Disable all the ports for pb_graph_node io[0] +# Disable unused pins for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_top_1__2_/logical_tile_io_mode_io__1/* +set_disable_timing grid_io_top_1__2_/logical_tile_io_mode_io__1/io_inpad[0] ####################################### -# Disable all the ports for pb_graph_node iopad[0] +# Disable unused mux_inputs for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_top_1__2_/logical_tile_io_mode_io__1/logical_tile_io_mode_physical__iopad_0/* +set_disable_timing grid_io_top_1__2_/logical_tile_io_mode_io__1//direct_interc_0_/in[0] +####################################### +# Disable unused pins for pb_graph_node iopad[0] +####################################### +set_disable_timing grid_io_top_1__2_/logical_tile_io_mode_io__1/logical_tile_io_mode_physical__iopad_0/iopad_inpad[0] ####################################### # Disable Timing for unused grid[1][2][2] ####################################### @@ -1539,31 +1583,31 @@ set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__2/* ####################################### set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__2/logical_tile_io_mode_physical__iopad_0/* ####################################### -# Disable Timing for unused grid[2][1][3] -####################################### -####################################### -# Disable all the ports for pb_graph_node io[0] -####################################### -set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__3/* -####################################### -# Disable all the ports for pb_graph_node iopad[0] -####################################### -set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__3/logical_tile_io_mode_physical__iopad_0/* -####################################### -# Disable Timing for unused resources in grid[2][1][4] +# Disable Timing for unused resources in grid[2][1][3] ####################################### ####################################### # Disable unused pins for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__4/io_inpad[0] +set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__3/io_outpad[0] ####################################### # Disable unused mux_inputs for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__4//direct_interc_0_/in[0] +set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__3//direct_interc_1_/in[0] ####################################### # Disable unused pins for pb_graph_node iopad[0] ####################################### -set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__4/logical_tile_io_mode_physical__iopad_0/iopad_inpad[0] +set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__3/logical_tile_io_mode_physical__iopad_0/iopad_outpad[0] +####################################### +# Disable Timing for unused grid[2][1][4] +####################################### +####################################### +# Disable all the ports for pb_graph_node io[0] +####################################### +set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__4/* +####################################### +# Disable all the ports for pb_graph_node iopad[0] +####################################### +set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__4/logical_tile_io_mode_physical__iopad_0/* ####################################### # Disable Timing for unused grid[2][1][5] ####################################### @@ -1576,31 +1620,31 @@ set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__5/* ####################################### set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__5/logical_tile_io_mode_physical__iopad_0/* ####################################### -# Disable Timing for unused grid[2][1][6] -####################################### -####################################### -# Disable all the ports for pb_graph_node io[0] -####################################### -set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__6/* -####################################### -# Disable all the ports for pb_graph_node iopad[0] -####################################### -set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__6/logical_tile_io_mode_physical__iopad_0/* -####################################### -# Disable Timing for unused resources in grid[2][1][7] +# Disable Timing for unused resources in grid[2][1][6] ####################################### ####################################### # Disable unused pins for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__7/io_outpad[0] +set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__6/io_outpad[0] ####################################### # Disable unused mux_inputs for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__7//direct_interc_1_/in[0] +set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__6//direct_interc_1_/in[0] ####################################### # Disable unused pins for pb_graph_node iopad[0] ####################################### -set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__7/logical_tile_io_mode_physical__iopad_0/iopad_outpad[0] +set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__6/logical_tile_io_mode_physical__iopad_0/iopad_outpad[0] +####################################### +# Disable Timing for unused grid[2][1][7] +####################################### +####################################### +# Disable all the ports for pb_graph_node io[0] +####################################### +set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__7/* +####################################### +# Disable all the ports for pb_graph_node iopad[0] +####################################### +set_disable_timing grid_io_right_2__1_/logical_tile_io_mode_io__7/logical_tile_io_mode_physical__iopad_0/* ####################################### # Disable Timing for grid[1][0] ####################################### @@ -1729,20 +1773,16 @@ set_disable_timing grid_io_left_0__1_/logical_tile_io_mode_io__2/* ####################################### set_disable_timing grid_io_left_0__1_/logical_tile_io_mode_io__2/logical_tile_io_mode_physical__iopad_0/* ####################################### -# Disable Timing for unused resources in grid[0][1][3] +# Disable Timing for unused grid[0][1][3] ####################################### ####################################### -# Disable unused pins for pb_graph_node io[0] +# Disable all the ports for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_left_0__1_/logical_tile_io_mode_io__3/io_outpad[0] +set_disable_timing grid_io_left_0__1_/logical_tile_io_mode_io__3/* ####################################### -# Disable unused mux_inputs for pb_graph_node io[0] +# Disable all the ports for pb_graph_node iopad[0] ####################################### -set_disable_timing grid_io_left_0__1_/logical_tile_io_mode_io__3//direct_interc_1_/in[0] -####################################### -# Disable unused pins for pb_graph_node iopad[0] -####################################### -set_disable_timing grid_io_left_0__1_/logical_tile_io_mode_io__3/logical_tile_io_mode_physical__iopad_0/iopad_outpad[0] +set_disable_timing grid_io_left_0__1_/logical_tile_io_mode_io__3/logical_tile_io_mode_physical__iopad_0/* ####################################### # Disable Timing for unused grid[0][1][4] ####################################### diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/and2_top_formal_verification.v b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/and2_top_formal_verification.v index 237dad23a..35b9564df 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/and2_top_formal_verification.v +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/and2_top_formal_verification.v @@ -42,18 +42,17 @@ wire [0:0] clk_fm; // ----- End Connect Global ports of FPGA top module ----- // ----- Link BLIF Benchmark I/Os to FPGA I/Os ----- -// ----- Blif Benchmark input a is mapped to FPGA IOPAD gfpga_pad_GPIO_PAD_fm[27] ----- - assign gfpga_pad_GPIO_PAD_fm[27] = a[0]; +// ----- Blif Benchmark input a is mapped to FPGA IOPAD gfpga_pad_GPIO_PAD_fm[11] ----- + assign gfpga_pad_GPIO_PAD_fm[11] = a[0]; -// ----- Blif Benchmark input b is mapped to FPGA IOPAD gfpga_pad_GPIO_PAD_fm[15] ----- - assign gfpga_pad_GPIO_PAD_fm[15] = b[0]; +// ----- Blif Benchmark input b is mapped to FPGA IOPAD gfpga_pad_GPIO_PAD_fm[14] ----- + assign gfpga_pad_GPIO_PAD_fm[14] = b[0]; -// ----- Blif Benchmark output c is mapped to FPGA IOPAD gfpga_pad_GPIO_PAD_fm[12] ----- - assign c[0] = gfpga_pad_GPIO_PAD_fm[12]; +// ----- Blif Benchmark output c is mapped to FPGA IOPAD gfpga_pad_GPIO_PAD_fm[1] ----- + assign c[0] = gfpga_pad_GPIO_PAD_fm[1]; // ----- Wire unused FPGA I/Os to constants ----- assign gfpga_pad_GPIO_PAD_fm[0] = 1'b0; - assign gfpga_pad_GPIO_PAD_fm[1] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[2] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[3] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[4] = 1'b0; @@ -63,9 +62,9 @@ wire [0:0] clk_fm; assign gfpga_pad_GPIO_PAD_fm[8] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[9] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[10] = 1'b0; - assign gfpga_pad_GPIO_PAD_fm[11] = 1'b0; + assign gfpga_pad_GPIO_PAD_fm[12] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[13] = 1'b0; - assign gfpga_pad_GPIO_PAD_fm[14] = 1'b0; + assign gfpga_pad_GPIO_PAD_fm[15] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[16] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[17] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[18] = 1'b0; @@ -77,6 +76,7 @@ wire [0:0] clk_fm; assign gfpga_pad_GPIO_PAD_fm[24] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[25] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[26] = 1'b0; + assign gfpga_pad_GPIO_PAD_fm[27] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[28] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[29] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[30] = 1'b0; @@ -125,8 +125,8 @@ initial begin force U0_formal_verification.grid_clb_1__1_.logical_tile_clb_mode_clb__0.mem_fle_2_in_2.mem_outb[0:3] = {4{1'b1}}; force U0_formal_verification.grid_clb_1__1_.logical_tile_clb_mode_clb__0.mem_fle_2_in_3.mem_out[0:3] = {4{1'b0}}; force U0_formal_verification.grid_clb_1__1_.logical_tile_clb_mode_clb__0.mem_fle_2_in_3.mem_outb[0:3] = {4{1'b1}}; - force U0_formal_verification.grid_clb_1__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_0.mem_out[0:3] = {4{1'b1}}; - force U0_formal_verification.grid_clb_1__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_0.mem_outb[0:3] = {4{1'b0}}; + force U0_formal_verification.grid_clb_1__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_0.mem_out[0:3] = 4'b0110; + force U0_formal_verification.grid_clb_1__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_0.mem_outb[0:3] = 4'b1001; force U0_formal_verification.grid_clb_1__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_1.mem_out[0:3] = {4{1'b0}}; force U0_formal_verification.grid_clb_1__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_1.mem_outb[0:3] = {4{1'b1}}; force U0_formal_verification.grid_clb_1__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_2.mem_out[0:3] = {4{1'b0}}; @@ -135,8 +135,8 @@ initial begin force U0_formal_verification.grid_clb_1__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_3.mem_outb[0:3] = 4'b1000; force U0_formal_verification.grid_io_top_1__2_.logical_tile_io_mode_io__0.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_out[0] = 1'b1; force U0_formal_verification.grid_io_top_1__2_.logical_tile_io_mode_io__0.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_outb[0] = 1'b0; - force U0_formal_verification.grid_io_top_1__2_.logical_tile_io_mode_io__1.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_out[0] = 1'b1; - force U0_formal_verification.grid_io_top_1__2_.logical_tile_io_mode_io__1.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_outb[0] = 1'b0; + force U0_formal_verification.grid_io_top_1__2_.logical_tile_io_mode_io__1.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_out[0] = 1'b0; + force U0_formal_verification.grid_io_top_1__2_.logical_tile_io_mode_io__1.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_outb[0] = 1'b1; force U0_formal_verification.grid_io_top_1__2_.logical_tile_io_mode_io__2.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_out[0] = 1'b1; force U0_formal_verification.grid_io_top_1__2_.logical_tile_io_mode_io__2.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_outb[0] = 1'b0; force U0_formal_verification.grid_io_top_1__2_.logical_tile_io_mode_io__3.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_out[0] = 1'b1; @@ -157,8 +157,8 @@ initial begin force U0_formal_verification.grid_io_right_2__1_.logical_tile_io_mode_io__2.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_outb[0] = 1'b0; force U0_formal_verification.grid_io_right_2__1_.logical_tile_io_mode_io__3.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_out[0] = 1'b1; force U0_formal_verification.grid_io_right_2__1_.logical_tile_io_mode_io__3.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_outb[0] = 1'b0; - force U0_formal_verification.grid_io_right_2__1_.logical_tile_io_mode_io__4.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_out[0] = 1'b0; - force U0_formal_verification.grid_io_right_2__1_.logical_tile_io_mode_io__4.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_outb[0] = 1'b1; + force U0_formal_verification.grid_io_right_2__1_.logical_tile_io_mode_io__4.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_out[0] = 1'b1; + force U0_formal_verification.grid_io_right_2__1_.logical_tile_io_mode_io__4.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_outb[0] = 1'b0; force U0_formal_verification.grid_io_right_2__1_.logical_tile_io_mode_io__5.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_out[0] = 1'b1; force U0_formal_verification.grid_io_right_2__1_.logical_tile_io_mode_io__5.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_outb[0] = 1'b0; force U0_formal_verification.grid_io_right_2__1_.logical_tile_io_mode_io__6.logical_tile_io_mode_physical__iopad_0.GPIO_DFF_mem.mem_out[0] = 1'b1; @@ -203,8 +203,8 @@ initial begin force U0_formal_verification.sb_0__0_.mem_top_track_2.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__0_.mem_top_track_4.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_0__0_.mem_top_track_4.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_0__0_.mem_top_track_6.mem_out[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_0__0_.mem_top_track_6.mem_outb[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_0__0_.mem_top_track_6.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_0__0_.mem_top_track_6.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__0_.mem_top_track_8.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_0__0_.mem_top_track_8.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__0_.mem_top_track_10.mem_out[0:1] = {2{1'b0}}; @@ -241,12 +241,12 @@ initial begin force U0_formal_verification.sb_0__0_.mem_right_track_14.mem_outb[0:2] = {3{1'b1}}; force U0_formal_verification.sb_0__0_.mem_right_track_16.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_0__0_.mem_right_track_16.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_0__0_.mem_right_track_18.mem_out[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_0__0_.mem_right_track_18.mem_outb[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_0__0_.mem_right_track_18.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_0__0_.mem_right_track_18.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__0_.mem_right_track_20.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_0__0_.mem_right_track_20.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_0__0_.mem_right_track_22.mem_out[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_0__0_.mem_right_track_22.mem_outb[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_0__0_.mem_right_track_22.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_0__0_.mem_right_track_22.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__0_.mem_right_track_24.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_0__0_.mem_right_track_24.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__1_.mem_right_track_0.mem_out[0:2] = {3{1'b0}}; @@ -265,8 +265,8 @@ initial begin force U0_formal_verification.sb_0__1_.mem_right_track_12.mem_outb[0:2] = {3{1'b1}}; force U0_formal_verification.sb_0__1_.mem_right_track_14.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_0__1_.mem_right_track_14.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_0__1_.mem_right_track_16.mem_out[0:1] = 2'b10; - force U0_formal_verification.sb_0__1_.mem_right_track_16.mem_outb[0:1] = 2'b01; + force U0_formal_verification.sb_0__1_.mem_right_track_16.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_0__1_.mem_right_track_16.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__1_.mem_right_track_18.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_0__1_.mem_right_track_18.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__1_.mem_right_track_20.mem_out[0:1] = {2{1'b0}}; @@ -291,38 +291,38 @@ initial begin force U0_formal_verification.sb_0__1_.mem_bottom_track_13.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__1_.mem_bottom_track_15.mem_out[0:2] = {3{1'b0}}; force U0_formal_verification.sb_0__1_.mem_bottom_track_15.mem_outb[0:2] = {3{1'b1}}; - force U0_formal_verification.sb_0__1_.mem_bottom_track_17.mem_out[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_0__1_.mem_bottom_track_17.mem_outb[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_0__1_.mem_bottom_track_17.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_0__1_.mem_bottom_track_17.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__1_.mem_bottom_track_19.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_0__1_.mem_bottom_track_19.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_0__1_.mem_bottom_track_21.mem_out[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_0__1_.mem_bottom_track_21.mem_outb[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_0__1_.mem_bottom_track_21.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_0__1_.mem_bottom_track_21.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__1_.mem_bottom_track_23.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_0__1_.mem_bottom_track_23.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__1_.mem_bottom_track_25.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_0__1_.mem_bottom_track_25.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_1__0_.mem_top_track_0.mem_out[0:2] = {3{1'b1}}; force U0_formal_verification.sb_1__0_.mem_top_track_0.mem_outb[0:2] = {3{1'b0}}; - force U0_formal_verification.sb_1__0_.mem_top_track_2.mem_out[0:2] = 3'b001; - force U0_formal_verification.sb_1__0_.mem_top_track_2.mem_outb[0:2] = 3'b110; - force U0_formal_verification.sb_1__0_.mem_top_track_4.mem_out[0:1] = 2'b10; - force U0_formal_verification.sb_1__0_.mem_top_track_4.mem_outb[0:1] = 2'b01; + force U0_formal_verification.sb_1__0_.mem_top_track_2.mem_out[0:2] = {3{1'b0}}; + force U0_formal_verification.sb_1__0_.mem_top_track_2.mem_outb[0:2] = {3{1'b1}}; + force U0_formal_verification.sb_1__0_.mem_top_track_4.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_1__0_.mem_top_track_4.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_1__0_.mem_top_track_6.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_1__0_.mem_top_track_6.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_1__0_.mem_top_track_8.mem_out[0:1] = 2'b10; - force U0_formal_verification.sb_1__0_.mem_top_track_8.mem_outb[0:1] = 2'b01; + force U0_formal_verification.sb_1__0_.mem_top_track_8.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_1__0_.mem_top_track_8.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_1__0_.mem_top_track_10.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_1__0_.mem_top_track_10.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_1__0_.mem_top_track_12.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_1__0_.mem_top_track_12.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_1__0_.mem_top_track_14.mem_out[0:2] = {3{1'b0}}; - force U0_formal_verification.sb_1__0_.mem_top_track_14.mem_outb[0:2] = {3{1'b1}}; + force U0_formal_verification.sb_1__0_.mem_top_track_14.mem_out[0:2] = 3'b011; + force U0_formal_verification.sb_1__0_.mem_top_track_14.mem_outb[0:2] = 3'b100; force U0_formal_verification.sb_1__0_.mem_top_track_16.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_1__0_.mem_top_track_16.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_1__0_.mem_top_track_18.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_1__0_.mem_top_track_18.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_1__0_.mem_top_track_20.mem_out[0:1] = {2{1'b0}}; - force U0_formal_verification.sb_1__0_.mem_top_track_20.mem_outb[0:1] = {2{1'b1}}; + force U0_formal_verification.sb_1__0_.mem_top_track_20.mem_out[0:1] = {2{1'b1}}; + force U0_formal_verification.sb_1__0_.mem_top_track_20.mem_outb[0:1] = {2{1'b0}}; force U0_formal_verification.sb_1__0_.mem_top_track_22.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_1__0_.mem_top_track_22.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_1__0_.mem_top_track_24.mem_out[0:1] = {2{1'b0}}; @@ -385,8 +385,8 @@ initial begin force U0_formal_verification.sb_1__1_.mem_left_track_3.mem_outb[0:1] = {2{1'b0}}; force U0_formal_verification.sb_1__1_.mem_left_track_5.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_1__1_.mem_left_track_5.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_1__1_.mem_left_track_7.mem_out[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_1__1_.mem_left_track_7.mem_outb[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_1__1_.mem_left_track_7.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_1__1_.mem_left_track_7.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_1__1_.mem_left_track_9.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_1__1_.mem_left_track_9.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_1__1_.mem_left_track_11.mem_out[0:1] = {2{1'b0}}; @@ -429,8 +429,8 @@ initial begin force U0_formal_verification.cbx_1__0_.mem_top_ipin_7.mem_outb[0:2] = {3{1'b1}}; force U0_formal_verification.cbx_1__1_.mem_bottom_ipin_0.mem_out[0:2] = {3{1'b0}}; force U0_formal_verification.cbx_1__1_.mem_bottom_ipin_0.mem_outb[0:2] = {3{1'b1}}; - force U0_formal_verification.cbx_1__1_.mem_bottom_ipin_1.mem_out[0:2] = {3{1'b0}}; - force U0_formal_verification.cbx_1__1_.mem_bottom_ipin_1.mem_outb[0:2] = {3{1'b1}}; + force U0_formal_verification.cbx_1__1_.mem_bottom_ipin_1.mem_out[0:2] = 3'b001; + force U0_formal_verification.cbx_1__1_.mem_bottom_ipin_1.mem_outb[0:2] = 3'b110; force U0_formal_verification.cbx_1__1_.mem_bottom_ipin_2.mem_out[0:2] = {3{1'b0}}; force U0_formal_verification.cbx_1__1_.mem_bottom_ipin_2.mem_outb[0:2] = {3{1'b1}}; force U0_formal_verification.cbx_1__1_.mem_bottom_ipin_3.mem_out[0:2] = {3{1'b0}}; @@ -443,8 +443,8 @@ initial begin force U0_formal_verification.cbx_1__1_.mem_bottom_ipin_6.mem_outb[0:2] = {3{1'b1}}; force U0_formal_verification.cbx_1__1_.mem_bottom_ipin_7.mem_out[0:2] = {3{1'b0}}; force U0_formal_verification.cbx_1__1_.mem_bottom_ipin_7.mem_outb[0:2] = {3{1'b1}}; - force U0_formal_verification.cbx_1__1_.mem_top_ipin_0.mem_out[0:2] = 3'b110; - force U0_formal_verification.cbx_1__1_.mem_top_ipin_0.mem_outb[0:2] = 3'b001; + force U0_formal_verification.cbx_1__1_.mem_top_ipin_0.mem_out[0:2] = {3{1'b0}}; + force U0_formal_verification.cbx_1__1_.mem_top_ipin_0.mem_outb[0:2] = {3{1'b1}}; force U0_formal_verification.cbx_1__1_.mem_top_ipin_1.mem_out[0:2] = {3{1'b0}}; force U0_formal_verification.cbx_1__1_.mem_top_ipin_1.mem_outb[0:2] = {3{1'b1}}; force U0_formal_verification.cbx_1__1_.mem_top_ipin_2.mem_out[0:2] = {3{1'b0}}; @@ -477,20 +477,20 @@ initial begin force U0_formal_verification.cby_1__1_.mem_left_ipin_2.mem_outb[0:2] = {3{1'b1}}; force U0_formal_verification.cby_1__1_.mem_left_ipin_3.mem_out[0:2] = {3{1'b0}}; force U0_formal_verification.cby_1__1_.mem_left_ipin_3.mem_outb[0:2] = {3{1'b1}}; - force U0_formal_verification.cby_1__1_.mem_left_ipin_4.mem_out[0:2] = 3'b101; - force U0_formal_verification.cby_1__1_.mem_left_ipin_4.mem_outb[0:2] = 3'b010; + force U0_formal_verification.cby_1__1_.mem_left_ipin_4.mem_out[0:2] = {3{1'b0}}; + force U0_formal_verification.cby_1__1_.mem_left_ipin_4.mem_outb[0:2] = {3{1'b1}}; force U0_formal_verification.cby_1__1_.mem_left_ipin_5.mem_out[0:2] = {3{1'b0}}; force U0_formal_verification.cby_1__1_.mem_left_ipin_5.mem_outb[0:2] = {3{1'b1}}; force U0_formal_verification.cby_1__1_.mem_left_ipin_6.mem_out[0:2] = {3{1'b0}}; force U0_formal_verification.cby_1__1_.mem_left_ipin_6.mem_outb[0:2] = {3{1'b1}}; force U0_formal_verification.cby_1__1_.mem_left_ipin_7.mem_out[0:2] = {3{1'b0}}; force U0_formal_verification.cby_1__1_.mem_left_ipin_7.mem_outb[0:2] = {3{1'b1}}; - force U0_formal_verification.cby_1__1_.mem_right_ipin_0.mem_out[0:2] = {3{1'b1}}; - force U0_formal_verification.cby_1__1_.mem_right_ipin_0.mem_outb[0:2] = {3{1'b0}}; + force U0_formal_verification.cby_1__1_.mem_right_ipin_0.mem_out[0:2] = 3'b101; + force U0_formal_verification.cby_1__1_.mem_right_ipin_0.mem_outb[0:2] = 3'b010; force U0_formal_verification.cby_1__1_.mem_right_ipin_1.mem_out[0:2] = {3{1'b0}}; force U0_formal_verification.cby_1__1_.mem_right_ipin_1.mem_outb[0:2] = {3{1'b1}}; - force U0_formal_verification.cby_1__1_.mem_right_ipin_2.mem_out[0:2] = {3{1'b0}}; - force U0_formal_verification.cby_1__1_.mem_right_ipin_2.mem_outb[0:2] = {3{1'b1}}; + force U0_formal_verification.cby_1__1_.mem_right_ipin_2.mem_out[0:2] = 3'b110; + force U0_formal_verification.cby_1__1_.mem_right_ipin_2.mem_outb[0:2] = 3'b001; end // ----- End assign bitstream to configuration memories ----- // ----- End load bitstream to configuration memories ----- diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_bitstream.bit b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_bitstream.bit index 7ed88111a..3387708eb 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_bitstream.bit +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_bitstream.bit @@ -13,10 +13,10 @@ 0 0 0 +0 1 1 -1 -1 +0 0 0 0 @@ -138,13 +138,13 @@ 0 0 0 +1 +1 0 0 0 -0 -0 -1 1 +0 1 0 0 @@ -155,9 +155,7 @@ 0 0 0 -1 0 -1 0 0 0 @@ -239,10 +237,15 @@ 0 0 0 +1 +1 0 0 0 0 +1 +1 +0 0 0 0 @@ -251,12 +254,9 @@ 0 0 0 -1 0 0 0 -1 -1 0 0 1 @@ -302,12 +302,8 @@ 0 0 0 -1 -1 0 0 -1 -1 0 0 0 @@ -349,8 +345,6 @@ 0 0 0 -1 -1 0 0 0 @@ -362,12 +356,8 @@ 0 0 0 -1 -1 0 0 -1 -1 0 0 0 @@ -396,7 +386,17 @@ 0 0 0 -1 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 0 0 0 @@ -421,7 +421,7 @@ 1 1 1 -1 +0 1 0 0 @@ -430,8 +430,6 @@ 0 0 0 -1 -1 0 0 0 @@ -452,6 +450,10 @@ 0 0 0 +1 +0 +0 +0 0 0 0 @@ -475,8 +477,6 @@ 0 0 0 -1 -1 0 0 1 @@ -515,7 +515,7 @@ 1 1 1 -0 +1 1 1 1 diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_bitstream.xml b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_bitstream.xml index 419917847..4edf9cf2b 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_bitstream.xml +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_bitstream.xml @@ -30,13 +30,13 @@ - + - + @@ -280,9 +280,9 @@ - + - + @@ -292,7 +292,7 @@ - + @@ -314,11 +314,11 @@ - + - + @@ -478,9 +478,9 @@ - + - + @@ -490,9 +490,9 @@ - + - + @@ -506,7 +506,7 @@ - + @@ -514,9 +514,9 @@ - + - + @@ -608,17 +608,17 @@ - + - + - + - + @@ -702,9 +702,9 @@ - + - + @@ -728,17 +728,17 @@ - + - + - + - + @@ -796,7 +796,7 @@ - + @@ -846,7 +846,7 @@ - + @@ -864,9 +864,9 @@ - + - + @@ -904,7 +904,7 @@ - + @@ -954,9 +954,9 @@ - + - + @@ -1034,7 +1034,7 @@ - + diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_independent_bitstream.xml b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_independent_bitstream.xml index eddb1a906..1b1ecfa0d 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_independent_bitstream.xml +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_independent_bitstream.xml @@ -431,7 +431,7 @@ - + @@ -440,7 +440,7 @@ - + @@ -449,11 +449,11 @@ - - + + - + @@ -498,7 +498,7 @@ - + @@ -507,7 +507,7 @@ - + @@ -553,7 +553,7 @@ - + @@ -731,7 +731,7 @@ - + @@ -1094,7 +1094,7 @@ - + @@ -1112,16 +1112,16 @@ - + - + - - - + + + @@ -1228,7 +1228,7 @@ - + @@ -1480,15 +1480,15 @@ - + - + - - - + + + @@ -1516,15 +1516,15 @@ - + - + - - - + + + @@ -1713,13 +1713,13 @@ - + - + - - + + @@ -1866,7 +1866,7 @@ - + @@ -1884,7 +1884,7 @@ - + @@ -1961,16 +1961,16 @@ - + - + - - - + + + @@ -1999,14 +1999,14 @@ - + - + - - - + + + @@ -2056,7 +2056,7 @@ - + @@ -2077,16 +2077,16 @@ - + - + - + - + @@ -2098,13 +2098,13 @@ - + - + - - + + @@ -2116,7 +2116,7 @@ - + @@ -2134,15 +2134,15 @@ - + - + - + - - + + @@ -2173,7 +2173,7 @@ - + @@ -2192,17 +2192,17 @@ - - + + - + - + - - + + @@ -2213,7 +2213,7 @@ - + @@ -2249,15 +2249,15 @@ - + - + - - - + + + @@ -2559,7 +2559,7 @@ - + @@ -2598,7 +2598,7 @@ - + @@ -2616,7 +2616,7 @@ - + @@ -2655,7 +2655,7 @@ - + @@ -2674,8 +2674,8 @@ - - + + @@ -2695,8 +2695,8 @@ - - + + @@ -2732,7 +2732,7 @@ - + @@ -2787,7 +2787,7 @@ - + @@ -2845,7 +2845,7 @@ - + @@ -2864,16 +2864,16 @@ - + - + - - - + + + @@ -2902,7 +2902,7 @@ - + @@ -2961,7 +2961,7 @@ - + @@ -3016,7 +3016,7 @@ - + @@ -3128,7 +3128,7 @@ - + @@ -3174,7 +3174,7 @@ - + @@ -3266,7 +3266,7 @@ - + @@ -3287,7 +3287,7 @@ - + @@ -3341,12 +3341,12 @@ - + - + - + @@ -3360,7 +3360,7 @@ - + @@ -3382,7 +3382,7 @@ - + @@ -3403,7 +3403,7 @@ - + @@ -3498,15 +3498,15 @@ - + - + - - - + + + @@ -3519,7 +3519,7 @@ - + @@ -3541,7 +3541,7 @@ - + @@ -3616,7 +3616,7 @@ - + @@ -3636,7 +3636,7 @@ - + @@ -3657,12 +3657,12 @@ - + - + @@ -3754,7 +3754,7 @@ - + @@ -3775,7 +3775,7 @@ - + @@ -3822,9 +3822,9 @@ - + - + @@ -3843,9 +3843,9 @@ - + - + @@ -3866,7 +3866,7 @@ - + @@ -3891,18 +3891,18 @@ - + - + - + - - + + - + @@ -3912,7 +3912,7 @@ - + @@ -3962,7 +3962,7 @@ - + @@ -3981,9 +3981,9 @@ - + - + @@ -3991,9 +3991,9 @@ - + - + @@ -4004,7 +4004,7 @@ - + @@ -4031,15 +4031,15 @@ - + - + - - - + + + diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/global_ports.sdc b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/global_ports.sdc index 7b26126b2..0cf7373f8 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/global_ports.sdc +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/global_ports.sdc @@ -14,7 +14,7 @@ set_units -time s ################################################## # Create clock ################################################## -create_clock -name clk[0] -period 1.525308102e-09 -waveform {0 7.62654051e-10} [get_ports {clk[0]}] +create_clock -name clk[0] -period 9.25781396e-10 -waveform {0 4.62890698e-10} [get_ports {clk[0]}] ################################################## # Create programmable clock ################################################## diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/pin_mapping.xml b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/pin_mapping.xml index e39c498fe..89523007a 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/pin_mapping.xml +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/pin_mapping.xml @@ -3,7 +3,7 @@ --> - - - + + + diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/and2_formal_random_top_tb.v b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/and2_formal_random_top_tb.v index c6df32c2a..59580560b 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/and2_formal_random_top_tb.v +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/and2_formal_random_top_tb.v @@ -50,7 +50,7 @@ module and2_top_formal_verification_random_tb; initial begin clk[0] <= 1'b0; while(1) begin - #0.4485172927 + #0.4866067469 clk[0] <= !clk[0]; end end @@ -109,7 +109,7 @@ initial begin $timeformat(-9, 2, "ns", 20); $display("Simulation start"); // ----- Can be changed by the user for his/her need ------- - #6.279242039 + #6.812494755 if(nb_error == 0) begin $display("Simulation Succeed"); end else begin diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/and2_fpga_top_analysis.sdc b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/and2_fpga_top_analysis.sdc index 0ab31beb3..73d00c19f 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/and2_fpga_top_analysis.sdc +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/and2_fpga_top_analysis.sdc @@ -9,14 +9,14 @@ ################################################## # Create clock ################################################## -create_clock clk[0] -period 8.970345577e-10 -waveform {0 4.485172789e-10} +create_clock clk[0] -period 9.732135098e-10 -waveform {0 4.866067549e-10} ################################################## # Create input and output delays for used I/Os ################################################## -set_input_delay -clock clk[0] -max 8.970345577e-10 gfpga_pad_GPIO_PAD[39] -set_input_delay -clock clk[0] -max 8.970345577e-10 gfpga_pad_GPIO_PAD[48] -set_output_delay -clock clk[0] -max 8.970345577e-10 gfpga_pad_GPIO_PAD[34] +set_input_delay -clock clk[0] -max 9.732135098e-10 gfpga_pad_GPIO_PAD[22] +set_input_delay -clock clk[0] -max 9.732135098e-10 gfpga_pad_GPIO_PAD[26] +set_output_delay -clock clk[0] -max 9.732135098e-10 gfpga_pad_GPIO_PAD[9] ################################################## # Disable timing for unused I/Os @@ -30,7 +30,6 @@ set_disable_timing gfpga_pad_GPIO_PAD[5] set_disable_timing gfpga_pad_GPIO_PAD[6] set_disable_timing gfpga_pad_GPIO_PAD[7] set_disable_timing gfpga_pad_GPIO_PAD[8] -set_disable_timing gfpga_pad_GPIO_PAD[9] set_disable_timing gfpga_pad_GPIO_PAD[10] set_disable_timing gfpga_pad_GPIO_PAD[11] set_disable_timing gfpga_pad_GPIO_PAD[12] @@ -43,11 +42,9 @@ set_disable_timing gfpga_pad_GPIO_PAD[18] set_disable_timing gfpga_pad_GPIO_PAD[19] set_disable_timing gfpga_pad_GPIO_PAD[20] set_disable_timing gfpga_pad_GPIO_PAD[21] -set_disable_timing gfpga_pad_GPIO_PAD[22] set_disable_timing gfpga_pad_GPIO_PAD[23] set_disable_timing gfpga_pad_GPIO_PAD[24] set_disable_timing gfpga_pad_GPIO_PAD[25] -set_disable_timing gfpga_pad_GPIO_PAD[26] set_disable_timing gfpga_pad_GPIO_PAD[27] set_disable_timing gfpga_pad_GPIO_PAD[28] set_disable_timing gfpga_pad_GPIO_PAD[29] @@ -55,10 +52,12 @@ set_disable_timing gfpga_pad_GPIO_PAD[30] set_disable_timing gfpga_pad_GPIO_PAD[31] set_disable_timing gfpga_pad_GPIO_PAD[32] set_disable_timing gfpga_pad_GPIO_PAD[33] +set_disable_timing gfpga_pad_GPIO_PAD[34] set_disable_timing gfpga_pad_GPIO_PAD[35] set_disable_timing gfpga_pad_GPIO_PAD[36] set_disable_timing gfpga_pad_GPIO_PAD[37] set_disable_timing gfpga_pad_GPIO_PAD[38] +set_disable_timing gfpga_pad_GPIO_PAD[39] set_disable_timing gfpga_pad_GPIO_PAD[40] set_disable_timing gfpga_pad_GPIO_PAD[41] set_disable_timing gfpga_pad_GPIO_PAD[42] @@ -67,6 +66,7 @@ set_disable_timing gfpga_pad_GPIO_PAD[44] set_disable_timing gfpga_pad_GPIO_PAD[45] set_disable_timing gfpga_pad_GPIO_PAD[46] set_disable_timing gfpga_pad_GPIO_PAD[47] +set_disable_timing gfpga_pad_GPIO_PAD[48] set_disable_timing gfpga_pad_GPIO_PAD[49] set_disable_timing gfpga_pad_GPIO_PAD[50] set_disable_timing gfpga_pad_GPIO_PAD[51] @@ -229,8 +229,10 @@ set_disable_timing fpga_top/sb_*__*_/mem_left_track_*/DFFR_*_/QN ################################################## set_disable_timing cbx_1__0_/chanx_left_in[0] set_disable_timing cbx_1__0_/chanx_right_in[0] +set_disable_timing cbx_1__0_/chanx_left_in[1] set_disable_timing cbx_1__0_/chanx_right_in[1] set_disable_timing cbx_1__0_/chanx_left_in[2] +set_disable_timing cbx_1__0_/chanx_right_in[2] set_disable_timing cbx_1__0_/chanx_left_in[3] set_disable_timing cbx_1__0_/chanx_right_in[3] set_disable_timing cbx_1__0_/chanx_left_in[4] @@ -247,8 +249,10 @@ set_disable_timing cbx_1__0_/chanx_left_in[9] set_disable_timing cbx_1__0_/chanx_right_in[9] set_disable_timing cbx_1__0_/chanx_left_out[0] set_disable_timing cbx_1__0_/chanx_right_out[0] +set_disable_timing cbx_1__0_/chanx_left_out[1] set_disable_timing cbx_1__0_/chanx_right_out[1] set_disable_timing cbx_1__0_/chanx_left_out[2] +set_disable_timing cbx_1__0_/chanx_right_out[2] set_disable_timing cbx_1__0_/chanx_left_out[3] set_disable_timing cbx_1__0_/chanx_right_out[3] set_disable_timing cbx_1__0_/chanx_left_out[4] @@ -390,7 +394,6 @@ set_disable_timing cbx_1__2_/chanx_right_in[0] set_disable_timing cbx_1__2_/chanx_left_in[1] set_disable_timing cbx_1__2_/chanx_right_in[1] set_disable_timing cbx_1__2_/chanx_left_in[2] -set_disable_timing cbx_1__2_/chanx_right_in[2] set_disable_timing cbx_1__2_/chanx_left_in[3] set_disable_timing cbx_1__2_/chanx_right_in[3] set_disable_timing cbx_1__2_/chanx_left_in[4] @@ -410,7 +413,6 @@ set_disable_timing cbx_1__2_/chanx_right_out[0] set_disable_timing cbx_1__2_/chanx_left_out[1] set_disable_timing cbx_1__2_/chanx_right_out[1] set_disable_timing cbx_1__2_/chanx_left_out[2] -set_disable_timing cbx_1__2_/chanx_right_out[2] set_disable_timing cbx_1__2_/chanx_left_out[3] set_disable_timing cbx_1__2_/chanx_right_out[3] set_disable_timing cbx_1__2_/chanx_left_out[4] @@ -471,8 +473,11 @@ set_disable_timing cbx_1__2_/mux_bottom_ipin_4/in[2] set_disable_timing cbx_2__0_/chanx_left_in[0] set_disable_timing cbx_2__0_/chanx_right_in[0] set_disable_timing cbx_2__0_/chanx_left_in[1] +set_disable_timing cbx_2__0_/chanx_right_in[1] +set_disable_timing cbx_2__0_/chanx_left_in[2] set_disable_timing cbx_2__0_/chanx_right_in[2] set_disable_timing cbx_2__0_/chanx_left_in[3] +set_disable_timing cbx_2__0_/chanx_right_in[3] set_disable_timing cbx_2__0_/chanx_left_in[4] set_disable_timing cbx_2__0_/chanx_right_in[4] set_disable_timing cbx_2__0_/chanx_left_in[5] @@ -488,8 +493,11 @@ set_disable_timing cbx_2__0_/chanx_right_in[9] set_disable_timing cbx_2__0_/chanx_left_out[0] set_disable_timing cbx_2__0_/chanx_right_out[0] set_disable_timing cbx_2__0_/chanx_left_out[1] +set_disable_timing cbx_2__0_/chanx_right_out[1] +set_disable_timing cbx_2__0_/chanx_left_out[2] set_disable_timing cbx_2__0_/chanx_right_out[2] set_disable_timing cbx_2__0_/chanx_left_out[3] +set_disable_timing cbx_2__0_/chanx_right_out[3] set_disable_timing cbx_2__0_/chanx_left_out[4] set_disable_timing cbx_2__0_/chanx_right_out[4] set_disable_timing cbx_2__0_/chanx_left_out[5] @@ -503,11 +511,14 @@ set_disable_timing cbx_2__0_/chanx_right_out[8] set_disable_timing cbx_2__0_/chanx_left_out[9] set_disable_timing cbx_2__0_/chanx_right_out[9] set_disable_timing cbx_2__0_/top_grid_bottom_width_0_height_0_subtile_0__pin_I_6_[0] +set_disable_timing cbx_2__0_/top_grid_bottom_width_0_height_0_subtile_0__pin_I_7_[0] +set_disable_timing cbx_2__0_/top_grid_bottom_width_0_height_0_subtile_0__pin_I_8_[0] set_disable_timing cbx_2__0_/top_grid_bottom_width_0_height_0_subtile_0__pin_I_9_[0] set_disable_timing cbx_2__0_/top_grid_bottom_width_0_height_0_subtile_0__pin_I_10_[0] set_disable_timing cbx_2__0_/top_grid_bottom_width_0_height_0_subtile_0__pin_I_11_[0] set_disable_timing cbx_2__0_/bottom_grid_top_width_0_height_0_subtile_0__pin_outpad_0_[0] set_disable_timing cbx_2__0_/bottom_grid_top_width_0_height_0_subtile_1__pin_outpad_0_[0] +set_disable_timing cbx_2__0_/bottom_grid_top_width_0_height_0_subtile_2__pin_outpad_0_[0] set_disable_timing cbx_2__0_/bottom_grid_top_width_0_height_0_subtile_3__pin_outpad_0_[0] set_disable_timing cbx_2__0_/bottom_grid_top_width_0_height_0_subtile_4__pin_outpad_0_[0] set_disable_timing cbx_2__0_/bottom_grid_top_width_0_height_0_subtile_5__pin_outpad_0_[0] @@ -520,8 +531,10 @@ set_disable_timing cbx_2__0_/mux_top_ipin_4/in[0] set_disable_timing cbx_2__0_/mux_bottom_ipin_1/in[1] set_disable_timing cbx_2__0_/mux_top_ipin_0/in[1] set_disable_timing cbx_2__0_/mux_top_ipin_5/in[1] +set_disable_timing cbx_2__0_/mux_bottom_ipin_1/in[0] set_disable_timing cbx_2__0_/mux_top_ipin_0/in[0] set_disable_timing cbx_2__0_/mux_top_ipin_5/in[0] +set_disable_timing cbx_2__0_/mux_bottom_ipin_2/in[1] set_disable_timing cbx_2__0_/mux_top_ipin_1/in[1] set_disable_timing cbx_2__0_/mux_top_ipin_6/in[1] set_disable_timing cbx_2__0_/mux_bottom_ipin_2/in[0] @@ -531,6 +544,7 @@ set_disable_timing cbx_2__0_/mux_bottom_ipin_3/in[1] set_disable_timing cbx_2__0_/mux_top_ipin_2/in[1] set_disable_timing cbx_2__0_/mux_top_ipin_7/in[1] set_disable_timing cbx_2__0_/mux_bottom_ipin_3/in[0] +set_disable_timing cbx_2__0_/mux_top_ipin_2/in[0] set_disable_timing cbx_2__0_/mux_top_ipin_7/in[0] set_disable_timing cbx_2__0_/mux_bottom_ipin_4/in[1] set_disable_timing cbx_2__0_/mux_top_ipin_3/in[1] @@ -557,7 +571,6 @@ set_disable_timing cbx_2__0_/mux_top_ipin_3/in[2] ################################################## # Disable timing for Connection block cbx_1__1_ ################################################## -set_disable_timing cbx_2__1_/chanx_left_in[0] set_disable_timing cbx_2__1_/chanx_right_in[0] set_disable_timing cbx_2__1_/chanx_left_in[1] set_disable_timing cbx_2__1_/chanx_right_in[1] @@ -577,7 +590,6 @@ set_disable_timing cbx_2__1_/chanx_left_in[8] set_disable_timing cbx_2__1_/chanx_right_in[8] set_disable_timing cbx_2__1_/chanx_left_in[9] set_disable_timing cbx_2__1_/chanx_right_in[9] -set_disable_timing cbx_2__1_/chanx_left_out[0] set_disable_timing cbx_2__1_/chanx_right_out[0] set_disable_timing cbx_2__1_/chanx_left_out[1] set_disable_timing cbx_2__1_/chanx_right_out[1] @@ -621,7 +633,6 @@ set_disable_timing cbx_2__1_/mux_bottom_ipin_5/in[0] set_disable_timing cbx_2__2_/chanx_left_in[0] set_disable_timing cbx_2__2_/chanx_right_in[0] set_disable_timing cbx_2__2_/chanx_left_in[1] -set_disable_timing cbx_2__2_/chanx_right_in[1] set_disable_timing cbx_2__2_/chanx_left_in[2] set_disable_timing cbx_2__2_/chanx_right_in[2] set_disable_timing cbx_2__2_/chanx_left_in[3] @@ -641,7 +652,6 @@ set_disable_timing cbx_2__2_/chanx_right_in[9] set_disable_timing cbx_2__2_/chanx_left_out[0] set_disable_timing cbx_2__2_/chanx_right_out[0] set_disable_timing cbx_2__2_/chanx_left_out[1] -set_disable_timing cbx_2__2_/chanx_right_out[1] set_disable_timing cbx_2__2_/chanx_left_out[2] set_disable_timing cbx_2__2_/chanx_right_out[2] set_disable_timing cbx_2__2_/chanx_left_out[3] @@ -659,7 +669,6 @@ set_disable_timing cbx_2__2_/chanx_right_out[8] set_disable_timing cbx_2__2_/chanx_left_out[9] set_disable_timing cbx_2__2_/chanx_right_out[9] set_disable_timing cbx_2__2_/top_grid_bottom_width_0_height_0_subtile_0__pin_outpad_0_[0] -set_disable_timing cbx_2__2_/top_grid_bottom_width_0_height_0_subtile_1__pin_outpad_0_[0] set_disable_timing cbx_2__2_/top_grid_bottom_width_0_height_0_subtile_2__pin_outpad_0_[0] set_disable_timing cbx_2__2_/top_grid_bottom_width_0_height_0_subtile_3__pin_outpad_0_[0] set_disable_timing cbx_2__2_/top_grid_bottom_width_0_height_0_subtile_4__pin_outpad_0_[0] @@ -672,7 +681,6 @@ set_disable_timing cbx_2__2_/mux_bottom_ipin_0/in[0] set_disable_timing cbx_2__2_/mux_bottom_ipin_5/in[0] set_disable_timing cbx_2__2_/mux_bottom_ipin_1/in[1] set_disable_timing cbx_2__2_/mux_bottom_ipin_6/in[1] -set_disable_timing cbx_2__2_/mux_bottom_ipin_1/in[0] set_disable_timing cbx_2__2_/mux_bottom_ipin_6/in[0] set_disable_timing cbx_2__2_/mux_bottom_ipin_2/in[1] set_disable_timing cbx_2__2_/mux_bottom_ipin_7/in[1] @@ -702,6 +710,7 @@ set_disable_timing cbx_2__2_/mux_bottom_ipin_4/in[2] # Disable timing for Connection block cby_0__1_ ################################################## set_disable_timing cby_0__1_/chany_bottom_in[0] +set_disable_timing cby_0__1_/chany_top_in[0] set_disable_timing cby_0__1_/chany_bottom_in[1] set_disable_timing cby_0__1_/chany_top_in[1] set_disable_timing cby_0__1_/chany_bottom_in[2] @@ -721,6 +730,7 @@ set_disable_timing cby_0__1_/chany_top_in[8] set_disable_timing cby_0__1_/chany_bottom_in[9] set_disable_timing cby_0__1_/chany_top_in[9] set_disable_timing cby_0__1_/chany_bottom_out[0] +set_disable_timing cby_0__1_/chany_top_out[0] set_disable_timing cby_0__1_/chany_bottom_out[1] set_disable_timing cby_0__1_/chany_top_out[1] set_disable_timing cby_0__1_/chany_bottom_out[2] @@ -1043,7 +1053,6 @@ set_disable_timing cby_2__1_/chany_bottom_in[4] set_disable_timing cby_2__1_/chany_top_in[4] set_disable_timing cby_2__1_/chany_bottom_in[5] set_disable_timing cby_2__1_/chany_top_in[5] -set_disable_timing cby_2__1_/chany_bottom_in[6] set_disable_timing cby_2__1_/chany_top_in[6] set_disable_timing cby_2__1_/chany_bottom_in[7] set_disable_timing cby_2__1_/chany_top_in[7] @@ -1063,7 +1072,6 @@ set_disable_timing cby_2__1_/chany_bottom_out[4] set_disable_timing cby_2__1_/chany_top_out[4] set_disable_timing cby_2__1_/chany_bottom_out[5] set_disable_timing cby_2__1_/chany_top_out[5] -set_disable_timing cby_2__1_/chany_bottom_out[6] set_disable_timing cby_2__1_/chany_top_out[6] set_disable_timing cby_2__1_/chany_bottom_out[7] set_disable_timing cby_2__1_/chany_top_out[7] @@ -1144,7 +1152,6 @@ set_disable_timing cby_2__1_/mux_right_ipin_1/in[2] ################################################## # Disable timing for Connection block cby_2__1_ ################################################## -set_disable_timing cby_2__2_/chany_bottom_in[0] set_disable_timing cby_2__2_/chany_top_in[0] set_disable_timing cby_2__2_/chany_bottom_in[1] set_disable_timing cby_2__2_/chany_top_in[1] @@ -1152,19 +1159,16 @@ set_disable_timing cby_2__2_/chany_bottom_in[2] set_disable_timing cby_2__2_/chany_top_in[2] set_disable_timing cby_2__2_/chany_bottom_in[3] set_disable_timing cby_2__2_/chany_top_in[3] -set_disable_timing cby_2__2_/chany_bottom_in[4] set_disable_timing cby_2__2_/chany_top_in[4] set_disable_timing cby_2__2_/chany_bottom_in[5] set_disable_timing cby_2__2_/chany_top_in[5] set_disable_timing cby_2__2_/chany_bottom_in[6] set_disable_timing cby_2__2_/chany_top_in[6] -set_disable_timing cby_2__2_/chany_bottom_in[7] set_disable_timing cby_2__2_/chany_top_in[7] set_disable_timing cby_2__2_/chany_bottom_in[8] set_disable_timing cby_2__2_/chany_top_in[8] set_disable_timing cby_2__2_/chany_bottom_in[9] set_disable_timing cby_2__2_/chany_top_in[9] -set_disable_timing cby_2__2_/chany_bottom_out[0] set_disable_timing cby_2__2_/chany_top_out[0] set_disable_timing cby_2__2_/chany_bottom_out[1] set_disable_timing cby_2__2_/chany_top_out[1] @@ -1172,13 +1176,11 @@ set_disable_timing cby_2__2_/chany_bottom_out[2] set_disable_timing cby_2__2_/chany_top_out[2] set_disable_timing cby_2__2_/chany_bottom_out[3] set_disable_timing cby_2__2_/chany_top_out[3] -set_disable_timing cby_2__2_/chany_bottom_out[4] set_disable_timing cby_2__2_/chany_top_out[4] set_disable_timing cby_2__2_/chany_bottom_out[5] set_disable_timing cby_2__2_/chany_top_out[5] set_disable_timing cby_2__2_/chany_bottom_out[6] set_disable_timing cby_2__2_/chany_top_out[6] -set_disable_timing cby_2__2_/chany_bottom_out[7] set_disable_timing cby_2__2_/chany_top_out[7] set_disable_timing cby_2__2_/chany_bottom_out[8] set_disable_timing cby_2__2_/chany_top_out[8] @@ -1193,10 +1195,8 @@ set_disable_timing cby_2__2_/right_grid_left_width_0_height_0_subtile_5__pin_out set_disable_timing cby_2__2_/right_grid_left_width_0_height_0_subtile_6__pin_outpad_0_[0] set_disable_timing cby_2__2_/right_grid_left_width_0_height_0_subtile_7__pin_outpad_0_[0] set_disable_timing cby_2__2_/left_grid_right_width_0_height_0_subtile_0__pin_I_0_[0] -set_disable_timing cby_2__2_/left_grid_right_width_0_height_0_subtile_0__pin_I_1_[0] set_disable_timing cby_2__2_/left_grid_right_width_0_height_0_subtile_0__pin_I_2_[0] set_disable_timing cby_2__2_/left_grid_right_width_0_height_0_subtile_0__pin_I_3_[0] -set_disable_timing cby_2__2_/left_grid_right_width_0_height_0_subtile_0__pin_I_4_[0] set_disable_timing cby_2__2_/left_grid_right_width_0_height_0_subtile_0__pin_I_5_[0] set_disable_timing cby_2__2_/mux_left_ipin_0/in[1] set_disable_timing cby_2__2_/mux_left_ipin_5/in[1] @@ -1223,7 +1223,6 @@ set_disable_timing cby_2__2_/mux_left_ipin_3/in[0] set_disable_timing cby_2__2_/mux_right_ipin_0/in[0] set_disable_timing cby_2__2_/mux_right_ipin_5/in[0] set_disable_timing cby_2__2_/mux_left_ipin_4/in[1] -set_disable_timing cby_2__2_/mux_right_ipin_1/in[1] set_disable_timing cby_2__2_/mux_left_ipin_4/in[0] set_disable_timing cby_2__2_/mux_right_ipin_1/in[0] set_disable_timing cby_2__2_/mux_left_ipin_0/in[3] @@ -1240,7 +1239,6 @@ set_disable_timing cby_2__2_/mux_left_ipin_6/in[2] set_disable_timing cby_2__2_/mux_right_ipin_3/in[2] set_disable_timing cby_2__2_/mux_left_ipin_2/in[3] set_disable_timing cby_2__2_/mux_left_ipin_7/in[3] -set_disable_timing cby_2__2_/mux_right_ipin_4/in[3] set_disable_timing cby_2__2_/mux_left_ipin_2/in[2] set_disable_timing cby_2__2_/mux_left_ipin_7/in[2] set_disable_timing cby_2__2_/mux_right_ipin_4/in[2] @@ -1258,6 +1256,7 @@ set_disable_timing cby_2__2_/mux_right_ipin_1/in[2] # Disable timing for Switch block sb_0__0_ ################################################## set_disable_timing sb_0__0_/chany_top_out[0] +set_disable_timing sb_0__0_/chany_top_in[0] set_disable_timing sb_0__0_/chany_top_out[1] set_disable_timing sb_0__0_/chany_top_in[1] set_disable_timing sb_0__0_/chany_top_out[2] @@ -1278,8 +1277,10 @@ set_disable_timing sb_0__0_/chany_top_out[9] set_disable_timing sb_0__0_/chany_top_in[9] set_disable_timing sb_0__0_/chanx_right_out[0] set_disable_timing sb_0__0_/chanx_right_in[0] +set_disable_timing sb_0__0_/chanx_right_out[1] set_disable_timing sb_0__0_/chanx_right_in[1] set_disable_timing sb_0__0_/chanx_right_out[2] +set_disable_timing sb_0__0_/chanx_right_in[2] set_disable_timing sb_0__0_/chanx_right_out[3] set_disable_timing sb_0__0_/chanx_right_in[3] set_disable_timing sb_0__0_/chanx_right_out[4] @@ -1294,6 +1295,7 @@ set_disable_timing sb_0__0_/chanx_right_out[8] set_disable_timing sb_0__0_/chanx_right_in[8] set_disable_timing sb_0__0_/chanx_right_out[9] set_disable_timing sb_0__0_/chanx_right_in[9] +set_disable_timing sb_0__0_/top_left_grid_right_width_0_height_0_subtile_0__pin_inpad_0_[0] set_disable_timing sb_0__0_/top_left_grid_right_width_0_height_0_subtile_1__pin_inpad_0_[0] set_disable_timing sb_0__0_/top_left_grid_right_width_0_height_0_subtile_2__pin_inpad_0_[0] set_disable_timing sb_0__0_/top_left_grid_right_width_0_height_0_subtile_3__pin_inpad_0_[0] @@ -1333,6 +1335,7 @@ set_disable_timing sb_0__0_/mux_right_track_16/in[1] set_disable_timing sb_0__0_/mux_right_track_18/in[1] set_disable_timing sb_0__0_/mux_right_track_0/in[2] set_disable_timing sb_0__0_/mux_right_track_2/in[2] +set_disable_timing sb_0__0_/mux_right_track_2/in[0] set_disable_timing sb_0__0_/mux_right_track_4/in[0] set_disable_timing sb_0__0_/mux_right_track_6/in[0] set_disable_timing sb_0__0_/mux_right_track_8/in[0] @@ -1394,6 +1397,7 @@ set_disable_timing sb_0__1_/chanx_right_in[8] set_disable_timing sb_0__1_/chanx_right_out[9] set_disable_timing sb_0__1_/chanx_right_in[9] set_disable_timing sb_0__1_/chany_bottom_in[0] +set_disable_timing sb_0__1_/chany_bottom_out[0] set_disable_timing sb_0__1_/chany_bottom_in[1] set_disable_timing sb_0__1_/chany_bottom_out[1] set_disable_timing sb_0__1_/chany_bottom_in[2] @@ -1424,6 +1428,7 @@ set_disable_timing sb_0__1_/right_top_grid_bottom_width_0_height_0_subtile_0__pi set_disable_timing sb_0__1_/right_top_grid_bottom_width_0_height_0_subtile_0__pin_O_5_[0] set_disable_timing sb_0__1_/right_top_grid_bottom_width_0_height_0_subtile_0__pin_O_6_[0] set_disable_timing sb_0__1_/right_top_grid_bottom_width_0_height_0_subtile_0__pin_O_7_[0] +set_disable_timing sb_0__1_/bottom_left_grid_right_width_0_height_0_subtile_0__pin_inpad_0_[0] set_disable_timing sb_0__1_/bottom_left_grid_right_width_0_height_0_subtile_1__pin_inpad_0_[0] set_disable_timing sb_0__1_/bottom_left_grid_right_width_0_height_0_subtile_2__pin_inpad_0_[0] set_disable_timing sb_0__1_/bottom_left_grid_right_width_0_height_0_subtile_3__pin_inpad_0_[0] @@ -1443,6 +1448,7 @@ set_disable_timing sb_0__1_/mux_right_track_0/in[1] set_disable_timing sb_0__1_/mux_right_track_2/in[2] set_disable_timing sb_0__1_/mux_right_track_4/in[2] set_disable_timing sb_0__1_/mux_right_track_6/in[2] +set_disable_timing sb_0__1_/mux_bottom_track_1/in[6] set_disable_timing sb_0__1_/mux_bottom_track_9/in[6] set_disable_timing sb_0__1_/mux_bottom_track_17/in[5] set_disable_timing sb_0__1_/mux_bottom_track_1/in[7] @@ -1510,7 +1516,6 @@ set_disable_timing sb_0__2_/chanx_right_in[0] set_disable_timing sb_0__2_/chanx_right_out[1] set_disable_timing sb_0__2_/chanx_right_in[1] set_disable_timing sb_0__2_/chanx_right_out[2] -set_disable_timing sb_0__2_/chanx_right_in[2] set_disable_timing sb_0__2_/chanx_right_out[3] set_disable_timing sb_0__2_/chanx_right_in[3] set_disable_timing sb_0__2_/chanx_right_out[4] @@ -1619,8 +1624,11 @@ set_disable_timing sb_1__0_/chany_top_in[9] set_disable_timing sb_1__0_/chanx_right_out[0] set_disable_timing sb_1__0_/chanx_right_in[0] set_disable_timing sb_1__0_/chanx_right_out[1] +set_disable_timing sb_1__0_/chanx_right_in[1] +set_disable_timing sb_1__0_/chanx_right_out[2] set_disable_timing sb_1__0_/chanx_right_in[2] set_disable_timing sb_1__0_/chanx_right_out[3] +set_disable_timing sb_1__0_/chanx_right_in[3] set_disable_timing sb_1__0_/chanx_right_out[4] set_disable_timing sb_1__0_/chanx_right_in[4] set_disable_timing sb_1__0_/chanx_right_out[5] @@ -1635,8 +1643,10 @@ set_disable_timing sb_1__0_/chanx_right_out[9] set_disable_timing sb_1__0_/chanx_right_in[9] set_disable_timing sb_1__0_/chanx_left_in[0] set_disable_timing sb_1__0_/chanx_left_out[0] +set_disable_timing sb_1__0_/chanx_left_in[1] set_disable_timing sb_1__0_/chanx_left_out[1] set_disable_timing sb_1__0_/chanx_left_in[2] +set_disable_timing sb_1__0_/chanx_left_out[2] set_disable_timing sb_1__0_/chanx_left_in[3] set_disable_timing sb_1__0_/chanx_left_out[3] set_disable_timing sb_1__0_/chanx_left_in[4] @@ -1658,6 +1668,7 @@ set_disable_timing sb_1__0_/top_left_grid_right_width_0_height_0_subtile_0__pin_ set_disable_timing sb_1__0_/right_top_grid_bottom_width_0_height_0_subtile_0__pin_O_4_[0] set_disable_timing sb_1__0_/right_top_grid_bottom_width_0_height_0_subtile_0__pin_O_5_[0] set_disable_timing sb_1__0_/right_top_grid_bottom_width_0_height_0_subtile_0__pin_O_6_[0] +set_disable_timing sb_1__0_/right_top_grid_bottom_width_0_height_0_subtile_0__pin_O_7_[0] set_disable_timing sb_1__0_/right_bottom_grid_top_width_0_height_0_subtile_0__pin_inpad_0_[0] set_disable_timing sb_1__0_/right_bottom_grid_top_width_0_height_0_subtile_1__pin_inpad_0_[0] set_disable_timing sb_1__0_/right_bottom_grid_top_width_0_height_0_subtile_2__pin_inpad_0_[0] @@ -1665,6 +1676,7 @@ set_disable_timing sb_1__0_/right_bottom_grid_top_width_0_height_0_subtile_3__pi set_disable_timing sb_1__0_/right_bottom_grid_top_width_0_height_0_subtile_4__pin_inpad_0_[0] set_disable_timing sb_1__0_/right_bottom_grid_top_width_0_height_0_subtile_5__pin_inpad_0_[0] set_disable_timing sb_1__0_/right_bottom_grid_top_width_0_height_0_subtile_6__pin_inpad_0_[0] +set_disable_timing sb_1__0_/right_bottom_grid_top_width_0_height_0_subtile_7__pin_inpad_0_[0] set_disable_timing sb_1__0_/left_top_grid_bottom_width_0_height_0_subtile_0__pin_O_4_[0] set_disable_timing sb_1__0_/left_top_grid_bottom_width_0_height_0_subtile_0__pin_O_5_[0] set_disable_timing sb_1__0_/left_top_grid_bottom_width_0_height_0_subtile_0__pin_O_6_[0] @@ -1781,7 +1793,6 @@ set_disable_timing sb_1__1_/chany_top_out[8] set_disable_timing sb_1__1_/chany_top_in[8] set_disable_timing sb_1__1_/chany_top_out[9] set_disable_timing sb_1__1_/chany_top_in[9] -set_disable_timing sb_1__1_/chanx_right_out[0] set_disable_timing sb_1__1_/chanx_right_in[0] set_disable_timing sb_1__1_/chanx_right_out[1] set_disable_timing sb_1__1_/chanx_right_in[1] @@ -1848,7 +1859,6 @@ set_disable_timing sb_1__1_/top_left_grid_right_width_0_height_0_subtile_0__pin_ set_disable_timing sb_1__1_/right_top_grid_bottom_width_0_height_0_subtile_0__pin_O_4_[0] set_disable_timing sb_1__1_/right_top_grid_bottom_width_0_height_0_subtile_0__pin_O_5_[0] set_disable_timing sb_1__1_/right_top_grid_bottom_width_0_height_0_subtile_0__pin_O_6_[0] -set_disable_timing sb_1__1_/right_top_grid_bottom_width_0_height_0_subtile_0__pin_O_7_[0] set_disable_timing sb_1__1_/bottom_left_grid_right_width_0_height_0_subtile_0__pin_O_0_[0] set_disable_timing sb_1__1_/bottom_left_grid_right_width_0_height_0_subtile_0__pin_O_1_[0] set_disable_timing sb_1__1_/bottom_left_grid_right_width_0_height_0_subtile_0__pin_O_2_[0] @@ -1864,7 +1874,6 @@ set_disable_timing sb_1__1_/mux_top_track_0/in[1] set_disable_timing sb_1__1_/mux_right_track_0/in[4] set_disable_timing sb_1__1_/mux_right_track_8/in[3] set_disable_timing sb_1__1_/mux_right_track_16/in[3] -set_disable_timing sb_1__1_/mux_right_track_0/in[5] set_disable_timing sb_1__1_/mux_bottom_track_1/in[7] set_disable_timing sb_1__1_/mux_bottom_track_9/in[5] set_disable_timing sb_1__1_/mux_bottom_track_17/in[5] @@ -1987,7 +1996,6 @@ set_disable_timing sb_1__1_/mux_bottom_track_9/in[8] set_disable_timing sb_1__2_/chanx_right_out[0] set_disable_timing sb_1__2_/chanx_right_in[0] set_disable_timing sb_1__2_/chanx_right_out[1] -set_disable_timing sb_1__2_/chanx_right_in[1] set_disable_timing sb_1__2_/chanx_right_out[2] set_disable_timing sb_1__2_/chanx_right_in[2] set_disable_timing sb_1__2_/chanx_right_out[3] @@ -2029,7 +2037,6 @@ set_disable_timing sb_1__2_/chanx_left_out[0] set_disable_timing sb_1__2_/chanx_left_in[1] set_disable_timing sb_1__2_/chanx_left_out[1] set_disable_timing sb_1__2_/chanx_left_in[2] -set_disable_timing sb_1__2_/chanx_left_out[2] set_disable_timing sb_1__2_/chanx_left_in[3] set_disable_timing sb_1__2_/chanx_left_out[3] set_disable_timing sb_1__2_/chanx_left_in[4] @@ -2150,7 +2157,6 @@ set_disable_timing sb_2__0_/chany_top_out[4] set_disable_timing sb_2__0_/chany_top_in[4] set_disable_timing sb_2__0_/chany_top_out[5] set_disable_timing sb_2__0_/chany_top_in[5] -set_disable_timing sb_2__0_/chany_top_out[6] set_disable_timing sb_2__0_/chany_top_in[6] set_disable_timing sb_2__0_/chany_top_out[7] set_disable_timing sb_2__0_/chany_top_in[7] @@ -2161,8 +2167,11 @@ set_disable_timing sb_2__0_/chany_top_in[9] set_disable_timing sb_2__0_/chanx_left_in[0] set_disable_timing sb_2__0_/chanx_left_out[0] set_disable_timing sb_2__0_/chanx_left_in[1] +set_disable_timing sb_2__0_/chanx_left_out[1] +set_disable_timing sb_2__0_/chanx_left_in[2] set_disable_timing sb_2__0_/chanx_left_out[2] set_disable_timing sb_2__0_/chanx_left_in[3] +set_disable_timing sb_2__0_/chanx_left_out[3] set_disable_timing sb_2__0_/chanx_left_in[4] set_disable_timing sb_2__0_/chanx_left_out[4] set_disable_timing sb_2__0_/chanx_left_in[5] @@ -2181,7 +2190,6 @@ set_disable_timing sb_2__0_/top_left_grid_right_width_0_height_0_subtile_0__pin_ set_disable_timing sb_2__0_/top_left_grid_right_width_0_height_0_subtile_0__pin_O_3_[0] set_disable_timing sb_2__0_/top_right_grid_left_width_0_height_0_subtile_0__pin_inpad_0_[0] set_disable_timing sb_2__0_/top_right_grid_left_width_0_height_0_subtile_1__pin_inpad_0_[0] -set_disable_timing sb_2__0_/top_right_grid_left_width_0_height_0_subtile_2__pin_inpad_0_[0] set_disable_timing sb_2__0_/top_right_grid_left_width_0_height_0_subtile_3__pin_inpad_0_[0] set_disable_timing sb_2__0_/top_right_grid_left_width_0_height_0_subtile_4__pin_inpad_0_[0] set_disable_timing sb_2__0_/top_right_grid_left_width_0_height_0_subtile_5__pin_inpad_0_[0] @@ -2190,6 +2198,7 @@ set_disable_timing sb_2__0_/top_right_grid_left_width_0_height_0_subtile_7__pin_ set_disable_timing sb_2__0_/left_top_grid_bottom_width_0_height_0_subtile_0__pin_O_4_[0] set_disable_timing sb_2__0_/left_top_grid_bottom_width_0_height_0_subtile_0__pin_O_5_[0] set_disable_timing sb_2__0_/left_top_grid_bottom_width_0_height_0_subtile_0__pin_O_6_[0] +set_disable_timing sb_2__0_/left_top_grid_bottom_width_0_height_0_subtile_0__pin_O_7_[0] set_disable_timing sb_2__0_/left_bottom_grid_top_width_0_height_0_subtile_0__pin_inpad_0_[0] set_disable_timing sb_2__0_/left_bottom_grid_top_width_0_height_0_subtile_1__pin_inpad_0_[0] set_disable_timing sb_2__0_/left_bottom_grid_top_width_0_height_0_subtile_2__pin_inpad_0_[0] @@ -2197,13 +2206,13 @@ set_disable_timing sb_2__0_/left_bottom_grid_top_width_0_height_0_subtile_3__pin set_disable_timing sb_2__0_/left_bottom_grid_top_width_0_height_0_subtile_4__pin_inpad_0_[0] set_disable_timing sb_2__0_/left_bottom_grid_top_width_0_height_0_subtile_5__pin_inpad_0_[0] set_disable_timing sb_2__0_/left_bottom_grid_top_width_0_height_0_subtile_6__pin_inpad_0_[0] +set_disable_timing sb_2__0_/left_bottom_grid_top_width_0_height_0_subtile_7__pin_inpad_0_[0] set_disable_timing sb_2__0_/mux_top_track_0/in[0] set_disable_timing sb_2__0_/mux_top_track_2/in[0] set_disable_timing sb_2__0_/mux_top_track_4/in[0] set_disable_timing sb_2__0_/mux_top_track_6/in[0] set_disable_timing sb_2__0_/mux_top_track_8/in[0] set_disable_timing sb_2__0_/mux_top_track_10/in[0] -set_disable_timing sb_2__0_/mux_top_track_12/in[0] set_disable_timing sb_2__0_/mux_top_track_14/in[0] set_disable_timing sb_2__0_/mux_top_track_16/in[0] set_disable_timing sb_2__0_/mux_top_track_18/in[0] @@ -2212,6 +2221,7 @@ set_disable_timing sb_2__0_/mux_top_track_2/in[1] set_disable_timing sb_2__0_/mux_left_track_1/in[1] set_disable_timing sb_2__0_/mux_left_track_3/in[1] set_disable_timing sb_2__0_/mux_left_track_5/in[1] +set_disable_timing sb_2__0_/mux_left_track_7/in[1] set_disable_timing sb_2__0_/mux_left_track_9/in[1] set_disable_timing sb_2__0_/mux_left_track_11/in[1] set_disable_timing sb_2__0_/mux_left_track_13/in[1] @@ -2219,6 +2229,7 @@ set_disable_timing sb_2__0_/mux_left_track_15/in[1] set_disable_timing sb_2__0_/mux_left_track_17/in[1] set_disable_timing sb_2__0_/mux_left_track_19/in[1] set_disable_timing sb_2__0_/mux_left_track_1/in[2] +set_disable_timing sb_2__0_/mux_left_track_3/in[2] set_disable_timing sb_2__0_/mux_left_track_1/in[0] set_disable_timing sb_2__0_/mux_left_track_19/in[0] set_disable_timing sb_2__0_/mux_left_track_17/in[0] @@ -2242,7 +2253,6 @@ set_disable_timing sb_2__0_/mux_top_track_2/in[2] ################################################## # Disable timing for Switch block sb_2__1_ ################################################## -set_disable_timing sb_2__1_/chany_top_out[0] set_disable_timing sb_2__1_/chany_top_in[0] set_disable_timing sb_2__1_/chany_top_out[1] set_disable_timing sb_2__1_/chany_top_in[1] @@ -2250,13 +2260,11 @@ set_disable_timing sb_2__1_/chany_top_out[2] set_disable_timing sb_2__1_/chany_top_in[2] set_disable_timing sb_2__1_/chany_top_out[3] set_disable_timing sb_2__1_/chany_top_in[3] -set_disable_timing sb_2__1_/chany_top_out[4] set_disable_timing sb_2__1_/chany_top_in[4] set_disable_timing sb_2__1_/chany_top_out[5] set_disable_timing sb_2__1_/chany_top_in[5] set_disable_timing sb_2__1_/chany_top_out[6] set_disable_timing sb_2__1_/chany_top_in[6] -set_disable_timing sb_2__1_/chany_top_out[7] set_disable_timing sb_2__1_/chany_top_in[7] set_disable_timing sb_2__1_/chany_top_out[8] set_disable_timing sb_2__1_/chany_top_in[8] @@ -2274,7 +2282,6 @@ set_disable_timing sb_2__1_/chany_bottom_in[4] set_disable_timing sb_2__1_/chany_bottom_out[4] set_disable_timing sb_2__1_/chany_bottom_in[5] set_disable_timing sb_2__1_/chany_bottom_out[5] -set_disable_timing sb_2__1_/chany_bottom_in[6] set_disable_timing sb_2__1_/chany_bottom_out[6] set_disable_timing sb_2__1_/chany_bottom_in[7] set_disable_timing sb_2__1_/chany_bottom_out[7] @@ -2282,7 +2289,6 @@ set_disable_timing sb_2__1_/chany_bottom_in[8] set_disable_timing sb_2__1_/chany_bottom_out[8] set_disable_timing sb_2__1_/chany_bottom_in[9] set_disable_timing sb_2__1_/chany_bottom_out[9] -set_disable_timing sb_2__1_/chanx_left_in[0] set_disable_timing sb_2__1_/chanx_left_out[0] set_disable_timing sb_2__1_/chanx_left_in[1] set_disable_timing sb_2__1_/chanx_left_out[1] @@ -2312,11 +2318,9 @@ set_disable_timing sb_2__1_/top_right_grid_left_width_0_height_0_subtile_2__pin_ set_disable_timing sb_2__1_/top_right_grid_left_width_0_height_0_subtile_3__pin_inpad_0_[0] set_disable_timing sb_2__1_/top_right_grid_left_width_0_height_0_subtile_4__pin_inpad_0_[0] set_disable_timing sb_2__1_/top_right_grid_left_width_0_height_0_subtile_5__pin_inpad_0_[0] -set_disable_timing sb_2__1_/top_right_grid_left_width_0_height_0_subtile_6__pin_inpad_0_[0] set_disable_timing sb_2__1_/top_right_grid_left_width_0_height_0_subtile_7__pin_inpad_0_[0] set_disable_timing sb_2__1_/bottom_right_grid_left_width_0_height_0_subtile_0__pin_inpad_0_[0] set_disable_timing sb_2__1_/bottom_right_grid_left_width_0_height_0_subtile_1__pin_inpad_0_[0] -set_disable_timing sb_2__1_/bottom_right_grid_left_width_0_height_0_subtile_2__pin_inpad_0_[0] set_disable_timing sb_2__1_/bottom_right_grid_left_width_0_height_0_subtile_3__pin_inpad_0_[0] set_disable_timing sb_2__1_/bottom_right_grid_left_width_0_height_0_subtile_4__pin_inpad_0_[0] set_disable_timing sb_2__1_/bottom_right_grid_left_width_0_height_0_subtile_5__pin_inpad_0_[0] @@ -2329,7 +2333,6 @@ set_disable_timing sb_2__1_/bottom_left_grid_right_width_0_height_0_subtile_0__p set_disable_timing sb_2__1_/left_top_grid_bottom_width_0_height_0_subtile_0__pin_O_4_[0] set_disable_timing sb_2__1_/left_top_grid_bottom_width_0_height_0_subtile_0__pin_O_5_[0] set_disable_timing sb_2__1_/left_top_grid_bottom_width_0_height_0_subtile_0__pin_O_6_[0] -set_disable_timing sb_2__1_/left_top_grid_bottom_width_0_height_0_subtile_0__pin_O_7_[0] set_disable_timing sb_2__1_/mux_top_track_0/in[0] set_disable_timing sb_2__1_/mux_top_track_8/in[0] set_disable_timing sb_2__1_/mux_top_track_16/in[0] @@ -2340,7 +2343,6 @@ set_disable_timing sb_2__1_/mux_top_track_0/in[2] set_disable_timing sb_2__1_/mux_top_track_8/in[2] set_disable_timing sb_2__1_/mux_top_track_16/in[2] set_disable_timing sb_2__1_/mux_top_track_0/in[3] -set_disable_timing sb_2__1_/mux_top_track_8/in[3] set_disable_timing sb_2__1_/mux_top_track_16/in[3] set_disable_timing sb_2__1_/mux_bottom_track_1/in[3] set_disable_timing sb_2__1_/mux_bottom_track_9/in[2] @@ -2390,7 +2392,6 @@ set_disable_timing sb_2__1_/mux_left_track_5/in[2] set_disable_timing sb_2__1_/mux_top_track_0/in[6] set_disable_timing sb_2__1_/mux_left_track_13/in[1] set_disable_timing sb_2__1_/mux_left_track_7/in[2] -set_disable_timing sb_2__1_/mux_top_track_0/in[7] set_disable_timing sb_2__1_/mux_bottom_track_17/in[6] set_disable_timing sb_2__1_/mux_top_track_16/in[6] set_disable_timing sb_2__1_/mux_bottom_track_1/in[7] @@ -2413,7 +2414,6 @@ set_disable_timing sb_2__1_/mux_bottom_track_17/in[9] ################################################## # Disable timing for Switch block sb_2__2_ ################################################## -set_disable_timing sb_2__2_/chany_bottom_in[0] set_disable_timing sb_2__2_/chany_bottom_out[0] set_disable_timing sb_2__2_/chany_bottom_in[1] set_disable_timing sb_2__2_/chany_bottom_out[1] @@ -2421,13 +2421,11 @@ set_disable_timing sb_2__2_/chany_bottom_in[2] set_disable_timing sb_2__2_/chany_bottom_out[2] set_disable_timing sb_2__2_/chany_bottom_in[3] set_disable_timing sb_2__2_/chany_bottom_out[3] -set_disable_timing sb_2__2_/chany_bottom_in[4] set_disable_timing sb_2__2_/chany_bottom_out[4] set_disable_timing sb_2__2_/chany_bottom_in[5] set_disable_timing sb_2__2_/chany_bottom_out[5] set_disable_timing sb_2__2_/chany_bottom_in[6] set_disable_timing sb_2__2_/chany_bottom_out[6] -set_disable_timing sb_2__2_/chany_bottom_in[7] set_disable_timing sb_2__2_/chany_bottom_out[7] set_disable_timing sb_2__2_/chany_bottom_in[8] set_disable_timing sb_2__2_/chany_bottom_out[8] @@ -2436,7 +2434,6 @@ set_disable_timing sb_2__2_/chany_bottom_out[9] set_disable_timing sb_2__2_/chanx_left_in[0] set_disable_timing sb_2__2_/chanx_left_out[0] set_disable_timing sb_2__2_/chanx_left_in[1] -set_disable_timing sb_2__2_/chanx_left_out[1] set_disable_timing sb_2__2_/chanx_left_in[2] set_disable_timing sb_2__2_/chanx_left_out[2] set_disable_timing sb_2__2_/chanx_left_in[3] @@ -2459,7 +2456,6 @@ set_disable_timing sb_2__2_/bottom_right_grid_left_width_0_height_0_subtile_2__p set_disable_timing sb_2__2_/bottom_right_grid_left_width_0_height_0_subtile_3__pin_inpad_0_[0] set_disable_timing sb_2__2_/bottom_right_grid_left_width_0_height_0_subtile_4__pin_inpad_0_[0] set_disable_timing sb_2__2_/bottom_right_grid_left_width_0_height_0_subtile_5__pin_inpad_0_[0] -set_disable_timing sb_2__2_/bottom_right_grid_left_width_0_height_0_subtile_6__pin_inpad_0_[0] set_disable_timing sb_2__2_/bottom_right_grid_left_width_0_height_0_subtile_7__pin_inpad_0_[0] set_disable_timing sb_2__2_/bottom_left_grid_right_width_0_height_0_subtile_0__pin_O_0_[0] set_disable_timing sb_2__2_/bottom_left_grid_right_width_0_height_0_subtile_0__pin_O_1_[0] @@ -2493,7 +2489,6 @@ set_disable_timing sb_2__2_/mux_left_track_9/in[1] set_disable_timing sb_2__2_/mux_left_track_11/in[1] set_disable_timing sb_2__2_/mux_left_track_13/in[1] set_disable_timing sb_2__2_/mux_left_track_15/in[1] -set_disable_timing sb_2__2_/mux_left_track_3/in[0] set_disable_timing sb_2__2_/mux_left_track_5/in[0] set_disable_timing sb_2__2_/mux_left_track_7/in[0] set_disable_timing sb_2__2_/mux_left_track_9/in[0] @@ -2759,561 +2754,561 @@ set_disable_timing grid_clb_1__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_ # Disable Timing for grid[2][1] ####################################### ####################################### -# Disable Timing for unused resources in grid[2][1][0] -####################################### -####################################### -# Disable unused pins for pb_graph_node clb[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_I[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_I[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_I[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_I[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_I[4] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_I[5] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_I[6] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_I[9] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_I[10] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_I[11] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_cin[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_O[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_O[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_O[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_O[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_O[4] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_O[5] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_O[6] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_cout[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/clb_clk[0] -####################################### -# Disable unused mux_inputs for pb_graph_node clb[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[4] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[5] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[6] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[7] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[8] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[9] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[10] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[11] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//direct_interc_9_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//direct_interc_16_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[12] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[13] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//direct_interc_11_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[14] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[15] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//direct_interc_13_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[16] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[17] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//direct_interc_15_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[18] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[19] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0//direct_interc_8_/in[0] -####################################### -# Disable unused pins for pb_graph_node fle[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_in[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_cin[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_out[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_cout[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_clk[0] -####################################### -# Disable unused mux_inputs for pb_graph_node fle[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_3_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_4_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_5_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_6_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_7_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_8_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_0_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_1_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_2_/in[0] -####################################### -# Disable unused pins for pb_graph_node fabric[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cin[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cout[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_clk[0] -####################################### -# Disable unused mux_inputs for pb_graph_node fabric[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_1_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_2_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_3_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_4_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_9_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_6_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_7_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_8_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_0/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_1/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_0_D_0/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_1_D_0/in[1] -####################################### -# Disable unused pins for pb_graph_node frac_logic[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[1] -####################################### -# Disable unused mux_inputs for pb_graph_node frac_logic[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_1_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_2_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_3_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_4_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_0_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[0] -####################################### -# Disable unused pins for pb_graph_node frac_lut4[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut4_out[0] -####################################### -# Disable unused pins for pb_graph_node ff[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_D[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_Q[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_clk[0] -####################################### -# Disable unused pins for pb_graph_node ff[1] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_D[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_Q[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_clk[0] -####################################### -# Disable unused pins for pb_graph_node adder[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_a[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_b[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cin[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cout[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_sumout[0] -####################################### -# Disable unused pins for pb_graph_node fle[1] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_in[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_cin[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_out[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_cout[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_clk[0] -####################################### -# Disable unused mux_inputs for pb_graph_node fle[1] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_3_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_4_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_5_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_6_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_7_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_8_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_0_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_1_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_2_/in[0] -####################################### -# Disable unused pins for pb_graph_node fabric[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cin[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cout[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_clk[0] -####################################### -# Disable unused mux_inputs for pb_graph_node fabric[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_1_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_2_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_3_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_4_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_9_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_6_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_7_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_8_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_0/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_1/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_0_D_0/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_1_D_0/in[1] -####################################### -# Disable unused pins for pb_graph_node frac_logic[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[1] -####################################### -# Disable unused mux_inputs for pb_graph_node frac_logic[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_1_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_2_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_3_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_4_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_0_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[0] -####################################### -# Disable unused pins for pb_graph_node frac_lut4[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut4_out[0] -####################################### -# Disable unused pins for pb_graph_node ff[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_D[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_Q[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_clk[0] -####################################### -# Disable unused pins for pb_graph_node ff[1] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_D[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_Q[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_clk[0] -####################################### -# Disable unused pins for pb_graph_node adder[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_a[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_b[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cin[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cout[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_sumout[0] -####################################### -# Disable unused pins for pb_graph_node fle[2] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_in[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_cin[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_out[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_cout[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_clk[0] -####################################### -# Disable unused mux_inputs for pb_graph_node fle[2] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_3_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_4_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_5_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_6_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_7_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_8_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_0_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_1_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_2_/in[0] -####################################### -# Disable unused pins for pb_graph_node fabric[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cin[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cout[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_clk[0] -####################################### -# Disable unused mux_inputs for pb_graph_node fabric[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_1_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_2_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_3_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_4_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_9_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_6_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_7_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_8_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_0/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_1/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_0_D_0/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_1_D_0/in[1] -####################################### -# Disable unused pins for pb_graph_node frac_logic[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[1] -####################################### -# Disable unused mux_inputs for pb_graph_node frac_logic[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_1_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_2_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_3_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_4_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_0_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[0] -####################################### -# Disable unused pins for pb_graph_node frac_lut4[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[2] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut4_out[0] -####################################### -# Disable unused pins for pb_graph_node ff[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_D[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_Q[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_clk[0] -####################################### -# Disable unused pins for pb_graph_node ff[1] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_D[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_Q[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_clk[0] -####################################### -# Disable unused pins for pb_graph_node adder[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_a[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_b[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cin[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cout[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_sumout[0] -####################################### -# Disable unused pins for pb_graph_node fle[3] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/fle_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/fle_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/fle_cin[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/fle_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/fle_cout[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/fle_clk[0] -####################################### -# Disable unused mux_inputs for pb_graph_node fle[3] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3//direct_interc_3_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3//direct_interc_6_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3//direct_interc_7_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3//direct_interc_8_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3//direct_interc_0_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3//direct_interc_2_/in[0] -####################################### -# Disable unused pins for pb_graph_node fabric[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cin[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cout[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_clk[0] -####################################### -# Disable unused mux_inputs for pb_graph_node fabric[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_1_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_4_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_9_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_6_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_7_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_0/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_1/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_0_D_0/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_1_D_0/in[1] -####################################### -# Disable unused pins for pb_graph_node frac_logic[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[0] -####################################### -# Disable unused mux_inputs for pb_graph_node frac_logic[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_1_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_4_/in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[1] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[0] -####################################### -# Disable unused pins for pb_graph_node frac_lut4[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[3] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut4_out[0] -####################################### -# Disable unused pins for pb_graph_node ff[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_D[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_Q[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_clk[0] -####################################### -# Disable unused pins for pb_graph_node ff[1] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_D[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_Q[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_clk[0] -####################################### -# Disable unused pins for pb_graph_node adder[0] -####################################### -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_a[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_b[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cin[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cout[0] -set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_sumout[0] -####################################### -# Disable Timing for grid[2][2] -####################################### -####################################### -# Disable Timing for unused grid[2][2][0] +# Disable Timing for unused grid[2][1][0] ####################################### ####################################### # Disable all the ports for pb_graph_node clb[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/* ####################################### # Disable all the ports for pb_graph_node fle[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/* ####################################### # Disable all the ports for pb_graph_node fabric[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/* ####################################### # Disable all the ports for pb_graph_node frac_logic[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/* ####################################### # Disable all the ports for pb_graph_node frac_lut4[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/* ####################################### # Disable all the ports for pb_graph_node ff[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/* ####################################### # Disable all the ports for pb_graph_node ff[1] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/* ####################################### # Disable all the ports for pb_graph_node adder[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/* ####################################### # Disable all the ports for pb_graph_node fle[1] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/* ####################################### # Disable all the ports for pb_graph_node fabric[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/* ####################################### # Disable all the ports for pb_graph_node frac_logic[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/* ####################################### # Disable all the ports for pb_graph_node frac_lut4[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/* ####################################### # Disable all the ports for pb_graph_node ff[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/* ####################################### # Disable all the ports for pb_graph_node ff[1] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/* ####################################### # Disable all the ports for pb_graph_node adder[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/* ####################################### # Disable all the ports for pb_graph_node fle[2] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/* ####################################### # Disable all the ports for pb_graph_node fabric[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/* ####################################### # Disable all the ports for pb_graph_node frac_logic[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/* ####################################### # Disable all the ports for pb_graph_node frac_lut4[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/* ####################################### # Disable all the ports for pb_graph_node ff[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/* ####################################### # Disable all the ports for pb_graph_node ff[1] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/* ####################################### # Disable all the ports for pb_graph_node adder[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/* ####################################### # Disable all the ports for pb_graph_node fle[3] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/* ####################################### # Disable all the ports for pb_graph_node fabric[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/* ####################################### # Disable all the ports for pb_graph_node frac_logic[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/* ####################################### # Disable all the ports for pb_graph_node frac_lut4[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/* ####################################### # Disable all the ports for pb_graph_node ff[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/* ####################################### # Disable all the ports for pb_graph_node ff[1] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/* ####################################### # Disable all the ports for pb_graph_node adder[0] ####################################### -set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/* +set_disable_timing grid_clb_2__1_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/* +####################################### +# Disable Timing for grid[2][2] +####################################### +####################################### +# Disable Timing for unused resources in grid[2][2][0] +####################################### +####################################### +# Disable unused pins for pb_graph_node clb[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_I[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_I[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_I[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_I[5] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_I[6] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_I[7] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_I[8] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_I[9] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_I[10] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_I[11] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_cin[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_O[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_O[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_O[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_O[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_O[4] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_O[5] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_O[6] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_cout[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/clb_clk[0] +####################################### +# Disable unused mux_inputs for pb_graph_node clb[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[4] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[5] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[6] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[7] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[8] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[9] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[10] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[11] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//direct_interc_9_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//direct_interc_16_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[12] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[13] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//direct_interc_11_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[14] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[15] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//direct_interc_13_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[16] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[17] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//direct_interc_15_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[18] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//mux_fle_3_in_3/in[19] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0//direct_interc_8_/in[0] +####################################### +# Disable unused pins for pb_graph_node fle[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_in[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_cin[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_out[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_cout[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/fle_clk[0] +####################################### +# Disable unused mux_inputs for pb_graph_node fle[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_3_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_4_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_5_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_6_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_7_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_8_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_0_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_1_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0//direct_interc_2_/in[0] +####################################### +# Disable unused pins for pb_graph_node fabric[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cin[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cout[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_clk[0] +####################################### +# Disable unused mux_inputs for pb_graph_node fabric[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_1_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_2_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_3_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_4_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_9_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_6_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_7_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_8_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_0/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_1/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_0_D_0/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_1_D_0/in[1] +####################################### +# Disable unused pins for pb_graph_node frac_logic[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[1] +####################################### +# Disable unused mux_inputs for pb_graph_node frac_logic[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_1_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_2_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_3_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_4_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_0_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[0] +####################################### +# Disable unused pins for pb_graph_node frac_lut4[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut4_out[0] +####################################### +# Disable unused pins for pb_graph_node ff[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_D[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_Q[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_clk[0] +####################################### +# Disable unused pins for pb_graph_node ff[1] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_D[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_Q[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_clk[0] +####################################### +# Disable unused pins for pb_graph_node adder[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_a[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_b[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cin[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cout[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_sumout[0] +####################################### +# Disable unused pins for pb_graph_node fle[1] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_in[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_cin[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_out[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_cout[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/fle_clk[0] +####################################### +# Disable unused mux_inputs for pb_graph_node fle[1] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_3_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_4_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_5_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_6_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_7_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_8_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_0_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_1_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1//direct_interc_2_/in[0] +####################################### +# Disable unused pins for pb_graph_node fabric[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cin[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cout[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_clk[0] +####################################### +# Disable unused mux_inputs for pb_graph_node fabric[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_1_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_2_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_3_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_4_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_9_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_6_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_7_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_8_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_0/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_1/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_0_D_0/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_1_D_0/in[1] +####################################### +# Disable unused pins for pb_graph_node frac_logic[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[1] +####################################### +# Disable unused mux_inputs for pb_graph_node frac_logic[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_1_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_2_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_3_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_4_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_0_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[0] +####################################### +# Disable unused pins for pb_graph_node frac_lut4[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut4_out[0] +####################################### +# Disable unused pins for pb_graph_node ff[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_D[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_Q[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_clk[0] +####################################### +# Disable unused pins for pb_graph_node ff[1] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_D[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_Q[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_clk[0] +####################################### +# Disable unused pins for pb_graph_node adder[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_a[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_b[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cin[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cout[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_1/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_sumout[0] +####################################### +# Disable unused pins for pb_graph_node fle[2] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_in[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_cin[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_out[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_cout[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/fle_clk[0] +####################################### +# Disable unused mux_inputs for pb_graph_node fle[2] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_3_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_4_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_5_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_6_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_7_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_8_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_0_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_1_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2//direct_interc_2_/in[0] +####################################### +# Disable unused pins for pb_graph_node fabric[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cin[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cout[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_clk[0] +####################################### +# Disable unused mux_inputs for pb_graph_node fabric[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_1_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_2_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_3_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_4_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_9_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_6_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_7_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_8_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_0/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_1/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_0_D_0/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_1_D_0/in[1] +####################################### +# Disable unused pins for pb_graph_node frac_logic[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[1] +####################################### +# Disable unused mux_inputs for pb_graph_node frac_logic[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_1_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_2_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_3_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_4_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_0_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[0] +####################################### +# Disable unused pins for pb_graph_node frac_lut4[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[2] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut4_out[0] +####################################### +# Disable unused pins for pb_graph_node ff[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_D[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_Q[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_clk[0] +####################################### +# Disable unused pins for pb_graph_node ff[1] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_D[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_Q[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_clk[0] +####################################### +# Disable unused pins for pb_graph_node adder[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_a[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_b[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cin[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cout[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_2/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_sumout[0] +####################################### +# Disable unused pins for pb_graph_node fle[3] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/fle_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/fle_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/fle_cin[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/fle_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/fle_cout[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/fle_clk[0] +####################################### +# Disable unused mux_inputs for pb_graph_node fle[3] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3//direct_interc_3_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3//direct_interc_6_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3//direct_interc_7_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3//direct_interc_8_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3//direct_interc_0_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3//direct_interc_2_/in[0] +####################################### +# Disable unused pins for pb_graph_node fabric[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cin[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_cout[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/fabric_clk[0] +####################################### +# Disable unused mux_inputs for pb_graph_node fabric[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_1_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_4_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_9_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_6_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//direct_interc_7_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_0/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_fabric_out_1/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_0_D_0/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0//mux_ff_1_D_0/in[1] +####################################### +# Disable unused pins for pb_graph_node frac_logic[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/frac_logic_out[0] +####################################### +# Disable unused mux_inputs for pb_graph_node frac_logic[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_1_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//direct_interc_4_/in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[1] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0//mux_frac_logic_out_0/in[0] +####################################### +# Disable unused pins for pb_graph_node frac_lut4[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_in[3] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut3_out[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0/frac_lut4_lut4_out[0] +####################################### +# Disable unused pins for pb_graph_node ff[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_D[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_Q[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_0/ff_clk[0] +####################################### +# Disable unused pins for pb_graph_node ff[1] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_D[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_Q[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__ff_1/ff_clk[0] +####################################### +# Disable unused pins for pb_graph_node adder[0] +####################################### +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_a[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_b[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cin[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_cout[0] +set_disable_timing grid_clb_2__2_/logical_tile_clb_mode_clb__0/logical_tile_clb_mode_default__fle_3/logical_tile_clb_mode_default__fle_mode_physical__fabric_0/logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__adder_0/adder_sumout[0] ####################################### # Disable Timing for grid[1][3] ####################################### @@ -3420,16 +3415,20 @@ set_disable_timing grid_io_top_2__3_/logical_tile_io_mode_io__0/* ####################################### set_disable_timing grid_io_top_2__3_/logical_tile_io_mode_io__0/logical_tile_io_mode_physical__iopad_0/* ####################################### -# Disable Timing for unused grid[2][3][1] +# Disable Timing for unused resources in grid[2][3][1] ####################################### ####################################### -# Disable all the ports for pb_graph_node io[0] +# Disable unused pins for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_top_2__3_/logical_tile_io_mode_io__1/* +set_disable_timing grid_io_top_2__3_/logical_tile_io_mode_io__1/io_inpad[0] ####################################### -# Disable all the ports for pb_graph_node iopad[0] +# Disable unused mux_inputs for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_top_2__3_/logical_tile_io_mode_io__1/logical_tile_io_mode_physical__iopad_0/* +set_disable_timing grid_io_top_2__3_/logical_tile_io_mode_io__1//direct_interc_0_/in[0] +####################################### +# Disable unused pins for pb_graph_node iopad[0] +####################################### +set_disable_timing grid_io_top_2__3_/logical_tile_io_mode_io__1/logical_tile_io_mode_physical__iopad_0/iopad_inpad[0] ####################################### # Disable Timing for unused grid[2][3][2] ####################################### @@ -3566,16 +3565,20 @@ set_disable_timing grid_io_right_3__2_/logical_tile_io_mode_io__5/* ####################################### set_disable_timing grid_io_right_3__2_/logical_tile_io_mode_io__5/logical_tile_io_mode_physical__iopad_0/* ####################################### -# Disable Timing for unused grid[3][2][6] +# Disable Timing for unused resources in grid[3][2][6] ####################################### ####################################### -# Disable all the ports for pb_graph_node io[0] +# Disable unused pins for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_right_3__2_/logical_tile_io_mode_io__6/* +set_disable_timing grid_io_right_3__2_/logical_tile_io_mode_io__6/io_outpad[0] ####################################### -# Disable all the ports for pb_graph_node iopad[0] +# Disable unused mux_inputs for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_right_3__2_/logical_tile_io_mode_io__6/logical_tile_io_mode_physical__iopad_0/* +set_disable_timing grid_io_right_3__2_/logical_tile_io_mode_io__6//direct_interc_1_/in[0] +####################################### +# Disable unused pins for pb_graph_node iopad[0] +####################################### +set_disable_timing grid_io_right_3__2_/logical_tile_io_mode_io__6/logical_tile_io_mode_physical__iopad_0/iopad_outpad[0] ####################################### # Disable Timing for unused grid[3][2][7] ####################################### @@ -3613,16 +3616,20 @@ set_disable_timing grid_io_right_3__1_/logical_tile_io_mode_io__1/* ####################################### set_disable_timing grid_io_right_3__1_/logical_tile_io_mode_io__1/logical_tile_io_mode_physical__iopad_0/* ####################################### -# Disable Timing for unused grid[3][1][2] +# Disable Timing for unused resources in grid[3][1][2] ####################################### ####################################### -# Disable all the ports for pb_graph_node io[0] +# Disable unused pins for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_right_3__1_/logical_tile_io_mode_io__2/* +set_disable_timing grid_io_right_3__1_/logical_tile_io_mode_io__2/io_outpad[0] ####################################### -# Disable all the ports for pb_graph_node iopad[0] +# Disable unused mux_inputs for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_right_3__1_/logical_tile_io_mode_io__2/logical_tile_io_mode_physical__iopad_0/* +set_disable_timing grid_io_right_3__1_/logical_tile_io_mode_io__2//direct_interc_1_/in[0] +####################################### +# Disable unused pins for pb_graph_node iopad[0] +####################################### +set_disable_timing grid_io_right_3__1_/logical_tile_io_mode_io__2/logical_tile_io_mode_physical__iopad_0/iopad_outpad[0] ####################################### # Disable Timing for unused grid[3][1][3] ####################################### @@ -3704,20 +3711,16 @@ set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__1/* ####################################### set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__1/logical_tile_io_mode_physical__iopad_0/* ####################################### -# Disable Timing for unused resources in grid[2][0][2] +# Disable Timing for unused grid[2][0][2] ####################################### ####################################### -# Disable unused pins for pb_graph_node io[0] +# Disable all the ports for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__2/io_inpad[0] +set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__2/* ####################################### -# Disable unused mux_inputs for pb_graph_node io[0] +# Disable all the ports for pb_graph_node iopad[0] ####################################### -set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__2//direct_interc_0_/in[0] -####################################### -# Disable unused pins for pb_graph_node iopad[0] -####################################### -set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__2/logical_tile_io_mode_physical__iopad_0/iopad_inpad[0] +set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__2/logical_tile_io_mode_physical__iopad_0/* ####################################### # Disable Timing for unused grid[2][0][3] ####################################### @@ -3763,20 +3766,16 @@ set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__6/* ####################################### set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__6/logical_tile_io_mode_physical__iopad_0/* ####################################### -# Disable Timing for unused resources in grid[2][0][7] +# Disable Timing for unused grid[2][0][7] ####################################### ####################################### -# Disable unused pins for pb_graph_node io[0] +# Disable all the ports for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__7/io_outpad[0] +set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__7/* ####################################### -# Disable unused mux_inputs for pb_graph_node io[0] +# Disable all the ports for pb_graph_node iopad[0] ####################################### -set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__7//direct_interc_1_/in[0] -####################################### -# Disable unused pins for pb_graph_node iopad[0] -####################################### -set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__7/logical_tile_io_mode_physical__iopad_0/iopad_outpad[0] +set_disable_timing grid_io_bottom_2__0_/logical_tile_io_mode_io__7/logical_tile_io_mode_physical__iopad_0/* ####################################### # Disable Timing for grid[1][0] ####################################### @@ -3872,20 +3871,16 @@ set_disable_timing grid_io_bottom_1__0_/logical_tile_io_mode_io__7/logical_tile_ # Disable Timing for grid[0][1] ####################################### ####################################### -# Disable Timing for unused resources in grid[0][1][0] +# Disable Timing for unused grid[0][1][0] ####################################### ####################################### -# Disable unused pins for pb_graph_node io[0] +# Disable all the ports for pb_graph_node io[0] ####################################### -set_disable_timing grid_io_left_0__1_/logical_tile_io_mode_io__0/io_outpad[0] +set_disable_timing grid_io_left_0__1_/logical_tile_io_mode_io__0/* ####################################### -# Disable unused mux_inputs for pb_graph_node io[0] +# Disable all the ports for pb_graph_node iopad[0] ####################################### -set_disable_timing grid_io_left_0__1_/logical_tile_io_mode_io__0//direct_interc_1_/in[0] -####################################### -# Disable unused pins for pb_graph_node iopad[0] -####################################### -set_disable_timing grid_io_left_0__1_/logical_tile_io_mode_io__0/logical_tile_io_mode_physical__iopad_0/iopad_outpad[0] +set_disable_timing grid_io_left_0__1_/logical_tile_io_mode_io__0/logical_tile_io_mode_physical__iopad_0/* ####################################### # Disable Timing for unused grid[0][1][1] ####################################### diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/and2_top_formal_verification.v b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/and2_top_formal_verification.v index 6f261adc1..5df277771 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/and2_top_formal_verification.v +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/and2_top_formal_verification.v @@ -45,14 +45,14 @@ wire [0:0] clk_fm; // ----- End Connect Global ports of FPGA top module ----- // ----- Link BLIF Benchmark I/Os to FPGA I/Os ----- -// ----- Blif Benchmark input a is mapped to FPGA IOPAD gfpga_pad_GPIO_PAD_fm[39] ----- - assign gfpga_pad_GPIO_PAD_fm[39] = a[0]; +// ----- Blif Benchmark input a is mapped to FPGA IOPAD gfpga_pad_GPIO_PAD_fm[22] ----- + assign gfpga_pad_GPIO_PAD_fm[22] = a[0]; -// ----- Blif Benchmark input b is mapped to FPGA IOPAD gfpga_pad_GPIO_PAD_fm[48] ----- - assign gfpga_pad_GPIO_PAD_fm[48] = b[0]; +// ----- Blif Benchmark input b is mapped to FPGA IOPAD gfpga_pad_GPIO_PAD_fm[26] ----- + assign gfpga_pad_GPIO_PAD_fm[26] = b[0]; -// ----- Blif Benchmark output c is mapped to FPGA IOPAD gfpga_pad_GPIO_PAD_fm[34] ----- - assign c[0] = gfpga_pad_GPIO_PAD_fm[34]; +// ----- Blif Benchmark output c is mapped to FPGA IOPAD gfpga_pad_GPIO_PAD_fm[9] ----- + assign c[0] = gfpga_pad_GPIO_PAD_fm[9]; // ----- Wire unused FPGA I/Os to constants ----- assign gfpga_pad_GPIO_PAD_fm[0] = 1'b0; @@ -64,7 +64,6 @@ wire [0:0] clk_fm; assign gfpga_pad_GPIO_PAD_fm[6] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[7] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[8] = 1'b0; - assign gfpga_pad_GPIO_PAD_fm[9] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[10] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[11] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[12] = 1'b0; @@ -77,11 +76,9 @@ wire [0:0] clk_fm; assign gfpga_pad_GPIO_PAD_fm[19] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[20] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[21] = 1'b0; - assign gfpga_pad_GPIO_PAD_fm[22] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[23] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[24] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[25] = 1'b0; - assign gfpga_pad_GPIO_PAD_fm[26] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[27] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[28] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[29] = 1'b0; @@ -89,10 +86,12 @@ wire [0:0] clk_fm; assign gfpga_pad_GPIO_PAD_fm[31] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[32] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[33] = 1'b0; + assign gfpga_pad_GPIO_PAD_fm[34] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[35] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[36] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[37] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[38] = 1'b0; + assign gfpga_pad_GPIO_PAD_fm[39] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[40] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[41] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[42] = 1'b0; @@ -101,6 +100,7 @@ wire [0:0] clk_fm; assign gfpga_pad_GPIO_PAD_fm[45] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[46] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[47] = 1'b0; + assign gfpga_pad_GPIO_PAD_fm[48] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[49] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[50] = 1'b0; assign gfpga_pad_GPIO_PAD_fm[51] = 1'b0; @@ -316,14 +316,14 @@ initial begin force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_2.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_ff_0_D_0.mem_outb[0:2] = 3'b110; force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_2.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_ff_1_D_0.mem_out[0:2] = 3'b001; force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_2.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_ff_1_D_0.mem_outb[0:2] = 3'b110; - force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0.frac_lut4_DFFR_mem.mem_out[0:16] = 17'b00000000110000001; - force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0.frac_lut4_DFFR_mem.mem_outb[0:16] = 17'b11111111001111110; + force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0.frac_lut4_DFFR_mem.mem_out[0:16] = {17{1'b0}}; + force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0.frac_lut4_DFFR_mem.mem_outb[0:16] = {17{1'b1}}; force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0.mem_frac_logic_out_0.mem_out[0:2] = 3'b001; force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0.mem_frac_logic_out_0.mem_outb[0:2] = 3'b110; force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_fabric_out_0.mem_out[0:3] = 4'b0001; force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_fabric_out_0.mem_outb[0:3] = 4'b1110; - force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_fabric_out_1.mem_out[0:3] = 4'b0010; - force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_fabric_out_1.mem_outb[0:3] = 4'b1101; + force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_fabric_out_1.mem_out[0:3] = 4'b0001; + force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_fabric_out_1.mem_outb[0:3] = 4'b1110; force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_ff_0_D_0.mem_out[0:2] = 3'b001; force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_ff_0_D_0.mem_outb[0:2] = 3'b110; force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_ff_1_D_0.mem_out[0:2] = 3'b001; @@ -354,10 +354,10 @@ initial begin force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.mem_fle_2_in_3.mem_outb[0:9] = 10'b1111111110; force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_0.mem_out[0:9] = 10'b0000000001; force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_0.mem_outb[0:9] = 10'b1111111110; - force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_1.mem_out[0:9] = 10'b0001001000; - force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_1.mem_outb[0:9] = 10'b1110110111; - force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_2.mem_out[0:9] = 10'b0010001000; - force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_2.mem_outb[0:9] = 10'b1101110111; + force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_1.mem_out[0:9] = 10'b0000000001; + force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_1.mem_outb[0:9] = 10'b1111111110; + force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_2.mem_out[0:9] = 10'b0000000001; + force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_2.mem_outb[0:9] = 10'b1111111110; force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_3.mem_out[0:9] = 10'b0000000001; force U0_formal_verification.grid_clb_2__1_.logical_tile_clb_mode_clb__0.mem_fle_3_in_3.mem_outb[0:9] = 10'b1111111110; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0.frac_lut4_DFFR_mem.mem_out[0:16] = {17{1'b0}}; @@ -396,14 +396,14 @@ initial begin force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_2.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_ff_0_D_0.mem_outb[0:2] = 3'b110; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_2.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_ff_1_D_0.mem_out[0:2] = 3'b001; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_2.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_ff_1_D_0.mem_outb[0:2] = 3'b110; - force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0.frac_lut4_DFFR_mem.mem_out[0:16] = {17{1'b0}}; - force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0.frac_lut4_DFFR_mem.mem_outb[0:16] = {17{1'b1}}; + force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0.frac_lut4_DFFR_mem.mem_out[0:16] = 17'b00000000110000001; + force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_mode_default__frac_lut4_0.frac_lut4_DFFR_mem.mem_outb[0:16] = 17'b11111111001111110; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0.mem_frac_logic_out_0.mem_out[0:2] = 3'b001; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.logical_tile_clb_mode_default__fle_mode_physical__fabric_mode_default__frac_logic_0.mem_frac_logic_out_0.mem_outb[0:2] = 3'b110; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_fabric_out_0.mem_out[0:3] = 4'b0001; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_fabric_out_0.mem_outb[0:3] = 4'b1110; - force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_fabric_out_1.mem_out[0:3] = 4'b0001; - force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_fabric_out_1.mem_outb[0:3] = 4'b1110; + force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_fabric_out_1.mem_out[0:3] = 4'b0010; + force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_fabric_out_1.mem_outb[0:3] = 4'b1101; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_ff_0_D_0.mem_out[0:2] = 3'b001; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_ff_0_D_0.mem_outb[0:2] = 3'b110; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.logical_tile_clb_mode_default__fle_3.logical_tile_clb_mode_default__fle_mode_physical__fabric_0.mem_ff_1_D_0.mem_out[0:2] = 3'b001; @@ -434,10 +434,10 @@ initial begin force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.mem_fle_2_in_3.mem_outb[0:9] = 10'b1111111110; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.mem_fle_3_in_0.mem_out[0:9] = 10'b0000000001; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.mem_fle_3_in_0.mem_outb[0:9] = 10'b1111111110; - force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.mem_fle_3_in_1.mem_out[0:9] = 10'b0000000001; - force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.mem_fle_3_in_1.mem_outb[0:9] = 10'b1111111110; - force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.mem_fle_3_in_2.mem_out[0:9] = 10'b0000000001; - force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.mem_fle_3_in_2.mem_outb[0:9] = 10'b1111111110; + force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.mem_fle_3_in_1.mem_out[0:9] = 10'b0000110000; + force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.mem_fle_3_in_1.mem_outb[0:9] = 10'b1111001111; + force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.mem_fle_3_in_2.mem_out[0:9] = 10'b0100010000; + force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.mem_fle_3_in_2.mem_outb[0:9] = 10'b1011101111; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.mem_fle_3_in_3.mem_out[0:9] = 10'b0000000001; force U0_formal_verification.grid_clb_2__2_.logical_tile_clb_mode_clb__0.mem_fle_3_in_3.mem_outb[0:9] = 10'b1111111110; force U0_formal_verification.grid_io_top_1__3_.logical_tile_io_mode_io__0.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_out[0] = 1'b1; @@ -458,8 +458,8 @@ initial begin force U0_formal_verification.grid_io_top_1__3_.logical_tile_io_mode_io__7.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_outb[0] = 1'b0; force U0_formal_verification.grid_io_top_2__3_.logical_tile_io_mode_io__0.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_out[0] = 1'b1; force U0_formal_verification.grid_io_top_2__3_.logical_tile_io_mode_io__0.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_outb[0] = 1'b0; - force U0_formal_verification.grid_io_top_2__3_.logical_tile_io_mode_io__1.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_out[0] = 1'b1; - force U0_formal_verification.grid_io_top_2__3_.logical_tile_io_mode_io__1.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_outb[0] = 1'b0; + force U0_formal_verification.grid_io_top_2__3_.logical_tile_io_mode_io__1.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_out[0] = 1'b0; + force U0_formal_verification.grid_io_top_2__3_.logical_tile_io_mode_io__1.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_outb[0] = 1'b1; force U0_formal_verification.grid_io_top_2__3_.logical_tile_io_mode_io__2.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_out[0] = 1'b1; force U0_formal_verification.grid_io_top_2__3_.logical_tile_io_mode_io__2.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_outb[0] = 1'b0; force U0_formal_verification.grid_io_top_2__3_.logical_tile_io_mode_io__3.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_out[0] = 1'b1; @@ -508,8 +508,8 @@ initial begin force U0_formal_verification.grid_io_bottom_2__0_.logical_tile_io_mode_io__0.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_outb[0] = 1'b0; force U0_formal_verification.grid_io_bottom_2__0_.logical_tile_io_mode_io__1.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_out[0] = 1'b1; force U0_formal_verification.grid_io_bottom_2__0_.logical_tile_io_mode_io__1.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_outb[0] = 1'b0; - force U0_formal_verification.grid_io_bottom_2__0_.logical_tile_io_mode_io__2.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_out[0] = 1'b0; - force U0_formal_verification.grid_io_bottom_2__0_.logical_tile_io_mode_io__2.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_outb[0] = 1'b1; + force U0_formal_verification.grid_io_bottom_2__0_.logical_tile_io_mode_io__2.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_out[0] = 1'b1; + force U0_formal_verification.grid_io_bottom_2__0_.logical_tile_io_mode_io__2.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_outb[0] = 1'b0; force U0_formal_verification.grid_io_bottom_2__0_.logical_tile_io_mode_io__3.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_out[0] = 1'b1; force U0_formal_verification.grid_io_bottom_2__0_.logical_tile_io_mode_io__3.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_outb[0] = 1'b0; force U0_formal_verification.grid_io_bottom_2__0_.logical_tile_io_mode_io__4.logical_tile_io_mode_physical__iopad_0.GPIO_DFFR_mem.mem_out[0] = 1'b1; @@ -586,8 +586,8 @@ initial begin force U0_formal_verification.sb_0__0_.mem_top_track_14.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__0_.mem_right_track_0.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_0__0_.mem_right_track_0.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_0__0_.mem_right_track_2.mem_out[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_0__0_.mem_right_track_2.mem_outb[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_0__0_.mem_right_track_2.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_0__0_.mem_right_track_2.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__0_.mem_right_track_4.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_0__0_.mem_right_track_4.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__0_.mem_right_track_6.mem_out[0:1] = {2{1'b0}}; @@ -624,8 +624,8 @@ initial begin force U0_formal_verification.sb_0__1_.mem_right_track_10.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_0__1_.mem_right_track_12.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_0__1_.mem_right_track_12.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_0__1_.mem_bottom_track_1.mem_out[0:7] = 8'b00100100; - force U0_formal_verification.sb_0__1_.mem_bottom_track_1.mem_outb[0:7] = 8'b11011011; + force U0_formal_verification.sb_0__1_.mem_bottom_track_1.mem_out[0:7] = 8'b00000001; + force U0_formal_verification.sb_0__1_.mem_bottom_track_1.mem_outb[0:7] = 8'b11111110; force U0_formal_verification.sb_0__1_.mem_bottom_track_9.mem_out[0:7] = 8'b00000001; force U0_formal_verification.sb_0__1_.mem_bottom_track_9.mem_outb[0:7] = 8'b11111110; force U0_formal_verification.sb_0__1_.mem_bottom_track_17.mem_out[0:5] = 6'b010001; @@ -696,8 +696,8 @@ initial begin force U0_formal_verification.sb_1__1_.mem_top_track_8.mem_outb[0:7] = 8'b11111110; force U0_formal_verification.sb_1__1_.mem_top_track_16.mem_out[0:7] = 8'b00000001; force U0_formal_verification.sb_1__1_.mem_top_track_16.mem_outb[0:7] = 8'b11111110; - force U0_formal_verification.sb_1__1_.mem_right_track_0.mem_out[0:7] = 8'b01000001; - force U0_formal_verification.sb_1__1_.mem_right_track_0.mem_outb[0:7] = 8'b10111110; + force U0_formal_verification.sb_1__1_.mem_right_track_0.mem_out[0:7] = 8'b01000100; + force U0_formal_verification.sb_1__1_.mem_right_track_0.mem_outb[0:7] = 8'b10111011; force U0_formal_verification.sb_1__1_.mem_right_track_8.mem_out[0:7] = 8'b00000001; force U0_formal_verification.sb_1__1_.mem_right_track_8.mem_outb[0:7] = 8'b11111110; force U0_formal_verification.sb_1__1_.mem_right_track_16.mem_out[0:7] = 8'b00000001; @@ -752,8 +752,8 @@ initial begin force U0_formal_verification.sb_2__0_.mem_top_track_8.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_2__0_.mem_top_track_10.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_2__0_.mem_top_track_10.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_2__0_.mem_top_track_12.mem_out[0:1] = {2{1'b0}}; - force U0_formal_verification.sb_2__0_.mem_top_track_12.mem_outb[0:1] = {2{1'b1}}; + force U0_formal_verification.sb_2__0_.mem_top_track_12.mem_out[0:1] = {2{1'b1}}; + force U0_formal_verification.sb_2__0_.mem_top_track_12.mem_outb[0:1] = {2{1'b0}}; force U0_formal_verification.sb_2__0_.mem_top_track_14.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_2__0_.mem_top_track_14.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_2__0_.mem_top_track_16.mem_out[0:1] = {2{1'b0}}; @@ -762,12 +762,12 @@ initial begin force U0_formal_verification.sb_2__0_.mem_top_track_18.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_2__0_.mem_left_track_1.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_2__0_.mem_left_track_1.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_2__0_.mem_left_track_3.mem_out[0:1] = 2'b10; - force U0_formal_verification.sb_2__0_.mem_left_track_3.mem_outb[0:1] = 2'b01; + force U0_formal_verification.sb_2__0_.mem_left_track_3.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_2__0_.mem_left_track_3.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_2__0_.mem_left_track_5.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_2__0_.mem_left_track_5.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_2__0_.mem_left_track_7.mem_out[0:1] = 2'b01; - force U0_formal_verification.sb_2__0_.mem_left_track_7.mem_outb[0:1] = 2'b10; + force U0_formal_verification.sb_2__0_.mem_left_track_7.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.sb_2__0_.mem_left_track_7.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_2__0_.mem_left_track_9.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_2__0_.mem_left_track_9.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_2__0_.mem_left_track_11.mem_out[0:1] = {2{1'b0}}; @@ -780,10 +780,10 @@ initial begin force U0_formal_verification.sb_2__0_.mem_left_track_17.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_2__0_.mem_left_track_19.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_2__0_.mem_left_track_19.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_2__1_.mem_top_track_0.mem_out[0:7] = 8'b00000001; - force U0_formal_verification.sb_2__1_.mem_top_track_0.mem_outb[0:7] = 8'b11111110; - force U0_formal_verification.sb_2__1_.mem_top_track_8.mem_out[0:7] = 8'b00000001; - force U0_formal_verification.sb_2__1_.mem_top_track_8.mem_outb[0:7] = 8'b11111110; + force U0_formal_verification.sb_2__1_.mem_top_track_0.mem_out[0:7] = 8'b00010100; + force U0_formal_verification.sb_2__1_.mem_top_track_0.mem_outb[0:7] = 8'b11101011; + force U0_formal_verification.sb_2__1_.mem_top_track_8.mem_out[0:7] = 8'b00011000; + force U0_formal_verification.sb_2__1_.mem_top_track_8.mem_outb[0:7] = 8'b11100111; force U0_formal_verification.sb_2__1_.mem_top_track_16.mem_out[0:7] = 8'b00000001; force U0_formal_verification.sb_2__1_.mem_top_track_16.mem_outb[0:7] = 8'b11111110; force U0_formal_verification.sb_2__1_.mem_bottom_track_1.mem_out[0:7] = 8'b00000001; @@ -828,8 +828,8 @@ initial begin force U0_formal_verification.sb_2__2_.mem_bottom_track_19.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_2__2_.mem_left_track_1.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_2__2_.mem_left_track_1.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.sb_2__2_.mem_left_track_3.mem_out[0:1] = {2{1'b0}}; - force U0_formal_verification.sb_2__2_.mem_left_track_3.mem_outb[0:1] = {2{1'b1}}; + force U0_formal_verification.sb_2__2_.mem_left_track_3.mem_out[0:1] = {2{1'b1}}; + force U0_formal_verification.sb_2__2_.mem_left_track_3.mem_outb[0:1] = {2{1'b0}}; force U0_formal_verification.sb_2__2_.mem_left_track_5.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.sb_2__2_.mem_left_track_5.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.sb_2__2_.mem_left_track_7.mem_out[0:1] = {2{1'b0}}; @@ -900,10 +900,10 @@ initial begin force U0_formal_verification.cbx_1__2_.mem_bottom_ipin_7.mem_outb[0:5] = 6'b111110; force U0_formal_verification.cbx_2__0_.mem_bottom_ipin_0.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.cbx_2__0_.mem_bottom_ipin_0.mem_outb[0:1] = {2{1'b1}}; - force U0_formal_verification.cbx_2__0_.mem_bottom_ipin_1.mem_out[0:1] = 2'b01; - force U0_formal_verification.cbx_2__0_.mem_bottom_ipin_1.mem_outb[0:1] = 2'b10; - force U0_formal_verification.cbx_2__0_.mem_bottom_ipin_2.mem_out[0:1] = {2{1'b1}}; - force U0_formal_verification.cbx_2__0_.mem_bottom_ipin_2.mem_outb[0:1] = {2{1'b0}}; + force U0_formal_verification.cbx_2__0_.mem_bottom_ipin_1.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.cbx_2__0_.mem_bottom_ipin_1.mem_outb[0:1] = {2{1'b1}}; + force U0_formal_verification.cbx_2__0_.mem_bottom_ipin_2.mem_out[0:1] = {2{1'b0}}; + force U0_formal_verification.cbx_2__0_.mem_bottom_ipin_2.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.cbx_2__0_.mem_bottom_ipin_3.mem_out[0:1] = {2{1'b0}}; force U0_formal_verification.cbx_2__0_.mem_bottom_ipin_3.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.cbx_2__0_.mem_bottom_ipin_4.mem_out[0:1] = {2{1'b0}}; @@ -914,8 +914,8 @@ initial begin force U0_formal_verification.cbx_2__0_.mem_top_ipin_0.mem_outb[0:5] = 6'b111110; force U0_formal_verification.cbx_2__0_.mem_top_ipin_1.mem_out[0:5] = 6'b000001; force U0_formal_verification.cbx_2__0_.mem_top_ipin_1.mem_outb[0:5] = 6'b111110; - force U0_formal_verification.cbx_2__0_.mem_top_ipin_2.mem_out[0:5] = 6'b010100; - force U0_formal_verification.cbx_2__0_.mem_top_ipin_2.mem_outb[0:5] = 6'b101011; + force U0_formal_verification.cbx_2__0_.mem_top_ipin_2.mem_out[0:5] = 6'b000001; + force U0_formal_verification.cbx_2__0_.mem_top_ipin_2.mem_outb[0:5] = 6'b111110; force U0_formal_verification.cbx_2__0_.mem_top_ipin_3.mem_out[0:5] = 6'b000001; force U0_formal_verification.cbx_2__0_.mem_top_ipin_3.mem_outb[0:5] = 6'b111110; force U0_formal_verification.cbx_2__0_.mem_top_ipin_4.mem_out[0:5] = 6'b000001; @@ -940,8 +940,8 @@ initial begin force U0_formal_verification.cbx_2__1_.mem_bottom_ipin_5.mem_outb[0:1] = {2{1'b1}}; force U0_formal_verification.cbx_2__2_.mem_bottom_ipin_0.mem_out[0:5] = 6'b000001; force U0_formal_verification.cbx_2__2_.mem_bottom_ipin_0.mem_outb[0:5] = 6'b111110; - force U0_formal_verification.cbx_2__2_.mem_bottom_ipin_1.mem_out[0:5] = 6'b000001; - force U0_formal_verification.cbx_2__2_.mem_bottom_ipin_1.mem_outb[0:5] = 6'b111110; + force U0_formal_verification.cbx_2__2_.mem_bottom_ipin_1.mem_out[0:5] = 6'b010100; + force U0_formal_verification.cbx_2__2_.mem_bottom_ipin_1.mem_outb[0:5] = 6'b101011; force U0_formal_verification.cbx_2__2_.mem_bottom_ipin_2.mem_out[0:5] = 6'b000001; force U0_formal_verification.cbx_2__2_.mem_bottom_ipin_2.mem_outb[0:5] = 6'b111110; force U0_formal_verification.cbx_2__2_.mem_bottom_ipin_3.mem_out[0:5] = 6'b000001; @@ -1064,14 +1064,14 @@ initial begin force U0_formal_verification.cby_2__2_.mem_left_ipin_7.mem_outb[0:5] = 6'b111110; force U0_formal_verification.cby_2__2_.mem_right_ipin_0.mem_out[0:5] = 6'b000001; force U0_formal_verification.cby_2__2_.mem_right_ipin_0.mem_outb[0:5] = 6'b111110; - force U0_formal_verification.cby_2__2_.mem_right_ipin_1.mem_out[0:5] = 6'b000001; - force U0_formal_verification.cby_2__2_.mem_right_ipin_1.mem_outb[0:5] = 6'b111110; + force U0_formal_verification.cby_2__2_.mem_right_ipin_1.mem_out[0:5] = 6'b100100; + force U0_formal_verification.cby_2__2_.mem_right_ipin_1.mem_outb[0:5] = 6'b011011; force U0_formal_verification.cby_2__2_.mem_right_ipin_2.mem_out[0:5] = 6'b000001; force U0_formal_verification.cby_2__2_.mem_right_ipin_2.mem_outb[0:5] = 6'b111110; force U0_formal_verification.cby_2__2_.mem_right_ipin_3.mem_out[0:5] = 6'b000001; force U0_formal_verification.cby_2__2_.mem_right_ipin_3.mem_outb[0:5] = 6'b111110; - force U0_formal_verification.cby_2__2_.mem_right_ipin_4.mem_out[0:5] = 6'b000001; - force U0_formal_verification.cby_2__2_.mem_right_ipin_4.mem_outb[0:5] = 6'b111110; + force U0_formal_verification.cby_2__2_.mem_right_ipin_4.mem_out[0:5] = 6'b001100; + force U0_formal_verification.cby_2__2_.mem_right_ipin_4.mem_outb[0:5] = 6'b110011; force U0_formal_verification.cby_2__2_.mem_right_ipin_5.mem_out[0:5] = 6'b000001; force U0_formal_verification.cby_2__2_.mem_right_ipin_5.mem_outb[0:5] = 6'b111110; end diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_bitstream.bit b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_bitstream.bit index a665d29db..3575fa16e 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_bitstream.bit +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_bitstream.bit @@ -415,9 +415,9 @@ 0 0 0 -1 0 0 +1 0 0 0 @@ -457,13 +457,11 @@ 0 0 0 -1 -0 -0 0 0 0 0 +1 0 0 0 @@ -473,6 +471,8 @@ 0 0 0 +1 +1 0 0 0 @@ -613,26 +613,26 @@ 1 0 0 -1 -0 -0 0 1 0 0 -0 1 0 0 0 +1 0 0 +1 0 0 0 0 0 0 +1 +1 0 0 0 @@ -749,10 +749,10 @@ 0 0 0 -1 -0 0 0 +1 +1 0 0 1 @@ -767,13 +767,13 @@ 0 0 0 -1 -0 0 0 +1 0 0 1 +1 0 0 0 @@ -901,36 +901,39 @@ 0 0 0 -1 0 0 0 +1 +1 +0 0 0 0 0 1 0 +1 0 0 0 +1 0 0 0 -1 0 0 0 0 0 0 +1 0 0 0 0 0 0 -1 0 0 0 @@ -940,11 +943,8 @@ 0 0 0 -1 0 0 -1 -0 0 0 1 @@ -1083,10 +1083,10 @@ 1 0 0 -0 1 0 0 +0 1 0 0 @@ -1094,15 +1094,15 @@ 1 0 0 -1 0 0 0 0 0 0 -1 -1 +0 +0 +0 0 0 0 @@ -1327,11 +1327,11 @@ 0 0 0 +1 +0 0 0 -1 0 -1 0 1 0 @@ -1351,9 +1351,6 @@ 0 0 0 -1 -1 -1 0 0 0 @@ -1369,12 +1366,10 @@ 0 0 0 -1 0 0 0 0 -1 0 0 0 @@ -1388,6 +1383,11 @@ 0 0 0 +1 +1 +0 +0 +0 0 0 0 @@ -1945,8 +1945,8 @@ 0 0 0 -1 -1 +0 +0 0 0 0 @@ -2041,12 +2041,12 @@ 0 0 0 +1 +0 0 0 -1 0 0 -1 0 0 0 @@ -2255,7 +2255,7 @@ 1 1 1 -1 +0 1 1 0 @@ -2293,15 +2293,13 @@ 0 0 0 -1 -0 -0 -0 0 0 1 0 +1 0 +1 0 0 0 @@ -2319,6 +2317,8 @@ 0 0 0 +1 +1 0 0 0 @@ -2362,7 +2362,7 @@ 1 1 1 -0 +1 1 1 1 diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_bitstream.xml b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_bitstream.xml index 5004ccc7b..a2bf18b06 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_bitstream.xml +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_bitstream.xml @@ -834,11 +834,11 @@ - + - + @@ -918,7 +918,7 @@ - + @@ -926,7 +926,7 @@ - + @@ -934,11 +934,11 @@ - + - + @@ -946,9 +946,9 @@ - + - + @@ -1230,9 +1230,9 @@ - + - + @@ -1252,7 +1252,7 @@ - + @@ -1266,9 +1266,9 @@ - + - + @@ -1502,13 +1502,13 @@ - + - + - + @@ -1538,17 +1538,17 @@ - + - + - + @@ -1806,15 +1806,15 @@ - + - + - + @@ -1822,15 +1822,15 @@ - + - + - + @@ -1858,13 +1858,13 @@ - + - + @@ -1872,25 +1872,25 @@ - + - + - + - + @@ -2170,9 +2170,9 @@ - + - + @@ -2192,7 +2192,7 @@ - + @@ -2206,9 +2206,9 @@ - + - + @@ -2658,15 +2658,15 @@ - + - + - + @@ -2706,11 +2706,11 @@ - + - + - + @@ -2742,7 +2742,7 @@ - + @@ -2752,7 +2752,7 @@ - + @@ -2770,9 +2770,9 @@ - + - + @@ -3894,9 +3894,9 @@ - + - + @@ -4086,17 +4086,17 @@ - + - + - + @@ -4514,7 +4514,7 @@ - + @@ -4590,15 +4590,15 @@ - + - + - + @@ -4638,9 +4638,9 @@ - + - + @@ -4728,7 +4728,7 @@ - + diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_independent_bitstream.xml b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_independent_bitstream.xml index 8e51c806a..a8cc1c0c7 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_independent_bitstream.xml +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_independent_bitstream.xml @@ -2227,15 +2227,15 @@ - - + + - + @@ -2287,19 +2287,14 @@ - - - - - - + - + - - + + @@ -2646,42 +2641,20 @@ - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + @@ -2691,42 +2664,20 @@ - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + @@ -3176,15 +3127,15 @@ - - + + - + @@ -3236,14 +3187,19 @@ - + + + + + + - + - - + + @@ -3590,20 +3546,42 @@ - + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + @@ -3613,20 +3591,42 @@ - + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + @@ -3812,7 +3812,7 @@ - + @@ -4218,7 +4218,7 @@ - + @@ -4702,7 +4702,7 @@ - + @@ -4721,7 +4721,7 @@ - + @@ -4865,16 +4865,16 @@ - + - + - - - + + + @@ -5269,22 +5269,22 @@ - + - + - + - + - + - + @@ -5606,7 +5606,7 @@ - + @@ -5670,9 +5670,9 @@ - + - + @@ -5785,7 +5785,7 @@ - + @@ -5807,7 +5807,7 @@ - + @@ -5843,7 +5843,7 @@ - + @@ -5873,7 +5873,7 @@ - + @@ -5934,7 +5934,7 @@ - + @@ -6097,7 +6097,7 @@ - + @@ -6107,17 +6107,17 @@ - + - + - + - + @@ -6498,7 +6498,7 @@ - + @@ -6646,7 +6646,7 @@ - + @@ -6816,15 +6816,15 @@ - + - + - - - + + + @@ -6853,7 +6853,7 @@ - + @@ -6909,13 +6909,13 @@ - + - + - - + + @@ -6945,14 +6945,14 @@ - + - + - + - + @@ -7079,23 +7079,23 @@ - + - + - + - + - + - + @@ -7108,7 +7108,7 @@ - + @@ -7116,17 +7116,17 @@ - + - + - - + + - + @@ -7141,7 +7141,7 @@ - + @@ -7232,11 +7232,11 @@ - + - + @@ -7337,7 +7337,7 @@ - + @@ -7377,7 +7377,7 @@ - + @@ -7524,7 +7524,7 @@ - + @@ -7614,15 +7614,15 @@ - + - + - - - + + + @@ -7686,7 +7686,7 @@ - + @@ -7760,7 +7760,7 @@ - + @@ -7779,7 +7779,7 @@ - + @@ -7850,7 +7850,7 @@ - + @@ -7875,7 +7875,7 @@ - + @@ -7970,7 +7970,7 @@ - + @@ -7995,7 +7995,7 @@ - + @@ -8203,7 +8203,7 @@ - + @@ -8323,7 +8323,7 @@ - + @@ -8367,14 +8367,14 @@ - + - + - + - + @@ -8384,15 +8384,15 @@ - + - + - - - + + + @@ -8403,7 +8403,7 @@ - + @@ -8457,7 +8457,7 @@ - + @@ -8480,7 +8480,7 @@ - + @@ -8505,20 +8505,20 @@ - + - + - + - + - + - + @@ -8577,7 +8577,7 @@ - + @@ -8600,7 +8600,7 @@ - + @@ -8625,7 +8625,7 @@ - + @@ -8650,7 +8650,7 @@ - + @@ -8785,20 +8785,20 @@ - + - + - + - + - + - + @@ -8905,7 +8905,7 @@ - + @@ -8955,7 +8955,7 @@ - + @@ -9075,7 +9075,7 @@ - + @@ -9756,7 +9756,7 @@ - + @@ -9876,7 +9876,7 @@ - + @@ -9996,7 +9996,7 @@ - + @@ -10068,7 +10068,7 @@ - + @@ -10118,7 +10118,7 @@ - + @@ -10164,7 +10164,7 @@ - + @@ -10188,7 +10188,7 @@ - + @@ -10238,7 +10238,7 @@ - + @@ -10284,21 +10284,21 @@ - + - + - - + + - + - + @@ -10308,7 +10308,7 @@ - + @@ -10358,19 +10358,19 @@ - + - + - + - - + + - + diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/global_ports.sdc b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/global_ports.sdc index 8070cff56..f0d0e3c9b 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/global_ports.sdc +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/global_ports.sdc @@ -14,7 +14,7 @@ set_units -time s ################################################## # Create clock ################################################## -create_clock -name clk[0] -period 8.970345577e-10 -waveform {0 4.485172789e-10} [get_ports {clk[0]}] +create_clock -name clk[0] -period 9.732135098e-10 -waveform {0 4.866067549e-10} [get_ports {clk[0]}] ################################################## # Create programmable clock ################################################## diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/pin_mapping.xml b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/pin_mapping.xml index c107f9429..d15ba5ca0 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/pin_mapping.xml +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/pin_mapping.xml @@ -3,7 +3,7 @@ --> - - - + + + From a1c8d30f82960de88633d34b8baf426958aa58b5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 10 Jun 2023 00:02:31 +0000 Subject: [PATCH 077/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index ee64a2b38..764a157d7 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1069 +1.2.1086 From c81d6a3253aade9517386ec944f95d7b671f9454 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 07:01:57 +0000 Subject: [PATCH 078/391] Bump yosys from `5813809` to `236e15f` Bumps [yosys](https://github.com/YosysHQ/yosys) from `5813809` to `236e15f`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/5813809ad9afbe1c38f65c6aae7c3441d7614d0b...236e15f3b06fc4a05143399cf10b682996d5c509) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 5813809ad..236e15f3b 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 5813809ad9afbe1c38f65c6aae7c3441d7614d0b +Subproject commit 236e15f3b06fc4a05143399cf10b682996d5c509 From ed2f25109fea5b53955a9f2de8add219de2ba27e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 07:02:13 +0000 Subject: [PATCH 079/391] Bump yosys-plugins from `6689e20` to `c8d2d14` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `6689e20` to `c8d2d14`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/6689e20e5fdb0b8d6f494acd65b08fd4646edc0e...c8d2d149861e7981745a667b502191daf96aaf69) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index 6689e20e5..c8d2d1498 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit 6689e20e5fdb0b8d6f494acd65b08fd4646edc0e +Subproject commit c8d2d149861e7981745a667b502191daf96aaf69 From f0f5326dadf143a8c423b4520757d2b2a0772e64 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 00:02:30 +0000 Subject: [PATCH 080/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 764a157d7..8deb57e85 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1086 +1.2.1092 From c8f342581dd5cf17ab20961baa3a9080e2d57f0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 06:58:46 +0000 Subject: [PATCH 081/391] Bump yosys from `236e15f` to `8b2a001` Bumps [yosys](https://github.com/YosysHQ/yosys) from `236e15f` to `8b2a001`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/236e15f3b06fc4a05143399cf10b682996d5c509...8b2a0010216f9a15c09bd2f4dc63691949b126df) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 236e15f3b..8b2a00102 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 236e15f3b06fc4a05143399cf10b682996d5c509 +Subproject commit 8b2a0010216f9a15c09bd2f4dc63691949b126df From 7ade8daefc3c80760d3caee6419e4857874d6aab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 07:00:18 +0000 Subject: [PATCH 082/391] Bump yosys-plugins from `c8d2d14` to `0ad1af2` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `c8d2d14` to `0ad1af2`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/c8d2d149861e7981745a667b502191daf96aaf69...0ad1af26a29243a9e76379943d735e119dcd0cc6) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index c8d2d1498..0ad1af26a 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit c8d2d149861e7981745a667b502191daf96aaf69 +Subproject commit 0ad1af26a29243a9e76379943d735e119dcd0cc6 From 841bfb0b5ef5162efb475f3e4c9a362f72082dc8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 15 Jun 2023 00:02:25 +0000 Subject: [PATCH 083/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 8deb57e85..0f32edd99 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1092 +1.2.1096 From 8bc70b590ad500e14584637833bbcb16758030b1 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 17 Jun 2023 23:42:45 -0700 Subject: [PATCH 084/391] [core] developing fpga_core insertion --- .../src/base/openfpga_build_fabric_template.h | 12 ++++++ .../base/openfpga_setup_command_template.h | 40 +++++++++++++++++++ openfpga/src/fabric/build_device_module.cpp | 27 +++++++++++++ openfpga/src/fabric/build_device_module.h | 2 + 4 files changed, 81 insertions(+) diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index a42b51533..065f421d0 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -231,6 +231,18 @@ int write_fabric_io_info_template(const T& openfpga_ctx, const Command& cmd, cmd_context.option_enable(cmd, opt_verbose)); } +/******************************************************************** + * Add fpga_core module to the module graph + *******************************************************************/ +template +int add_fpga_core_to_fabric_template(T& openfpga_ctx, const Command& cmd, + const CommandContext& cmd_context) { + CommandOptionId opt_verbose = cmd.option("verbose"); + bool verbose_output = cmd_context.option_enable(cmd, opt_verbose); + + return add_fpga_core_to_device_module_graph(openfpga_ctx.mutable_module_graph(), verbose_output); +} + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/base/openfpga_setup_command_template.h b/openfpga/src/base/openfpga_setup_command_template.h index abe17dfeb..9a3498d32 100644 --- a/openfpga/src/base/openfpga_setup_command_template.h +++ b/openfpga/src/base/openfpga_setup_command_template.h @@ -692,6 +692,35 @@ ShellCommandId add_route_clock_rr_graph_command_template( return shell_cmd_id; } +/******************************************************************** + * - Add a command to Shell environment: add_fpga_core_to_fabric + * - Add associated options + * - Add command dependency + *******************************************************************/ +template +ShellCommandId add_add_fpga_core_to_fabric_command_template( + openfpga::Shell& shell, const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds, const bool& hidden) { + Command shell_cmd("add_fpga_core_to_fabric"); + + /* Add an option '--verbose' */ + shell_cmd.add_option("verbose", false, "Show verbose outputs"); + + /* Add command 'pb_pin_fixup' to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command( + shell_cmd, + "Add fpga_core as an intermediate layer to FPGA fabric. After this command, the fpga_top will remain the top-level module while there is a new module fpga_core under it. Under fpga_core, there will be the detailed building blocks", + hidden); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_execute_function(shell_cmd_id, + add_fpga_core_to_fabric_template); + + /* Add command dependency to the Shell */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + template void add_setup_command_templates(openfpga::Shell& shell, const bool& hidden = false) { @@ -859,6 +888,7 @@ void add_setup_command_templates(openfpga::Shell& shell, lut_tt_fixup_dependent_cmds.push_back(vpr_cmd_id); add_lut_truth_table_fixup_command_template( shell, openfpga_setup_cmd_class, lut_tt_fixup_dependent_cmds, hidden); + /******************************** * Command 'build_fabric' */ @@ -869,6 +899,16 @@ void add_setup_command_templates(openfpga::Shell& shell, ShellCommandId build_fabric_cmd_id = add_build_fabric_command_template( shell, openfpga_setup_cmd_class, build_fabric_dependent_cmds, hidden); + /******************************** + * Command 'add_fpga_core_to_fabric' + */ + /* The command should NOT be executed before + * 'build_fabric' */ + std::vector add_fpga_core_to_fabric_dependent_cmds; + add_fpga_core_to_fabric_dependent_cmds.push_back(build_fabric_cmd_id); + ShellCommandId add_fpga_core_to_fabric_cmd_id = add_add_fpga_core_to_fabric_command_template( + shell, openfpga_setup_cmd_class, add_fpga_core_to_fabric_dependent_cmds, hidden); + /******************************** * Command 'write_fabric_hierarchy' */ diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index 97ba9e219..f255b773f 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -126,4 +126,31 @@ int build_device_module_graph( return status; } +/******************************************************************** + * The main function to be called for adding the fpga_core wrapper to a FPGA fabric + * - Rename existing fpga_top to fpga_core + * - Create a wrapper module 'fpga_top' on the fpga_core + *******************************************************************/ +int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, const bool& verbose) { + int status = CMD_EXEC_SUCCESS; + + /* Execute the module graph api */ + std::string top_module_name = generate_fpga_top_module_name(); + ModuleId top_module = module_manager.find_module(top_module_name); + if (!module_manager.valid_module_id(top_module)) { + return CMD_EXEC_FATAL_ERROR; + } + /* TODO: Use a constant for the top_module name */ + + /* Rename existing top module to fpga_core */ + module_manager.set_module_name(top_module, "fpga_core"); + /* Create a wrapper module under the existing fpga_top */ + ModuleId new_top_module = module_manager.create_wrapper(top_module, top_module_name), + if (!module_manager.valid_module_id(new_top_module)) { + return CMD_EXEC_FATAL_ERROR; + } + + return status; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_device_module.h b/openfpga/src/fabric/build_device_module.h index 11a28f1f3..e11ae0bf9 100644 --- a/openfpga/src/fabric/build_device_module.h +++ b/openfpga/src/fabric/build_device_module.h @@ -23,6 +23,8 @@ int build_device_module_graph( const bool& duplicate_grid_pin, const FabricKey& fabric_key, const bool& generate_random_fabric_key, const bool& verbose); +int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, const bool& verbose); + } /* end namespace openfpga */ #endif From c7ade7220021997c7baddb5001515d666284cac5 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 18 Jun 2023 19:17:42 -0700 Subject: [PATCH 085/391] [core] code complete for the core wrapper creator. Start debugging --- .../src/openfpga_reserved_words.h | 2 + .../src/base/openfpga_build_fabric_template.h | 13 ++- openfpga/src/base/openfpga_naming.cpp | 16 +++ openfpga/src/base/openfpga_naming.h | 4 + .../base/openfpga_setup_command_template.h | 20 +++- openfpga/src/fabric/build_device_module.cpp | 24 +++- openfpga/src/fabric/build_device_module.h | 5 +- openfpga/src/fabric/module_manager.cpp | 103 ++++++++++++++++++ openfpga/src/fabric/module_manager.h | 24 ++++ 9 files changed, 201 insertions(+), 10 deletions(-) diff --git a/libs/libopenfpgautil/src/openfpga_reserved_words.h b/libs/libopenfpgautil/src/openfpga_reserved_words.h index 05d1e2d2e..11dd44ef0 100644 --- a/libs/libopenfpgautil/src/openfpga_reserved_words.h +++ b/libs/libopenfpgautil/src/openfpga_reserved_words.h @@ -12,6 +12,8 @@ namespace openfpga { /* Top-level module name */ constexpr const char* FPGA_TOP_MODULE_NAME = "fpga_top"; +constexpr const char* FPGA_CORE_MODULE_NAME = "fpga_core"; +constexpr const char* FPGA_CORE_INSTANCE_NAME = "fpga_instance"; /* Configuration chain naming constant strings */ constexpr const char* CONFIGURABLE_MEMORY_CHAIN_IN_NAME = "ccff_head"; diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index 065f421d0..e025b2a03 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -14,6 +14,7 @@ #include "fabric_hierarchy_writer.h" #include "fabric_key_writer.h" #include "globals.h" +#include "openfpga_naming.h" #include "read_xml_fabric_key.h" #include "vtr_log.h" #include "vtr_time.h" @@ -237,10 +238,20 @@ int write_fabric_io_info_template(const T& openfpga_ctx, const Command& cmd, template int add_fpga_core_to_fabric_template(T& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context) { + CommandOptionId opt_frame_view = cmd.option("frame_view"); + bool frame_view = cmd_context.option_enable(cmd, opt_frame_view); CommandOptionId opt_verbose = cmd.option("verbose"); bool verbose_output = cmd_context.option_enable(cmd, opt_verbose); - return add_fpga_core_to_device_module_graph(openfpga_ctx.mutable_module_graph(), verbose_output); + CommandOptionId opt_inst_name = cmd.option("instance_name"); + std::string core_inst_name = generate_fpga_core_instance_name(); + if (true == cmd_context.option_enable(cmd, opt_inst_name)) { + core_inst_name = cmd_context.option_value(cmd, opt_inst_name); + } + + return add_fpga_core_to_device_module_graph( + openfpga_ctx.mutable_module_graph(), core_inst_name, frame_view, + verbose_output); } } /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index 38dabf0e8..13eabe4b1 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -1399,6 +1399,22 @@ std::string generate_fpga_top_module_name() { return std::string(FPGA_TOP_MODULE_NAME); } +/********************************************************************* + * Generate the module name for the fpga core module + * We give a fixed name here, because it is independent from benchmark file + ********************************************************************/ +std::string generate_fpga_core_module_name() { + return std::string(FPGA_CORE_MODULE_NAME); +} + +/********************************************************************* + * Generate the module name for the fpga core module + * We give a fixed name here, because it is independent from benchmark file + ********************************************************************/ +std::string generate_fpga_core_instance_name() { + return std::string(FPGA_CORE_INSTANCE_NAME); +} + /********************************************************************* * Generate the netlist name for the top-level module * The top-level module is actually the FPGA fabric diff --git a/openfpga/src/base/openfpga_naming.h b/openfpga/src/base/openfpga_naming.h index 8e6fb631a..3400406c8 100644 --- a/openfpga/src/base/openfpga_naming.h +++ b/openfpga/src/base/openfpga_naming.h @@ -258,6 +258,10 @@ std::string generate_fpga_global_io_port_name( std::string generate_fpga_top_module_name(); +std::string generate_fpga_core_module_name(); + +std::string generate_fpga_core_instance_name(); + std::string generate_fpga_top_netlist_name(const std::string& postfix); std::string generate_const_value_module_name(const size_t& const_val); diff --git a/openfpga/src/base/openfpga_setup_command_template.h b/openfpga/src/base/openfpga_setup_command_template.h index 9a3498d32..2c8e38de5 100644 --- a/openfpga/src/base/openfpga_setup_command_template.h +++ b/openfpga/src/base/openfpga_setup_command_template.h @@ -703,13 +703,25 @@ ShellCommandId add_add_fpga_core_to_fabric_command_template( const std::vector& dependent_cmds, const bool& hidden) { Command shell_cmd("add_fpga_core_to_fabric"); + /* Add an option '--instance_name'*/ + CommandOptionId opt_inst_name = shell_cmd.add_option( + "instance_name", false, "specify the instance of fpga_core under fpga_top"); + shell_cmd.set_option_require_value(opt_inst_name, openfpga::OPT_STRING); + + /* Add an option '--verbose' */ + shell_cmd.add_option( + "frame_view", false, + "Build only frame view of the fabric (nets are skipped)"); /* Add an option '--verbose' */ shell_cmd.add_option("verbose", false, "Show verbose outputs"); /* Add command 'pb_pin_fixup' to the Shell */ ShellCommandId shell_cmd_id = shell.add_command( shell_cmd, - "Add fpga_core as an intermediate layer to FPGA fabric. After this command, the fpga_top will remain the top-level module while there is a new module fpga_core under it. Under fpga_core, there will be the detailed building blocks", + "Add fpga_core as an intermediate layer to FPGA fabric. After this " + "command, the fpga_top will remain the top-level module while there is a " + "new module fpga_core under it. Under fpga_core, there will be the " + "detailed building blocks", hidden); shell.set_command_class(shell_cmd_id, cmd_class_id); shell.set_command_execute_function(shell_cmd_id, @@ -906,8 +918,10 @@ void add_setup_command_templates(openfpga::Shell& shell, * 'build_fabric' */ std::vector add_fpga_core_to_fabric_dependent_cmds; add_fpga_core_to_fabric_dependent_cmds.push_back(build_fabric_cmd_id); - ShellCommandId add_fpga_core_to_fabric_cmd_id = add_add_fpga_core_to_fabric_command_template( - shell, openfpga_setup_cmd_class, add_fpga_core_to_fabric_dependent_cmds, hidden); + ShellCommandId add_fpga_core_to_fabric_cmd_id = + add_add_fpga_core_to_fabric_command_template( + shell, openfpga_setup_cmd_class, add_fpga_core_to_fabric_dependent_cmds, + hidden); /******************************** * Command 'write_fabric_hierarchy' diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index f255b773f..90778f8b7 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -20,6 +20,7 @@ #include "build_top_module.h" #include "build_wire_modules.h" #include "command_exit_codes.h" +#include "openfpga_naming.h" /* begin namespace openfpga */ namespace openfpga { @@ -127,11 +128,15 @@ int build_device_module_graph( } /******************************************************************** - * The main function to be called for adding the fpga_core wrapper to a FPGA fabric + * The main function to be called for adding the fpga_core wrapper to a FPGA + *fabric * - Rename existing fpga_top to fpga_core * - Create a wrapper module 'fpga_top' on the fpga_core *******************************************************************/ -int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, const bool& verbose) { +int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, + const std::string& core_inst_name, + const bool& frame_view, + const bool& verbose) { int status = CMD_EXEC_SUCCESS; /* Execute the module graph api */ @@ -140,15 +145,24 @@ int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, const bo if (!module_manager.valid_module_id(top_module)) { return CMD_EXEC_FATAL_ERROR; } - /* TODO: Use a constant for the top_module name */ /* Rename existing top module to fpga_core */ - module_manager.set_module_name(top_module, "fpga_core"); + std::string core_module_name = generate_fpga_core_module_name(); + module_manager.set_module_name(top_module, core_module_name); + VTR_LOGV(verbose, "Rename current top-level module '%s' to '%s'\n", + top_module_name.c_str(), core_module_name.c_str()); + /* Create a wrapper module under the existing fpga_top */ - ModuleId new_top_module = module_manager.create_wrapper(top_module, top_module_name), + ModuleId new_top_module = module_manager.create_wrapper_module( + top_module, top_module_name, core_inst_name, frame_view); if (!module_manager.valid_module_id(new_top_module)) { + VTR_LOGV_ERROR(verbose, + "Failed to create a wrapper module '%s' on top of '%s'!\n", + top_module_name.c_str(), core_module_name.c_str()); return CMD_EXEC_FATAL_ERROR; } + VTR_LOGV(verbose, "Created a wrapper module '%s' on top of '%s'\n", + top_module_name.c_str(), core_module_name.c_str()); return status; } diff --git a/openfpga/src/fabric/build_device_module.h b/openfpga/src/fabric/build_device_module.h index e11ae0bf9..9ab88547b 100644 --- a/openfpga/src/fabric/build_device_module.h +++ b/openfpga/src/fabric/build_device_module.h @@ -23,7 +23,10 @@ int build_device_module_graph( const bool& duplicate_grid_pin, const FabricKey& fabric_key, const bool& generate_random_fabric_key, const bool& verbose); -int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, const bool& verbose); +int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, + const std::string& core_inst_name, + const bool& frame_view, + const bool& verbose); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index b1474835a..fb584cd38 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -768,6 +768,14 @@ void ModuleManager::set_port_is_wire(const ModuleId& module, port_is_wire_[module][port] = is_wire; } +/* Set a port to be a wire */ +void ModuleManager::set_port_is_wire(const ModuleId& module, + const ModulePortId& port_id, + const bool& is_wire) { + VTR_ASSERT(valid_module_port_id(module, port_id)); + port_is_wire_[module][port_id] = is_wire; +} + /* Set a port to be a mappable I/O */ void ModuleManager::set_port_is_mappable_io(const ModuleId& module, const ModulePortId& port_id, @@ -788,6 +796,14 @@ void ModuleManager::set_port_is_register(const ModuleId& module, port_is_register_[module][port] = is_register; } +/* Set a port to be a register */ +void ModuleManager::set_port_is_register(const ModuleId& module, + const ModulePortId& port_id, + const bool& is_register) { + VTR_ASSERT(valid_module_port_id(module, port_id)); + port_is_register_[module][port_id] = is_register; +} + /* Set the preprocessing flag for a port */ void ModuleManager::set_port_preproc_flag(const ModuleId& module, const ModulePortId& port, @@ -1185,6 +1201,93 @@ ModuleNetSinkId ModuleManager::add_module_net_sink( return net_sink; } +ModuleId ModuleManager::create_wrapper_module( + const ModuleId& existing_module, const std::string& wrapper_module_name, + const std::string& instance_name, const bool& add_nets) { + /* Create a new module with the given name */ + ModuleId wrapper_module = add_module(wrapper_module_name); + if (!wrapper_module) { + return wrapper_module; + } + /* Add the existing module as an instance */ + add_child_module(wrapper_module, existing_module, false); + set_child_instance_name(wrapper_module, existing_module, 0, instance_name); + + /* A fast-lookup on the port linking: wrapper_module port -> existing_module + * port */ + std::map port_map; + /* Herit ports */ + for (ModulePortId existing_port : module_ports(existing_module)) { + /* Create new port */ + BasicPort existing_port_info = module_port(existing_module, existing_port); + ModuleManager::e_module_port_type existing_port_type = + port_type(existing_module, existing_port); + ModulePortId new_port = + add_port(wrapper_module, existing_port_info, existing_port_type); + /* Set port attributes */ + set_port_is_wire(wrapper_module, new_port, + port_is_wire(existing_module, existing_port)); + set_port_is_mappable_io( + wrapper_module, new_port, + port_is_mappable_io(existing_module, existing_port)); + set_port_is_register(wrapper_module, new_port, + port_is_register(existing_module, existing_port)); + set_port_preproc_flag(wrapper_module, new_port, + port_preproc_flag(existing_module, existing_port)); + /* Register in port mapping */ + port_map[new_port] = existing_port; + } + + /* Add nets */ + if (!add_nets) { + return wrapper_module; + } + /* The number of nets are the sum of input and output pins */ + size_t num_nets_to_reserve = 0; + for (ModulePortId new_port : module_ports(wrapper_module)) { + BasicPort new_port_info = module_port(wrapper_module, new_port); + num_nets_to_reserve += new_port_info.get_width(); + } + reserve_module_nets(wrapper_module, num_nets_to_reserve); + for (ModulePortId new_port : module_ports(wrapper_module)) { + BasicPort new_port_info = module_port(wrapper_module, new_port); + /* Create new net */ + ModuleNetId new_net = create_module_net(wrapper_module); + VTR_ASSERT(valid_module_net_id(wrapper_module, new_net)); + /* For each input pin, create a new source */ + ModuleManager::e_module_port_type new_port_type = + port_type(wrapper_module, new_port); + /* Check if the pin size are matching or not */ + ModulePortId existing_port = port_map[new_port]; + BasicPort existing_port_info = module_port(existing_module, existing_port); + VTR_ASSERT(existing_port_info == new_port_info); + reserve_module_net_sources(wrapper_module, new_net, + new_port_info.pins().size()); + reserve_module_net_sinks(wrapper_module, new_net, + new_port_info.pins().size()); + if (new_port_type != + ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT) { + for (auto pin : new_port_info.pins()) { + add_module_net_source(wrapper_module, new_net, wrapper_module, 0, + new_port, pin); + add_module_net_sink(wrapper_module, new_net, existing_module, 0, + existing_port, pin); + } + } else { + VTR_ASSERT_SAFE(new_port_type == + ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); + for (auto pin : new_port_info.pins()) { + add_module_net_source(wrapper_module, new_net, existing_module, 0, + existing_port, pin); + add_module_net_sink(wrapper_module, new_net, wrapper_module, 0, + new_port, pin); + } + } + } + + return wrapper_module; +} + /****************************************************************************** * Public Deconstructor ******************************************************************************/ diff --git a/openfpga/src/fabric/module_manager.h b/openfpga/src/fabric/module_manager.h index 2c8633b42..dd0f299dc 100644 --- a/openfpga/src/fabric/module_manager.h +++ b/openfpga/src/fabric/module_manager.h @@ -317,6 +317,8 @@ class ModuleManager { /* Set a port to be a wire */ void set_port_is_wire(const ModuleId& module, const std::string& port_name, const bool& is_wire); + void set_port_is_wire(const ModuleId& module, const ModulePortId& port_id, + const bool& is_wire); /* Set a port to be mappable to an I/O from users' implemenations */ void set_port_is_mappable_io(const ModuleId& module, const ModulePortId& port_id, @@ -325,6 +327,8 @@ class ModuleManager { void set_port_is_register(const ModuleId& module, const std::string& port_name, const bool& is_register); + void set_port_is_register(const ModuleId& module, const ModulePortId& port_id, + const bool& is_register); /* Set the preprocessing flag for a port */ void set_port_preproc_flag(const ModuleId& module, const ModulePortId& port, const std::string& preproc_flag); @@ -423,6 +427,26 @@ class ModuleManager { const ModulePortId& sink_port, const size_t& sink_pin); + /** @brief Create a wrapper module on an existing module. The wrapper module + * will herit all the ports with the same direction, width and names from the + * selected module. The wrapper module will contain the existing module. For + * example, + * + * Wrapper module + * +------------------------+ + * | existing module | + * | +------------------+ | + * | | | | + * a ->+->+ a b-+--+-> b + * | | | | + * | +------------------+ | + * +------------------------+ + */ + ModuleId create_wrapper_module(const ModuleId& existing_module, + const std::string& wrapper_module_name, + const std::string& instance_name, + const bool& add_nets); + public: /* Public deconstructors */ /* This is a strong function which will remove all the configurable children * under a given parent module From 97b089ae3c99605efa40aeeb76f990e94848186c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 18 Jun 2023 21:01:37 -0700 Subject: [PATCH 086/391] [test] added new testcases to validate fpga core wrapper --- .../fpga_core_example_script.openfpga | 75 +++++++++++++++++++ ...estbench_fpga_core_example_script.openfpga | 73 ++++++++++++++++++ .../regression_test_scripts/basic_reg_test.sh | 4 + .../fpga_core_wrapper/config/task.conf | 45 +++++++++++ .../fpga_core_wrapper/config/task.conf | 44 +++++++++++ 5 files changed, 241 insertions(+) create mode 100644 openfpga_flow/openfpga_shell_scripts/fpga_core_example_script.openfpga create mode 100644 openfpga_flow/openfpga_shell_scripts/write_full_testbench_fpga_core_example_script.openfpga create mode 100644 openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper/config/task.conf diff --git a/openfpga_flow/openfpga_shell_scripts/fpga_core_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fpga_core_example_script.openfpga new file mode 100644 index 000000000..93bf0df48 --- /dev/null +++ b/openfpga_flow/openfpga_shell_scripts/fpga_core_example_script.openfpga @@ -0,0 +1,75 @@ +# 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 #--verbose +# Add a fpga core between fpga top and the underlying modules +add_fpga_core_to_fabric --instance_name fpga_core_inst --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_preconfigured_fabric_wrapper --embed_bitstream iverilog --file ./SRC --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping + +# Write the SDC files for PnR backend +# - Turn on every options here +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/openfpga_shell_scripts/write_full_testbench_fpga_core_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_fpga_core_example_script.openfpga new file mode 100644 index 000000000..3d3d12a56 --- /dev/null +++ b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_fpga_core_example_script.openfpga @@ -0,0 +1,73 @@ +# Run VPR for the 'and' design +#--write_rr_graph example_rr_graph.xml +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling ideal ${OPENFPGA_VPR_DEVICE_LAYOUT} + +# 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 #--verbose +# Add a fpga core between fpga top and the underlying modules +add_fpga_core_to_fabric --instance_name fpga_core_inst --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 ${OPENFPGA_FAST_CONFIGURATION} + +# 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} --include_signal_init --explicit_port_mapping --bitstream fabric_bitstream.bit ${OPENFPGA_FAST_CONFIGURATION} + +# Write the SDC files for PnR backend +# - Turn on every options here +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 489120590..d4198e1a9 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -15,6 +15,10 @@ echo -e "Test source commands in openfpga shell" run-task basic_tests/source_command/source_string $@ run-task basic_tests/source_command/source_file $@ +echo -e "Testing testbenches using fpga core wrapper" +run-task basic_tests/full_testbench/fpga_core_wrapper $@ +run-task basic_tests/preconfig_testbench/fpga_core_wrapper $@ + echo -e "Testing configuration chain of a K4N4 FPGA"; run-task basic_tests/full_testbench/configuration_chain $@ run-task basic_tests/full_testbench/configuration_chain_no_time_stamp $@ diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper/config/task.conf new file mode 100644 index 000000000..b82cc5b14 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper/config/task.conf @@ -0,0 +1,45 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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/write_full_testbench_fpga_core_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_vpr_device_layout= +openfpga_fast_configuration= + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2 +bench0_chan_width = 300 + +bench1_top = or2 +bench1_chan_width = 300 + +bench2_top = and2_latch +bench2_chan_width = 300 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= diff --git a/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper/config/task.conf new file mode 100644 index 000000000..9986926a8 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper/config/task.conf @@ -0,0 +1,44 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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/fpga_core_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 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2 +bench0_chan_width = 300 + +bench1_top = or2 +bench1_chan_width = 300 + +bench2_top = and2_latch +bench2_chan_width = 300 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= +vpr_fpga_verilog_formal_verification_top_netlist= From 4b661cbd6a3b9d7584b6ae37c2a0f4bbc7e8d6ce Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 18 Jun 2023 21:08:08 -0700 Subject: [PATCH 087/391] [doc] update documentation about the new command --- .../openfpga_commands/setup_commands.rst | 23 +++++++++++++++++++ 1 file changed, 23 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 5971f75f2..fd5631f6a 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst @@ -281,6 +281,29 @@ build_fabric .. note:: This is a must-run command before launching FPGA-Verilog, FPGA-Bitstream, FPGA-SDC and FPGA-SPICE +.. _cmd_add_fpga_core_to_fabric: + +add_fpga_core_to_fabric +~~~~~~~~~~~~~~~~~~~~~~~ + + Add a wrapper module ``fpga_core`` as an intermediate layer to FPGA fabric. After this command, the existing module ``fpga_top`` will remain the top-level module while there is a new module ``fpga_core`` under it. Under fpga_core, there will be the detailed building blocks. + + .. option:: --instance_name + + This is optional. Specify the instance name to be used when instanciate the ``fpga_core`` module under the top-level module ``fpga_top``. If not defined, by default it is ``fpga_core_inst``. + + + .. option:: --frame_view + + Create only frame views of the module graph. When enabled, top-level module will not include any nets. This option is made for save runtime and memory. + + .. warning:: Recommend to turn the option on when bitstream generation is the only purpose of the flow. Do not use it when you need generate netlists! + + .. option:: --verbose + + Show verbose log + + write_fabric_hierarchy ~~~~~~~~~~~~~~~~~~~~~~ From cef573529d60fb59ac9d80347b787f696710cf39 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 18 Jun 2023 21:17:50 -0700 Subject: [PATCH 088/391] [core] now fpga verilog can output fpga core netlist --- openfpga/src/base/openfpga_naming.cpp | 9 +++ openfpga/src/base/openfpga_naming.h | 2 + openfpga/src/fpga_verilog/verilog_api.cpp | 3 + .../src/fpga_verilog/verilog_top_module.cpp | 59 +++++++++++++++++++ .../src/fpga_verilog/verilog_top_module.h | 5 ++ 5 files changed, 78 insertions(+) diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index 13eabe4b1..a53a87ade 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -1424,6 +1424,15 @@ std::string generate_fpga_top_netlist_name(const std::string& postfix) { return std::string(FPGA_TOP_MODULE_NAME + postfix); } +/********************************************************************* + * Generate the netlist name for the top-level module + * The top-level module is actually the FPGA fabric + * We give a fixed name here, because it is independent from benchmark file + ********************************************************************/ +std::string generate_fpga_core_netlist_name(const std::string& postfix) { + return std::string(FPGA_CORE_MODULE_NAME + postfix); +} + /********************************************************************* * Generate the module name for a constant generator * either VDD or GND, depending on the input argument diff --git a/openfpga/src/base/openfpga_naming.h b/openfpga/src/base/openfpga_naming.h index 3400406c8..e14b54549 100644 --- a/openfpga/src/base/openfpga_naming.h +++ b/openfpga/src/base/openfpga_naming.h @@ -264,6 +264,8 @@ std::string generate_fpga_core_instance_name(); std::string generate_fpga_top_netlist_name(const std::string& postfix); +std::string generate_fpga_core_netlist_name(const std::string& postfix); + std::string generate_const_value_module_name(const size_t& const_val); std::string generate_const_value_module_output_port_name( diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 4bc5e6ced..ae5779285 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -116,6 +116,9 @@ void fpga_fabric_verilog( std::string(DEFAULT_LB_DIR_NAME), options, options.verbose_output()); /* Generate FPGA fabric */ + print_verilog_core_module(netlist_manager, + const_cast(module_manager), + src_dir_path, options); print_verilog_top_module(netlist_manager, const_cast(module_manager), src_dir_path, options); diff --git a/openfpga/src/fpga_verilog/verilog_top_module.cpp b/openfpga/src/fpga_verilog/verilog_top_module.cpp index 0fb6b0d0c..357b9bf5e 100644 --- a/openfpga/src/fpga_verilog/verilog_top_module.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_module.cpp @@ -21,6 +21,65 @@ /* begin namespace openfpga */ namespace openfpga { +/******************************************************************** + * Print the wrapper module for the FPGA fabric in Verilog format + *******************************************************************/ +void print_verilog_core_module(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& verilog_dir, + const FabricVerilogOption& options) { + /* Create a module as the top-level fabric, and add it to the module manager + */ + std::string core_module_name = generate_fpga_core_module_name(); + ModuleId core_module = module_manager.find_module(core_module_name); + /* It could happen that the module does not exist, just return with no errors */ + if (!module_manager.valid_module_id(core_module)) { + return; + } + + /* Start printing out Verilog netlists */ + /* Create the file name for Verilog netlist */ + std::string verilog_fname( + generate_fpga_core_netlist_name(std::string(VERILOG_NETLIST_FILE_POSTFIX))); + std::string verilog_fpath(verilog_dir + verilog_fname); + + VTR_LOG("Writing Verilog netlist for wrapper module of FPGA fabric '%s'...", + verilog_fpath.c_str()); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fpath, std::fstream::out | std::fstream::trunc); + + check_file_stream(verilog_fpath.c_str(), fp); + + print_verilog_file_header( + fp, std::string("Wrapper Verilog module for FPGA"), options.time_stamp()); + + /* Write the module content in Verilog format */ + write_verilog_module_to_file(fp, module_manager, core_module, + options.explicit_port_mapping(), + options.default_net_type()); + + /* Add an empty line as a splitter */ + fp << std::endl; + + /* Close file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = NetlistId::INVALID(); + if (options.use_relative_path()) { + nlist_id = netlist_manager.add_netlist(verilog_fname); + } else { + nlist_id = netlist_manager.add_netlist(verilog_fpath); + } + VTR_ASSERT(nlist_id); + netlist_manager.set_netlist_type(nlist_id, + NetlistManager::TOP_MODULE_NETLIST); + + VTR_LOG("Done\n"); +} + /******************************************************************** * Print the top-level module for the FPGA fabric in Verilog format * This function will diff --git a/openfpga/src/fpga_verilog/verilog_top_module.h b/openfpga/src/fpga_verilog/verilog_top_module.h index b4b0bab20..22222b8b4 100644 --- a/openfpga/src/fpga_verilog/verilog_top_module.h +++ b/openfpga/src/fpga_verilog/verilog_top_module.h @@ -17,6 +17,11 @@ /* begin namespace openfpga */ namespace openfpga { +void print_verilog_core_module(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& verilog_dir, + const FabricVerilogOption& options); + void print_verilog_top_module(NetlistManager& netlist_manager, const ModuleManager& module_manager, const std::string& verilog_dir, From bdda695cc0e5d400d18c5a0b52924b2a60f51694 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 18 Jun 2023 21:18:35 -0700 Subject: [PATCH 089/391] [core] format --- openfpga/src/fpga_verilog/verilog_api.cpp | 4 ++-- openfpga/src/fpga_verilog/verilog_top_module.cpp | 13 +++++++------ openfpga/src/fpga_verilog/verilog_top_module.h | 6 +++--- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index ae5779285..dae1d7a46 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -117,8 +117,8 @@ void fpga_fabric_verilog( /* Generate FPGA fabric */ print_verilog_core_module(netlist_manager, - const_cast(module_manager), - src_dir_path, options); + const_cast(module_manager), + src_dir_path, options); print_verilog_top_module(netlist_manager, const_cast(module_manager), src_dir_path, options); diff --git a/openfpga/src/fpga_verilog/verilog_top_module.cpp b/openfpga/src/fpga_verilog/verilog_top_module.cpp index 357b9bf5e..fc87412ca 100644 --- a/openfpga/src/fpga_verilog/verilog_top_module.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_module.cpp @@ -25,14 +25,15 @@ namespace openfpga { * Print the wrapper module for the FPGA fabric in Verilog format *******************************************************************/ void print_verilog_core_module(NetlistManager& netlist_manager, - const ModuleManager& module_manager, - const std::string& verilog_dir, - const FabricVerilogOption& options) { + const ModuleManager& module_manager, + const std::string& verilog_dir, + const FabricVerilogOption& options) { /* Create a module as the top-level fabric, and add it to the module manager */ std::string core_module_name = generate_fpga_core_module_name(); ModuleId core_module = module_manager.find_module(core_module_name); - /* It could happen that the module does not exist, just return with no errors */ + /* It could happen that the module does not exist, just return with no errors + */ if (!module_manager.valid_module_id(core_module)) { return; } @@ -52,8 +53,8 @@ void print_verilog_core_module(NetlistManager& netlist_manager, check_file_stream(verilog_fpath.c_str(), fp); - print_verilog_file_header( - fp, std::string("Wrapper Verilog module for FPGA"), options.time_stamp()); + print_verilog_file_header(fp, std::string("Wrapper Verilog module for FPGA"), + options.time_stamp()); /* Write the module content in Verilog format */ write_verilog_module_to_file(fp, module_manager, core_module, diff --git a/openfpga/src/fpga_verilog/verilog_top_module.h b/openfpga/src/fpga_verilog/verilog_top_module.h index 22222b8b4..c10dd5e89 100644 --- a/openfpga/src/fpga_verilog/verilog_top_module.h +++ b/openfpga/src/fpga_verilog/verilog_top_module.h @@ -18,9 +18,9 @@ namespace openfpga { void print_verilog_core_module(NetlistManager& netlist_manager, - const ModuleManager& module_manager, - const std::string& verilog_dir, - const FabricVerilogOption& options); + const ModuleManager& module_manager, + const std::string& verilog_dir, + const FabricVerilogOption& options); void print_verilog_top_module(NetlistManager& netlist_manager, const ModuleManager& module_manager, From d9499f2b40e3ecc365f6fc140f6f67e3e4db5033 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 18 Jun 2023 21:58:36 -0700 Subject: [PATCH 090/391] [core] now fpga bitstream supports the wrapper module --- openfpga/src/fabric/build_device_module.cpp | 6 +++++ .../fpga_bitstream/build_device_bitstream.cpp | 16 +++++++++++-- .../fpga_bitstream/build_fabric_bitstream.cpp | 23 +++++++++++++++---- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index 90778f8b7..45c77a050 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -164,6 +164,12 @@ int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, VTR_LOGV(verbose, "Created a wrapper module '%s' on top of '%s'\n", top_module_name.c_str(), core_module_name.c_str()); + /* 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); + + /* TODO: Update the fabric global ports */ + return status; } diff --git a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp index e4bd1b79c..689a77c89 100644 --- a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp @@ -155,10 +155,22 @@ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx, */ std::string top_block_name = generate_fpga_top_module_name(); ConfigBlockId top_block = bitstream_manager.add_block(top_block_name); - const ModuleId& top_module = - openfpga_ctx.module_graph().find_module(top_block_name); + ModuleId top_module = openfpga_ctx.module_graph().find_module(top_block_name); VTR_ASSERT(true == openfpga_ctx.module_graph().valid_module_id(top_module)); + /* Create the core block when the fpga_core is added */ + std::string core_block_name = generate_fpga_core_module_name(); + const ModuleId& core_module = + openfpga_ctx.module_graph().find_module(core_block_name); + if (openfpga_ctx.module_graph().valid_module_id(core_module)) { + ConfigBlockId core_block = bitstream_manager.add_block(core_block_name); + bitstream_manager.add_child_block(top_block, core_block); + /* Now we use the core_block as the top-level block for the remaining + * functions */ + top_module = core_module; + top_block = core_block; + } + /* Estimate the number of blocks to be added to the database */ size_t num_blocks_to_reserve = rec_estimate_device_bitstream_num_blocks( openfpga_ctx.module_graph(), top_module); diff --git a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp index 141237841..b4d526627 100644 --- a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp @@ -768,17 +768,30 @@ FabricBitstream build_fabric_dependent_bitstream( VTR_ASSERT(true == module_manager.valid_module_id(top_module)); /* Find the top block in bitstream manager, which has not parents */ - std::vector top_block = + std::vector top_blocks = find_bitstream_manager_top_blocks(bitstream_manager); /* Make sure we have only 1 top block and its name matches the top module */ - VTR_ASSERT(1 == top_block.size()); + VTR_ASSERT(1 == top_blocks.size()); VTR_ASSERT( - 0 == top_module_name.compare(bitstream_manager.block_name(top_block[0]))); + 0 == top_module_name.compare(bitstream_manager.block_name(top_blocks[0]))); + ConfigBlockId top_block = top_blocks[0]; + + /* Create the core block when the fpga_core is added */ + std::string core_block_name = generate_fpga_core_module_name(); + const ModuleId& core_module = module_manager.find_module(core_block_name); + if (module_manager.valid_module_id(core_module)) { + /* Now we use the core_block as the top-level block for the remaining + * functions */ + VTR_ASSERT(bitstream_manager.block_children(top_block).size() == 1); + ConfigBlockId core_block = bitstream_manager.block_children(top_block)[0]; + top_module = core_module; + top_block = core_block; + } /* Start build-up formally */ build_module_fabric_dependent_bitstream( - config_protocol, circuit_lib, bitstream_manager, top_block[0], - module_manager, top_module, fabric_bitstream); + config_protocol, circuit_lib, bitstream_manager, top_block, module_manager, + top_module, fabric_bitstream); VTR_LOGV(verbose, "Built %lu configuration bits for fabric\n", fabric_bitstream.num_bits()); From 63ee0c980e8bdfee79f4006f2b8ca024100f4c82 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 18 Jun 2023 22:12:54 -0700 Subject: [PATCH 091/391] [core] fixed some bugs --- openfpga/src/fabric/module_manager.cpp | 6 ++++++ openfpga/src/fpga_bitstream/build_device_bitstream.cpp | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index fb584cd38..27b059a87 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -747,7 +747,13 @@ void ModuleManager::set_module_name(const ModuleId& module, const std::string& name) { /* Validate the id of module */ VTR_ASSERT(valid_module_id(module)); + std::string old_name = names_[module]; names_[module] = name; + + /* Unregister the old name */ + name_id_map_.erase(old_name); + /* Register the new name */ + name_id_map_[name] = module; } void ModuleManager::set_module_usage(const ModuleId& module, diff --git a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp index 689a77c89..46fd7156e 100644 --- a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp @@ -159,6 +159,7 @@ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx, VTR_ASSERT(true == openfpga_ctx.module_graph().valid_module_id(top_module)); /* Create the core block when the fpga_core is added */ + size_t num_blocks_to_reserve = 0; std::string core_block_name = generate_fpga_core_module_name(); const ModuleId& core_module = openfpga_ctx.module_graph().find_module(core_block_name); @@ -169,10 +170,12 @@ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx, * functions */ top_module = core_module; top_block = core_block; + /* Count in fpga core as a block to reserve */ + num_blocks_to_reserve += 1; } /* Estimate the number of blocks to be added to the database */ - size_t num_blocks_to_reserve = rec_estimate_device_bitstream_num_blocks( + num_blocks_to_reserve += rec_estimate_device_bitstream_num_blocks( openfpga_ctx.module_graph(), top_module); bitstream_manager.reserve_blocks(num_blocks_to_reserve); VTR_LOGV(verbose, "Reserved %lu configurable blocks\n", From a4f26798b010d176ab0c340ce904337eba03ce39 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 19 Jun 2023 11:59:48 -0700 Subject: [PATCH 092/391] [core] fixed the bug which causes wrong fpga top connections and failed in fpga sdc --- openfpga/src/fabric/build_device_module.cpp | 2 +- openfpga/src/fabric/module_manager.cpp | 25 +++++++++---------- openfpga/src/fpga_sdc/analysis_sdc_writer.cpp | 9 +++++++ openfpga/src/fpga_sdc/pnr_sdc_writer.cpp | 9 +++++++ 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index 45c77a050..a3a29ae8f 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -154,7 +154,7 @@ int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, /* Create a wrapper module under the existing fpga_top */ ModuleId new_top_module = module_manager.create_wrapper_module( - top_module, top_module_name, core_inst_name, frame_view); + top_module, top_module_name, core_inst_name, !frame_view); if (!module_manager.valid_module_id(new_top_module)) { VTR_LOGV_ERROR(verbose, "Failed to create a wrapper module '%s' on top of '%s'!\n", diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index 27b059a87..24efd51b2 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -1257,9 +1257,6 @@ ModuleId ModuleManager::create_wrapper_module( reserve_module_nets(wrapper_module, num_nets_to_reserve); for (ModulePortId new_port : module_ports(wrapper_module)) { BasicPort new_port_info = module_port(wrapper_module, new_port); - /* Create new net */ - ModuleNetId new_net = create_module_net(wrapper_module); - VTR_ASSERT(valid_module_net_id(wrapper_module, new_net)); /* For each input pin, create a new source */ ModuleManager::e_module_port_type new_port_type = port_type(wrapper_module, new_port); @@ -1267,26 +1264,28 @@ ModuleId ModuleManager::create_wrapper_module( ModulePortId existing_port = port_map[new_port]; BasicPort existing_port_info = module_port(existing_module, existing_port); VTR_ASSERT(existing_port_info == new_port_info); - reserve_module_net_sources(wrapper_module, new_net, - new_port_info.pins().size()); - reserve_module_net_sinks(wrapper_module, new_net, - new_port_info.pins().size()); if (new_port_type != ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT) { - for (auto pin : new_port_info.pins()) { + for (size_t ipin = 0; ipin < new_port_info.pins().size(); ++ipin) { + /* Create new net */ + ModuleNetId new_net = create_module_net(wrapper_module); + VTR_ASSERT(valid_module_net_id(wrapper_module, new_net)); add_module_net_source(wrapper_module, new_net, wrapper_module, 0, - new_port, pin); + new_port, new_port_info.pins()[ipin]); add_module_net_sink(wrapper_module, new_net, existing_module, 0, - existing_port, pin); + existing_port, existing_port_info.pins()[ipin]); } } else { VTR_ASSERT_SAFE(new_port_type == ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); - for (auto pin : new_port_info.pins()) { + for (size_t ipin = 0; ipin < new_port_info.pins().size(); ++ipin) { + /* Create new net */ + ModuleNetId new_net = create_module_net(wrapper_module); + VTR_ASSERT(valid_module_net_id(wrapper_module, new_net)); add_module_net_source(wrapper_module, new_net, existing_module, 0, - existing_port, pin); + existing_port, new_port_info.pins()[ipin]); add_module_net_sink(wrapper_module, new_net, wrapper_module, 0, - new_port, pin); + new_port, existing_port_info.pins()[ipin]); } } } diff --git a/openfpga/src/fpga_sdc/analysis_sdc_writer.cpp b/openfpga/src/fpga_sdc/analysis_sdc_writer.cpp index 00281eebb..65a0d3efa 100644 --- a/openfpga/src/fpga_sdc/analysis_sdc_writer.cpp +++ b/openfpga/src/fpga_sdc/analysis_sdc_writer.cpp @@ -258,6 +258,15 @@ void print_analysis_sdc(const AnalysisSdcOption& option, ModuleId top_module = openfpga_ctx.module_graph().find_module(generate_fpga_top_module_name()); VTR_ASSERT(true == openfpga_ctx.module_graph().valid_module_id(top_module)); + /* Use the core module as the top when the fpga_core is added */ + std::string core_block_name = generate_fpga_core_module_name(); + const ModuleId& core_module = + openfpga_ctx.module_graph().find_module(core_block_name); + if (openfpga_ctx.module_graph().valid_module_id(core_module)) { + /* Now we use the core_block as the top-level block for the remaining + * functions */ + top_module = core_module; + } /* Create clock and set I/O ports with input/output delays * FIXME: Now different I/Os have different delays due to multiple clock diff --git a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp index b7b272641..0a6ad650b 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp +++ b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp @@ -351,6 +351,15 @@ void print_pnr_sdc(const PnrSdcOption& sdc_options, ModuleId top_module = module_manager.find_module(top_module_name); VTR_ASSERT(true == module_manager.valid_module_id(top_module)); + /* Use the core module as the top when the fpga_core is added */ + std::string core_block_name = generate_fpga_core_module_name(); + const ModuleId& core_module = module_manager.find_module(core_block_name); + if (module_manager.valid_module_id(core_module)) { + /* Now we use the core_block as the top-level block for the remaining + * functions */ + top_module = core_module; + } + /* Constrain global ports */ if (true == sdc_options.constrain_global_port()) { print_pnr_sdc_global_ports(sdc_options, module_manager, top_module, From efc9bf9907e32b23b2f09d6406658a24760b70c7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 19 Jun 2023 12:40:37 -0700 Subject: [PATCH 093/391] [test] added new test case to validate bitstream generation --- ...generate_bitstream_example_script.openfpga | 2 ++ .../fpga_bitstream_reg_test.sh | 3 ++ .../fpga_core_wrapper/config/task.conf | 33 +++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 openfpga_flow/tasks/fpga_bitstream/generate_bitstream/fpga_core_wrapper/config/task.conf diff --git a/openfpga_flow/openfpga_shell_scripts/generate_bitstream_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/generate_bitstream_example_script.openfpga index f96daafb9..f96bc27eb 100644 --- a/openfpga_flow/openfpga_shell_scripts/generate_bitstream_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/generate_bitstream_example_script.openfpga @@ -24,6 +24,8 @@ lut_truth_table_fixup # Note that this is turned on when bitstream generation # is the ONLY purpose of the flow!!! build_fabric --compress_routing --frame_view #--verbose +# Add a fpga core between fpga top and the underlying modules +add_fpga_core_to_fabric --instance_name fpga_core_inst --frame_view --verbose # Repack the netlist to physical pbs # This must be done before bitstream generator and testbench generation diff --git a/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh b/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh index 585cd3f19..f5bb9a78f 100755 --- a/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh @@ -20,6 +20,9 @@ echo -e "Testing bitstream generation for an 96x96 FPGA device"; run-task fpga_bitstream/generate_bitstream/configuration_chain/device_96x96 $@ run-task fpga_bitstream/generate_bitstream/ql_memory_bank_shift_register/device_72x72 $@ +echo -e "Testing bitstream generation when fpga core wrapper is added"; +run-task fpga_bitstream/generate_bitstream/fpga_core_wrapper $@ + echo -e "Testing loading architecture bitstream from an external file"; run-task fpga_bitstream/load_external_architecture_bitstream $@ diff --git a/openfpga_flow/tasks/fpga_bitstream/generate_bitstream/fpga_core_wrapper/config/task.conf b/openfpga_flow/tasks/fpga_bitstream/generate_bitstream/fpga_core_wrapper/config/task.conf new file mode 100644 index 000000000..9d6eb21a2 --- /dev/null +++ b/openfpga_flow/tasks/fpga_bitstream/generate_bitstream/fpga_core_wrapper/config/task.conf @@ -0,0 +1,33 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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/generate_bitstream_fpga_core_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_40nm_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] From 299b42873d5e6bdaaff4e585eb51419cb861511b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 19 Jun 2023 13:01:43 -0700 Subject: [PATCH 094/391] [core] fix no warning build --- openfpga/src/base/openfpga_setup_command_template.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openfpga/src/base/openfpga_setup_command_template.h b/openfpga/src/base/openfpga_setup_command_template.h index 2c8e38de5..a8307aaa3 100644 --- a/openfpga/src/base/openfpga_setup_command_template.h +++ b/openfpga/src/base/openfpga_setup_command_template.h @@ -918,10 +918,9 @@ void add_setup_command_templates(openfpga::Shell& shell, * 'build_fabric' */ std::vector add_fpga_core_to_fabric_dependent_cmds; add_fpga_core_to_fabric_dependent_cmds.push_back(build_fabric_cmd_id); - ShellCommandId add_fpga_core_to_fabric_cmd_id = - add_add_fpga_core_to_fabric_command_template( - shell, openfpga_setup_cmd_class, add_fpga_core_to_fabric_dependent_cmds, - hidden); + add_add_fpga_core_to_fabric_command_template( + shell, openfpga_setup_cmd_class, add_fpga_core_to_fabric_dependent_cmds, + hidden); /******************************** * Command 'write_fabric_hierarchy' From b2d1d1b6bd7194f1338d1a4173dc9cdfedb31379 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 19 Jun 2023 14:40:38 -0700 Subject: [PATCH 095/391] [core] fixed a bug on fpga bitstream when supporting fpga_core --- openfpga/src/fpga_bitstream/build_device_bitstream.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp index 46fd7156e..0cbbdb242 100644 --- a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp @@ -164,7 +164,9 @@ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx, const ModuleId& core_module = openfpga_ctx.module_graph().find_module(core_block_name); if (openfpga_ctx.module_graph().valid_module_id(core_module)) { - ConfigBlockId core_block = bitstream_manager.add_block(core_block_name); + std::string core_inst_name = + openfpga_ctx.module_graph().instance_name(top_module, core_module, 0); + ConfigBlockId core_block = bitstream_manager.add_block(core_inst_name); bitstream_manager.add_child_block(top_block, core_block); /* Now we use the core_block as the top-level block for the remaining * functions */ From fd8f371d85dade11b81669b7cb30650030d14af4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 19 Jun 2023 16:44:11 -0700 Subject: [PATCH 096/391] [test] add missing file --- ...itstream_fpga_core_example_script.openfpga | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 openfpga_flow/openfpga_shell_scripts/generate_bitstream_fpga_core_example_script.openfpga diff --git a/openfpga_flow/openfpga_shell_scripts/generate_bitstream_fpga_core_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/generate_bitstream_fpga_core_example_script.openfpga new file mode 100644 index 000000000..f96daafb9 --- /dev/null +++ b/openfpga_flow/openfpga_shell_scripts/generate_bitstream_fpga_core_example_script.openfpga @@ -0,0 +1,48 @@ +# Run VPR for the 'and' design +#--write_rr_graph example_rr_graph.xml +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route --absorb_buffer_luts off + +# 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 +# - Enabled frame view creation to save runtime and memory +# Note that this is turned on when bitstream generation +# is the ONLY purpose of the flow!!! +build_fabric --compress_routing --frame_view #--verbose + +# 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.txt --format plain_text +write_fabric_bitstream --file fabric_bitstream.xml --format xml + +# Finish and exit OpenFPGA +exit + +# Note : +# To run verification at the end of the flow maintain source in ./SRC directory From 781c0c6f71ee20f5065ebde8ca1b527ff596f7b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 06:59:10 +0000 Subject: [PATCH 097/391] Bump yosys from `8b2a001` to `2595471` Bumps [yosys](https://github.com/YosysHQ/yosys) from `8b2a001` to `2595471`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/8b2a0010216f9a15c09bd2f4dc63691949b126df...25954715f0a8a2fc65d5961c3f09f9ee95774760) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 8b2a00102..25954715f 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 8b2a0010216f9a15c09bd2f4dc63691949b126df +Subproject commit 25954715f0a8a2fc65d5961c3f09f9ee95774760 From dba48fb17132da971ac9574037ac566ca51d03ab Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 20 Jun 2023 16:57:08 -0700 Subject: [PATCH 098/391] [test] reworking adder mapping flow to validate carry chain mapping --- .../misc/ys_tmpl_yosys_vpr_adder_flow.ys | 48 ++++ .../openfpga_adders_sim.v | 1 + .../openfpga_arith_map.v | 211 +++++------------- openfpga_flow/scripts/run_fpga_task.py | 1 + .../config/dummy_pin_constraints.xml | 4 + .../adder/hard_adder/config/task.conf | 24 +- 6 files changed, 125 insertions(+), 164 deletions(-) create mode 100644 openfpga_flow/misc/ys_tmpl_yosys_vpr_adder_flow.ys create mode 100644 openfpga_flow/tasks/fpga_verilog/adder/hard_adder/config/dummy_pin_constraints.xml diff --git a/openfpga_flow/misc/ys_tmpl_yosys_vpr_adder_flow.ys b/openfpga_flow/misc/ys_tmpl_yosys_vpr_adder_flow.ys new file mode 100644 index 000000000..092aeea3b --- /dev/null +++ b/openfpga_flow/misc/ys_tmpl_yosys_vpr_adder_flow.ys @@ -0,0 +1,48 @@ +# Yosys synthesis script for ${TOP_MODULE} +# Read verilog files +read_verilog ${READ_VERILOG_OPTIONS} ${VERILOG_FILES} + +# Technology mapping +hierarchy -top ${TOP_MODULE} +proc +techmap -D NO_LUT -map +/adff2dff.v + +# Synthesis +flatten +opt_expr +opt_clean +check +opt -nodffe -nosdff +fsm +opt -nodffe -nosdff +wreduce +peepopt +opt_clean + +# Map Adders +techmap -map +/techmap.v -map ${YOSYS_ADDER_MAP_VERILOG} +opt -fast -nodffe -nosdff +opt_expr +opt_merge +opt_clean +opt -nodffe -nosdff + +# +memory -nomap +opt_clean +opt -fast -full -nodffe -nosdff +memory_map +opt -full -nodffe -nosdff +techmap +opt -fast -nodffe -nosdff +clean + +# LUT mapping +abc -lut ${LUT_SIZE} + +# Check +synth -run check + +# Clean and output blif +opt_clean -purge +write_blif ${OUTPUT_BLIF} diff --git a/openfpga_flow/openfpga_yosys_techlib/openfpga_adders_sim.v b/openfpga_flow/openfpga_yosys_techlib/openfpga_adders_sim.v index 9d82dc715..da3baa337 100644 --- a/openfpga_flow/openfpga_yosys_techlib/openfpga_adders_sim.v +++ b/openfpga_flow/openfpga_yosys_techlib/openfpga_adders_sim.v @@ -1,6 +1,7 @@ //--------------------------------------- // 1-bit adder //--------------------------------------- +(* abc9_box, lib_whitebox *) module adder( input cin, input a, diff --git a/openfpga_flow/openfpga_yosys_techlib/openfpga_arith_map.v b/openfpga_flow/openfpga_yosys_techlib/openfpga_arith_map.v index d5227c16a..8763d6ced 100644 --- a/openfpga_flow/openfpga_yosys_techlib/openfpga_arith_map.v +++ b/openfpga_flow/openfpga_yosys_techlib/openfpga_arith_map.v @@ -1,164 +1,67 @@ -////////////////////////// -// arithmetic // -////////////////////////// +// Arithmetic units: adder +// Adapt from: https://github.com/chipsalliance/yosys-f4pga-plugins/blob/0ad1af26a29243a9e76379943d735e119dcd0cc6/ql-qlf-plugin/qlf_k6n10/cells_sim.v +// Many thanks to F4PGA for their contribution -module \$alu (A, B, CI, BI, X, Y, CO); +(* techmap_celltype = "$alu" *) +module _openfpga_alu (A, B, CI, BI, X, Y, CO); -parameter A_SIGNED = 0; -parameter B_SIGNED = 0; -parameter A_WIDTH = 1; -parameter B_WIDTH = 1; -parameter Y_WIDTH = 1; + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 1; + parameter B_WIDTH = 1; + parameter Y_WIDTH = 1; -input [A_WIDTH-1:0] A; -input [B_WIDTH-1:0] B; -output [Y_WIDTH:0] X, Y; + input [A_WIDTH-1:0] A; + input [B_WIDTH-1:0] B; + output [Y_WIDTH-1:0] X, Y; -input CI, BI; -output [Y_WIDTH:0] CO; + input CI, BI; + output [Y_WIDTH-1:0] CO; -wire [Y_WIDTH-1:0] AA, BB; -wire [1024:0] _TECHMAP_DO_ = "splitnets CARRY; clean"; + wire [1024:0] _TECHMAP_DO_ = "splitnets CARRY; clean"; -generate - if (A_SIGNED && B_SIGNED) begin:BLOCK1 - assign AA = $signed(A), BB = BI ? ~$signed(B) : $signed(B); - end else begin:BLOCK2 - assign AA = $unsigned(A), BB = BI ? ~$unsigned(B) : $unsigned(B); - end -endgenerate + (* force_downto *) + wire [Y_WIDTH-1:0] A_buf, B_buf; + \$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf)); + \$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf)); - wire [Y_WIDTH: 0 ] CARRY; - assign CARRY[0] = CI; + (* force_downto *) + wire [Y_WIDTH-1:0] AA = A_buf; + (* force_downto *) + wire [Y_WIDTH-1:0] BB = BI ? ~B_buf : B_buf; + wire [Y_WIDTH: 0 ] CARRY; - genvar i; - generate for (i = 0; i < Y_WIDTH - 1; i = i+1) begin:gen3 - adder my_adder ( - .cin (CARRY[i]), - .cout (CARRY[i+1]), - .a (AA[i]), - .b (BB[i]), - .sumout (Y[i]) - ); - end endgenerate + assign CO[Y_WIDTH-1:0] = CARRY[Y_WIDTH:1]; + // Due to VPR limitations regarding IO connexion to carry chain, + // we generate the carry chain input signal using an intermediate adder + // since we can connect a & b from io pads, but not cin & cout + generate + adder intermediate_adder ( + .cin ( ), + .cout (CARRY[0]), + .a (CI ), + .b (CI ), + .sumout ( ) + ); - generate if ((Y_WIDTH -1) % 20 == 0) begin:gen4 - assign Y[Y_WIDTH-1] = CARRY[Y_WIDTH-1]; - end else begin:gen5 - adder my_adder ( - .cin (CARRY[Y_WIDTH - 1]), - .cout (CARRY[Y_WIDTH]), - .a (1'b0), - .b (1'b0), - .sumout (Y[Y_WIDTH -1]) - ); - end - endgenerate - endmodule + adder first_adder ( + .cin (CARRY[0]), + .cout (CARRY[1]), + .a (AA[0] ), + .b (BB[0] ), + .sumout (Y[0] ) + ); + endgenerate -//--------------------------------------------------------- - -module \$fa (A, B, C, X, Y); - -parameter A_SIGNED = 0; -parameter B_SIGNED = 0; -parameter A_WIDTH = 1; -parameter B_WIDTH = 1; -parameter Y_WIDTH = 1; - -input [A_WIDTH-1:0] A; -input [B_WIDTH-1:0] B; -input C; -output [Y_WIDTH:0] X, Y; - -wire [Y_WIDTH-1:0] AA, BB; -wire [1024:0] _TECHMAP_DO_ = "splitnets CARRY; clean"; - -generate - if (A_SIGNED && B_SIGNED) begin:BLOCK1 - assign AA = $signed(A), BB = $signed(B); - end else begin:BLOCK2 - assign AA = $unsigned(A), BB = $unsigned(B); - end -endgenerate - - wire [Y_WIDTH: 0 ] CARRY; - assign CARRY[0] = C; - - genvar i; - generate for (i = 0; i < Y_WIDTH - 1; i = i+1) begin:gen3 - adder my_adder ( - .cin (CARRY[i]), - .cout (CARRY[i+1]), - .a (AA[i]), - .b (BB[i]), - .sumout (Y[i]) - ); - end endgenerate - - generate if ((Y_WIDTH -1) % 20 == 0) begin:gen4 - assign Y[Y_WIDTH-1] = CARRY[Y_WIDTH-1]; - end else begin:gen5 - adder my_adder ( - .cin (CARRY[Y_WIDTH - 1]), - .cout (CARRY[Y_WIDTH]), - .a (1'b0), - .b (1'b0), - .sumout (Y[Y_WIDTH -1]) - ); - end - endgenerate - endmodule - -//--------------------------------------------------------- - -module \$add (A, B, Y); - -parameter A_SIGNED = 0; -parameter B_SIGNED = 0; -parameter A_WIDTH = 1; -parameter B_WIDTH = 1; -parameter Y_WIDTH = 1; - -input [A_WIDTH-1:0] A; -input [B_WIDTH-1:0] B; -output [Y_WIDTH:0] Y; - -wire [Y_WIDTH-1:0] AA, BB; -wire [1024:0] _TECHMAP_DO_ = "splitnets CARRY; clean"; - -generate - if (A_SIGNED && B_SIGNED) begin:BLOCK1 - assign AA = $signed(A), BB = $signed(B); - end else begin:BLOCK2 - assign AA = $unsigned(A), BB = $unsigned(B); - end -endgenerate - - wire [Y_WIDTH: 0 ] CARRY; - assign CARRY[0] = 1'b0; - - genvar i; - generate for (i = 0; i < Y_WIDTH - 1; i = i+1) begin:gen3 - adder my_adder ( - .cin (CARRY[i]), - .cout (CARRY[i+1]), - .a (AA[i]), - .b (BB[i]), - .sumout (Y[i]) - ); - end endgenerate - - generate if ((Y_WIDTH -1) % 20 == 0) begin:gen4 - assign Y[Y_WIDTH-1] = CARRY[Y_WIDTH-1]; - end else begin:gen5 - adder my_adder ( - .cin (CARRY[Y_WIDTH - 1]), - .cout (CARRY[Y_WIDTH]), - .a (1'b0), - .b (1'b0), - .sumout (Y[Y_WIDTH -1]) - ); - end - endgenerate - endmodule + genvar i; + generate for (i = 1; i < Y_WIDTH ; i = i+1) begin:gen3 + adder my_adder ( + .cin (CARRY[i] ), + .cout (CARRY[i+1]), + .a (AA[i] ), + .b (BB[i] ), + .sumout (Y[i] ) + ); + end endgenerate + assign X = AA ^ BB; +endmodule diff --git a/openfpga_flow/scripts/run_fpga_task.py b/openfpga_flow/scripts/run_fpga_task.py index e215a3f50..c021eebf7 100644 --- a/openfpga_flow/scripts/run_fpga_task.py +++ b/openfpga_flow/scripts/run_fpga_task.py @@ -294,6 +294,7 @@ def generate_each_task_actions(taskname): yosys_params = [ "read_verilog_options", "yosys_args", + "yosys_adder_map_verilog", "yosys_bram_map_rules", "yosys_bram_map_verilog", "yosys_cell_sim_verilog", diff --git a/openfpga_flow/tasks/fpga_verilog/adder/hard_adder/config/dummy_pin_constraints.xml b/openfpga_flow/tasks/fpga_verilog/adder/hard_adder/config/dummy_pin_constraints.xml new file mode 100644 index 000000000..c7bb833ec --- /dev/null +++ b/openfpga_flow/tasks/fpga_verilog/adder/hard_adder/config/dummy_pin_constraints.xml @@ -0,0 +1,4 @@ + + + + diff --git a/openfpga_flow/tasks/fpga_verilog/adder/hard_adder/config/task.conf b/openfpga_flow/tasks/fpga_verilog/adder/hard_adder/config/task.conf index 839bd3e09..54f0901c9 100644 --- a/openfpga_flow/tasks/fpga_verilog/adder/hard_adder/config/task.conf +++ b/openfpga_flow/tasks/fpga_verilog/adder/hard_adder/config/task.conf @@ -9,29 +9,33 @@ [GENERAL] run_engine=openfpga_shell power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml -power_analysis = true +power_analysis = false spice_output=false verilog_output=true timeout_each_job = 20*60 -fpga_flow=vpr_blif +fpga_flow=yosys_vpr [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/example_without_ace_script.openfpga openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_40nm_openfpga.xml -openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml -openfpga_vpr_device_layout=2x2 +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml +openfpga_pin_constraints_file=${PATH:TASK_DIR}/config/dummy_pin_constraints.xml [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.blif +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/adder/adder_8/adder_8.v [SYNTHESIS_PARAM] -bench0_top = and2 -bench0_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.act -bench0_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v -bench0_chan_width = 300 +# Yosys script parameters +bench_yosys_cell_sim_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/openfpga_adders_sim.v +bench_yosys_adder_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/openfpga_arith_map.v +bench_read_verilog_options_common = -nolatches +bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_adder_flow.ys + +# Benchmark information +bench0_top = adder_8 [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From 84edd41342c811c7adafb9bcf13a4c2dcec315e2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 20 Jun 2023 17:09:31 -0700 Subject: [PATCH 099/391] [test] fixed the bug in adder mapping --- .../misc/ys_tmpl_yosys_vpr_adder_flow.ys | 2 +- .../openfpga_adders_sim.v | 17 ++++++++--------- .../openfpga_yosys_techlib/openfpga_arith_map.v | 2 +- .../adder/hard_adder/config/task.conf | 1 + 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/openfpga_flow/misc/ys_tmpl_yosys_vpr_adder_flow.ys b/openfpga_flow/misc/ys_tmpl_yosys_vpr_adder_flow.ys index 092aeea3b..3d3acb5a5 100644 --- a/openfpga_flow/misc/ys_tmpl_yosys_vpr_adder_flow.ys +++ b/openfpga_flow/misc/ys_tmpl_yosys_vpr_adder_flow.ys @@ -41,7 +41,7 @@ clean abc -lut ${LUT_SIZE} # Check -synth -run check +#synth -run check # Clean and output blif opt_clean -purge diff --git a/openfpga_flow/openfpga_yosys_techlib/openfpga_adders_sim.v b/openfpga_flow/openfpga_yosys_techlib/openfpga_adders_sim.v index da3baa337..953ddf685 100644 --- a/openfpga_flow/openfpga_yosys_techlib/openfpga_adders_sim.v +++ b/openfpga_flow/openfpga_yosys_techlib/openfpga_adders_sim.v @@ -3,14 +3,13 @@ //--------------------------------------- (* abc9_box, lib_whitebox *) module adder( - input cin, - input a, - input b, - output cout, - output sumout ); - - - assign sumout = a ^ b ^ cin; - assign cout = (a & b) | ((a | b) & cin); + output sumout, + output cout, + input a, + input b, + input cin +); + assign sumout = a ^ b ^ cin; + assign cout = (a & b) | ((a | b) & cin); endmodule diff --git a/openfpga_flow/openfpga_yosys_techlib/openfpga_arith_map.v b/openfpga_flow/openfpga_yosys_techlib/openfpga_arith_map.v index 8763d6ced..d363226fa 100644 --- a/openfpga_flow/openfpga_yosys_techlib/openfpga_arith_map.v +++ b/openfpga_flow/openfpga_yosys_techlib/openfpga_arith_map.v @@ -3,7 +3,7 @@ // Many thanks to F4PGA for their contribution (* techmap_celltype = "$alu" *) -module _openfpga_alu (A, B, CI, BI, X, Y, CO); +module _80_quicklogic_alu (A, B, CI, BI, X, Y, CO); parameter A_SIGNED = 0; parameter B_SIGNED = 0; diff --git a/openfpga_flow/tasks/fpga_verilog/adder/hard_adder/config/task.conf b/openfpga_flow/tasks/fpga_verilog/adder/hard_adder/config/task.conf index 54f0901c9..d9a5f151a 100644 --- a/openfpga_flow/tasks/fpga_verilog/adder/hard_adder/config/task.conf +++ b/openfpga_flow/tasks/fpga_verilog/adder/hard_adder/config/task.conf @@ -33,6 +33,7 @@ bench_yosys_cell_sim_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga bench_yosys_adder_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/openfpga_arith_map.v bench_read_verilog_options_common = -nolatches bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_adder_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 # Benchmark information bench0_top = adder_8 From 462fbdd52de7f387d47b76e8eb852274cbb48d74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 06:59:24 +0000 Subject: [PATCH 100/391] Bump yosys from `2595471` to `104edb4` Bumps [yosys](https://github.com/YosysHQ/yosys) from `2595471` to `104edb4`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/25954715f0a8a2fc65d5961c3f09f9ee95774760...104edb458784fdbf2bb782f095c2afada9b0299c) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 25954715f..104edb458 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 25954715f0a8a2fc65d5961c3f09f9ee95774760 +Subproject commit 104edb458784fdbf2bb782f095c2afada9b0299c From 61544af2b461553612a0013364d28a6ec2254aa0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Jun 2023 14:01:00 -0700 Subject: [PATCH 101/391] [core] start adding new options --- .../fpga_verilog_commands.rst | 12 +++++++++ .../openfpga_commands/setup_commands.rst | 5 +++- .../base/openfpga_setup_command_template.h | 5 ++++ .../base/openfpga_verilog_command_template.h | 26 ++++++++++++++++--- 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst index b085df0bf..d29c4c172 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst @@ -49,6 +49,10 @@ write_full_testbench The output directory for all the testbench netlists. We suggest the use of same output directory as fabric Verilog netlists. For example, ``--file /temp/testbench`` + .. option:: --dut_module + + Specify the name of *Design Under Test* (DUT) module to be considered in the testbench. Can be either ``fpga_top`` or ``fpga_core. By default, it is ``fpga_top``. + .. option:: --bitstream The bitstream file to be loaded to the full testbench, which should be in the same file format that OpenFPGA can outputs (See detailes in :ref:`file_formats_fabric_bitstream_plain_text`). For example, ``--bitstream and2.bit`` @@ -120,6 +124,10 @@ write_preconfigured_fabric_wrapper Specify the fabric Verilog file if they are not in the same directory as the testbenches to be generated. If not specified, OpenFPGA will assume that the fabric netlists are the in the same directory as testbenches and assign default names. For example, ``--file /temp/fabric/fabric_netlists.v`` + .. option:: --dut_module + + Specify the name of *Design Under Test* (DUT) module to be considered in the testbench. Can be either ``fpga_top`` or ``fpga_core. By default, it is ``fpga_top``. + .. option:: --pin_constraints_file or -pcf Specify the *Pin Constraints File* (PCF) if you want to custom stimulus in testbenches. For example, ``-pin_constraints_file pin_constraints.xml`` @@ -175,6 +183,10 @@ write_mock_fpga_wrapper The output directory for the netlists. We suggest the use of same output directory as fabric Verilog netlists. For example, ``--file /temp/testbench`` + .. option:: --top_module + + Specify the name of top-level module to be considered in the wrapper. Can be either ``fpga_top`` or ``fpga_core. By default, it is ``fpga_top``. + .. option:: --pin_constraints_file or -pcf Specify the *Pin Constraints File* (PCF) if you want to custom stimulus in testbenches. For example, ``-pin_constraints_file pin_constraints.xml`` 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 fd5631f6a..68fd3d251 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst @@ -288,11 +288,14 @@ add_fpga_core_to_fabric Add a wrapper module ``fpga_core`` as an intermediate layer to FPGA fabric. After this command, the existing module ``fpga_top`` will remain the top-level module while there is a new module ``fpga_core`` under it. Under fpga_core, there will be the detailed building blocks. + .. option:: --io_naming + + This is optional. Specify the I/O naming rules when connecting I/Os of ``fpga_core`` module to the top-level module ``fpga_top``. If not defined, the ``fpga_top`` will be the same as ``fpga_core`` w.r.t. ports. See details about the file format of I/O naming rules in :ref:`file_format_io_naming_file`. + .. option:: --instance_name This is optional. Specify the instance name to be used when instanciate the ``fpga_core`` module under the top-level module ``fpga_top``. If not defined, by default it is ``fpga_core_inst``. - .. option:: --frame_view Create only frame views of the module graph. When enabled, top-level module will not include any nets. This option is made for save runtime and memory. diff --git a/openfpga/src/base/openfpga_setup_command_template.h b/openfpga/src/base/openfpga_setup_command_template.h index a8307aaa3..845873e7e 100644 --- a/openfpga/src/base/openfpga_setup_command_template.h +++ b/openfpga/src/base/openfpga_setup_command_template.h @@ -703,6 +703,11 @@ ShellCommandId add_add_fpga_core_to_fabric_command_template( const std::vector& dependent_cmds, const bool& hidden) { Command shell_cmd("add_fpga_core_to_fabric"); + /* Add an option '--io_naming'*/ + CommandOptionId opt_io_naming = shell_cmd.add_option( + "io_naming", false, "specify the file path to the I/O naming rules"); + shell_cmd.set_option_require_value(opt_io_naming, openfpga::OPT_STRING); + /* Add an option '--instance_name'*/ CommandOptionId opt_inst_name = shell_cmd.add_option( "instance_name", false, "specify the instance of fpga_core under fpga_top"); diff --git a/openfpga/src/base/openfpga_verilog_command_template.h b/openfpga/src/base/openfpga_verilog_command_template.h index 010b73802..5c9531c37 100644 --- a/openfpga/src/base/openfpga_verilog_command_template.h +++ b/openfpga/src/base/openfpga_verilog_command_template.h @@ -92,6 +92,13 @@ ShellCommandId add_write_full_testbench_command_template( shell_cmd.set_option_short_name(output_opt, "f"); shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING); + /* add an option '--dut_module'*/ + CommandOptionId dut_module_opt = shell_cmd.add_option( + "dut_module", false, + "specify the module name of DUT to be used in the testbench. Can be either " + "fpga_top or fpga_core. By default, it is fpga_top."); + shell_cmd.set_option_require_value(dut_module_opt, openfpga::OPT_STRING); + /* add an option '--bitstream'*/ CommandOptionId bitstream_opt = shell_cmd.add_option( "bitstream", true, "specify the bitstream to be loaded in the testbench"); @@ -196,6 +203,13 @@ ShellCommandId add_write_preconfigured_fabric_wrapper_command_template( "specify the file path to the fabric hdl netlist"); shell_cmd.set_option_require_value(fabric_netlist_opt, openfpga::OPT_STRING); + /* add an option '--dut_module'*/ + CommandOptionId dut_module_opt = shell_cmd.add_option( + "dut_module", false, + "specify the module name of DUT to be used in the testbench. Can be either " + "fpga_top or fpga_core. By default, it is fpga_top."); + shell_cmd.set_option_require_value(dut_module_opt, openfpga::OPT_STRING); + /* add an option '--pin_constraints_file in short '-pcf' */ CommandOptionId pcf_opt = shell_cmd.add_option("pin_constraints_file", false, @@ -275,6 +289,14 @@ ShellCommandId add_write_mock_fpga_wrapper_command_template( shell_cmd.set_option_short_name(pcf_opt, "pcf"); shell_cmd.set_option_require_value(pcf_opt, openfpga::OPT_STRING); + /* add an option '--top_module'*/ + CommandOptionId top_module_opt = + shell_cmd.add_option("dut_module", false, + "specify the top-level module name to be used in the " + "wrapper, which matters the I/O names. Can be either " + "fpga_top or fpga_core. By default, it is fpga_top."); + shell_cmd.set_option_require_value(top_module_opt, openfpga::OPT_STRING); + /* add an option '--bus_group_file in short '-bgf' */ CommandOptionId bgf_opt = shell_cmd.add_option( "bus_group_file", false, "specify the file path to the group pins to bus"); @@ -297,10 +319,6 @@ ShellCommandId add_write_mock_fpga_wrapper_command_template( shell_cmd.set_option_require_value(default_net_type_opt, openfpga::OPT_STRING); - /* add an option '--explicit_port_mapping' */ - shell_cmd.add_option("explicit_port_mapping", false, - "use explicit port mapping in verilog netlists"); - /* Add an option '--no_time_stamp' */ shell_cmd.add_option("no_time_stamp", false, "Do not print a time stamp in the output files"); From 0e033de9b5e71e5dc39d4478334f5b51595b51fb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 22 Jun 2023 00:02:36 +0000 Subject: [PATCH 102/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 0f32edd99..0609725c2 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1096 +1.2.1121 From b42677aa9d95832cd8dc734a0dcd458175db4dd6 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Jun 2023 17:33:40 -0700 Subject: [PATCH 103/391] [lib] developing the io name mapping data structure --- libs/CMakeLists.txt | 1 + libs/libionamemap/src/base/io_name_map.cpp | 83 ++++++++++++++++++++++ libs/libionamemap/src/base/io_name_map.h | 49 +++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 libs/libionamemap/src/base/io_name_map.cpp create mode 100644 libs/libionamemap/src/base/io_name_map.h diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index a871fa27e..9a8c23e43 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory(libfabrickey) add_subdirectory(libfpgabitstream) add_subdirectory(libpcf) add_subdirectory(libbusgroup) +add_subdirectory(libionamemap) diff --git a/libs/libionamemap/src/base/io_name_map.cpp b/libs/libionamemap/src/base/io_name_map.cpp new file mode 100644 index 000000000..70a39ecd7 --- /dev/null +++ b/libs/libionamemap/src/base/io_name_map.cpp @@ -0,0 +1,83 @@ +/****************************************************************************** + * Memember functions for data structure IoLocationMap + ******************************************************************************/ +/* Headers from vtrutil library */ +#include "io_name_map.h" + +#include + +#include "command_exit_codes.h" +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/************************************************** + * Public Accessors + *************************************************/ +BasicPort IoNameMap::fpga_core_port(const BasicPort& fpga_top_port) const { + BasicPort core_port; + auto result = top2core_io_name_map_.find(fpga_top_port); + if (result != top2core_io_name_map_.end()) { + core_port = result->second; + } + return core_port; +} + +BasicPort IoNameMap::fpga_top_port(const BasicPort& fpga_core_port) const { + BasicPort top_port; + auto result = core2top_io_name_map_.find(fpga_core_port); + if (result != core2top_io_name_map_.end()) { + top_port = result->second; + } + return top_port; +} + +int IoNameMap::set_io_pair(const BasicPort& fpga_top_port, + const BasicPort& fpga_core_port) { + /* Ensure the two ports are matching in size */ + if (fpga_top_port.get_width() != fpga_core_port.get_width()) { + VTR_LOG_ERROR( + "Unable to pair two ports 'fpga_top.%s[%lu:%lu]' and " + "'fpga_core.%s[%lu:%lu]' which are in the same size!\n", + fpga_top_port.get_name().c_str(), fpga_top_port.get_lsb(), + fpga_top_port.get_msb(), fpga_core_port.get_name().c_str(), + fpga_core_port.get_lsb(), fpga_core_port.get_msb()); + return CMD_EXEC_FATAL_ERROR; + } + VTR_ASSERT_SAFE(fpga_top_port.get_width() != fpga_core_port.get_width()); + for (size_t ipin = 0; ipin < fpga_top_port.pins().size(); ++ipin) { + BasicPort top_pin(fpga_top_port.get_name(), fpga_top_port.pins()[ipin], + fpga_top_port.pins()[ipin]); + BasicPort core_pin(fpga_core_port.get_name(), fpga_core_port.pins()[ipin], + fpga_core_port.pins()[ipin]); + top2core_io_name_map_[top_pin] = core_pin; + core2top_io_name_map_[core_pin] = top_pin; + } + return CMD_EXEC_SUCCESS; +} + +int IoNameMap::set_dummy_io(const BasicPort& fpga_top_port) { + /* Must be a true dummy port, none of its pins have been paired! */ + for (size_t ipin = 0; ipin < fpga_top_port.pins().size(); ++ipin) { + BasicPort top_pin(fpga_top_port.get_name(), fpga_top_port.pins()[ipin], + fpga_top_port.pins()[ipin]); + auto result = top2core_io_name_map_.find(top_pin); + if (result != top2core_io_name_map_.end() && result->second.is_valid()) { + VTR_LOG_ERROR( + "Pin '%lu' in a dummy port '%s[%lu:%lu]' of fpga_top is already mapped " + "to a valid pin '%s[%lu:%lu]' of fpga_core!\n", + top_pin.get_lsb(), fpga_top_port.get_name().c_str(), + fpga_top_port.get_lsb(), fpga_top_port.get_msb(), + result->second.get_name().c_str(), result->second.get_lsb(), + result->second.get_msb()); + return CMD_EXEC_FATAL_ERROR; + } + top2core_io_name_map_[top_pin] = BasicPort(); + } + return CMD_EXEC_SUCCESS; +} + +} /* end namespace openfpga */ diff --git a/libs/libionamemap/src/base/io_name_map.h b/libs/libionamemap/src/base/io_name_map.h new file mode 100644 index 000000000..698a29b4b --- /dev/null +++ b/libs/libionamemap/src/base/io_name_map.h @@ -0,0 +1,49 @@ +#ifndef IO_NAME_MAP_H +#define IO_NAME_MAP_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +#include + +#include "openfpga_port.h" + +/* Begin namespace openfpga */ +namespace openfpga { + +/** + * @brief I/O name map is a data structure to show mapping between the ports + * of fpga_top and fpga_core, which are the two possible top-level modules that + * modeling a complete FPGA fabric Using the data structure, developers can find + * - the corresponding port of fpga_core, with a given port of fpga_top + * - the corresponding port of fpga_top, with a given port of fpga_core + */ +class IoNameMap { + public: /* Public accessors */ + /** @brief With a given port at fpga_top, find the corresponding I/O at + * fpga_core. Return an invalid port if not found */ + BasicPort fpga_core_port(const BasicPort& fpga_top_port) const; + /** @brief With a given port at fpga_core, find the corresponding I/O at + * fpga_top. Return an invalid port if not found */ + BasicPort fpga_top_port(const BasicPort& fpga_core_port) const; + + public: /* Public mutators */ + /** @brief Create the one-on-one mapping between an port of fpga_top and + * fpga_core. Return 0 for success, return 1 for fail */ + int set_io_pair(const BasicPort& fpga_top_port, + const BasicPort& fpga_core_port); + /** @brief Add a dummy port at the fpga top, which is not mapped any port at + * fpga_core */ + int set_dummy_io(const BasicPort& fpga_top_port); + + private: /* Internal Data */ + /* fpga_top -> fpga_core io name mapping, each port is in the size of 1. This + * is designed to fast look-up but at the cost of potential large memory + * footprints. TODO: Optimize if we see such issue */ + std::map top2core_io_name_map_; + std::map core2top_io_name_map_; +}; + +} /* End namespace openfpga*/ + +#endif From 2ed86d18976b301a1f84bd705fe0290cb129c127 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Jun 2023 18:08:45 -0700 Subject: [PATCH 104/391] [lib] developing io for io naming rule --- .../src/io/io_name_map_xml_constants.h | 12 +++ .../src/io/read_xml_io_name_map.cpp | 92 +++++++++++++++++++ .../src/io/read_xml_io_name_map.h | 21 +++++ 3 files changed, 125 insertions(+) create mode 100644 libs/libionamemap/src/io/io_name_map_xml_constants.h create mode 100644 libs/libionamemap/src/io/read_xml_io_name_map.cpp create mode 100644 libs/libionamemap/src/io/read_xml_io_name_map.h diff --git a/libs/libionamemap/src/io/io_name_map_xml_constants.h b/libs/libionamemap/src/io/io_name_map_xml_constants.h new file mode 100644 index 000000000..532497935 --- /dev/null +++ b/libs/libionamemap/src/io/io_name_map_xml_constants.h @@ -0,0 +1,12 @@ +#ifndef IO_NAME_MAP_XML_CONSTANTS_H +#define IO_NAME_MAP_XML_CONSTANTS_H + +/* Constants required by XML parser */ + +constexpr const char* XML_IO_NAME_MAP_ROOT_NAME = "ports"; +constexpr const char* XML_IO_NAME_MAP_NODE_NAME = "port"; +constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_TOP_NAME = "top_name"; +constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_CORE_NAME = "core_name"; +constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_IS_DUMMY = "is_dummy"; + +#endif diff --git a/libs/libionamemap/src/io/read_xml_io_name_map.cpp b/libs/libionamemap/src/io/read_xml_io_name_map.cpp new file mode 100644 index 000000000..8cbe566dd --- /dev/null +++ b/libs/libionamemap/src/io/read_xml_io_name_map.cpp @@ -0,0 +1,92 @@ +/******************************************************************** + * This file includes the top-level function of this library + * which reads an XML of clock network file to the associated + * data structures + *******************************************************************/ +#include + +/* Headers from pugi XML library */ +#include "pugixml.hpp" +#include "pugixml_util.hpp" + +/* Headers from vtr util library */ +#include "vtr_assert.h" +#include "vtr_time.h" + +/* Headers from libopenfpga util library */ +#include "openfpga_port_parser.h" + +/* Headers from libarchfpga */ +#include "arch_error.h" +#include "command_exit_codes.h" +#include "io_name_map_xml_constants.h" +#include "read_xml_io_name_map.h" +#include "read_xml_util.h" + +namespace openfpga { // Begin namespace openfpga + +/******************************************************************** + * Parse XML codes of a to an object of I/O naming + *******************************************************************/ +static int read_xml_io_map_port(pugi::xml_node& xml_port, + const pugiutil::loc_data& loc_data, + IoNameMap& io_name_map) { + /* Parse fpga top port information */ + std::string top_name = + get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_TOP_NAME, loc_data) + .as_string(); + BasicPort top_port = openfpga::PortParser(top_name).port(); + + /* For dummy port, create the dummy io */ + bool is_dummy = + get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_IS_DUMMY, loc_data) + .as_bool(); + if (is_dummy) { + return io_name_map.set_dummy_io(top_port); /* Early return */ + } + + /* This is not a dummy io, create the io mapping */ + std::string core_name = + get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_CORE_NAME, loc_data) + .as_string(); + BasicPort core_port = openfpga::PortParser(core_name).port(); + + return io_name_map.set_io_pair(top_port, core_port); +} + +/******************************************************************** + * Parse XML codes about to an object of ClockNetwork + *******************************************************************/ +int read_xml_io_name_map(const char* fname, IoNameMap& io_name_map) { + vtr::ScopedStartFinishTimer timer("Read I/O naming rules"); + + int status = CMD_EXEC_SUCCESS; + + /* Parse the file */ + pugi::xml_document doc; + pugiutil::loc_data loc_data; + + try { + loc_data = pugiutil::load_xml(doc, fname); + + pugi::xml_node xml_root = + get_single_child(doc, XML_IO_NAME_MAP_ROOT_NAME, loc_data); + + for (pugi::xml_node xml_port : xml_root.children()) { + /* Error out if the XML child has an invalid name! */ + if (xml_port.name() != std::string(XML_IO_NAME_MAP_NODE_NAME)) { + bad_tag(xml_port, loc_data, xml_root, {XML_IO_NAME_MAP_NODE_NAME}); + } + status = read_xml_io_map_port(xml_port, loc_data, io_name_map); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } + } catch (pugiutil::XmlError& e) { + archfpga_throw(fname, e.line(), "%s", e.what()); + } + + return status; +} + +} // End of namespace openfpga diff --git a/libs/libionamemap/src/io/read_xml_io_name_map.h b/libs/libionamemap/src/io/read_xml_io_name_map.h new file mode 100644 index 000000000..5fc441a84 --- /dev/null +++ b/libs/libionamemap/src/io/read_xml_io_name_map.h @@ -0,0 +1,21 @@ +#ifndef READ_XML_IO_NAME_MAP_H +#define READ_XML_IO_NAME_MAP_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "io_name_map.h" +#include "pugixml.hpp" +#include "pugixml_util.hpp" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +namespace openfpga { // Begin namespace openfpga + +int read_xml_io_name_map(const char* fname, IoNameMap& io_name_map); + +} // End of namespace openfpga + +#endif From f3c07d61383aa9787ed0f467ed0a79d39009fcab Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Jun 2023 21:48:52 -0700 Subject: [PATCH 105/391] [lib] finish the io for io naming rules --- libs/libionamemap/src/base/io_name_map.cpp | 15 +++ libs/libionamemap/src/base/io_name_map.h | 4 + .../src/io/write_xml_io_name_map.cpp | 102 ++++++++++++++++++ .../src/io/write_xml_io_name_map.h | 20 ++++ 4 files changed, 141 insertions(+) create mode 100644 libs/libionamemap/src/io/write_xml_io_name_map.cpp create mode 100644 libs/libionamemap/src/io/write_xml_io_name_map.h diff --git a/libs/libionamemap/src/base/io_name_map.cpp b/libs/libionamemap/src/base/io_name_map.cpp index 70a39ecd7..bf093c98a 100644 --- a/libs/libionamemap/src/base/io_name_map.cpp +++ b/libs/libionamemap/src/base/io_name_map.cpp @@ -17,6 +17,17 @@ namespace openfpga { /************************************************** * Public Accessors *************************************************/ +std::vector IoNameMap::fpga_top_ports() const { + std::vector ports; + + for (auto it = top2core_io_name_map_.begin(); + it != top2core_io_name_map_.end(); ++it) { + ports.push_back(it->first); + } + + return ports; +} + BasicPort IoNameMap::fpga_core_port(const BasicPort& fpga_top_port) const { BasicPort core_port; auto result = top2core_io_name_map_.find(fpga_top_port); @@ -35,6 +46,10 @@ BasicPort IoNameMap::fpga_top_port(const BasicPort& fpga_core_port) const { return top_port; } +bool IoNameMap::fpga_top_port_is_dummy(const BasicPort& fpga_top_port) const { + return !fpga_core_port(fpga_top_port).is_valid(); +} + int IoNameMap::set_io_pair(const BasicPort& fpga_top_port, const BasicPort& fpga_core_port) { /* Ensure the two ports are matching in size */ diff --git a/libs/libionamemap/src/base/io_name_map.h b/libs/libionamemap/src/base/io_name_map.h index 698a29b4b..6b2cc3078 100644 --- a/libs/libionamemap/src/base/io_name_map.h +++ b/libs/libionamemap/src/base/io_name_map.h @@ -20,12 +20,16 @@ namespace openfpga { */ class IoNameMap { public: /* Public accessors */ + /** @brief Get all the fpga top ports */ + std::vector fpga_top_ports() const; /** @brief With a given port at fpga_top, find the corresponding I/O at * fpga_core. Return an invalid port if not found */ BasicPort fpga_core_port(const BasicPort& fpga_top_port) const; /** @brief With a given port at fpga_core, find the corresponding I/O at * fpga_top. Return an invalid port if not found */ BasicPort fpga_top_port(const BasicPort& fpga_core_port) const; + /** @brief Identify if the fpga_top port is dummy or not */ + bool fpga_top_port_is_dummy(const BasicPort& fpga_top_port) const; public: /* Public mutators */ /** @brief Create the one-on-one mapping between an port of fpga_top and diff --git a/libs/libionamemap/src/io/write_xml_io_name_map.cpp b/libs/libionamemap/src/io/write_xml_io_name_map.cpp new file mode 100644 index 000000000..951aaabc3 --- /dev/null +++ b/libs/libionamemap/src/io/write_xml_io_name_map.cpp @@ -0,0 +1,102 @@ +/******************************************************************** + * This file includes functions that outputs a clock network object to XML + *format + *******************************************************************/ +/* Headers from system goes first */ +#include +#include + +/* Headers from vtr util library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from openfpga util library */ +#include "openfpga_digest.h" + +/* Headers from arch openfpga library */ +#include "write_xml_utils.h" + +/* Headers from pin constraint library */ +#include "io_name_map_xml_constants.h" +#include "write_xml_io_name_map.h" + +namespace openfpga { // Begin namespace openfpga + +/******************************************************************** + * A writer to output a I/O name mapping to XML format + * + * Return 0 if successful + * Return 1 if there are more serious bugs in the architecture + * Return 2 if fail when creating files + *******************************************************************/ +static int write_xml_io_map_port(std::fstream& fp, const IoNameMap& io_name_map, + const BasicPort& fpga_top_port) { + /* Validate the file stream */ + if (false == openfpga::valid_file_stream(fp)) { + return 2; + } + + openfpga::write_tab_to_file(fp, 1); + fp << "<" << XML_IO_NAME_MAP_NODE_NAME << ""; + write_xml_attribute(fp, XML_IO_NAME_MAP_ATTRIBUTE_TOP_NAME, + generate_xml_port_name(fpga_top_port).c_str()); + + if (io_name_map.fpga_top_port_is_dummy(fpga_top_port)) { + write_xml_attribute(fp, XML_IO_NAME_MAP_ATTRIBUTE_IS_DUMMY, "true"); + } else { + BasicPort fpga_core_port = io_name_map.fpga_core_port(fpga_top_port); + write_xml_attribute(fp, XML_IO_NAME_MAP_ATTRIBUTE_CORE_NAME, + generate_xml_port_name(fpga_core_port).c_str()); + } + fp << ">" + << "\n"; + + return 0; +} + +/******************************************************************** + * A writer to output an object to XML format + * + * Return 0 if successful + * Return 1 if there are more serious bugs in the architecture + * Return 2 if fail when creating files + *******************************************************************/ +int write_xml_io_name_map(const char* fname, const IoNameMap& io_name_map) { + vtr::ScopedStartFinishTimer timer("Write I/O naming rules"); + + /* Create a file handler */ + std::fstream fp; + /* Open the file stream */ + fp.open(std::string(fname), std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + openfpga::check_file_stream(fname, fp); + + /* Write the root node */ + fp << "<" << XML_IO_NAME_MAP_ROOT_NAME; + fp << ">" + << "\n"; + + int err_code = 0; + + /* Write each port */ + for (BasicPort fpga_top_port : io_name_map.fpga_top_ports()) { + /* Write bus */ + err_code = write_xml_io_map_port(fp, io_name_map, fpga_top_port); + if (0 != err_code) { + return err_code; + } + } + + /* Finish writing the root node */ + fp << "" + << "\n"; + + /* Close the file stream */ + fp.close(); + + return err_code; +} + +} // End of namespace openfpga diff --git a/libs/libionamemap/src/io/write_xml_io_name_map.h b/libs/libionamemap/src/io/write_xml_io_name_map.h new file mode 100644 index 000000000..b51064fd9 --- /dev/null +++ b/libs/libionamemap/src/io/write_xml_io_name_map.h @@ -0,0 +1,20 @@ +#ifndef WRITE_XML_IO_NAME_MAP_H +#define WRITE_XML_IO_NAME_MAP_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +#include "io_name_map.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ +namespace openfpga { // Begin namespace openfpga + +int write_xml_io_name_map(const char* fname, const IoNameMap& io_name_map); + +} // End of namespace openfpga + +#endif From 77b082ab552f2a6e51ce35ce4a889a61edccec6a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Jun 2023 22:50:37 -0700 Subject: [PATCH 106/391] [src] debugging --- .../src/io/read_xml_io_name_map.cpp | 6 +-- libs/libionamemap/test/xml_io_io_name_map.cpp | 38 +++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 libs/libionamemap/test/xml_io_io_name_map.cpp diff --git a/libs/libionamemap/src/io/read_xml_io_name_map.cpp b/libs/libionamemap/src/io/read_xml_io_name_map.cpp index 8cbe566dd..b599290f4 100644 --- a/libs/libionamemap/src/io/read_xml_io_name_map.cpp +++ b/libs/libionamemap/src/io/read_xml_io_name_map.cpp @@ -39,8 +39,8 @@ static int read_xml_io_map_port(pugi::xml_node& xml_port, /* For dummy port, create the dummy io */ bool is_dummy = - get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_IS_DUMMY, loc_data) - .as_bool(); + get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_IS_DUMMY, loc_data, pugiutil::ReqOpt::OPTIONAL) + .as_bool(false); if (is_dummy) { return io_name_map.set_dummy_io(top_port); /* Early return */ } @@ -55,7 +55,7 @@ static int read_xml_io_map_port(pugi::xml_node& xml_port, } /******************************************************************** - * Parse XML codes about to an object of ClockNetwork + * Parse XML codes about to an object of ClockNetwork *******************************************************************/ int read_xml_io_name_map(const char* fname, IoNameMap& io_name_map) { vtr::ScopedStartFinishTimer timer("Read I/O naming rules"); diff --git a/libs/libionamemap/test/xml_io_io_name_map.cpp b/libs/libionamemap/test/xml_io_io_name_map.cpp new file mode 100644 index 000000000..9140bdc13 --- /dev/null +++ b/libs/libionamemap/test/xml_io_io_name_map.cpp @@ -0,0 +1,38 @@ +/******************************************************************** + * Unit test functions to validate the correctness of + * 1. parser of data structures + * 2. writer of data structures + *******************************************************************/ +/* Headers from vtrutils */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from readarchopenfpga */ +#include "read_xml_io_name_map.h" +#include "write_xml_io_name_map.h" + +int main(int argc, const char** argv) { + /* Ensure we have only one or two argument */ + VTR_ASSERT((2 == argc) || (3 == argc)); + + int status = 0; + + /* Parse the circuit library from an XML file */ + openfpga::IoNameMap io_name_map; + status = openfpga::read_xml_io_name_map(argv[1], io_name_map); + if (status != 0) { + return status; + } + VTR_LOG("Parsed %lu fpga top ports from XML.\n", + io_name_map.fpga_top_ports().size()); + + /* Output the bus group to an XML file + * This is optional only used when there is a second argument + */ + if (3 <= argc) { + status = openfpga::write_xml_io_name_map(argv[2], io_name_map); + VTR_LOG("Write the I/O name mapping to an XML file: %s.\n", argv[2]); + } + + return status; +} From 227d147dca550278e72bf87629f81a54f4733d9a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Jun 2023 22:51:15 -0700 Subject: [PATCH 107/391] [lib] add an example file --- libs/libionamemap/example/example.xml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 libs/libionamemap/example/example.xml diff --git a/libs/libionamemap/example/example.xml b/libs/libionamemap/example/example.xml new file mode 100644 index 000000000..88b519fa0 --- /dev/null +++ b/libs/libionamemap/example/example.xml @@ -0,0 +1,8 @@ + + + + + + + + From b8d89d2a5c11a69e2a81b04abc5dd1a8e169bc91 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Jun 2023 22:51:38 -0700 Subject: [PATCH 108/391] [lib] code format --- libs/libionamemap/src/io/read_xml_io_name_map.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/libionamemap/src/io/read_xml_io_name_map.cpp b/libs/libionamemap/src/io/read_xml_io_name_map.cpp index b599290f4..c3f01ee42 100644 --- a/libs/libionamemap/src/io/read_xml_io_name_map.cpp +++ b/libs/libionamemap/src/io/read_xml_io_name_map.cpp @@ -38,9 +38,9 @@ static int read_xml_io_map_port(pugi::xml_node& xml_port, BasicPort top_port = openfpga::PortParser(top_name).port(); /* For dummy port, create the dummy io */ - bool is_dummy = - get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_IS_DUMMY, loc_data, pugiutil::ReqOpt::OPTIONAL) - .as_bool(false); + bool is_dummy = get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_IS_DUMMY, + loc_data, pugiutil::ReqOpt::OPTIONAL) + .as_bool(false); if (is_dummy) { return io_name_map.set_dummy_io(top_port); /* Early return */ } From a628a1e7b05560044f88cdf8b31c0c172a7f6768 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 21 Jun 2023 23:02:43 -0700 Subject: [PATCH 109/391] [lib] add missing file --- libs/libionamemap/CMakeLists.txt | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 libs/libionamemap/CMakeLists.txt diff --git a/libs/libionamemap/CMakeLists.txt b/libs/libionamemap/CMakeLists.txt new file mode 100644 index 000000000..e99236d35 --- /dev/null +++ b/libs/libionamemap/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.9) + +project("libionamemap") + +file(GLOB_RECURSE EXEC_SOURCES test/*.cpp) +file(GLOB_RECURSE LIB_SOURCES src/*/*.cpp) +file(GLOB_RECURSE LIB_HEADERS src/*/*.h) +files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS) + +#Remove test executable from library +list(REMOVE_ITEM LIB_SOURCES ${EXEC_SOURCES}) + +#Create the library +add_library(libionamemap STATIC + ${LIB_HEADERS} + ${LIB_SOURCES}) +target_include_directories(libionamemap PUBLIC ${LIB_INCLUDE_DIRS}) +set_target_properties(libionamemap PROPERTIES PREFIX "") #Avoid extra 'lib' prefix + +#Specify link-time dependancies +target_link_libraries(libionamemap + libarchopenfpga + libopenfpgautil + libopenfpgashell + libvtrutil + libpugiutil) + +#Create the test executable +foreach(testsourcefile ${EXEC_SOURCES}) + # Use a simple string replace, to cut off .cpp. + get_filename_component(testname ${testsourcefile} NAME_WE) + add_executable(${testname} ${testsourcefile}) + # Make sure the library is linked to each test executable + target_link_libraries(${testname} libionamemap) +endforeach(testsourcefile ${EXEC_SOURCES}) + +install(TARGETS libionamemap DESTINATION bin) From 05d2caf6b886b4f088731f95727c98bca64f5701 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Jun 2023 06:59:35 +0000 Subject: [PATCH 110/391] Bump yosys from `104edb4` to `8f7a9a0` Bumps [yosys](https://github.com/YosysHQ/yosys) from `104edb4` to `8f7a9a0`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/104edb458784fdbf2bb782f095c2afada9b0299c...8f7a9a0b667031d661cd91a66c5dcab39eb470d6) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 104edb458..8f7a9a0b6 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 104edb458784fdbf2bb782f095c2afada9b0299c +Subproject commit 8f7a9a0b667031d661cd91a66c5dcab39eb470d6 From 615934b2d66e4b18d239b6b6a721b8d506b9bf0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Jun 2023 06:59:40 +0000 Subject: [PATCH 111/391] Bump yosys-plugins from `0ad1af2` to `7303812` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `0ad1af2` to `7303812`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/0ad1af26a29243a9e76379943d735e119dcd0cc6...73038124b0a2943fe9d591c43f46292bcbf82105) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index 0ad1af26a..73038124b 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit 0ad1af26a29243a9e76379943d735e119dcd0cc6 +Subproject commit 73038124b0a2943fe9d591c43f46292bcbf82105 From 75cb3166ef7ff5c8bb6554dcc0c6029529aba80b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 23 Jun 2023 00:02:42 +0000 Subject: [PATCH 112/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 0609725c2..3b082e1ee 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1121 +1.2.1127 From 4d265c396545531bb03a4e597dde3e099f25c89d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 22 Jun 2023 17:44:07 -0700 Subject: [PATCH 113/391] [lib] reworked io name map data structure. Passed I/O test --- libs/libionamemap/src/base/io_name_map.cpp | 182 ++++++++++++++++++--- libs/libionamemap/src/base/io_name_map.h | 21 ++- libs/libopenfpgautil/src/openfpga_port.cpp | 19 +++ libs/libopenfpgautil/src/openfpga_port.h | 4 + 4 files changed, 194 insertions(+), 32 deletions(-) diff --git a/libs/libionamemap/src/base/io_name_map.cpp b/libs/libionamemap/src/base/io_name_map.cpp index bf093c98a..7227d5541 100644 --- a/libs/libionamemap/src/base/io_name_map.cpp +++ b/libs/libionamemap/src/base/io_name_map.cpp @@ -7,6 +7,7 @@ #include #include "command_exit_codes.h" +#include "openfpga_port_parser.h" #include "vtr_assert.h" #include "vtr_log.h" #include "vtr_time.h" @@ -22,7 +23,7 @@ std::vector IoNameMap::fpga_top_ports() const { for (auto it = top2core_io_name_map_.begin(); it != top2core_io_name_map_.end(); ++it) { - ports.push_back(it->first); + ports.push_back(str2port(it->first)); } return ports; @@ -30,18 +31,86 @@ std::vector IoNameMap::fpga_top_ports() const { BasicPort IoNameMap::fpga_core_port(const BasicPort& fpga_top_port) const { BasicPort core_port; - auto result = top2core_io_name_map_.find(fpga_top_port); - if (result != top2core_io_name_map_.end()) { - core_port = result->second; + /* First, find the pin name matching */ + auto result_key = top2core_io_name_keys_.find(fpga_top_port.get_name()); + if (result_key == top2core_io_name_keys_.end()) { + return core_port; /* Not found, return invalid port */ + } + /* Second, find the exact key */ + std::string top_port_key; + for (std::string cand : result_key->second) { + BasicPort cand_port = str2port(cand); + /* if the top port is part of the cand port, e.g., clk[1] vs. clk[0:2], the + * candidate is the key that we want! */ + if (cand_port.contained(fpga_top_port)) { + top_port_key = cand; + break; + } + } + if (top_port_key.empty()) { + return core_port; /* Not found, return invalid port */ + } + auto result = top2core_io_name_map_.find(top_port_key); + if (result != top2core_io_name_map_.end() && result->second.is_valid()) { + BasicPort top_port_pool = str2port(top_port_key); + BasicPort fpga_top_port_lsb(fpga_top_port.get_name(), + fpga_top_port.get_lsb(), + fpga_top_port.get_lsb()); + BasicPort fpga_top_port_msb(fpga_top_port.get_name(), + fpga_top_port.get_msb(), + fpga_top_port.get_msb()); + size_t ipin_anchor_lsb = top_port_pool.find_ipin(fpga_top_port_lsb); + size_t ipin_anchor_msb = top_port_pool.find_ipin(fpga_top_port_msb); + /* Now find the exact pin and spot the core port with pin index */ + if (ipin_anchor_lsb < top_port_pool.get_width() && + ipin_anchor_msb < top_port_pool.get_width()) { + core_port.set_name(result->second.get_name()); + core_port.set_lsb(result->second.pins()[ipin_anchor_lsb]); + core_port.set_msb(result->second.pins()[ipin_anchor_msb]); + } } return core_port; } BasicPort IoNameMap::fpga_top_port(const BasicPort& fpga_core_port) const { BasicPort top_port; - auto result = core2top_io_name_map_.find(fpga_core_port); - if (result != core2top_io_name_map_.end()) { - top_port = result->second; + /* First, find the pin name matching */ + auto result_key = core2top_io_name_keys_.find(fpga_core_port.get_name()); + if (result_key == core2top_io_name_keys_.end()) { + return top_port; /* Not found, return invalid port */ + } + /* Second, find the exact key */ + std::string core_port_key; + for (std::string cand : result_key->second) { + BasicPort cand_port = str2port(cand); + /* if the top port is part of the cand port, e.g., clk[1] vs. clk[0:2], the + * candidate is the key that we want! */ + if (cand_port.contained(fpga_core_port)) { + core_port_key = cand; + break; + } + } + if (core_port_key.empty()) { + return top_port; /* Not found, return invalid port */ + } + auto result = core2top_io_name_map_.find(core_port_key); + if (result != core2top_io_name_map_.end() && result->second.is_valid()) { + BasicPort core_port_pool = str2port(core_port_key); + BasicPort fpga_core_port_lsb(fpga_core_port.get_name(), + fpga_core_port.get_lsb(), + fpga_core_port.get_lsb()); + BasicPort fpga_core_port_msb(fpga_core_port.get_name(), + fpga_core_port.get_msb(), + fpga_core_port.get_msb()); + size_t ipin_anchor_lsb = core_port_pool.find_ipin(fpga_core_port); + size_t ipin_anchor_msb = core_port_pool.find_ipin(fpga_core_port); + /* Now find the exact pin and spot the core port with pin index */ + if (ipin_anchor_lsb < core_port_pool.get_width() && + ipin_anchor_msb < core_port_pool.get_width()) { + top_port.set_name(result->second.get_name()); + top_port.set_lsb(result->second.pins()[ipin_anchor_lsb]); + top_port.set_msb(result->second.pins()[ipin_anchor_msb]); + } } return top_port; } @@ -63,36 +132,95 @@ int IoNameMap::set_io_pair(const BasicPort& fpga_top_port, return CMD_EXEC_FATAL_ERROR; } VTR_ASSERT_SAFE(fpga_top_port.get_width() != fpga_core_port.get_width()); - for (size_t ipin = 0; ipin < fpga_top_port.pins().size(); ++ipin) { - BasicPort top_pin(fpga_top_port.get_name(), fpga_top_port.pins()[ipin], - fpga_top_port.pins()[ipin]); - BasicPort core_pin(fpga_core_port.get_name(), fpga_core_port.pins()[ipin], - fpga_core_port.pins()[ipin]); - top2core_io_name_map_[top_pin] = core_pin; - core2top_io_name_map_[core_pin] = top_pin; + /* Register in the key first, and then add to the exact name mapping */ + { + std::string top_port_str = port2str(fpga_top_port); + auto result_key = top2core_io_name_keys_.find(fpga_top_port.get_name()); + if (result_key == top2core_io_name_keys_.end()) { + /* Add to the key registery */ + top2core_io_name_keys_[fpga_top_port.get_name()].push_back(top_port_str); + top2core_io_name_map_[top_port_str] = fpga_core_port; + } else { + /* Ensure that the key is not duplicated */ + if (std::find(result_key->second.begin(), result_key->second.end(), + top_port_str) == result_key->second.end()) { + top2core_io_name_keys_[fpga_top_port.get_name()].push_back( + top_port_str); + top2core_io_name_map_[top_port_str] = fpga_core_port; + } else { + /* Throw a warning since we have to overwrite */ + VTR_LOG_WARN( + "Overwrite the top-to-core pin mapping: top pin '%s' to core pin " + "'%s' (previously was '%s')!\n", + top_port_str, port2str(fpga_core_port).c_str(), + port2str(top2core_io_name_map_[top_port_str]).c_str()); + top2core_io_name_map_[top_port_str] = fpga_core_port; + } + } + } + /* Now, do similar to the core port */ + { + std::string core_port_str = port2str(fpga_core_port); + auto result_key = core2top_io_name_keys_.find(fpga_core_port.get_name()); + if (result_key == core2top_io_name_keys_.end()) { + /* Add to the key registery */ + core2top_io_name_keys_[fpga_core_port.get_name()].push_back( + core_port_str); + core2top_io_name_map_[core_port_str] = fpga_top_port; + } else { + /* Ensure that the key is not duplicated */ + if (std::find(result_key->second.begin(), result_key->second.end(), + core_port_str) == result_key->second.end()) { + core2top_io_name_keys_[fpga_core_port.get_name()].push_back( + core_port_str); + core2top_io_name_map_[core_port_str] = fpga_top_port; + } else { + /* Throw a warning since we have to overwrite */ + VTR_LOG_WARN( + "Overwrite the core-to-top pin mapping: core pin '%s' to top pin " + "'%s' (previously was '%s')!\n", + core_port_str, port2str(fpga_top_port).c_str(), + port2str(core2top_io_name_map_[core_port_str]).c_str()); + core2top_io_name_map_[core_port_str] = fpga_top_port; + } + } } return CMD_EXEC_SUCCESS; } int IoNameMap::set_dummy_io(const BasicPort& fpga_top_port) { /* Must be a true dummy port, none of its pins have been paired! */ - for (size_t ipin = 0; ipin < fpga_top_port.pins().size(); ++ipin) { - BasicPort top_pin(fpga_top_port.get_name(), fpga_top_port.pins()[ipin], - fpga_top_port.pins()[ipin]); - auto result = top2core_io_name_map_.find(top_pin); - if (result != top2core_io_name_map_.end() && result->second.is_valid()) { + std::string top_port_str = port2str(fpga_top_port); + /* First, find the pin name matching */ + auto result_key = top2core_io_name_keys_.find(fpga_top_port.get_name()); + if (result_key == top2core_io_name_keys_.end()) { + /* Add to the key registery */ + top2core_io_name_keys_[fpga_top_port.get_name()].push_back(top_port_str); + top2core_io_name_map_[top_port_str] = BasicPort(); + } else { + /* Ensure that the key is not duplicated */ + if (std::find(result_key->second.begin(), result_key->second.end(), + top_port_str) == result_key->second.end()) { + top2core_io_name_keys_[fpga_top_port.get_name()].push_back(top_port_str); + top2core_io_name_map_[top_port_str] = BasicPort(); + } else { + /* Throw a error because the dummy pin should NOT be mapped before! */ VTR_LOG_ERROR( - "Pin '%lu' in a dummy port '%s[%lu:%lu]' of fpga_top is already mapped " - "to a valid pin '%s[%lu:%lu]' of fpga_core!\n", - top_pin.get_lsb(), fpga_top_port.get_name().c_str(), - fpga_top_port.get_lsb(), fpga_top_port.get_msb(), - result->second.get_name().c_str(), result->second.get_lsb(), - result->second.get_msb()); - return CMD_EXEC_FATAL_ERROR; + "Dummy port '%s' of fpga_top is already mapped " + "to a valid pin '%s' of fpga_core!\n", + port2str(fpga_top_port).c_str(), + port2str(top2core_io_name_map_[top_port_str]).c_str()); } - top2core_io_name_map_[top_pin] = BasicPort(); } return CMD_EXEC_SUCCESS; } +std::string IoNameMap::port2str(const BasicPort& port) const { + return port.to_verilog_string(); +} + +BasicPort IoNameMap::str2port(const std::string& port_str) const { + return PortParser(port_str).port(); +} + } /* end namespace openfpga */ diff --git a/libs/libionamemap/src/base/io_name_map.h b/libs/libionamemap/src/base/io_name_map.h index 6b2cc3078..af3709acc 100644 --- a/libs/libionamemap/src/base/io_name_map.h +++ b/libs/libionamemap/src/base/io_name_map.h @@ -40,12 +40,23 @@ class IoNameMap { * fpga_core */ int set_dummy_io(const BasicPort& fpga_top_port); + private: /* Internal utility */ + /* Convert a port info to string, which can be used to store keys */ + std::string port2str(const BasicPort& port) const; + /* Convert a string to port, which can be used to echo internal info */ + BasicPort str2port(const std::string& port_str) const; + private: /* Internal Data */ - /* fpga_top -> fpga_core io name mapping, each port is in the size of 1. This - * is designed to fast look-up but at the cost of potential large memory - * footprints. TODO: Optimize if we see such issue */ - std::map top2core_io_name_map_; - std::map core2top_io_name_map_; + /* fpga_top -> fpga_core io_name_keys. Use the port name to find all the port + * details. For instance: prog_clk -> ["prog_clk[0:1]", "prog_clk[2:3]"] The + * keys are then used to spot the fpga core ports in the io_name_map_. For + * instance: "prog_clk[0:1]" -> pclk[0:1] + */ + std::map> top2core_io_name_keys_; + std::map top2core_io_name_map_; + + std::map> core2top_io_name_keys_; + std::map core2top_io_name_map_; }; } /* End namespace openfpga*/ diff --git a/libs/libopenfpgautil/src/openfpga_port.cpp b/libs/libopenfpgautil/src/openfpga_port.cpp index 2ca18f27e..cb42393ca 100644 --- a/libs/libopenfpgautil/src/openfpga_port.cpp +++ b/libs/libopenfpgautil/src/openfpga_port.cpp @@ -111,6 +111,25 @@ std::string BasicPort::to_verilog_string() const { std::to_string(get_msb()) + "]"; } +size_t BasicPort::find_ipin(const BasicPort& ref_port) const { + /* Port name should match first */ + if (!this->mergeable(ref_port)) { + return get_width(); /* Name does not match, no need to find the pin index, + return an invalid range */ + } + /* it should be only a pin (width = 1) */ + if (!ref_port.is_valid() || ref_port.get_width() != 1) { + return get_width(); /* Return an invalid range */ + } + /* Must cache the pin list otherwise the begin() and end() are not constant */ + auto pin_list = pins(); + auto it = std::find(pin_list.begin(), pin_list.end(), ref_port.get_lsb()); + if (it == pin_list.end()) { + return get_width(); /* Out of range, return an invalid range */ + } + return it - pin_list.begin(); +} + /************************************************************************ * Overloaded operators ***********************************************************************/ diff --git a/libs/libopenfpgautil/src/openfpga_port.h b/libs/libopenfpgautil/src/openfpga_port.h index 013a74b7b..be86334b5 100644 --- a/libs/libopenfpgautil/src/openfpga_port.h +++ b/libs/libopenfpgautil/src/openfpga_port.h @@ -37,6 +37,10 @@ class BasicPort { size_t get_origin_port_width() const; std::string to_verilog_string() const; /* Generate verilog-style string, e.g., a[0:1] */ + /** @brief Find the index of the pin in the reference port w.r.t. to this + * port. For example, given a reference pin 'a[1]', this port is 'a[0:2]', the + * pin is the second pin in the port. As a result, the index will be 1. */ + size_t find_ipin(const BasicPort& ref_port) const; public: /* Mutators */ void set(const BasicPort& basic_port); /* copy */ From 7961223eac6a81b959fc041579fe764ab2d8a973 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 22 Jun 2023 22:18:09 -0700 Subject: [PATCH 114/391] [core] enabling io naming rules in fabric builder --- libs/libionamemap/src/base/io_name_map.cpp | 41 +++++- libs/libionamemap/src/base/io_name_map.h | 15 +- .../src/io/io_name_map_xml_constants.h | 3 + .../src/io/read_xml_io_name_map.cpp | 8 ++ openfpga/CMakeLists.txt | 1 + .../src/base/openfpga_build_fabric_template.h | 10 +- openfpga/src/fabric/build_device_module.cpp | 46 ------ openfpga/src/fabric/build_device_module.h | 6 +- .../fabric/build_fpga_core_wrapper_module.cpp | 135 ++++++++++++++++++ .../fabric/build_fpga_core_wrapper_module.h | 26 ++++ 10 files changed, 235 insertions(+), 56 deletions(-) create mode 100644 openfpga/src/fabric/build_fpga_core_wrapper_module.cpp create mode 100644 openfpga/src/fabric/build_fpga_core_wrapper_module.h diff --git a/libs/libionamemap/src/base/io_name_map.cpp b/libs/libionamemap/src/base/io_name_map.cpp index 7227d5541..9f5ec4e95 100644 --- a/libs/libionamemap/src/base/io_name_map.cpp +++ b/libs/libionamemap/src/base/io_name_map.cpp @@ -119,6 +119,21 @@ bool IoNameMap::fpga_top_port_is_dummy(const BasicPort& fpga_top_port) const { return !fpga_core_port(fpga_top_port).is_valid(); } +IoNameMap::e_dummy_port_direction IoNameMap::fpga_top_dummy_port_direction(const BasicPort& fpga_top_port) const { + for (auto& kv : dummy_port_direction_) { + BasicPort cand = str2port(kv.first); + if (cand.contained(fpga_top_port)) { + return kv.second; + } + } + /* Return an invalid port type */ + return IoNameMap::e_dummy_port_direction::NUM_TYPES; +} + +bool IoNameMap::empty() const { + return top2core_io_name_keys_.empty() && top2core_io_name_map_.empty() && core2top_io_name_keys_.empty() && core2top_io_name_map_.empty() && dummy_port_direction_.empty(); +} + int IoNameMap::set_io_pair(const BasicPort& fpga_top_port, const BasicPort& fpga_core_port) { /* Ensure the two ports are matching in size */ @@ -152,7 +167,7 @@ int IoNameMap::set_io_pair(const BasicPort& fpga_top_port, VTR_LOG_WARN( "Overwrite the top-to-core pin mapping: top pin '%s' to core pin " "'%s' (previously was '%s')!\n", - top_port_str, port2str(fpga_core_port).c_str(), + top_port_str.c_str(), port2str(fpga_core_port).c_str(), port2str(top2core_io_name_map_[top_port_str]).c_str()); top2core_io_name_map_[top_port_str] = fpga_core_port; } @@ -179,7 +194,7 @@ int IoNameMap::set_io_pair(const BasicPort& fpga_top_port, VTR_LOG_WARN( "Overwrite the core-to-top pin mapping: core pin '%s' to top pin " "'%s' (previously was '%s')!\n", - core_port_str, port2str(fpga_top_port).c_str(), + core_port_str.c_str(), port2str(fpga_top_port).c_str(), port2str(core2top_io_name_map_[core_port_str]).c_str()); core2top_io_name_map_[core_port_str] = fpga_top_port; } @@ -188,7 +203,7 @@ int IoNameMap::set_io_pair(const BasicPort& fpga_top_port, return CMD_EXEC_SUCCESS; } -int IoNameMap::set_dummy_io(const BasicPort& fpga_top_port) { +int IoNameMap::set_dummy_io(const BasicPort& fpga_top_port, const e_dummy_port_direction& direction) { /* Must be a true dummy port, none of its pins have been paired! */ std::string top_port_str = port2str(fpga_top_port); /* First, find the pin name matching */ @@ -210,8 +225,28 @@ int IoNameMap::set_dummy_io(const BasicPort& fpga_top_port) { "to a valid pin '%s' of fpga_core!\n", port2str(fpga_top_port).c_str(), port2str(top2core_io_name_map_[top_port_str]).c_str()); + return CMD_EXEC_FATAL_ERROR; } } + /* Add the direction list */ + bool dir_defined = false; + for (auto& kv : dummy_port_direction_) { + BasicPort cand = str2port(kv.first); + if (cand.contained(fpga_top_port)) { + if (kv.second != direction) { + /* Throw a error because the dummy pin should NOT be mapped before! */ + VTR_LOG_ERROR( + "Dummy port '%s' of fpga_top is already assigned to a different direction through another dummy port definition '%s'!\n", + port2str(fpga_top_port).c_str(), port2str(cand).c_str()); + return CMD_EXEC_FATAL_ERROR; + } + dir_defined = true; + break; + } + } + if (!dir_defined) { + dummy_port_direction_[top_port_str] = direction; + } return CMD_EXEC_SUCCESS; } diff --git a/libs/libionamemap/src/base/io_name_map.h b/libs/libionamemap/src/base/io_name_map.h index af3709acc..551c32da2 100644 --- a/libs/libionamemap/src/base/io_name_map.h +++ b/libs/libionamemap/src/base/io_name_map.h @@ -19,6 +19,13 @@ namespace openfpga { * - the corresponding port of fpga_top, with a given port of fpga_core */ class IoNameMap { + public: /* Types */ + enum class e_dummy_port_direction { + INPUT, + OUTPUT, + INOUT, + NUM_TYPES + }; public: /* Public accessors */ /** @brief Get all the fpga top ports */ std::vector fpga_top_ports() const; @@ -30,6 +37,10 @@ class IoNameMap { BasicPort fpga_top_port(const BasicPort& fpga_core_port) const; /** @brief Identify if the fpga_top port is dummy or not */ bool fpga_top_port_is_dummy(const BasicPort& fpga_top_port) const; + /** @brief Get the direction of a dummy port */ + e_dummy_port_direction fpga_top_dummy_port_direction(const BasicPort& fpga_top_port) const; + /** @brief Identify if there are any naming rules inside */ + bool empty() const; public: /* Public mutators */ /** @brief Create the one-on-one mapping between an port of fpga_top and @@ -38,7 +49,7 @@ class IoNameMap { const BasicPort& fpga_core_port); /** @brief Add a dummy port at the fpga top, which is not mapped any port at * fpga_core */ - int set_dummy_io(const BasicPort& fpga_top_port); + int set_dummy_io(const BasicPort& fpga_top_port, const e_dummy_port_direction& direction); private: /* Internal utility */ /* Convert a port info to string, which can be used to store keys */ @@ -57,6 +68,8 @@ class IoNameMap { std::map> core2top_io_name_keys_; std::map core2top_io_name_map_; + + std::map dummy_port_direction_; }; } /* End namespace openfpga*/ diff --git a/libs/libionamemap/src/io/io_name_map_xml_constants.h b/libs/libionamemap/src/io/io_name_map_xml_constants.h index 532497935..199be5305 100644 --- a/libs/libionamemap/src/io/io_name_map_xml_constants.h +++ b/libs/libionamemap/src/io/io_name_map_xml_constants.h @@ -8,5 +8,8 @@ constexpr const char* XML_IO_NAME_MAP_NODE_NAME = "port"; constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_TOP_NAME = "top_name"; constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_CORE_NAME = "core_name"; constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_IS_DUMMY = "is_dummy"; +constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_DIRECTION = "direction"; + +constexpr std::array XML_IO_NAME_MAP_DUMMY_PORT_DIRECTION_STRING = {{"input", "output", "inout"}}; //String versions of side orientations #endif diff --git a/libs/libionamemap/src/io/read_xml_io_name_map.cpp b/libs/libionamemap/src/io/read_xml_io_name_map.cpp index c3f01ee42..35837ee29 100644 --- a/libs/libionamemap/src/io/read_xml_io_name_map.cpp +++ b/libs/libionamemap/src/io/read_xml_io_name_map.cpp @@ -42,6 +42,14 @@ static int read_xml_io_map_port(pugi::xml_node& xml_port, loc_data, pugiutil::ReqOpt::OPTIONAL) .as_bool(false); if (is_dummy) { + std::string dir_str = + get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_DIRECTION, loc_data) + .as_string(); + for (auto acceptable_dir_str : XML_IO_NAME_MAP_DUMMY_PORT_DIRECTION_STRING) { + if (dir_str == std::string(acceptable_dir_str)) { + + } + } return io_name_map.set_dummy_io(top_port); /* Early return */ } diff --git a/openfpga/CMakeLists.txt b/openfpga/CMakeLists.txt index 31a4e37b9..7c6efe83a 100644 --- a/openfpga/CMakeLists.txt +++ b/openfpga/CMakeLists.txt @@ -41,6 +41,7 @@ target_link_libraries(libopenfpga libpcf libvtrutil libbusgroup + libionamemap libpugixml libvpr) diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index e025b2a03..df6cb68a5 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -16,6 +16,7 @@ #include "globals.h" #include "openfpga_naming.h" #include "read_xml_fabric_key.h" +#include "read_xml_io_name_map.h" #include "vtr_log.h" #include "vtr_time.h" @@ -249,8 +250,15 @@ int add_fpga_core_to_fabric_template(T& openfpga_ctx, const Command& cmd, core_inst_name = cmd_context.option_value(cmd, opt_inst_name); } + /* Handle I/O naming rules if defined */ + IoNameMap io_name_map; + CommandOptionId opt_io_naming = cmd.option("io_naming"); + if (true == cmd_context.option_enable(cmd, opt_io_naming)) { + read_xml_io_name_map(cmd_context.option_value(cmd, opt_io_naming), io_name_map); + } + return add_fpga_core_to_device_module_graph( - openfpga_ctx.mutable_module_graph(), core_inst_name, frame_view, + openfpga_ctx.mutable_module_graph(), io_name_map, core_inst_name, frame_view, verbose_output); } diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index a3a29ae8f..4a6313870 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -127,50 +127,4 @@ int build_device_module_graph( return status; } -/******************************************************************** - * The main function to be called for adding the fpga_core wrapper to a FPGA - *fabric - * - Rename existing fpga_top to fpga_core - * - Create a wrapper module 'fpga_top' on the fpga_core - *******************************************************************/ -int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, - const std::string& core_inst_name, - const bool& frame_view, - const bool& verbose) { - int status = CMD_EXEC_SUCCESS; - - /* Execute the module graph api */ - std::string top_module_name = generate_fpga_top_module_name(); - ModuleId top_module = module_manager.find_module(top_module_name); - if (!module_manager.valid_module_id(top_module)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Rename existing top module to fpga_core */ - std::string core_module_name = generate_fpga_core_module_name(); - module_manager.set_module_name(top_module, core_module_name); - VTR_LOGV(verbose, "Rename current top-level module '%s' to '%s'\n", - top_module_name.c_str(), core_module_name.c_str()); - - /* Create a wrapper module under the existing fpga_top */ - ModuleId new_top_module = module_manager.create_wrapper_module( - top_module, top_module_name, core_inst_name, !frame_view); - if (!module_manager.valid_module_id(new_top_module)) { - VTR_LOGV_ERROR(verbose, - "Failed to create a wrapper module '%s' on top of '%s'!\n", - top_module_name.c_str(), core_module_name.c_str()); - return CMD_EXEC_FATAL_ERROR; - } - VTR_LOGV(verbose, "Created a wrapper module '%s' on top of '%s'\n", - top_module_name.c_str(), core_module_name.c_str()); - - /* 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); - - /* TODO: Update the fabric global ports */ - - return status; -} - } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_device_module.h b/openfpga/src/fabric/build_device_module.h index 9ab88547b..add422e5d 100644 --- a/openfpga/src/fabric/build_device_module.h +++ b/openfpga/src/fabric/build_device_module.h @@ -5,6 +5,7 @@ * Include header files that are required by function declaration *******************************************************************/ #include "fabric_key.h" +#include "io_name_map.h" #include "openfpga_context.h" #include "vpr_context.h" @@ -23,11 +24,6 @@ int build_device_module_graph( const bool& duplicate_grid_pin, const FabricKey& fabric_key, const bool& generate_random_fabric_key, const bool& verbose); -int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, - const std::string& core_inst_name, - const bool& frame_view, - const bool& verbose); - } /* end namespace openfpga */ #endif diff --git a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp new file mode 100644 index 000000000..b36b690fb --- /dev/null +++ b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp @@ -0,0 +1,135 @@ +/******************************************************************** + * This file includes the main function to build module graphs + * for the FPGA fabric + *******************************************************************/ + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" +#include "openfpga_naming.h" +#include "build_fpga_core_wrapper_module.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Create a custom fpga_top module by applying naming rules + *******************************************************************/ +static +int create_fpga_top_module_using_naming_rules(ModuleManager& module_manager, + const ModuleId& core_module, + const std::string& top_module_name, + const IoNameMap& io_naming, + const std::string& instance_name, + const bool& add_nets, + const bool& verbose) { + /* Create a new module with the given name */ + ModuleId wrapper_module = module_manager.add_module(top_module_name); + if (!wrapper_module) { + return CMD_EXEC_FATAL_ERROR; + } + /* Add the existing module as an instance */ + module_manager.add_child_module(wrapper_module, core_module, false); + module_manager.set_child_instance_name(wrapper_module, core_module, 0, instance_name); + + /* TODO: Add ports from I/O naming rules: + * - Add ports which has been defined in the naming rules + * - Add ports from the core module, which does not appear in the naming rules + */ + for (BasicPort top_port : io_naming.fpga_top_ports()) { + /* For dummy port, just add it. Port type should be defined from io naming rules */ + if (io_naming.fpga_top_port_is_dummy(top_port)) { + ModuleManager::e_module_port_type port_type = ModuleManager::e_module_port_type::MODULE_INOUT_PORT; + if (IoNameMap::e_dummy_port_direction::INPUT == io_naming.fpga_top_dummy_port_direction(top_port)) { + port_type = ModuleManager::e_module_port_type::MODULE_INPUT_PORT; + } else if (IoNameMap::e_dummy_port_direction::OUTPUT == io_naming.fpga_top_dummy_port_direction(top_port)) { + port_type = ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT; + } else if (IoNameMap::e_dummy_port_direction::INOUT == io_naming.fpga_top_dummy_port_direction(top_port)); + port_type = ModuleManager::e_module_port_type::MODULE_INOUT_PORT; + } else { + VTR_LOG_ERROR("fpga_top dummy port '%s' has an invalid direction. Expect [input|output|inout]!\n", top_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + module_manager.add_port(wrapper_module, top_port, port_type); + continue; /* Finish for this port addition */ + } + /* Get the port type which should be same as the fpga_core port */ + BasicPort core_port : io_naming.fpga_core_port(top_port); + if (!core_port.is_valid()) { + VTR_LOG_ERROR("fpga_top port '%s' is not mapped to any fpga_core port!\n", core_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + //module_manager.add_port(wrapper_module, top_port, ); + } + + /* TODO: Add nets */ + if (add_nets) { + + } + + /* TODO: Update the fabric global ports */ + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * The main function to be called for adding the fpga_core wrapper to a FPGA + *fabric + * - Rename existing fpga_top to fpga_core + * - Create a wrapper module 'fpga_top' on the fpga_core + *******************************************************************/ +int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, + const IoNameMap& io_naming, + const std::string& core_inst_name, + const bool& frame_view, + const bool& verbose) { + int status = CMD_EXEC_SUCCESS; + + /* Execute the module graph api */ + std::string top_module_name = generate_fpga_top_module_name(); + ModuleId top_module = module_manager.find_module(top_module_name); + if (!module_manager.valid_module_id(top_module)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Rename existing top module to fpga_core */ + std::string core_module_name = generate_fpga_core_module_name(); + module_manager.set_module_name(top_module, core_module_name); + VTR_LOGV(verbose, "Rename current top-level module '%s' to '%s'\n", + top_module_name.c_str(), core_module_name.c_str()); + + /* Create a wrapper module under the existing fpga_top + * - if there are no io naming rules, just use the default API to create a wrapper + * - if there are io naming rules, use dedicated function to handle + */ + ModuleId new_top_module; + if (io_naming.empty()) { + new_top_module = module_manager.create_wrapper_module( + top_module, top_module_name, core_inst_name, !frame_view); + if (!module_manager.valid_module_id(new_top_module)) { + VTR_LOGV_ERROR(verbose, + "Failed to create a wrapper module '%s' on top of '%s'!\n", + top_module_name.c_str(), core_module_name.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + } else { + status = create_fpga_top_module_using_naming_rules(module_manager, top_module, top_module_name, io_naming, core_inst_name, !frame_view, verbose); + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + } + VTR_LOGV(verbose, "Created a wrapper module '%s' on top of '%s'\n", + top_module_name.c_str(), core_module_name.c_str()); + + /* 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); + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_fpga_core_wrapper_module.h b/openfpga/src/fabric/build_fpga_core_wrapper_module.h new file mode 100644 index 000000000..1ba1fcbfb --- /dev/null +++ b/openfpga/src/fabric/build_fpga_core_wrapper_module.h @@ -0,0 +1,26 @@ +#ifndef BUILD_FPGA_CORE_WRAPPER_MODULE_H +#define BUILD_FPGA_CORE_WRAPPER_MODULE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "module_manager.h" +#include "io_name_map.h" +#include + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, + const IoNameMap& io_naming, + const std::string& core_inst_name, + const bool& frame_view, + const bool& verbose); + +} /* end namespace openfpga */ + +#endif From 0811409c4f76252cd31372ae040fd5c0ca00eca7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 22 Jun 2023 23:20:22 -0700 Subject: [PATCH 115/391] [lib] support dummy port direction in IoNameMap io --- libs/libionamemap/src/base/io_name_map.cpp | 16 ++++++++++++++++ libs/libionamemap/src/base/io_name_map.h | 9 ++++++++- .../libionamemap/src/io/read_xml_io_name_map.cpp | 10 +++++----- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/libs/libionamemap/src/base/io_name_map.cpp b/libs/libionamemap/src/base/io_name_map.cpp index 9f5ec4e95..0ab9069be 100644 --- a/libs/libionamemap/src/base/io_name_map.cpp +++ b/libs/libionamemap/src/base/io_name_map.cpp @@ -258,4 +258,20 @@ BasicPort IoNameMap::str2port(const std::string& port_str) const { return PortParser(port_str).port(); } +IoNameMap::e_dummy_port_direction IoNameMap::str2dummy_port_dir(const std::string& dir_str, const bool& verbose) const { + for (int itype = IoNameMap::e_dummy_port_direction::INPUT; itype != IoNameMap::e_dummy_direction::NUM_TYPES; ++itype) { + if (dir_str == std::string(DUMMY_PORT_DIR_STRING_[itype])) { + return static_case(itype); + } + } + std::string full_types = "["; + for (int itype = IoNameMap::e_dummy_port_direction::INPUT; itype != IoNameMap::e_dummy_direction::NUM_TYPES; ++itype) { + full_types += std::string(DUMMY_PORT_DIR_STRING_[itype]) + std::string("|"); + } + full_types.pop_back(); + full_types += "]"; + VTR_LOGV_ERROR(verbose, "Invalid direction for dummy port! Expect %s\n", full_types.c_str()); + return IoNameMap::e_dummy_port_direction::NUM_TYPES; +} + } /* end namespace openfpga */ diff --git a/libs/libionamemap/src/base/io_name_map.h b/libs/libionamemap/src/base/io_name_map.h index 551c32da2..1db529dec 100644 --- a/libs/libionamemap/src/base/io_name_map.h +++ b/libs/libionamemap/src/base/io_name_map.h @@ -21,7 +21,7 @@ namespace openfpga { class IoNameMap { public: /* Types */ enum class e_dummy_port_direction { - INPUT, + INPUT = 0, OUTPUT, INOUT, NUM_TYPES @@ -51,6 +51,10 @@ class IoNameMap { * fpga_core */ int set_dummy_io(const BasicPort& fpga_top_port, const e_dummy_port_direction& direction); + public: /* Public utility */ + /** @brief Parse the dummy port direction from string to valid type. Parser error can be turned on */ + e_dummy_port_direction str2dummy_port_dir(const std::string& dir_str, const bool& verbose = false) const; + private: /* Internal utility */ /* Convert a port info to string, which can be used to store keys */ std::string port2str(const BasicPort& port) const; @@ -70,6 +74,9 @@ class IoNameMap { std::map core2top_io_name_map_; std::map dummy_port_direction_; + + /* Constants */ + std::array DUMMY_PORT_DIR_STRING_; }; } /* End namespace openfpga*/ diff --git a/libs/libionamemap/src/io/read_xml_io_name_map.cpp b/libs/libionamemap/src/io/read_xml_io_name_map.cpp index 35837ee29..1c9f40437 100644 --- a/libs/libionamemap/src/io/read_xml_io_name_map.cpp +++ b/libs/libionamemap/src/io/read_xml_io_name_map.cpp @@ -45,12 +45,12 @@ static int read_xml_io_map_port(pugi::xml_node& xml_port, std::string dir_str = get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_DIRECTION, loc_data) .as_string(); - for (auto acceptable_dir_str : XML_IO_NAME_MAP_DUMMY_PORT_DIRECTION_STRING) { - if (dir_str == std::string(acceptable_dir_str)) { - - } + IoNameMap::e_dummy_port_direction dummy_port_dir = io_name_map.str2dummy_port_dir(dir_str, true); + if (!io_name_map.valid_dummy_port_direction(dummy_port_dir)) { + VTR_LOG_ERROR("Invalid direction for dummy port '%s'!\n", top_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; } - return io_name_map.set_dummy_io(top_port); /* Early return */ + return io_name_map.set_dummy_io(top_port, dummy_port_dir); /* Early return */ } /* This is not a dummy io, create the io mapping */ From 539e66a1dc63e8721a86f18c11840be8db45cb97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jun 2023 06:59:06 +0000 Subject: [PATCH 116/391] Bump yosys from `8f7a9a0` to `f9257d3` Bumps [yosys](https://github.com/YosysHQ/yosys) from `8f7a9a0` to `f9257d3`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/8f7a9a0b667031d661cd91a66c5dcab39eb470d6...f9257d319271917aac30f7bfb4f5b154ff559b18) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 8f7a9a0b6..f9257d319 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 8f7a9a0b667031d661cd91a66c5dcab39eb470d6 +Subproject commit f9257d319271917aac30f7bfb4f5b154ff559b18 From 8bd9ae02fdd51c978e463120739850f6e8ab2b1f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 23 Jun 2023 11:09:33 -0700 Subject: [PATCH 117/391] [core] io name map now supports dummy port direction --- libs/libionamemap/src/base/io_name_map.cpp | 56 ++++++++++++++----- libs/libionamemap/src/base/io_name_map.h | 32 +++++++---- .../src/io/io_name_map_xml_constants.h | 4 +- .../src/io/read_xml_io_name_map.cpp | 10 +++- .../src/io/write_xml_io_name_map.cpp | 4 ++ .../src/base/openfpga_build_fabric_template.h | 8 ++- .../fabric/build_fpga_core_wrapper_module.cpp | 54 ++++++++++-------- .../fabric/build_fpga_core_wrapper_module.h | 5 +- 8 files changed, 116 insertions(+), 57 deletions(-) diff --git a/libs/libionamemap/src/base/io_name_map.cpp b/libs/libionamemap/src/base/io_name_map.cpp index 0ab9069be..88d153c35 100644 --- a/libs/libionamemap/src/base/io_name_map.cpp +++ b/libs/libionamemap/src/base/io_name_map.cpp @@ -119,7 +119,8 @@ bool IoNameMap::fpga_top_port_is_dummy(const BasicPort& fpga_top_port) const { return !fpga_core_port(fpga_top_port).is_valid(); } -IoNameMap::e_dummy_port_direction IoNameMap::fpga_top_dummy_port_direction(const BasicPort& fpga_top_port) const { +IoNameMap::e_dummy_port_direction IoNameMap::fpga_top_dummy_port_direction( + const BasicPort& fpga_top_port) const { for (auto& kv : dummy_port_direction_) { BasicPort cand = str2port(kv.first); if (cand.contained(fpga_top_port)) { @@ -131,7 +132,9 @@ IoNameMap::e_dummy_port_direction IoNameMap::fpga_top_dummy_port_direction(const } bool IoNameMap::empty() const { - return top2core_io_name_keys_.empty() && top2core_io_name_map_.empty() && core2top_io_name_keys_.empty() && core2top_io_name_map_.empty() && dummy_port_direction_.empty(); + return top2core_io_name_keys_.empty() && top2core_io_name_map_.empty() && + core2top_io_name_keys_.empty() && core2top_io_name_map_.empty() && + dummy_port_direction_.empty(); } int IoNameMap::set_io_pair(const BasicPort& fpga_top_port, @@ -203,7 +206,8 @@ int IoNameMap::set_io_pair(const BasicPort& fpga_top_port, return CMD_EXEC_SUCCESS; } -int IoNameMap::set_dummy_io(const BasicPort& fpga_top_port, const e_dummy_port_direction& direction) { +int IoNameMap::set_dummy_io(const BasicPort& fpga_top_port, + const e_dummy_port_direction& direction) { /* Must be a true dummy port, none of its pins have been paired! */ std::string top_port_str = port2str(fpga_top_port); /* First, find the pin name matching */ @@ -236,7 +240,8 @@ int IoNameMap::set_dummy_io(const BasicPort& fpga_top_port, const e_dummy_port_d if (kv.second != direction) { /* Throw a error because the dummy pin should NOT be mapped before! */ VTR_LOG_ERROR( - "Dummy port '%s' of fpga_top is already assigned to a different direction through another dummy port definition '%s'!\n", + "Dummy port '%s' of fpga_top is already assigned to a different " + "direction through another dummy port definition '%s'!\n", port2str(fpga_top_port).c_str(), port2str(cand).c_str()); return CMD_EXEC_FATAL_ERROR; } @@ -246,7 +251,7 @@ int IoNameMap::set_dummy_io(const BasicPort& fpga_top_port, const e_dummy_port_d } if (!dir_defined) { dummy_port_direction_[top_port_str] = direction; - } + } return CMD_EXEC_SUCCESS; } @@ -258,20 +263,43 @@ BasicPort IoNameMap::str2port(const std::string& port_str) const { return PortParser(port_str).port(); } -IoNameMap::e_dummy_port_direction IoNameMap::str2dummy_port_dir(const std::string& dir_str, const bool& verbose) const { - for (int itype = IoNameMap::e_dummy_port_direction::INPUT; itype != IoNameMap::e_dummy_direction::NUM_TYPES; ++itype) { - if (dir_str == std::string(DUMMY_PORT_DIR_STRING_[itype])) { - return static_case(itype); - } - } +std::string IoNameMap::dummy_port_dir_all2str() const { std::string full_types = "["; - for (int itype = IoNameMap::e_dummy_port_direction::INPUT; itype != IoNameMap::e_dummy_direction::NUM_TYPES; ++itype) { + for (int itype = size_t(IoNameMap::e_dummy_port_direction::INPUT); + itype != size_t(IoNameMap::e_dummy_port_direction::NUM_TYPES); ++itype) { full_types += std::string(DUMMY_PORT_DIR_STRING_[itype]) + std::string("|"); - } + } full_types.pop_back(); full_types += "]"; - VTR_LOGV_ERROR(verbose, "Invalid direction for dummy port! Expect %s\n", full_types.c_str()); + return full_types; +} + +IoNameMap::e_dummy_port_direction IoNameMap::str2dummy_port_dir( + const std::string& dir_str, const bool& verbose) const { + for (int itype = size_t(IoNameMap::e_dummy_port_direction::INPUT); + itype != size_t(IoNameMap::e_dummy_port_direction::NUM_TYPES); ++itype) { + if (dir_str == std::string(DUMMY_PORT_DIR_STRING_[itype])) { + return static_cast(itype); + } + } + VTR_LOGV_ERROR(verbose, "Invalid direction for dummy port! Expect %s\n", + dummy_port_dir_all2str().c_str()); return IoNameMap::e_dummy_port_direction::NUM_TYPES; } +std::string IoNameMap::dummy_port_dir2str(const e_dummy_port_direction& dir, + const bool& verbose) const { + if (!valid_dummy_port_direction(dir)) { + VTR_LOGV_ERROR(verbose, "Invalid direction for dummy port! Expect %s\n", + dummy_port_dir_all2str().c_str()); + return std::string(); + } + return std::string(DUMMY_PORT_DIR_STRING_[size_t(dir)]); +} + +bool IoNameMap::valid_dummy_port_direction( + const IoNameMap::e_dummy_port_direction& direction) const { + return direction != IoNameMap::e_dummy_port_direction::NUM_TYPES; +} + } /* end namespace openfpga */ diff --git a/libs/libionamemap/src/base/io_name_map.h b/libs/libionamemap/src/base/io_name_map.h index 1db529dec..fb24772e5 100644 --- a/libs/libionamemap/src/base/io_name_map.h +++ b/libs/libionamemap/src/base/io_name_map.h @@ -20,12 +20,8 @@ namespace openfpga { */ class IoNameMap { public: /* Types */ - enum class e_dummy_port_direction { - INPUT = 0, - OUTPUT, - INOUT, - NUM_TYPES - }; + enum class e_dummy_port_direction { INPUT = 0, OUTPUT, INOUT, NUM_TYPES }; + public: /* Public accessors */ /** @brief Get all the fpga top ports */ std::vector fpga_top_ports() const; @@ -38,7 +34,8 @@ class IoNameMap { /** @brief Identify if the fpga_top port is dummy or not */ bool fpga_top_port_is_dummy(const BasicPort& fpga_top_port) const; /** @brief Get the direction of a dummy port */ - e_dummy_port_direction fpga_top_dummy_port_direction(const BasicPort& fpga_top_port) const; + e_dummy_port_direction fpga_top_dummy_port_direction( + const BasicPort& fpga_top_port) const; /** @brief Identify if there are any naming rules inside */ bool empty() const; @@ -49,17 +46,29 @@ class IoNameMap { const BasicPort& fpga_core_port); /** @brief Add a dummy port at the fpga top, which is not mapped any port at * fpga_core */ - int set_dummy_io(const BasicPort& fpga_top_port, const e_dummy_port_direction& direction); + int set_dummy_io(const BasicPort& fpga_top_port, + const e_dummy_port_direction& direction); public: /* Public utility */ - /** @brief Parse the dummy port direction from string to valid type. Parser error can be turned on */ - e_dummy_port_direction str2dummy_port_dir(const std::string& dir_str, const bool& verbose = false) const; + /** @brief Parse the dummy port direction from string to valid type. Parser + * error can be turned on */ + e_dummy_port_direction str2dummy_port_dir(const std::string& dir_str, + const bool& verbose = false) const; + /** @brief Output the string representing dummy port direction */ + std::string dummy_port_dir2str(const e_dummy_port_direction& dir, + const bool& verbose = false) const; + /** @brief Validate the dummy port direction */ + bool valid_dummy_port_direction( + const e_dummy_port_direction& direction) const; private: /* Internal utility */ /* Convert a port info to string, which can be used to store keys */ std::string port2str(const BasicPort& port) const; /* Convert a string to port, which can be used to echo internal info */ BasicPort str2port(const std::string& port_str) const; + /* Generate a string include all the valid directions of the dummy port. + * Useful for printing debugging messages */ + std::string dummy_port_dir_all2str() const; private: /* Internal Data */ /* fpga_top -> fpga_core io_name_keys. Use the port name to find all the port @@ -76,7 +85,8 @@ class IoNameMap { std::map dummy_port_direction_; /* Constants */ - std::array DUMMY_PORT_DIR_STRING_; + std::array + DUMMY_PORT_DIR_STRING_; }; } /* End namespace openfpga*/ diff --git a/libs/libionamemap/src/io/io_name_map_xml_constants.h b/libs/libionamemap/src/io/io_name_map_xml_constants.h index 199be5305..f7d7fe74e 100644 --- a/libs/libionamemap/src/io/io_name_map_xml_constants.h +++ b/libs/libionamemap/src/io/io_name_map_xml_constants.h @@ -10,6 +10,8 @@ constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_CORE_NAME = "core_name"; constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_IS_DUMMY = "is_dummy"; constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_DIRECTION = "direction"; -constexpr std::array XML_IO_NAME_MAP_DUMMY_PORT_DIRECTION_STRING = {{"input", "output", "inout"}}; //String versions of side orientations +constexpr std::array + XML_IO_NAME_MAP_DUMMY_PORT_DIRECTION_STRING = { + {"input", "output", "inout"}}; // String versions of side orientations #endif diff --git a/libs/libionamemap/src/io/read_xml_io_name_map.cpp b/libs/libionamemap/src/io/read_xml_io_name_map.cpp index 1c9f40437..4fc9f231a 100644 --- a/libs/libionamemap/src/io/read_xml_io_name_map.cpp +++ b/libs/libionamemap/src/io/read_xml_io_name_map.cpp @@ -11,6 +11,7 @@ /* Headers from vtr util library */ #include "vtr_assert.h" +#include "vtr_log.h" #include "vtr_time.h" /* Headers from libopenfpga util library */ @@ -45,12 +46,15 @@ static int read_xml_io_map_port(pugi::xml_node& xml_port, std::string dir_str = get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_DIRECTION, loc_data) .as_string(); - IoNameMap::e_dummy_port_direction dummy_port_dir = io_name_map.str2dummy_port_dir(dir_str, true); + IoNameMap::e_dummy_port_direction dummy_port_dir = + io_name_map.str2dummy_port_dir(dir_str, true); if (!io_name_map.valid_dummy_port_direction(dummy_port_dir)) { - VTR_LOG_ERROR("Invalid direction for dummy port '%s'!\n", top_port.to_verilog_string().c_str()); + VTR_LOG_ERROR("Invalid direction for dummy port '%s'!\n", + top_port.to_verilog_string().c_str()); return CMD_EXEC_FATAL_ERROR; } - return io_name_map.set_dummy_io(top_port, dummy_port_dir); /* Early return */ + return io_name_map.set_dummy_io(top_port, + dummy_port_dir); /* Early return */ } /* This is not a dummy io, create the io mapping */ diff --git a/libs/libionamemap/src/io/write_xml_io_name_map.cpp b/libs/libionamemap/src/io/write_xml_io_name_map.cpp index 951aaabc3..2b52f7a23 100644 --- a/libs/libionamemap/src/io/write_xml_io_name_map.cpp +++ b/libs/libionamemap/src/io/write_xml_io_name_map.cpp @@ -44,6 +44,10 @@ static int write_xml_io_map_port(std::fstream& fp, const IoNameMap& io_name_map, if (io_name_map.fpga_top_port_is_dummy(fpga_top_port)) { write_xml_attribute(fp, XML_IO_NAME_MAP_ATTRIBUTE_IS_DUMMY, "true"); + IoNameMap::e_dummy_port_direction dir = + io_name_map.fpga_top_dummy_port_direction(fpga_top_port); + write_xml_attribute(fp, XML_IO_NAME_MAP_ATTRIBUTE_DIRECTION, + io_name_map.dummy_port_dir2str(dir, true).c_str()); } else { BasicPort fpga_core_port = io_name_map.fpga_core_port(fpga_top_port); write_xml_attribute(fp, XML_IO_NAME_MAP_ATTRIBUTE_CORE_NAME, diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index df6cb68a5..51ee39b07 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -6,6 +6,7 @@ #include "build_device_module.h" #include "build_fabric_global_port_info.h" #include "build_fabric_io_location_map.h" +#include "build_fpga_core_wrapper_module.h" #include "command.h" #include "command_context.h" #include "command_exit_codes.h" @@ -254,12 +255,13 @@ int add_fpga_core_to_fabric_template(T& openfpga_ctx, const Command& cmd, IoNameMap io_name_map; CommandOptionId opt_io_naming = cmd.option("io_naming"); if (true == cmd_context.option_enable(cmd, opt_io_naming)) { - read_xml_io_name_map(cmd_context.option_value(cmd, opt_io_naming), io_name_map); + read_xml_io_name_map(cmd_context.option_value(cmd, opt_io_naming).c_str(), + io_name_map); } return add_fpga_core_to_device_module_graph( - openfpga_ctx.mutable_module_graph(), io_name_map, core_inst_name, frame_view, - verbose_output); + openfpga_ctx.mutable_module_graph(), io_name_map, core_inst_name, + frame_view, verbose_output); } } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp index b36b690fb..7aa741dde 100644 --- a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp +++ b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp @@ -9,9 +9,9 @@ #include "vtr_time.h" /* Headers from openfpgashell library */ +#include "build_fpga_core_wrapper_module.h" #include "command_exit_codes.h" #include "openfpga_naming.h" -#include "build_fpga_core_wrapper_module.h" /* begin namespace openfpga */ namespace openfpga { @@ -19,14 +19,10 @@ namespace openfpga { /******************************************************************** * Create a custom fpga_top module by applying naming rules *******************************************************************/ -static -int create_fpga_top_module_using_naming_rules(ModuleManager& module_manager, - const ModuleId& core_module, - const std::string& top_module_name, - const IoNameMap& io_naming, - const std::string& instance_name, - const bool& add_nets, - const bool& verbose) { +static int create_fpga_top_module_using_naming_rules( + ModuleManager& module_manager, const ModuleId& core_module, + const std::string& top_module_name, const IoNameMap& io_naming, + const std::string& instance_name, const bool& add_nets, const bool& verbose) { /* Create a new module with the given name */ ModuleId wrapper_module = module_manager.add_module(top_module_name); if (!wrapper_module) { @@ -34,41 +30,50 @@ int create_fpga_top_module_using_naming_rules(ModuleManager& module_manager, } /* Add the existing module as an instance */ module_manager.add_child_module(wrapper_module, core_module, false); - module_manager.set_child_instance_name(wrapper_module, core_module, 0, instance_name); + module_manager.set_child_instance_name(wrapper_module, core_module, 0, + instance_name); /* TODO: Add ports from I/O naming rules: * - Add ports which has been defined in the naming rules * - Add ports from the core module, which does not appear in the naming rules */ for (BasicPort top_port : io_naming.fpga_top_ports()) { - /* For dummy port, just add it. Port type should be defined from io naming rules */ + /* For dummy port, just add it. Port type should be defined from io naming + * rules */ if (io_naming.fpga_top_port_is_dummy(top_port)) { - ModuleManager::e_module_port_type port_type = ModuleManager::e_module_port_type::MODULE_INOUT_PORT; - if (IoNameMap::e_dummy_port_direction::INPUT == io_naming.fpga_top_dummy_port_direction(top_port)) { + ModuleManager::e_module_port_type port_type = + ModuleManager::e_module_port_type::MODULE_INOUT_PORT; + if (IoNameMap::e_dummy_port_direction::INPUT == + io_naming.fpga_top_dummy_port_direction(top_port)) { port_type = ModuleManager::e_module_port_type::MODULE_INPUT_PORT; - } else if (IoNameMap::e_dummy_port_direction::OUTPUT == io_naming.fpga_top_dummy_port_direction(top_port)) { + } else if (IoNameMap::e_dummy_port_direction::OUTPUT == + io_naming.fpga_top_dummy_port_direction(top_port)) { port_type = ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT; - } else if (IoNameMap::e_dummy_port_direction::INOUT == io_naming.fpga_top_dummy_port_direction(top_port)); + } else if (IoNameMap::e_dummy_port_direction::INOUT == + io_naming.fpga_top_dummy_port_direction(top_port)) { port_type = ModuleManager::e_module_port_type::MODULE_INOUT_PORT; } else { - VTR_LOG_ERROR("fpga_top dummy port '%s' has an invalid direction. Expect [input|output|inout]!\n", top_port.to_verilog_string().c_str()); + VTR_LOG_ERROR( + "fpga_top dummy port '%s' has an invalid direction. Expect " + "[input|output|inout]!\n", + top_port.to_verilog_string().c_str()); return CMD_EXEC_FATAL_ERROR; - } + } module_manager.add_port(wrapper_module, top_port, port_type); continue; /* Finish for this port addition */ } /* Get the port type which should be same as the fpga_core port */ - BasicPort core_port : io_naming.fpga_core_port(top_port); + BasicPort core_port = io_naming.fpga_core_port(top_port); if (!core_port.is_valid()) { - VTR_LOG_ERROR("fpga_top port '%s' is not mapped to any fpga_core port!\n", core_port.to_verilog_string().c_str()); + VTR_LOG_ERROR("fpga_top port '%s' is not mapped to any fpga_core port!\n", + core_port.to_verilog_string().c_str()); return CMD_EXEC_FATAL_ERROR; } - //module_manager.add_port(wrapper_module, top_port, ); + // module_manager.add_port(wrapper_module, top_port, ); } /* TODO: Add nets */ if (add_nets) { - } /* TODO: Update the fabric global ports */ @@ -103,7 +108,8 @@ int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, top_module_name.c_str(), core_module_name.c_str()); /* Create a wrapper module under the existing fpga_top - * - if there are no io naming rules, just use the default API to create a wrapper + * - if there are no io naming rules, just use the default API to create a + * wrapper * - if there are io naming rules, use dedicated function to handle */ ModuleId new_top_module; @@ -117,7 +123,9 @@ int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, return CMD_EXEC_FATAL_ERROR; } } else { - status = create_fpga_top_module_using_naming_rules(module_manager, top_module, top_module_name, io_naming, core_inst_name, !frame_view, verbose); + status = create_fpga_top_module_using_naming_rules( + module_manager, top_module, top_module_name, io_naming, core_inst_name, + !frame_view, verbose); if (CMD_EXEC_SUCCESS != status) { return CMD_EXEC_FATAL_ERROR; } diff --git a/openfpga/src/fabric/build_fpga_core_wrapper_module.h b/openfpga/src/fabric/build_fpga_core_wrapper_module.h index 1ba1fcbfb..3520f6d8b 100644 --- a/openfpga/src/fabric/build_fpga_core_wrapper_module.h +++ b/openfpga/src/fabric/build_fpga_core_wrapper_module.h @@ -4,10 +4,11 @@ /******************************************************************** * Include header files that are required by function declaration *******************************************************************/ -#include "module_manager.h" -#include "io_name_map.h" #include +#include "io_name_map.h" +#include "module_manager.h" + /******************************************************************** * Function declaration *******************************************************************/ From d9f271eaed93612216e552da26efb1a45f3ca4a3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 23 Jun 2023 11:18:36 -0700 Subject: [PATCH 118/391] [lib] fixed a bug where constant string is not initialized --- libs/libionamemap/example/example.xml | 2 +- libs/libionamemap/src/base/io_name_map.cpp | 4 ++++ libs/libionamemap/src/base/io_name_map.h | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libs/libionamemap/example/example.xml b/libs/libionamemap/example/example.xml index 88b519fa0..e00081951 100644 --- a/libs/libionamemap/example/example.xml +++ b/libs/libionamemap/example/example.xml @@ -4,5 +4,5 @@ - + diff --git a/libs/libionamemap/src/base/io_name_map.cpp b/libs/libionamemap/src/base/io_name_map.cpp index 88d153c35..10e4ee847 100644 --- a/libs/libionamemap/src/base/io_name_map.cpp +++ b/libs/libionamemap/src/base/io_name_map.cpp @@ -15,6 +15,10 @@ /* begin namespace openfpga */ namespace openfpga { +IoNameMap::IoNameMap() { + DUMMY_PORT_DIR_STRING_ = {"input", "output", "inout"}; +} + /************************************************** * Public Accessors *************************************************/ diff --git a/libs/libionamemap/src/base/io_name_map.h b/libs/libionamemap/src/base/io_name_map.h index fb24772e5..d73d3ca98 100644 --- a/libs/libionamemap/src/base/io_name_map.h +++ b/libs/libionamemap/src/base/io_name_map.h @@ -22,6 +22,9 @@ class IoNameMap { public: /* Types */ enum class e_dummy_port_direction { INPUT = 0, OUTPUT, INOUT, NUM_TYPES }; + public: /* Constructors */ + IoNameMap(); + public: /* Public accessors */ /** @brief Get all the fpga top ports */ std::vector fpga_top_ports() const; From 2484150ab6e1675a0112ecd5cdc4904d3a95ad57 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 23 Jun 2023 12:21:47 -0700 Subject: [PATCH 119/391] [core] working on port addition to top module --- .../fabric/build_fpga_core_wrapper_module.cpp | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp index 7aa741dde..13c2bc652 100644 --- a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp +++ b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp @@ -33,7 +33,7 @@ static int create_fpga_top_module_using_naming_rules( module_manager.set_child_instance_name(wrapper_module, core_module, 0, instance_name); - /* TODO: Add ports from I/O naming rules: + /* Add ports from I/O naming rules: * - Add ports which has been defined in the naming rules * - Add ports from the core module, which does not appear in the naming rules */ @@ -60,16 +60,52 @@ static int create_fpga_top_module_using_naming_rules( return CMD_EXEC_FATAL_ERROR; } module_manager.add_port(wrapper_module, top_port, port_type); + VTR_LOGV(verbose, + "Add dummy port '%s' to fpga_top by following naming rules\n", + top_port.to_verilog_string().c_str()); continue; /* Finish for this port addition */ } /* Get the port type which should be same as the fpga_core port */ BasicPort core_port = io_naming.fpga_core_port(top_port); if (!core_port.is_valid()) { VTR_LOG_ERROR("fpga_top port '%s' is not mapped to any fpga_core port!\n", - core_port.to_verilog_string().c_str()); + top_port.to_verilog_string().c_str()); return CMD_EXEC_FATAL_ERROR; } - // module_manager.add_port(wrapper_module, top_port, ); + ModulePortId core_port_id = + module_manager.find_module_port(core_module, core_port.get_name()); + if (!module_manager.valid_module_port_id(core_module, core_port_id)) { + VTR_LOG_ERROR( + "fpga_top port '%s' is mapped to an invalid fpga_core port '%s'!\n", + top_port.to_verilog_string().c_str(), + core_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + ModuleManager::e_module_port_type top_port_type = + module_manager.port_type(core_module, core_port_id); + module_manager.add_port(wrapper_module, top_port, top_port_type); + VTR_LOGV(verbose, + "Add port '%s' to fpga_top (correspond to '%s' of fpga_core) by " + "following naming rules\n", + top_port.to_verilog_string().c_str(), + core_port.to_verilog_string().c_str()); + } + /* Now walk through the ports of fpga_core, if port which is not mapped to + * fpga_top should be added */ + for (ModulePortId core_port_id : module_manager.module_ports(core_module)) { + BasicPort core_port = module_manager.module_port(core_module, core_port_id); + BasicPort top_port = io_naming.fpga_top_port(core_port); + if (top_port.is_valid()) { + continue; /* Port has been added in the previous loop, skip now */ + } + /* Add the port now */ + ModuleManager::e_module_port_type top_port_type = + module_manager.port_type(core_module, core_port_id); + module_manager.add_port(wrapper_module, core_port, top_port_type); + VTR_LOGV(verbose, + "Add port '%s' to fpga_top in the same name as the port of " + "fpga_core, since naming rules do not specify\n", + top_port.to_verilog_string().c_str()); } /* TODO: Add nets */ From b30148f8fb4961dc1e4b775badb8e8b34b701c0b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 23 Jun 2023 12:37:46 -0700 Subject: [PATCH 120/391] [core] apply more sanity checks on top module port --- libs/libionamemap/src/base/io_name_map.cpp | 6 ++ libs/libionamemap/src/base/io_name_map.h | 5 ++ .../fabric/build_fpga_core_wrapper_module.cpp | 61 +++++++++++++------ 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/libs/libionamemap/src/base/io_name_map.cpp b/libs/libionamemap/src/base/io_name_map.cpp index 10e4ee847..8030630b0 100644 --- a/libs/libionamemap/src/base/io_name_map.cpp +++ b/libs/libionamemap/src/base/io_name_map.cpp @@ -119,6 +119,12 @@ BasicPort IoNameMap::fpga_top_port(const BasicPort& fpga_core_port) const { return top_port; } +bool IoNameMap::mapped_fpga_core_port(const BasicPort& fpga_core_port) const { + /* First, find the pin name matching */ + auto result_key = core2top_io_name_keys_.find(fpga_core_port.get_name()); + return result_key != core2top_io_name_keys_.end(); +} + bool IoNameMap::fpga_top_port_is_dummy(const BasicPort& fpga_top_port) const { return !fpga_core_port(fpga_top_port).is_valid(); } diff --git a/libs/libionamemap/src/base/io_name_map.h b/libs/libionamemap/src/base/io_name_map.h index d73d3ca98..eb4190308 100644 --- a/libs/libionamemap/src/base/io_name_map.h +++ b/libs/libionamemap/src/base/io_name_map.h @@ -39,6 +39,11 @@ class IoNameMap { /** @brief Get the direction of a dummy port */ e_dummy_port_direction fpga_top_dummy_port_direction( const BasicPort& fpga_top_port) const; + /** @brief Check if a core port, by considering its port name only, has been + * mapped to fpga top. For example, there is a core port 'a[0:3]', while only + * 'a[0]' is mapped to the fpga top. We can use the port name find it quickly + */ + bool mapped_fpga_core_port(const BasicPort& fpga_core_port) const; /** @brief Identify if there are any naming rules inside */ bool empty() const; diff --git a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp index 13c2bc652..0df7ed36b 100644 --- a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp +++ b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp @@ -17,26 +17,14 @@ namespace openfpga { /******************************************************************** - * Create a custom fpga_top module by applying naming rules + * Add ports to top module based on I/O naming rules: + * - Add ports which has been defined in the naming rules + * - Add ports from the core module, which does not appear in the naming rules *******************************************************************/ -static int create_fpga_top_module_using_naming_rules( - ModuleManager& module_manager, const ModuleId& core_module, - const std::string& top_module_name, const IoNameMap& io_naming, - const std::string& instance_name, const bool& add_nets, const bool& verbose) { - /* Create a new module with the given name */ - ModuleId wrapper_module = module_manager.add_module(top_module_name); - if (!wrapper_module) { - return CMD_EXEC_FATAL_ERROR; - } - /* Add the existing module as an instance */ - module_manager.add_child_module(wrapper_module, core_module, false); - module_manager.set_child_instance_name(wrapper_module, core_module, 0, - instance_name); - - /* Add ports from I/O naming rules: - * - Add ports which has been defined in the naming rules - * - Add ports from the core module, which does not appear in the naming rules - */ +static int create_fpga_top_module_ports_using_naming_rules( + ModuleManager& module_manager, const ModuleId& wrapper_module, + const ModuleId& core_module, const IoNameMap& io_naming, + const bool& verbose) { for (BasicPort top_port : io_naming.fpga_top_ports()) { /* For dummy port, just add it. Port type should be defined from io naming * rules */ @@ -98,6 +86,15 @@ static int create_fpga_top_module_using_naming_rules( if (top_port.is_valid()) { continue; /* Port has been added in the previous loop, skip now */ } + /* Throw fatal error if part of the core port is mapped while other part is + * not mapped. This is not allowed! */ + if (io_naming.mapped_fpga_core_port(core_port)) { + VTR_LOG_ERROR( + "fpga_core port '%s' is partially mapped to fpga_top, which is not " + "allowed. Please cover the full-sized port in naming rules!\n", + core_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; + } /* Add the port now */ ModuleManager::e_module_port_type top_port_type = module_manager.port_type(core_module, core_port_id); @@ -107,6 +104,32 @@ static int create_fpga_top_module_using_naming_rules( "fpga_core, since naming rules do not specify\n", top_port.to_verilog_string().c_str()); } + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Create a custom fpga_top module by applying naming rules + *******************************************************************/ +static int create_fpga_top_module_using_naming_rules( + ModuleManager& module_manager, const ModuleId& core_module, + const std::string& top_module_name, const IoNameMap& io_naming, + const std::string& instance_name, const bool& add_nets, const bool& verbose) { + /* Create a new module with the given name */ + ModuleId wrapper_module = module_manager.add_module(top_module_name); + if (!wrapper_module) { + return CMD_EXEC_FATAL_ERROR; + } + /* Add the existing module as an instance */ + module_manager.add_child_module(wrapper_module, core_module, false); + module_manager.set_child_instance_name(wrapper_module, core_module, 0, + instance_name); + + /* Add ports */ + if (CMD_EXEC_SUCCESS != + create_fpga_top_module_ports_using_naming_rules( + module_manager, wrapper_module, core_module, io_naming, verbose)) { + return CMD_EXEC_FATAL_ERROR; + } /* TODO: Add nets */ if (add_nets) { From 463332c77aef55d08d1bcf350f36325c14853399 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 23 Jun 2023 13:21:25 -0700 Subject: [PATCH 121/391] [core] code complete for adding nets between top and core module --- .../fabric/build_fpga_core_wrapper_module.cpp | 160 +++++++++++++++++- 1 file changed, 155 insertions(+), 5 deletions(-) diff --git a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp index 0df7ed36b..46cf37f12 100644 --- a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp +++ b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp @@ -99,10 +99,155 @@ static int create_fpga_top_module_ports_using_naming_rules( ModuleManager::e_module_port_type top_port_type = module_manager.port_type(core_module, core_port_id); module_manager.add_port(wrapper_module, core_port, top_port_type); - VTR_LOGV(verbose, - "Add port '%s' to fpga_top in the same name as the port of " - "fpga_core, since naming rules do not specify\n", - top_port.to_verilog_string().c_str()); + VTR_LOG( + "Add port '%s' to fpga_top in the same name as the port of " + "fpga_core, since naming rules do not specify\n", + top_port.to_verilog_string().c_str()); + } + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Add nets between top module and core module based on I/O naming rules: + * - Dummy ports do not need any nets + * - For ports which are defined in the naming rules, create dedicated + *connections + * - For ports of the core module, which does not appear in the naming rules, + *create direct connections + *******************************************************************/ +static int create_fpga_top_module_nets_using_naming_rules( + ModuleManager& module_manager, const ModuleId& wrapper_module, + const ModuleId& core_module, const IoNameMap& io_naming, + const bool& verbose) { + for (BasicPort top_port : io_naming.fpga_top_ports()) { + if (io_naming.fpga_top_port_is_dummy(top_port)) { + VTR_LOGV("Skip nets for dummy port '%s' at top module\n", + top_port.to_verilog_string().c_str()); + continue; + } + /* Collect port-level information */ + ModulePortId top_port_id = + module_manager.find_module_port(wrapper_module, top_port.get_name()); + if (!module_manager.valid_module_port_id(wrapper_module, top_port_id)) { + VTR_LOG_ERROR("fpga_top port '%s' is not found at top module!\n", + top_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + BasicPort core_port = io_naming.fpga_core_port(top_port); + if (!core_port.is_valid()) { + VTR_LOG_ERROR("fpga_top port '%s' is not mapped to any fpga_core port!\n", + top_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + ModulePortId core_port_id = + module_manager.find_module_port(core_module, core_port.get_name()); + if (!module_manager.valid_module_port_id(core_module, core_port_id)) { + VTR_LOG_ERROR( + "fpga_top port '%s' is mapped to an invalid fpga_core port '%s'!\n", + top_port.to_verilog_string().c_str(), + core_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + ModuleManager::e_module_port_type top_port_type = + module_manager.port_type(core_module, core_port_id); + + /* Create net for each pin-to-pin connection */ + if (top_port.get_width() != core_port.get_width()) { + VTR_LOG_ERROR( + "fpga_top port '%s' does not match the width of fpga_core port '%s'!\n", + top_port.to_verilog_string().c_str(), + core_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + for (size_t ipin = 0; ipin < top_port.pins().size(); ++ipin) { + ModuleNetId new_net = module_manager.create_module_net(wrapper_module); + if (top_port_type != + ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT) { + module_manager.add_module_net_source(wrapper_module, new_net, + wrapper_module, 0, top_port_id, + top_port.pins()[ipin]); + module_manager.add_module_net_sink(wrapper_module, new_net, core_module, + 0, core_port_id, + core_port.pins()[ipin]); + } else { + VTR_ASSERT_SAFE(top_port_type == + ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); + module_manager.add_module_net_source(wrapper_module, new_net, + core_module, 0, core_port_id, + core_port.pins()[ipin]); + module_manager.add_module_net_sink(wrapper_module, new_net, + wrapper_module, 0, top_port_id, + top_port.pins()[ipin]); + } + VTR_LOGV( + verbose, + "Add nets to connect between fpga_top '%s' and fpga_core '%s' by " + "following naming rules\n", + top_port.to_verilog_string().c_str(), + core_port.to_verilog_string().c_str()); + } + } + /* Now walk through the ports of fpga_core, if port which is not mapped to + * fpga_top, nets should be added */ + for (ModulePortId core_port_id : module_manager.module_ports(core_module)) { + BasicPort core_port = module_manager.module_port(core_module, core_port_id); + BasicPort top_port = io_naming.fpga_top_port(core_port); + if (top_port.is_valid()) { + continue; /* Port has been added in the previous loop, skip now */ + } + /* Throw fatal error if part of the core port is mapped while other part is + * not mapped. This is not allowed! */ + if (io_naming.mapped_fpga_core_port(core_port)) { + VTR_LOG_ERROR( + "fpga_core port '%s' is partially mapped to fpga_top, which is not " + "allowed. Please cover the full-sized port in naming rules!\n", + core_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + ModuleManager::e_module_port_type top_port_type = + module_manager.port_type(core_module, core_port_id); + /* Collect port-level information */ + ModulePortId top_port_id = + module_manager.find_module_port(wrapper_module, core_port.get_name()); + if (!module_manager.valid_module_port_id(wrapper_module, top_port_id)) { + VTR_LOG_ERROR("fpga_top port '%s' is not found at top module!\n", + top_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + /* Create net for each pin-to-pin connection */ + if (top_port.get_width() != core_port.get_width()) { + VTR_LOG_ERROR( + "fpga_top port '%s' does not match the width of fpga_core port '%s'!\n", + top_port.to_verilog_string().c_str(), + core_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + for (size_t ipin = 0; ipin < top_port.pins().size(); ++ipin) { + ModuleNetId new_net = module_manager.create_module_net(wrapper_module); + if (top_port_type != + ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT) { + module_manager.add_module_net_source(wrapper_module, new_net, + wrapper_module, 0, top_port_id, + top_port.pins()[ipin]); + module_manager.add_module_net_sink(wrapper_module, new_net, core_module, + 0, core_port_id, + core_port.pins()[ipin]); + } else { + VTR_ASSERT_SAFE(top_port_type == + ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); + module_manager.add_module_net_source(wrapper_module, new_net, + core_module, 0, core_port_id, + core_port.pins()[ipin]); + module_manager.add_module_net_sink(wrapper_module, new_net, + wrapper_module, 0, top_port_id, + top_port.pins()[ipin]); + } + VTR_LOGV( + verbose, + "Add nets to connect fpga_top '%s' in the same name as the port of " + "fpga_core, since naming rules do not specify\n", + top_port.to_verilog_string().c_str()); + } } return CMD_EXEC_SUCCESS; } @@ -131,8 +276,13 @@ static int create_fpga_top_module_using_naming_rules( return CMD_EXEC_FATAL_ERROR; } - /* TODO: Add nets */ + /* Add nets */ if (add_nets) { + if (CMD_EXEC_SUCCESS != + create_fpga_top_module_nets_using_naming_rules( + module_manager, wrapper_module, core_module, io_naming, verbose)) { + return CMD_EXEC_FATAL_ERROR; + } } /* TODO: Update the fabric global ports */ From 962ba67e366d3b8785ee336ce953b42d68fa0d21 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 23 Jun 2023 14:47:21 -0700 Subject: [PATCH 122/391] [test] adding new tests to validate fpga core wrapper naming rules --- .../fpga_core_example_script.openfpga | 2 +- ...estbench_fpga_core_example_script.openfpga | 2 +- .../regression_test_scripts/basic_reg_test.sh | 2 + .../fpga_core_wrapper/config/task.conf | 4 +- .../config/task.conf | 43 +++++++++++++++++++ .../config/wrapper_io_naming.xml | 11 +++++ .../fpga_core_wrapper/config/task.conf | 4 +- .../config/task.conf | 42 ++++++++++++++++++ .../config/wrapper_io_naming.xml | 11 +++++ 9 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/wrapper_io_naming.xml create mode 100644 openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/wrapper_io_naming.xml diff --git a/openfpga_flow/openfpga_shell_scripts/fpga_core_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fpga_core_example_script.openfpga index 93bf0df48..12984cc7b 100644 --- a/openfpga_flow/openfpga_shell_scripts/fpga_core_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fpga_core_example_script.openfpga @@ -23,7 +23,7 @@ lut_truth_table_fixup # - Enable pin duplication on grid modules build_fabric --compress_routing #--verbose # Add a fpga core between fpga top and the underlying modules -add_fpga_core_to_fabric --instance_name fpga_core_inst --verbose +add_fpga_core_to_fabric ${OPENFPGA_WRAPPER_IO_NAMING_RULES} --instance_name fpga_core_inst --verbose # Write the fabric hierarchy of module graph to a file # This is used by hierarchical PnR flows diff --git a/openfpga_flow/openfpga_shell_scripts/write_full_testbench_fpga_core_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_fpga_core_example_script.openfpga index 3d3d12a56..244d44cf3 100644 --- a/openfpga_flow/openfpga_shell_scripts/write_full_testbench_fpga_core_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_fpga_core_example_script.openfpga @@ -23,7 +23,7 @@ lut_truth_table_fixup # - Enable pin duplication on grid modules build_fabric --compress_routing #--verbose # Add a fpga core between fpga top and the underlying modules -add_fpga_core_to_fabric --instance_name fpga_core_inst --verbose +add_fpga_core_to_fabric ${OPENFPGA_WRAPPER_IO_NAMING_RULES} --instance_name fpga_core_inst --verbose # Write the fabric hierarchy of module graph to a file # This is used by hierarchical PnR flows diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index d4198e1a9..549e0fe4b 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -17,7 +17,9 @@ run-task basic_tests/source_command/source_file $@ echo -e "Testing testbenches using fpga core wrapper" run-task basic_tests/full_testbench/fpga_core_wrapper $@ +run-task basic_tests/full_testbench/fpga_core_wrapper_naming_rules $@ run-task basic_tests/preconfig_testbench/fpga_core_wrapper $@ +run-task basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules $@ echo -e "Testing configuration chain of a K4N4 FPGA"; run-task basic_tests/full_testbench/configuration_chain $@ diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper/config/task.conf index b82cc5b14..953642825 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper/config/task.conf @@ -21,6 +21,7 @@ openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_ openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml openfpga_vpr_device_layout= openfpga_fast_configuration= +openfpga_wrapper_io_naming_rules= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml @@ -33,13 +34,10 @@ bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch [SYNTHESIS_PARAM] bench_read_verilog_options_common = -nolatches bench0_top = and2 -bench0_chan_width = 300 bench1_top = or2 -bench1_chan_width = 300 bench2_top = and2_latch -bench2_chan_width = 300 [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/task.conf new file mode 100644 index 000000000..d16207b3d --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/task.conf @@ -0,0 +1,43 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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/write_full_testbench_fpga_core_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_vpr_device_layout= +openfpga_fast_configuration= +openfpga_wrapper_io_naming_rules=--io_naming ${PATH:TASK_DIR}/wrapper_io_naming.xml + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2 + +bench1_top = or2 + +bench2_top = and2_latch + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/wrapper_io_naming.xml b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/wrapper_io_naming.xml new file mode 100644 index 000000000..616ce00dd --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/wrapper_io_naming.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper/config/task.conf index 9986926a8..6dab0dbcf 100644 --- a/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper/config/task.conf @@ -19,6 +19,7 @@ fpga_flow=yosys_vpr openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fpga_core_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_wrapper_io_naming_rules= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml @@ -31,13 +32,10 @@ bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch [SYNTHESIS_PARAM] bench_read_verilog_options_common = -nolatches bench0_top = and2 -bench0_chan_width = 300 bench1_top = or2 -bench1_chan_width = 300 bench2_top = and2_latch -bench2_chan_width = 300 [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= diff --git a/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/task.conf new file mode 100644 index 000000000..b03ecfd96 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/task.conf @@ -0,0 +1,42 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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/fpga_core_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_wrapper_io_naming_rules=--io_naming ${PATH:TASK_DIR}/wrapper_io_naming.xml + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2 + +bench1_top = or2 + +bench2_top = and2_latch + +[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/preconfig_testbench/fpga_core_wrapper_naming_rules/config/wrapper_io_naming.xml b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/wrapper_io_naming.xml new file mode 100644 index 000000000..616ce00dd --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/wrapper_io_naming.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + From 523e338d533d2b7380b53e46b96bd4e8fe669356 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 23 Jun 2023 14:49:52 -0700 Subject: [PATCH 123/391] [test] debugging --- .../fpga_core_wrapper_naming_rules/config/task.conf | 2 +- .../fpga_core_wrapper_naming_rules/config/task.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/task.conf index d16207b3d..5f23481c8 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/task.conf @@ -21,7 +21,7 @@ openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_ openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml openfpga_vpr_device_layout= openfpga_fast_configuration= -openfpga_wrapper_io_naming_rules=--io_naming ${PATH:TASK_DIR}/wrapper_io_naming.xml +openfpga_wrapper_io_naming_rules=--io_naming ${PATH:TASK_DIR}/config/wrapper_io_naming.xml [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/task.conf index b03ecfd96..7914b7ef5 100644 --- a/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/task.conf @@ -19,7 +19,7 @@ fpga_flow=yosys_vpr openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fpga_core_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_wrapper_io_naming_rules=--io_naming ${PATH:TASK_DIR}/wrapper_io_naming.xml +openfpga_wrapper_io_naming_rules=--io_naming ${PATH:TASK_DIR}/config/wrapper_io_naming.xml [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From 987a562e0ff381cf47d2f9f1833e090d7c517e33 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 23 Jun 2023 17:21:52 -0700 Subject: [PATCH 124/391] [core] fixed the bug when checking mapping status of fpga core ports --- libs/libionamemap/src/base/io_name_map.cpp | 37 ++++++++- libs/libionamemap/src/base/io_name_map.h | 15 +++- .../fabric/build_fpga_core_wrapper_module.cpp | 75 ++++++++++++++----- 3 files changed, 103 insertions(+), 24 deletions(-) diff --git a/libs/libionamemap/src/base/io_name_map.cpp b/libs/libionamemap/src/base/io_name_map.cpp index 8030630b0..000c2eeaf 100644 --- a/libs/libionamemap/src/base/io_name_map.cpp +++ b/libs/libionamemap/src/base/io_name_map.cpp @@ -119,10 +119,43 @@ BasicPort IoNameMap::fpga_top_port(const BasicPort& fpga_core_port) const { return top_port; } -bool IoNameMap::mapped_fpga_core_port(const BasicPort& fpga_core_port) const { +IoNameMap::e_port_mapping_status IoNameMap::fpga_core_port_mapping_status( + const BasicPort& fpga_core_port, const bool& verbose) const { /* First, find the pin name matching */ auto result_key = core2top_io_name_keys_.find(fpga_core_port.get_name()); - return result_key != core2top_io_name_keys_.end(); + if (result_key == core2top_io_name_keys_.end()) { + return IoNameMap::e_port_mapping_status::NONE; + } + /* Second, find the exact port. Create a scoreboard and check every pin. + * Expect only one hit per pin. Error on any pin which has been hit twice + * (indicate overlapped ports). Error on any pin which has no hit (indicate + * partially unmapped) */ + std::vector scoreboard(fpga_core_port.get_width(), 0); + for (std::string cand : result_key->second) { + BasicPort cand_port = str2port(cand); + for (auto pin : cand_port.pins()) { + scoreboard[pin - fpga_core_port.get_lsb()]++; + } + } + for (int8_t bit : scoreboard) { + if (bit == 0) { + VTR_LOGV_ERROR(verbose, + "Unmapped pin '%lu' of fpga_core port '%s'! Partially " + "mapping is not allowed\n", + fpga_core_port.pins()[bit + fpga_core_port.get_lsb()], + fpga_core_port.to_verilog_string().c_str()); + return IoNameMap::e_port_mapping_status::PARTIAL; + } + if (bit > 1) { + VTR_LOGV_ERROR(verbose, + "Overlapped %d times on pin '%lu' of fpga_core port '%s' " + "when mapping!\n", + bit, fpga_core_port.pins()[bit + fpga_core_port.get_lsb()], + fpga_core_port.to_verilog_string().c_str()); + return IoNameMap::e_port_mapping_status::OVERLAPPED; + } + } + return IoNameMap::e_port_mapping_status::FULL; } bool IoNameMap::fpga_top_port_is_dummy(const BasicPort& fpga_top_port) const { diff --git a/libs/libionamemap/src/base/io_name_map.h b/libs/libionamemap/src/base/io_name_map.h index eb4190308..b1f107a10 100644 --- a/libs/libionamemap/src/base/io_name_map.h +++ b/libs/libionamemap/src/base/io_name_map.h @@ -21,6 +21,13 @@ namespace openfpga { class IoNameMap { public: /* Types */ enum class e_dummy_port_direction { INPUT = 0, OUTPUT, INOUT, NUM_TYPES }; + enum class e_port_mapping_status { + NONE = 0, + PARTIAL, + FULL, + OVERLAPPED, + NUM_TYPES + }; public: /* Constructors */ IoNameMap(); @@ -40,10 +47,12 @@ class IoNameMap { e_dummy_port_direction fpga_top_dummy_port_direction( const BasicPort& fpga_top_port) const; /** @brief Check if a core port, by considering its port name only, has been - * mapped to fpga top. For example, there is a core port 'a[0:3]', while only - * 'a[0]' is mapped to the fpga top. We can use the port name find it quickly + * fully/partially/no mapped to fpga top. For example, there is a core port + * 'a[0:3]', while only 'a[0]' is mapped to the fpga top. We can use the port + * name find it quickly */ - bool mapped_fpga_core_port(const BasicPort& fpga_core_port) const; + e_port_mapping_status fpga_core_port_mapping_status( + const BasicPort& fpga_core_port, const bool& verbose = false) const; /** @brief Identify if there are any naming rules inside */ bool empty() const; diff --git a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp index 46cf37f12..4c3ea3257 100644 --- a/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp +++ b/openfpga/src/fabric/build_fpga_core_wrapper_module.cpp @@ -88,13 +88,27 @@ static int create_fpga_top_module_ports_using_naming_rules( } /* Throw fatal error if part of the core port is mapped while other part is * not mapped. This is not allowed! */ - if (io_naming.mapped_fpga_core_port(core_port)) { + IoNameMap::e_port_mapping_status mapping_status = + io_naming.fpga_core_port_mapping_status(core_port, true); + if (mapping_status == IoNameMap::e_port_mapping_status::FULL) { + continue; + } + if (mapping_status == IoNameMap::e_port_mapping_status::PARTIAL) { VTR_LOG_ERROR( "fpga_core port '%s' is partially mapped to fpga_top, which is not " "allowed. Please cover the full-sized port in naming rules!\n", core_port.to_verilog_string().c_str()); return CMD_EXEC_FATAL_ERROR; } + if (mapping_status == IoNameMap::e_port_mapping_status::OVERLAPPED) { + VTR_LOG_ERROR( + "fpga_core port '%s' is overlapped mapped to fpga_top, which is not " + "allowed. Please cover the full-sized port in naming rules!\n", + core_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + VTR_ASSERT(mapping_status == IoNameMap::e_port_mapping_status::NONE); + /* Add the port now */ ModuleManager::e_module_port_type top_port_type = module_manager.port_type(core_module, core_port_id); @@ -102,7 +116,7 @@ static int create_fpga_top_module_ports_using_naming_rules( VTR_LOG( "Add port '%s' to fpga_top in the same name as the port of " "fpga_core, since naming rules do not specify\n", - top_port.to_verilog_string().c_str()); + core_port.to_verilog_string().c_str()); } return CMD_EXEC_SUCCESS; } @@ -179,12 +193,16 @@ static int create_fpga_top_module_nets_using_naming_rules( wrapper_module, 0, top_port_id, top_port.pins()[ipin]); } + BasicPort top_pin(top_port.get_name(), top_port.pins()[ipin], + top_port.pins()[ipin]); + BasicPort core_pin(core_port.get_name(), core_port.pins()[ipin], + core_port.pins()[ipin]); VTR_LOGV( verbose, "Add nets to connect between fpga_top '%s' and fpga_core '%s' by " "following naming rules\n", - top_port.to_verilog_string().c_str(), - core_port.to_verilog_string().c_str()); + top_pin.to_verilog_string().c_str(), + core_pin.to_verilog_string().c_str()); } } /* Now walk through the ports of fpga_core, if port which is not mapped to @@ -197,18 +215,33 @@ static int create_fpga_top_module_nets_using_naming_rules( } /* Throw fatal error if part of the core port is mapped while other part is * not mapped. This is not allowed! */ - if (io_naming.mapped_fpga_core_port(core_port)) { + IoNameMap::e_port_mapping_status mapping_status = + io_naming.fpga_core_port_mapping_status(core_port); + if (mapping_status == IoNameMap::e_port_mapping_status::FULL) { + continue; + } + if (mapping_status == IoNameMap::e_port_mapping_status::PARTIAL) { VTR_LOG_ERROR( "fpga_core port '%s' is partially mapped to fpga_top, which is not " "allowed. Please cover the full-sized port in naming rules!\n", core_port.to_verilog_string().c_str()); return CMD_EXEC_FATAL_ERROR; } + if (mapping_status == IoNameMap::e_port_mapping_status::OVERLAPPED) { + VTR_LOG_ERROR( + "fpga_core port '%s' is overlapped mapped to fpga_top, which is not " + "allowed. Please cover the full-sized port in naming rules!\n", + core_port.to_verilog_string().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + VTR_ASSERT(mapping_status == IoNameMap::e_port_mapping_status::NONE); ModuleManager::e_module_port_type top_port_type = module_manager.port_type(core_module, core_port_id); /* Collect port-level information */ ModulePortId top_port_id = module_manager.find_module_port(wrapper_module, core_port.get_name()); + top_port = module_manager.module_port( + wrapper_module, top_port_id); /* Note: overwrite the top port */ if (!module_manager.valid_module_port_id(wrapper_module, top_port_id)) { VTR_LOG_ERROR("fpga_top port '%s' is not found at top module!\n", top_port.to_verilog_string().c_str()); @@ -222,7 +255,7 @@ static int create_fpga_top_module_nets_using_naming_rules( core_port.to_verilog_string().c_str()); return CMD_EXEC_FATAL_ERROR; } - for (size_t ipin = 0; ipin < top_port.pins().size(); ++ipin) { + for (size_t ipin = 0; ipin < core_port.pins().size(); ++ipin) { ModuleNetId new_net = module_manager.create_module_net(wrapper_module); if (top_port_type != ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT) { @@ -242,11 +275,13 @@ static int create_fpga_top_module_nets_using_naming_rules( wrapper_module, 0, top_port_id, top_port.pins()[ipin]); } + BasicPort core_pin(top_port.get_name(), core_port.pins()[ipin], + core_port.pins()[ipin]); VTR_LOGV( verbose, "Add nets to connect fpga_top '%s' in the same name as the port of " "fpga_core, since naming rules do not specify\n", - top_port.to_verilog_string().c_str()); + core_pin.to_verilog_string().c_str()); } } return CMD_EXEC_SUCCESS; @@ -256,11 +291,12 @@ static int create_fpga_top_module_nets_using_naming_rules( * Create a custom fpga_top module by applying naming rules *******************************************************************/ static int create_fpga_top_module_using_naming_rules( - ModuleManager& module_manager, const ModuleId& core_module, - const std::string& top_module_name, const IoNameMap& io_naming, - const std::string& instance_name, const bool& add_nets, const bool& verbose) { + ModuleManager& module_manager, ModuleId& wrapper_module, + const ModuleId& core_module, const std::string& top_module_name, + const IoNameMap& io_naming, const std::string& instance_name, + const bool& add_nets, const bool& verbose) { /* Create a new module with the given name */ - ModuleId wrapper_module = module_manager.add_module(top_module_name); + wrapper_module = module_manager.add_module(top_module_name); if (!wrapper_module) { return CMD_EXEC_FATAL_ERROR; } @@ -325,20 +361,21 @@ int add_fpga_core_to_device_module_graph(ModuleManager& module_manager, if (io_naming.empty()) { new_top_module = module_manager.create_wrapper_module( top_module, top_module_name, core_inst_name, !frame_view); - if (!module_manager.valid_module_id(new_top_module)) { - VTR_LOGV_ERROR(verbose, - "Failed to create a wrapper module '%s' on top of '%s'!\n", - top_module_name.c_str(), core_module_name.c_str()); - return CMD_EXEC_FATAL_ERROR; - } } else { status = create_fpga_top_module_using_naming_rules( - module_manager, top_module, top_module_name, io_naming, core_inst_name, - !frame_view, verbose); + module_manager, new_top_module, top_module, top_module_name, io_naming, + core_inst_name, !frame_view, verbose); if (CMD_EXEC_SUCCESS != status) { return CMD_EXEC_FATAL_ERROR; } } + if (!module_manager.valid_module_id(new_top_module)) { + VTR_LOGV_ERROR(verbose, + "Failed to create a wrapper module '%s' on top of '%s'!\n", + top_module_name.c_str(), core_module_name.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + VTR_LOGV(verbose, "Created a wrapper module '%s' on top of '%s'\n", top_module_name.c_str(), core_module_name.c_str()); From 150653287d5fc394a0cd3e16ef0b6abb38c0de5a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 25 Jun 2023 15:29:27 -0700 Subject: [PATCH 125/391] [core] supporting io naming for verilog testbench generators --- .../src/base/openfpga_build_fabric_template.h | 7 +-- openfpga/src/base/openfpga_context.h | 4 ++ openfpga/src/base/openfpga_verilog_template.h | 10 ++- openfpga/src/fpga_verilog/verilog_api.cpp | 6 +- openfpga/src/fpga_verilog/verilog_api.h | 3 + .../verilog_preconfig_top_module.cpp | 33 +++++++--- .../verilog_preconfig_top_module.h | 3 +- .../verilog_testbench_options.cpp | 17 +++++ .../fpga_verilog/verilog_testbench_options.h | 3 + .../fpga_verilog/verilog_testbench_utils.cpp | 62 ++++++++++++++++--- .../fpga_verilog/verilog_testbench_utils.h | 12 ++-- .../fpga_verilog/verilog_top_testbench.cpp | 37 +++++++---- .../src/fpga_verilog/verilog_top_testbench.h | 3 +- 13 files changed, 153 insertions(+), 47 deletions(-) diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index 51ee39b07..ba8db6e2c 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -252,16 +252,15 @@ int add_fpga_core_to_fabric_template(T& openfpga_ctx, const Command& cmd, } /* Handle I/O naming rules if defined */ - IoNameMap io_name_map; CommandOptionId opt_io_naming = cmd.option("io_naming"); if (true == cmd_context.option_enable(cmd, opt_io_naming)) { read_xml_io_name_map(cmd_context.option_value(cmd, opt_io_naming).c_str(), - io_name_map); + openfpga_ctx.mutable_io_name_map()); } return add_fpga_core_to_device_module_graph( - openfpga_ctx.mutable_module_graph(), io_name_map, core_inst_name, - frame_view, verbose_output); + openfpga_ctx.mutable_module_graph(), openfpga_ctx.io_name_map(), + core_inst_name, frame_view, verbose_output); } } /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_context.h b/openfpga/src/base/openfpga_context.h index 9455ed916..769bf0931 100644 --- a/openfpga/src/base/openfpga_context.h +++ b/openfpga/src/base/openfpga_context.h @@ -11,6 +11,7 @@ #include "fabric_bitstream.h" #include "fabric_global_port_info.h" #include "io_location_map.h" +#include "io_name_map.h" #include "memory_bank_shift_register_banks.h" #include "module_manager.h" #include "mux_library.h" @@ -104,6 +105,7 @@ class OpenfpgaContext : public Context { const openfpga::IoLocationMap& io_location_map() const { return io_location_map_; } + const openfpga::IoNameMap& io_name_map() const { return io_name_map_; } const openfpga::FabricGlobalPortInfo& fabric_global_port_info() const { return fabric_global_port_info_; } @@ -162,6 +164,7 @@ class OpenfpgaContext : public Context { openfpga::IoLocationMap& mutable_io_location_map() { return io_location_map_; } + openfpga::IoNameMap& mutable_io_name_map() { return io_name_map_; } openfpga::FabricGlobalPortInfo& mutable_fabric_global_port_info() { return fabric_global_port_info_; } @@ -216,6 +219,7 @@ class OpenfpgaContext : public Context { /* Fabric module graph */ openfpga::ModuleManager module_graph_; openfpga::IoLocationMap io_location_map_; + openfpga::IoNameMap io_name_map_; openfpga::FabricGlobalPortInfo fabric_global_port_info_; /* Bitstream database */ diff --git a/openfpga/src/base/openfpga_verilog_template.h b/openfpga/src/base/openfpga_verilog_template.h index a25ce81c1..913f6ddae 100644 --- a/openfpga/src/base/openfpga_verilog_template.h +++ b/openfpga/src/base/openfpga_verilog_template.h @@ -77,6 +77,7 @@ int write_full_testbench_template(const T& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context) { CommandOptionId opt_output_dir = cmd.option("file"); CommandOptionId opt_bitstream = cmd.option("bitstream"); + CommandOptionId opt_dut_module = cmd.option("dut_module"); CommandOptionId opt_fabric_netlist = cmd.option("fabric_netlist_file_path"); CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); CommandOptionId opt_bgf = cmd.option("bus_group_file"); @@ -96,6 +97,7 @@ int write_full_testbench_template(const T& openfpga_ctx, const Command& cmd, */ VerilogTestbenchOption options; options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); + options.set_dut_module(cmd_context.option_value(cmd, opt_dut_module)); options.set_fabric_netlist_file_path( cmd_context.option_value(cmd, opt_fabric_netlist)); options.set_reference_benchmark_file_path( @@ -135,7 +137,8 @@ int write_full_testbench_template(const T& openfpga_ctx, const Command& cmd, openfpga_ctx.fabric_bitstream(), openfpga_ctx.blwl_shift_register_banks(), g_vpr_ctx.atom(), g_vpr_ctx.placement(), pin_constraints, bus_group, cmd_context.option_value(cmd, opt_bitstream), - openfpga_ctx.io_location_map(), openfpga_ctx.fabric_global_port_info(), + openfpga_ctx.io_location_map(), openfpga_ctx.io_name_map(), + openfpga_ctx.fabric_global_port_info(), openfpga_ctx.vpr_netlist_annotation(), openfpga_ctx.arch().circuit_lib, openfpga_ctx.simulation_setting(), openfpga_ctx.arch().config_protocol, options); @@ -150,6 +153,7 @@ int write_preconfigured_fabric_wrapper_template( const T& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context) { CommandOptionId opt_output_dir = cmd.option("file"); + CommandOptionId opt_dut_module = cmd.option("dut_module"); CommandOptionId opt_fabric_netlist = cmd.option("fabric_netlist_file_path"); CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); CommandOptionId opt_bgf = cmd.option("bus_group_file"); @@ -166,6 +170,7 @@ int write_preconfigured_fabric_wrapper_template( */ VerilogTestbenchOption options; options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); + options.set_dut_module(cmd_context.option_value(cmd, opt_dut_module)); options.set_fabric_netlist_file_path( cmd_context.option_value(cmd, opt_fabric_netlist)); options.set_explicit_port_mapping( @@ -203,7 +208,8 @@ int write_preconfigured_fabric_wrapper_template( return fpga_verilog_preconfigured_fabric_wrapper( openfpga_ctx.module_graph(), openfpga_ctx.bitstream_manager(), g_vpr_ctx.atom(), g_vpr_ctx.placement(), pin_constraints, bus_group, - openfpga_ctx.io_location_map(), openfpga_ctx.fabric_global_port_info(), + openfpga_ctx.io_location_map(), openfpga_ctx.io_name_map(), + openfpga_ctx.fabric_global_port_info(), openfpga_ctx.vpr_netlist_annotation(), openfpga_ctx.arch().circuit_lib, openfpga_ctx.arch().config_protocol, options); } diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index dae1d7a46..4690aa2e0 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -148,6 +148,7 @@ int fpga_verilog_full_testbench( const AtomContext &atom_ctx, const PlacementContext &place_ctx, const PinConstraints &pin_constraints, const BusGroup &bus_group, const std::string &bitstream_file, const IoLocationMap &io_location_map, + const IoNameMap &io_name_map, const FabricGlobalPortInfo &fabric_global_port_info, const VprNetlistAnnotation &netlist_annotation, const CircuitLibrary &circuit_lib, @@ -174,7 +175,7 @@ int fpga_verilog_full_testbench( print_verilog_full_testbench( module_manager, bitstream_manager, fabric_bitstream, blwl_sr_banks, circuit_lib, config_protocol, fabric_global_port_info, atom_ctx, place_ctx, - pin_constraints, bus_group, bitstream_file, io_location_map, + pin_constraints, bus_group, bitstream_file, io_location_map, io_name_map, netlist_annotation, netlist_name, top_testbench_file_path, simulation_setting, options); @@ -197,6 +198,7 @@ int fpga_verilog_preconfigured_fabric_wrapper( const BitstreamManager &bitstream_manager, const AtomContext &atom_ctx, const PlacementContext &place_ctx, const PinConstraints &pin_constraints, const BusGroup &bus_group, const IoLocationMap &io_location_map, + const IoNameMap &io_name_map, const FabricGlobalPortInfo &fabric_global_port_info, const VprNetlistAnnotation &netlist_annotation, const CircuitLibrary &circuit_lib, const ConfigProtocol &config_protocol, @@ -221,7 +223,7 @@ int fpga_verilog_preconfigured_fabric_wrapper( status = print_verilog_preconfig_top_module( module_manager, bitstream_manager, config_protocol, circuit_lib, fabric_global_port_info, atom_ctx, place_ctx, pin_constraints, bus_group, - io_location_map, netlist_annotation, netlist_name, + io_location_map, io_name_map, netlist_annotation, netlist_name, formal_verification_top_netlist_file_path, options); return status; diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index 914e58d70..db448aae5 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -18,6 +18,7 @@ #include "fabric_global_port_info.h" #include "fabric_verilog_options.h" #include "io_location_map.h" +#include "io_name_map.h" #include "memory_bank_shift_register_banks.h" #include "module_manager.h" #include "mux_library.h" @@ -52,6 +53,7 @@ int fpga_verilog_full_testbench( const AtomContext& atom_ctx, const PlacementContext& place_ctx, const PinConstraints& pin_constraints, const BusGroup& bus_group, const std::string& bitstream_file, const IoLocationMap& io_location_map, + const IoNameMap& io_name_map, const FabricGlobalPortInfo& fabric_global_port_info, const VprNetlistAnnotation& netlist_annotation, const CircuitLibrary& circuit_lib, @@ -63,6 +65,7 @@ int fpga_verilog_preconfigured_fabric_wrapper( const BitstreamManager& bitstream_manager, const AtomContext& atom_ctx, const PlacementContext& place_ctx, const PinConstraints& pin_constraints, const BusGroup& bus_group, const IoLocationMap& io_location_map, + const IoNameMap& io_name_map, const FabricGlobalPortInfo& fabric_global_port_info, const VprNetlistAnnotation& netlist_annotation, const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, diff --git a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp index c0d3ef4d7..6bbfc3323 100644 --- a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp +++ b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp @@ -545,7 +545,7 @@ int print_verilog_preconfig_top_module( const FabricGlobalPortInfo &global_ports, const AtomContext &atom_ctx, const PlacementContext &place_ctx, const PinConstraints &pin_constraints, const BusGroup &bus_group, const IoLocationMap &io_location_map, - const VprNetlistAnnotation &netlist_annotation, + const IoNameMap &io_name_map, const VprNetlistAnnotation &netlist_annotation, const std::string &circuit_name, const std::string &verilog_fname, const VerilogTestbenchOption &options) { std::string timer_message = @@ -577,20 +577,33 @@ int print_verilog_preconfig_top_module( print_verilog_preconfig_top_module_ports(fp, circuit_name, atom_ctx, netlist_annotation, bus_group); - /* Find the top_module */ - ModuleId top_module = - module_manager.find_module(generate_fpga_top_module_name()); - VTR_ASSERT(true == module_manager.valid_module_id(top_module)); + /* Spot the dut module */ + ModuleId top_module = module_manager.find_module(options.dut_module()); + if (!module_manager.valid_module_id(top_module)) { + VTR_LOG_ERROR( + "Unable to find the DUT module '%s'. Please check if you create " + "dedicated module when building the fabric!\n", + options.dut_module().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + /* Note that we always need the core module as it contains the original port + * names before possible renaming at top-level module. If there is no core + * module, it means that the current top module is the core module */ + ModuleId core_module = + module_manager.find_module(generate_fpga_core_module_name()); + if (!module_manager.valid_module_id(core_module)) { + core_module = top_module; + } /* Print internal wires */ print_verilog_preconfig_top_module_internal_wires(fp, module_manager, - top_module); + core_module); /* Instanciate FPGA top-level module */ print_verilog_testbench_fpga_instance( - fp, module_manager, top_module, + fp, module_manager, top_module, core_module, std::string(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME), - std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX), + std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX), io_name_map, options.explicit_port_mapping()); /* Find clock ports in benchmark */ @@ -600,7 +613,7 @@ int print_verilog_preconfig_top_module( /* Connect FPGA top module global ports to constant or benchmark global * signals! */ status = print_verilog_preconfig_top_module_connect_global_ports( - fp, module_manager, top_module, pin_constraints, global_ports, + fp, module_manager, core_module, pin_constraints, global_ports, benchmark_clock_port_names); if (CMD_EXEC_FATAL_ERROR == status) { return status; @@ -608,7 +621,7 @@ int print_verilog_preconfig_top_module( /* Connect I/Os to benchmark I/Os or constant driver */ print_verilog_testbench_connect_fpga_ios( - fp, module_manager, top_module, atom_ctx, place_ctx, io_location_map, + fp, module_manager, core_module, atom_ctx, place_ctx, io_location_map, netlist_annotation, bus_group, std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX), std::string(), std::string(), std::vector(), diff --git a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.h b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.h index a309f9fae..59501a15f 100644 --- a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.h +++ b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.h @@ -13,6 +13,7 @@ #include "config_protocol.h" #include "fabric_global_port_info.h" #include "io_location_map.h" +#include "io_name_map.h" #include "module_manager.h" #include "pin_constraints.h" #include "verilog_testbench_options.h" @@ -33,7 +34,7 @@ int print_verilog_preconfig_top_module( const FabricGlobalPortInfo& global_ports, const AtomContext& atom_ctx, const PlacementContext& place_ctx, const PinConstraints& pin_constraints, const BusGroup& bus_group, const IoLocationMap& io_location_map, - const VprNetlistAnnotation& netlist_annotation, + const IoNameMap& io_name_map, const VprNetlistAnnotation& netlist_annotation, const std::string& circuit_name, const std::string& verilog_fname, const VerilogTestbenchOption& options); diff --git a/openfpga/src/fpga_verilog/verilog_testbench_options.cpp b/openfpga/src/fpga_verilog/verilog_testbench_options.cpp index 885ddc744..5e4709419 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_options.cpp +++ b/openfpga/src/fpga_verilog/verilog_testbench_options.cpp @@ -3,6 +3,7 @@ ******************************************************************************/ #include "verilog_testbench_options.h" +#include "openfpga_naming.h" #include "vtr_assert.h" #include "vtr_log.h" @@ -14,6 +15,7 @@ namespace openfpga { *************************************************/ VerilogTestbenchOption::VerilogTestbenchOption() { output_directory_.clear(); + dut_module_ = "fpga_top"; fabric_netlist_file_path_.clear(); reference_benchmark_file_path_.clear(); print_preconfig_top_testbench_ = false; @@ -37,6 +39,8 @@ std::string VerilogTestbenchOption::output_directory() const { return output_directory_; } +std::string VerilogTestbenchOption::dut_module() const { return dut_module_; } + std::string VerilogTestbenchOption::fabric_netlist_file_path() const { return fabric_netlist_file_path_; } @@ -108,6 +112,19 @@ void VerilogTestbenchOption::set_output_directory( output_directory_ = output_dir; } +void VerilogTestbenchOption::set_dut_module(const std::string& dut_module) { + /* Precheck: only accept two legal names */ + if (dut_module != generate_fpga_top_module_name() && + dut_module != generate_fpga_core_module_name()) { + VTR_LOG_ERROR( + "Invalid module name '%s' for Design Under Test (DUT)! Expect [%s|%s]\n", + dut_module.c_str(), generate_fpga_top_module_name().c_str(), + generate_fpga_core_module_name().c_str()); + exit(1); + } + dut_module_ = dut_module; +} + void VerilogTestbenchOption::set_fabric_netlist_file_path( const std::string& fabric_netlist_file_path) { fabric_netlist_file_path_ = fabric_netlist_file_path; diff --git a/openfpga/src/fpga_verilog/verilog_testbench_options.h b/openfpga/src/fpga_verilog/verilog_testbench_options.h index 7610f95b9..aafa15d71 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_options.h +++ b/openfpga/src/fpga_verilog/verilog_testbench_options.h @@ -37,6 +37,7 @@ class VerilogTestbenchOption { public: /* Public accessors */ std::string output_directory() const; + std::string dut_module() const; std::string fabric_netlist_file_path() const; std::string reference_benchmark_file_path() const; bool fast_configuration() const; @@ -60,6 +61,7 @@ class VerilogTestbenchOption { public: /* Public mutators */ void set_output_directory(const std::string& output_dir); + void set_dut_module(const std::string& dut_module); /* The reference verilog file path is the key parameters that will have an * impact on other options: * - print_preconfig_top_testbench @@ -93,6 +95,7 @@ class VerilogTestbenchOption { private: /* Internal Data */ std::string output_directory_; + std::string dut_module_; std::string fabric_netlist_file_path_; std::string reference_benchmark_file_path_; bool fast_configuration_; diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp index 09c900b3b..ff54d4494 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp @@ -34,12 +34,11 @@ namespace openfpga { .out(out_postfix>) ); *******************************************************************/ -void print_verilog_testbench_fpga_instance(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& top_module, - const std::string& top_instance_name, - const std::string& net_postfix, - const bool& explicit_port_mapping) { +void print_verilog_testbench_fpga_instance( + std::fstream& fp, const ModuleManager& module_manager, + const ModuleId& top_module, const ModuleId& core_module, + const std::string& top_instance_name, const std::string& net_postfix, + const IoNameMap& io_name_map, const bool& explicit_port_mapping) { /* Validate the file stream */ valid_file_stream(fp); @@ -47,15 +46,60 @@ void print_verilog_testbench_fpga_instance(std::fstream& fp, print_verilog_comment( fp, std::string("----- FPGA top-level module to be capsulated -----")); + /* Precheck on the top module and decide if we need to consider I/O naming + * - If we do have a fpga_core module added, and dut is fpga_top, we need a + * I/O naming + * - If we do NOT have a fpga_core module added, and dut is fpga_top, we do + * NOT need a I/O naming + * - If we do have a fpga_core module added, and dut is fpga_core, we do NOT + * need a I/O naming + * - If we do NOT have a fpga_core module added, and dut is fpga_core, it + * should error out earlier. + */ + bool require_io_naming = false; + if (top_module != core_module) { + require_io_naming = true; + } + /* Create an empty port-to-port name mapping, because we use default names */ std::map port2port_name_map; - if (!net_postfix.empty()) { + if (!require_io_naming) { + if (!net_postfix.empty()) { + for (const ModulePortId& module_port_id : + module_manager.module_ports(top_module)) { + BasicPort module_port = + module_manager.module_port(top_module, module_port_id); + BasicPort net_port = module_port; + net_port.set_name(module_port.get_name() + net_postfix); + port2port_name_map[module_port.get_name()] = net_port; + } + } + } else { + VTR_ASSERT_SAFE(require_io_naming); + /* We walk through the ports under top module. Find renamed ports at + * core-level and use them as net names */ for (const ModulePortId& module_port_id : module_manager.module_ports(top_module)) { BasicPort module_port = module_manager.module_port(top_module, module_port_id); - BasicPort net_port = module_port; - net_port.set_name(module_port.get_name() + net_postfix); + /* Bypass dummy port: the port does not exist at core module */ + if (io_name_map.fpga_top_port_is_dummy(module_port)) { + ModulePortId core_module_port = + module_manager.find_module_port(core_module, module_port.get_name()); + if (!module_manager.valid_module_port_id(core_module, + core_module_port)) { + continue; + } + } + /* Not a dummy port, if it is renamed, use the new name. Otherwise, keep + * the old name */ + BasicPort net_port = io_name_map.fpga_core_port(module_port); + if (net_port.is_valid()) { + net_port.set_name(net_port.get_name() + net_postfix); + } else { + net_port = module_port; + net_port.set_name(module_port.get_name() + net_postfix); + } port2port_name_map[module_port.get_name()] = net_port; } } diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.h b/openfpga/src/fpga_verilog/verilog_testbench_utils.h index 44850bb41..87059645f 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.h +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.h @@ -12,6 +12,7 @@ #include "circuit_library.h" #include "fabric_global_port_info.h" #include "io_location_map.h" +#include "io_name_map.h" #include "module_manager.h" #include "pin_constraints.h" #include "simulation_setting.h" @@ -25,12 +26,11 @@ /* begin namespace openfpga */ namespace openfpga { -void print_verilog_testbench_fpga_instance(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& top_module, - const std::string& top_instance_name, - const std::string& net_postfix, - const bool& explicit_port_mapping); +void print_verilog_testbench_fpga_instance( + std::fstream& fp, const ModuleManager& module_manager, + const ModuleId& top_module, const ModuleId& core_module, + const std::string& top_instance_name, const std::string& net_postfix, + const IoNameMap& io_name_map, const bool& explicit_port_mapping); void print_verilog_testbench_benchmark_instance( std::fstream& fp, const std::string& module_name, diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 02b77ad58..4146fa870 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -2435,7 +2435,7 @@ int print_verilog_full_testbench( const FabricGlobalPortInfo& global_ports, const AtomContext& atom_ctx, const PlacementContext& place_ctx, const PinConstraints& pin_constraints, const BusGroup& bus_group, const std::string& bitstream_file, - const IoLocationMap& io_location_map, + const IoLocationMap& io_location_map, const IoNameMap& io_name_map, const VprNetlistAnnotation& netlist_annotation, const std::string& circuit_name, const std::string& verilog_fname, const SimulationSetting& simulation_parameters, @@ -2465,10 +2465,23 @@ int print_verilog_full_testbench( circuit_name; print_verilog_file_header(fp, title, options.time_stamp()); - /* Find the top_module */ - ModuleId top_module = - module_manager.find_module(generate_fpga_top_module_name()); - VTR_ASSERT(true == module_manager.valid_module_id(top_module)); + /* Spot the dut module */ + ModuleId top_module = module_manager.find_module(options.dut_module()); + if (!module_manager.valid_module_id(top_module)) { + VTR_LOG_ERROR( + "Unable to find the DUT module '%s'. Please check if you create " + "dedicated module when building the fabric!\n", + options.dut_module().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + /* Note that we always need the core module as it contains the original port + * names before possible renaming at top-level module. If there is no core + * module, it means that the current top module is the core module */ + ModuleId core_module = + module_manager.find_module(generate_fpga_core_module_name()); + if (!module_manager.valid_module_id(core_module)) { + core_module = top_module; + } /* Preparation: find all the clock ports */ std::vector clock_port_names = @@ -2492,7 +2505,7 @@ int print_verilog_full_testbench( /* Start of testbench */ print_verilog_top_testbench_ports( - fp, module_manager, top_module, atom_ctx, netlist_annotation, + fp, module_manager, core_module, atom_ctx, netlist_annotation, clock_port_names, global_ports, pin_constraints, simulation_parameters, config_protocol, circuit_name, options); @@ -2522,7 +2535,7 @@ int print_verilog_full_testbench( /* Generate stimuli for programming interface */ int status = CMD_EXEC_SUCCESS; status = print_verilog_top_testbench_configuration_protocol_stimulus( - fp, config_protocol, simulation_parameters, module_manager, top_module, + fp, config_protocol, simulation_parameters, module_manager, core_module, fast_configuration, bit_value_to_skip, fabric_bitstream, blwl_sr_banks, prog_clock_period, VERILOG_SIM_TIMESCALE); @@ -2557,19 +2570,19 @@ int print_verilog_full_testbench( /* Generate stimuli for global ports or connect them to existed signals */ print_verilog_top_testbench_global_ports_stimuli( - fp, module_manager, top_module, pin_constraints, config_protocol, + fp, module_manager, core_module, pin_constraints, config_protocol, global_ports, simulation_parameters, active_global_prog_reset, active_global_prog_set); /* Instanciate FPGA top-level module */ print_verilog_testbench_fpga_instance( - fp, module_manager, top_module, - std::string(TOP_TESTBENCH_FPGA_INSTANCE_NAME), std::string(), + fp, module_manager, top_module, core_module, + std::string(TOP_TESTBENCH_FPGA_INSTANCE_NAME), std::string(), io_name_map, explicit_port_mapping); /* Connect I/Os to benchmark I/Os or constant driver */ print_verilog_testbench_connect_fpga_ios( - fp, module_manager, top_module, atom_ctx, place_ctx, io_location_map, + fp, module_manager, core_module, atom_ctx, place_ctx, io_location_map, netlist_annotation, BusGroup(), std::string(), std::string(TOP_TESTBENCH_SHARED_INPUT_POSTFIX), std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX), clock_port_names, @@ -2585,7 +2598,7 @@ int print_verilog_full_testbench( /* load bitstream to FPGA fabric in a configuration phase */ print_verilog_full_testbench_bitstream( fp, bitstream_file, config_protocol, apply_fast_configuration, - bit_value_to_skip, module_manager, top_module, bitstream_manager, + bit_value_to_skip, module_manager, core_module, bitstream_manager, fabric_bitstream, blwl_sr_banks); /* Add signal initialization: diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.h b/openfpga/src/fpga_verilog/verilog_top_testbench.h index dfe24a6c9..0e9b4865c 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.h +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.h @@ -14,6 +14,7 @@ #include "fabric_bitstream.h" #include "fabric_global_port_info.h" #include "io_location_map.h" +#include "io_name_map.h" #include "memory_bank_shift_register_banks.h" #include "module_manager.h" #include "pin_constraints.h" @@ -38,7 +39,7 @@ int print_verilog_full_testbench( const FabricGlobalPortInfo& global_ports, const AtomContext& atom_ctx, const PlacementContext& place_ctx, const PinConstraints& pin_constraints, const BusGroup& bus_group, const std::string& bitstream_file, - const IoLocationMap& io_location_map, + const IoLocationMap& io_location_map, const IoNameMap& io_name_map, const VprNetlistAnnotation& netlist_annotation, const std::string& circuit_name, const std::string& verilog_fname, const SimulationSetting& simulation_parameters, From ecf79fcb32bfefb75edc7ee811564e627fa14efc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 00:02:40 +0000 Subject: [PATCH 126/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 3b082e1ee..8080c2671 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1127 +1.2.1131 From 205881d0e7b5745a3a255dc8cb7282bd54ea923d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 25 Jun 2023 18:03:15 -0700 Subject: [PATCH 127/391] [core] fixed the bug when using fpga_core instead of fpga_top --- openfpga/src/base/openfpga_verilog_template.h | 10 ++++++++-- openfpga/src/fpga_verilog/verilog_testbench_utils.cpp | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/openfpga/src/base/openfpga_verilog_template.h b/openfpga/src/base/openfpga_verilog_template.h index 913f6ddae..6c7fc746b 100644 --- a/openfpga/src/base/openfpga_verilog_template.h +++ b/openfpga/src/base/openfpga_verilog_template.h @@ -97,7 +97,6 @@ int write_full_testbench_template(const T& openfpga_ctx, const Command& cmd, */ VerilogTestbenchOption options; options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); - options.set_dut_module(cmd_context.option_value(cmd, opt_dut_module)); options.set_fabric_netlist_file_path( cmd_context.option_value(cmd, opt_fabric_netlist)); options.set_reference_benchmark_file_path( @@ -118,6 +117,10 @@ int write_full_testbench_template(const T& openfpga_ctx, const Command& cmd, cmd_context.option_value(cmd, opt_default_net_type)); } + if (true == cmd_context.option_enable(cmd, opt_dut_module)) { + options.set_dut_module(cmd_context.option_value(cmd, opt_dut_module)); + } + /* If pin constraints are enabled by command options, read the file */ PinConstraints pin_constraints; if (true == cmd_context.option_enable(cmd, opt_pcf)) { @@ -170,7 +173,6 @@ int write_preconfigured_fabric_wrapper_template( */ VerilogTestbenchOption options; options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); - options.set_dut_module(cmd_context.option_value(cmd, opt_dut_module)); options.set_fabric_netlist_file_path( cmd_context.option_value(cmd, opt_fabric_netlist)); options.set_explicit_port_mapping( @@ -181,6 +183,10 @@ int write_preconfigured_fabric_wrapper_template( cmd_context.option_enable(cmd, opt_include_signal_init)); options.set_print_formal_verification_top_netlist(true); + if (true == cmd_context.option_enable(cmd, opt_dut_module)) { + options.set_dut_module(cmd_context.option_value(cmd, opt_dut_module)); + } + if (true == cmd_context.option_enable(cmd, opt_default_net_type)) { options.set_default_net_type( cmd_context.option_value(cmd, opt_default_net_type)); diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp index ff54d4494..e3895c28d 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp @@ -88,6 +88,9 @@ void print_verilog_testbench_fpga_instance( module_manager.find_module_port(core_module, module_port.get_name()); if (!module_manager.valid_module_port_id(core_module, core_module_port)) { + /* Print the wire for the dummy port */ + fp << generate_verilog_port(VERILOG_PORT_WIRE, module_port) << ";" + << std::endl; continue; } } From 919d6d86082fe6dc872a8f78cabdf404fcf42bc8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 25 Jun 2023 22:49:51 -0700 Subject: [PATCH 128/391] [test] added more testcases to validate the dut module option; fixing bugs on preconfigured testbenches --- .../verilog_preconfig_top_module.cpp | 26 +++++++---- .../fpga_core_example_script.openfpga | 4 +- ...estbench_fpga_core_example_script.openfpga | 2 +- .../regression_test_scripts/basic_reg_test.sh | 2 + .../fpga_core_wrapper/config/task.conf | 1 + .../config/task.conf | 44 +++++++++++++++++++ .../config/wrapper_io_naming.xml | 11 +++++ .../fpga_core_wrapper/config/task.conf | 1 + .../config/task.conf | 1 + .../config/task.conf | 43 ++++++++++++++++++ .../config/wrapper_io_naming.xml | 11 +++++ 11 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/wrapper_io_naming.xml create mode 100644 openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/wrapper_io_naming.xml diff --git a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp index 6bbfc3323..c80a3a782 100644 --- a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp +++ b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp @@ -330,10 +330,15 @@ static void print_verilog_preconfig_top_module_force_bitstream( /* Drop the first block, which is the top module, it should be replaced by * the instance name here */ /* Ensure that this is the module we want to drop! */ - VTR_ASSERT(0 == - module_manager.module_name(top_module) - .compare(bitstream_manager.block_name(block_hierarchy[0]))); - block_hierarchy.erase(block_hierarchy.begin()); + VTR_LOG("Top module: '%s', Block[0]: '%s', Block[1]: '%s'\n", module_manager.module_name(top_module).c_str(), bitstream_manager.block_name(block_hierarchy[0]).c_str(), bitstream_manager.block_name(block_hierarchy[1]).c_str()); + VTR_ASSERT(0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0])) + || 0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[1]))); + if (0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0]))) { + block_hierarchy.erase(block_hierarchy.begin()); + } else if (0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[1]))) { + block_hierarchy.erase(block_hierarchy.begin()); + block_hierarchy.erase(block_hierarchy.begin()); + } /* Build the full hierarchy path */ std::string bit_hierarchy_path(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME); for (const ConfigBlockId &temp_block : block_hierarchy) { @@ -410,10 +415,15 @@ static void print_verilog_preconfig_top_module_deposit_bitstream( /* Drop the first block, which is the top module, it should be replaced by * the instance name here */ /* Ensure that this is the module we want to drop! */ - VTR_ASSERT(0 == - module_manager.module_name(top_module) - .compare(bitstream_manager.block_name(block_hierarchy[0]))); - block_hierarchy.erase(block_hierarchy.begin()); + VTR_ASSERT(0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0])) + || 0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[1]))); + if (0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0]))) { + block_hierarchy.erase(block_hierarchy.begin()); + } else if (0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[1]))) { + block_hierarchy.erase(block_hierarchy.begin()); + block_hierarchy.erase(block_hierarchy.begin()); + } + /* Build the full hierarchy path */ std::string bit_hierarchy_path(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME); for (const ConfigBlockId &temp_block : block_hierarchy) { diff --git a/openfpga_flow/openfpga_shell_scripts/fpga_core_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/fpga_core_example_script.openfpga index 12984cc7b..9385b6b28 100644 --- a/openfpga_flow/openfpga_shell_scripts/fpga_core_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/fpga_core_example_script.openfpga @@ -54,8 +54,8 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - 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_preconfigured_fabric_wrapper --embed_bitstream iverilog --file ./SRC --explicit_port_mapping +write_full_testbench ${OPENFPGA_TESTBENCH_DUT_MODULE} --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping --include_signal_init --bitstream fabric_bitstream.bit +write_preconfigured_fabric_wrapper ${OPENFPGA_TESTBENCH_DUT_MODULE} --embed_bitstream iverilog --file ./SRC --explicit_port_mapping write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping # Write the SDC files for PnR backend diff --git a/openfpga_flow/openfpga_shell_scripts/write_full_testbench_fpga_core_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_fpga_core_example_script.openfpga index 244d44cf3..0608ad9a7 100644 --- a/openfpga_flow/openfpga_shell_scripts/write_full_testbench_fpga_core_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/write_full_testbench_fpga_core_example_script.openfpga @@ -54,7 +54,7 @@ write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --pri # - 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} --include_signal_init --explicit_port_mapping --bitstream fabric_bitstream.bit ${OPENFPGA_FAST_CONFIGURATION} +write_full_testbench ${OPENFPGA_TESTBENCH_DUT_MODULE} --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --explicit_port_mapping --bitstream fabric_bitstream.bit ${OPENFPGA_FAST_CONFIGURATION} # Write the SDC files for PnR backend # - Turn on every options here diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index 549e0fe4b..d860f9771 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -18,8 +18,10 @@ run-task basic_tests/source_command/source_file $@ echo -e "Testing testbenches using fpga core wrapper" run-task basic_tests/full_testbench/fpga_core_wrapper $@ run-task basic_tests/full_testbench/fpga_core_wrapper_naming_rules $@ +run-task basic_tests/full_testbench/fpga_core_wrapper_naming_rules_use_core_tb $@ run-task basic_tests/preconfig_testbench/fpga_core_wrapper $@ run-task basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules $@ +run-task basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules_use_core_tb $@ echo -e "Testing configuration chain of a K4N4 FPGA"; run-task basic_tests/full_testbench/configuration_chain $@ diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper/config/task.conf index 953642825..55d56b630 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper/config/task.conf @@ -22,6 +22,7 @@ openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulatio openfpga_vpr_device_layout= openfpga_fast_configuration= openfpga_wrapper_io_naming_rules= +openfpga_testbench_dut_module= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/task.conf new file mode 100644 index 000000000..f059afe33 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/task.conf @@ -0,0 +1,44 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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/write_full_testbench_fpga_core_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_vpr_device_layout= +openfpga_fast_configuration= +openfpga_wrapper_io_naming_rules=--io_naming ${PATH:TASK_DIR}/config/wrapper_io_naming.xml +openfpga_testbench_dut_module=--dut_module fpga_core + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2 + +bench1_top = or2 + +bench2_top = and2_latch + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/wrapper_io_naming.xml b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/wrapper_io_naming.xml new file mode 100644 index 000000000..616ce00dd --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/wrapper_io_naming.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper/config/task.conf index 6dab0dbcf..0655d77a2 100644 --- a/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper/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_wrapper_io_naming_rules= +openfpga_testbench_dut_module= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/task.conf index 7914b7ef5..727ae668a 100644 --- a/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules/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_wrapper_io_naming_rules=--io_naming ${PATH:TASK_DIR}/config/wrapper_io_naming.xml +openfpga_testbench_dut_module= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/task.conf new file mode 100644 index 000000000..9cc41ce37 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/task.conf @@ -0,0 +1,43 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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/fpga_core_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_wrapper_io_naming_rules=--io_naming ${PATH:TASK_DIR}/config/wrapper_io_naming.xml +openfpga_testbench_dut_module= --dut_module fpga_core + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2 + +bench1_top = or2 + +bench2_top = and2_latch + +[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/preconfig_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/wrapper_io_naming.xml b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/wrapper_io_naming.xml new file mode 100644 index 000000000..616ce00dd --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/preconfig_testbench/fpga_core_wrapper_naming_rules_use_core_tb/config/wrapper_io_naming.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + From 70f40cd21ab3def57310e81e259867f93b65b6fe Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Jun 2023 10:03:19 -0700 Subject: [PATCH 129/391] [core] fixing bugs in the preconfig module when supporting dut module of fpga_core --- .../src/bitstream_manager_utils.cpp | 7 ++- .../src/bitstream_manager_utils.h | 2 +- .../verilog_preconfig_top_module.cpp | 49 +++++++------------ 3 files changed, 26 insertions(+), 32 deletions(-) diff --git a/libs/libfpgabitstream/src/bitstream_manager_utils.cpp b/libs/libfpgabitstream/src/bitstream_manager_utils.cpp index c80431793..61770ae6a 100644 --- a/libs/libfpgabitstream/src/bitstream_manager_utils.cpp +++ b/libs/libfpgabitstream/src/bitstream_manager_utils.cpp @@ -19,15 +19,20 @@ namespace openfpga { * Return a vector of the block ids, where the top-level block * locates in the head, while the leaf block locates in the tail * top, next, ... , block + * Optionally, the top block name in the path can be specified. Useful to trim the hierarchy with a given range *******************************************************************/ std::vector find_bitstream_manager_block_hierarchy( - const BitstreamManager& bitstream_manager, const ConfigBlockId& block) { + const BitstreamManager& bitstream_manager, const ConfigBlockId& block, const std::string& top_block_name) { std::vector block_hierarchy; ConfigBlockId temp_block = block; /* Generate a tree of parent block */ while (true == bitstream_manager.valid_block_id(temp_block)) { block_hierarchy.push_back(temp_block); + /* Check if we have reached the designated top block */ + if (!top_block_name.empty() && bitstream_manager.block_name(temp_block) == top_block_name) { + break; + } /* Go to upper level */ temp_block = bitstream_manager.block_parent(temp_block); } diff --git a/libs/libfpgabitstream/src/bitstream_manager_utils.h b/libs/libfpgabitstream/src/bitstream_manager_utils.h index f30793eb7..1dbd363fa 100644 --- a/libs/libfpgabitstream/src/bitstream_manager_utils.h +++ b/libs/libfpgabitstream/src/bitstream_manager_utils.h @@ -16,7 +16,7 @@ namespace openfpga { std::vector find_bitstream_manager_block_hierarchy( - const BitstreamManager& bitstream_manager, const ConfigBlockId& block); + const BitstreamManager& bitstream_manager, const ConfigBlockId& block, const std::string& top_block_name = ""); std::vector find_bitstream_manager_top_blocks( const BitstreamManager& bitstream_manager); diff --git a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp index c80a3a782..1ddd69e5e 100644 --- a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp +++ b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp @@ -306,8 +306,7 @@ static int print_verilog_preconfig_top_module_connect_global_ports( * while uses 'force' syntax to impost the bitstream at mem_inv port *******************************************************************/ static void print_verilog_preconfig_top_module_force_bitstream( - std::fstream &fp, const ModuleManager &module_manager, - const ModuleId &top_module, const BitstreamManager &bitstream_manager, + std::fstream &fp, const std::string& top_block_name, const BitstreamManager &bitstream_manager, const bool &output_datab_bits) { /* Validate the file stream */ valid_file_stream(fp); @@ -326,19 +325,10 @@ static void print_verilog_preconfig_top_module_force_bitstream( /* Build the hierarchical path of the configuration bit in modules */ std::vector block_hierarchy = find_bitstream_manager_block_hierarchy(bitstream_manager, - config_block_id); - /* Drop the first block, which is the top module, it should be replaced by - * the instance name here */ + config_block_id, top_block_name); /* Ensure that this is the module we want to drop! */ - VTR_LOG("Top module: '%s', Block[0]: '%s', Block[1]: '%s'\n", module_manager.module_name(top_module).c_str(), bitstream_manager.block_name(block_hierarchy[0]).c_str(), bitstream_manager.block_name(block_hierarchy[1]).c_str()); - VTR_ASSERT(0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0])) - || 0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[1]))); - if (0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0]))) { - block_hierarchy.erase(block_hierarchy.begin()); - } else if (0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[1]))) { - block_hierarchy.erase(block_hierarchy.begin()); - block_hierarchy.erase(block_hierarchy.begin()); - } + VTR_ASSERT(top_block_name == bitstream_manager.block_name(block_hierarchy[0])); + block_hierarchy.erase(block_hierarchy.begin()); /* Build the full hierarchy path */ std::string bit_hierarchy_path(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME); for (const ConfigBlockId &temp_block : block_hierarchy) { @@ -391,8 +381,7 @@ static void print_verilog_preconfig_top_module_force_bitstream( * This function uses '$deposit' syntax to do so *******************************************************************/ static void print_verilog_preconfig_top_module_deposit_bitstream( - std::fstream &fp, const ModuleManager &module_manager, - const ModuleId &top_module, const BitstreamManager &bitstream_manager, + std::fstream &fp, const std::string& top_block_name, const BitstreamManager &bitstream_manager, const bool &output_datab_bits) { /* Validate the file stream */ valid_file_stream(fp); @@ -411,18 +400,12 @@ static void print_verilog_preconfig_top_module_deposit_bitstream( /* Build the hierarchical path of the configuration bit in modules */ std::vector block_hierarchy = find_bitstream_manager_block_hierarchy(bitstream_manager, - config_block_id); + config_block_id, top_block_name); /* Drop the first block, which is the top module, it should be replaced by * the instance name here */ /* Ensure that this is the module we want to drop! */ - VTR_ASSERT(0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0])) - || 0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[1]))); - if (0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0]))) { - block_hierarchy.erase(block_hierarchy.begin()); - } else if (0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[1]))) { - block_hierarchy.erase(block_hierarchy.begin()); - block_hierarchy.erase(block_hierarchy.begin()); - } + VTR_ASSERT(top_block_name == bitstream_manager.block_name(block_hierarchy[0])); + block_hierarchy.erase(block_hierarchy.begin()); /* Build the full hierarchy path */ std::string bit_hierarchy_path(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME); @@ -480,8 +463,7 @@ static void print_verilog_preconfig_top_module_deposit_bitstream( * 2. Mentor Modelsim prefers using '$deposit' syntax to do so *******************************************************************/ static void print_verilog_preconfig_top_module_load_bitstream( - std::fstream &fp, const ModuleManager &module_manager, - const ModuleId &top_module, const CircuitLibrary &circuit_lib, + std::fstream &fp, const std::string& top_block_name, const CircuitLibrary &circuit_lib, const CircuitModelId &mem_model, const BitstreamManager &bitstream_manager, const e_embedded_bitstream_hdl_type &embedded_bitstream_hdl_type) { /* Skip the datab port if there is only 1 output port in memory model @@ -504,11 +486,11 @@ static void print_verilog_preconfig_top_module_load_bitstream( /* Use assign syntax for Icarus simulator */ if (EMBEDDED_BITSTREAM_HDL_IVERILOG == embedded_bitstream_hdl_type) { print_verilog_preconfig_top_module_force_bitstream( - fp, module_manager, top_module, bitstream_manager, output_datab_bits); + fp, top_block_name, bitstream_manager, output_datab_bits); /* Use deposit syntax for other simulators */ } else if (EMBEDDED_BITSTREAM_HDL_MODELSIM == embedded_bitstream_hdl_type) { print_verilog_preconfig_top_module_deposit_bitstream( - fp, module_manager, top_module, bitstream_manager, output_datab_bits); + fp, top_block_name, bitstream_manager, output_datab_bits); } print_verilog_comment( @@ -641,10 +623,17 @@ int print_verilog_preconfig_top_module( CircuitModelId sram_model = config_protocol.memory_model(); VTR_ASSERT(true == circuit_lib.valid_model_id(sram_model)); + /* If we do have the core module, and the dut is specified as core module, the hierarchy path when adding should be the instance name of the core module */ + std::string inst_name = generate_fpga_top_module_name(); + if (options.dut_module() == generate_fpga_core_module_name()) { + ModuleId parent_module = module_manager.find_module(generate_fpga_top_module_name()); + inst_name = module_manager.instance_name(parent_module, core_module, 0); + } + /* Assign FPGA internal SRAM/Memory ports to bitstream values, only output * when needed */ print_verilog_preconfig_top_module_load_bitstream( - fp, module_manager, top_module, circuit_lib, sram_model, bitstream_manager, + fp, inst_name, circuit_lib, sram_model, bitstream_manager, options.embedded_bitstream_hdl_type()); /* Add signal initialization: From 83fa6a421e3087dc3ff9655ac48fd4dff574ac93 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Jun 2023 10:06:17 -0700 Subject: [PATCH 130/391] [core] code format --- .../src/bitstream_manager_utils.cpp | 9 +++-- .../src/bitstream_manager_utils.h | 3 +- .../verilog_preconfig_top_module.cpp | 34 +++++++++++-------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/libs/libfpgabitstream/src/bitstream_manager_utils.cpp b/libs/libfpgabitstream/src/bitstream_manager_utils.cpp index 61770ae6a..9a2ef1277 100644 --- a/libs/libfpgabitstream/src/bitstream_manager_utils.cpp +++ b/libs/libfpgabitstream/src/bitstream_manager_utils.cpp @@ -19,10 +19,12 @@ namespace openfpga { * Return a vector of the block ids, where the top-level block * locates in the head, while the leaf block locates in the tail * top, next, ... , block - * Optionally, the top block name in the path can be specified. Useful to trim the hierarchy with a given range + * Optionally, the top block name in the path can be specified. Useful to trim + *the hierarchy with a given range *******************************************************************/ std::vector find_bitstream_manager_block_hierarchy( - const BitstreamManager& bitstream_manager, const ConfigBlockId& block, const std::string& top_block_name) { + const BitstreamManager& bitstream_manager, const ConfigBlockId& block, + const std::string& top_block_name) { std::vector block_hierarchy; ConfigBlockId temp_block = block; @@ -30,7 +32,8 @@ std::vector find_bitstream_manager_block_hierarchy( while (true == bitstream_manager.valid_block_id(temp_block)) { block_hierarchy.push_back(temp_block); /* Check if we have reached the designated top block */ - if (!top_block_name.empty() && bitstream_manager.block_name(temp_block) == top_block_name) { + if (!top_block_name.empty() && + bitstream_manager.block_name(temp_block) == top_block_name) { break; } /* Go to upper level */ diff --git a/libs/libfpgabitstream/src/bitstream_manager_utils.h b/libs/libfpgabitstream/src/bitstream_manager_utils.h index 1dbd363fa..e427f834c 100644 --- a/libs/libfpgabitstream/src/bitstream_manager_utils.h +++ b/libs/libfpgabitstream/src/bitstream_manager_utils.h @@ -16,7 +16,8 @@ namespace openfpga { std::vector find_bitstream_manager_block_hierarchy( - const BitstreamManager& bitstream_manager, const ConfigBlockId& block, const std::string& top_block_name = ""); + const BitstreamManager& bitstream_manager, const ConfigBlockId& block, + const std::string& top_block_name = ""); std::vector find_bitstream_manager_top_blocks( const BitstreamManager& bitstream_manager); diff --git a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp index 1ddd69e5e..18e6c5e2d 100644 --- a/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp +++ b/openfpga/src/fpga_verilog/verilog_preconfig_top_module.cpp @@ -306,8 +306,8 @@ static int print_verilog_preconfig_top_module_connect_global_ports( * while uses 'force' syntax to impost the bitstream at mem_inv port *******************************************************************/ static void print_verilog_preconfig_top_module_force_bitstream( - std::fstream &fp, const std::string& top_block_name, const BitstreamManager &bitstream_manager, - const bool &output_datab_bits) { + std::fstream &fp, const std::string &top_block_name, + const BitstreamManager &bitstream_manager, const bool &output_datab_bits) { /* Validate the file stream */ valid_file_stream(fp); @@ -324,10 +324,11 @@ static void print_verilog_preconfig_top_module_force_bitstream( } /* Build the hierarchical path of the configuration bit in modules */ std::vector block_hierarchy = - find_bitstream_manager_block_hierarchy(bitstream_manager, - config_block_id, top_block_name); + find_bitstream_manager_block_hierarchy(bitstream_manager, config_block_id, + top_block_name); /* Ensure that this is the module we want to drop! */ - VTR_ASSERT(top_block_name == bitstream_manager.block_name(block_hierarchy[0])); + VTR_ASSERT(top_block_name == + bitstream_manager.block_name(block_hierarchy[0])); block_hierarchy.erase(block_hierarchy.begin()); /* Build the full hierarchy path */ std::string bit_hierarchy_path(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME); @@ -381,8 +382,8 @@ static void print_verilog_preconfig_top_module_force_bitstream( * This function uses '$deposit' syntax to do so *******************************************************************/ static void print_verilog_preconfig_top_module_deposit_bitstream( - std::fstream &fp, const std::string& top_block_name, const BitstreamManager &bitstream_manager, - const bool &output_datab_bits) { + std::fstream &fp, const std::string &top_block_name, + const BitstreamManager &bitstream_manager, const bool &output_datab_bits) { /* Validate the file stream */ valid_file_stream(fp); @@ -399,12 +400,13 @@ static void print_verilog_preconfig_top_module_deposit_bitstream( } /* Build the hierarchical path of the configuration bit in modules */ std::vector block_hierarchy = - find_bitstream_manager_block_hierarchy(bitstream_manager, - config_block_id, top_block_name); + find_bitstream_manager_block_hierarchy(bitstream_manager, config_block_id, + top_block_name); /* Drop the first block, which is the top module, it should be replaced by * the instance name here */ /* Ensure that this is the module we want to drop! */ - VTR_ASSERT(top_block_name == bitstream_manager.block_name(block_hierarchy[0])); + VTR_ASSERT(top_block_name == + bitstream_manager.block_name(block_hierarchy[0])); block_hierarchy.erase(block_hierarchy.begin()); /* Build the full hierarchy path */ @@ -463,8 +465,9 @@ static void print_verilog_preconfig_top_module_deposit_bitstream( * 2. Mentor Modelsim prefers using '$deposit' syntax to do so *******************************************************************/ static void print_verilog_preconfig_top_module_load_bitstream( - std::fstream &fp, const std::string& top_block_name, const CircuitLibrary &circuit_lib, - const CircuitModelId &mem_model, const BitstreamManager &bitstream_manager, + std::fstream &fp, const std::string &top_block_name, + const CircuitLibrary &circuit_lib, const CircuitModelId &mem_model, + const BitstreamManager &bitstream_manager, const e_embedded_bitstream_hdl_type &embedded_bitstream_hdl_type) { /* Skip the datab port if there is only 1 output port in memory model * Currently, it assumes that the data output port is always defined while @@ -623,10 +626,13 @@ int print_verilog_preconfig_top_module( CircuitModelId sram_model = config_protocol.memory_model(); VTR_ASSERT(true == circuit_lib.valid_model_id(sram_model)); - /* If we do have the core module, and the dut is specified as core module, the hierarchy path when adding should be the instance name of the core module */ + /* If we do have the core module, and the dut is specified as core module, the + * hierarchy path when adding should be the instance name of the core module + */ std::string inst_name = generate_fpga_top_module_name(); if (options.dut_module() == generate_fpga_core_module_name()) { - ModuleId parent_module = module_manager.find_module(generate_fpga_top_module_name()); + ModuleId parent_module = + module_manager.find_module(generate_fpga_top_module_name()); inst_name = module_manager.instance_name(parent_module, core_module, 0); } From 249964e98e6fe1f4d256d6159898e16024fda5a7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Jun 2023 11:31:02 -0700 Subject: [PATCH 131/391] [doc] add documentation about the io naming rules --- docs/source/manual/file_formats/index.rst | 2 + .../manual/file_formats/io_naming_file.rst | 65 +++++++++++++++++++ .../openfpga_commands/setup_commands.rst | 2 +- 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 docs/source/manual/file_formats/io_naming_file.rst diff --git a/docs/source/manual/file_formats/index.rst b/docs/source/manual/file_formats/index.rst index 45c91d0ce..1749fc68d 100644 --- a/docs/source/manual/file_formats/index.rst +++ b/docs/source/manual/file_formats/index.rst @@ -35,3 +35,5 @@ OpenFPGA widely uses XML format for interchangable files pin_table_file clock_network + + io_naming_file diff --git a/docs/source/manual/file_formats/io_naming_file.rst b/docs/source/manual/file_formats/io_naming_file.rst new file mode 100644 index 000000000..d733be45a --- /dev/null +++ b/docs/source/manual/file_formats/io_naming_file.rst @@ -0,0 +1,65 @@ +.. _file_formats_io_naming_file: + +Fabric I/O Naming (.xml) +------------------------ + +The XML-based description language is used to describe + +- I/O names for an FPGA fabric when creating a top-level wrapper +- I/O connections between the fabric and top-level wrappers + +Using the description language, users can customize the I/O names for each pin/port of an FPGA fabric, including dummy pins (not from an FPGA fabric but required for system integration). + +Under the root node ````, naming rules can be defined line-by-line through syntax ````. + +.. code-block:: xml + + + + + +Please be aware of the following restrictions: + +.. note:: Please note that when naming rules should be applied to a port at its full size. For example, given a port of ``in[0:31]``, naming rules should cover all the 32 bits. + +.. note:: Please note that we currently only supports port splitting at the top-level wrapper. For example, there is a port ``a[0:9]`` from the FPGA fabric, it can be split to ``a0[0:4]`` and ``a1[0:4]`` at the top-level wrapper. + +.. warning:: Port grouping is **NOT** supported yet. For example, there are ports ``b[0:7]`` and ``c[0:7]`` from the FPGA fabric, it can **NOT** be grouped to a port ``bnc[0:15]`` at the top-level wrapper. + +Detailed syntax are presented as follows. + +.. option:: top_name="" + + Define the port name and width which will appear in the top-level wrapper. For example, + + .. code-block:: xml + + top_name="a[0:2]" + +.. option:: core_name="" + + Define the port name and width which exists in the current FPGA fabric. For example, + + .. note:: You can find the available ports in the current top-level module of FPGA netlists. See details in :ref:`fabric_netlists`. + + .. code-block:: xml + + core_name="gfpga_pad_GPIO_PAD[0:2]" + +.. option:: is_dummy="" + + Define if the port is a dummy one in the top-level wrapper, which does not connect to any pin/port of the current FPGA fabric. For example, + + .. note:: When a dummy port is defined. ``core_name`` is not required. + + .. code-block:: xml + + is_dummy="true" + +.. option:: direction="" + + Direction can be ``input``|``output``|``inout``. Only applicable to dummy ports. For example, + + .. code-block:: xml + + direction="input" 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 68fd3d251..7f891eecd 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst @@ -290,7 +290,7 @@ add_fpga_core_to_fabric .. option:: --io_naming - This is optional. Specify the I/O naming rules when connecting I/Os of ``fpga_core`` module to the top-level module ``fpga_top``. If not defined, the ``fpga_top`` will be the same as ``fpga_core`` w.r.t. ports. See details about the file format of I/O naming rules in :ref:`file_format_io_naming_file`. + This is optional. Specify the I/O naming rules when connecting I/Os of ``fpga_core`` module to the top-level module ``fpga_top``. If not defined, the ``fpga_top`` will be the same as ``fpga_core`` w.r.t. ports. See details about the file format of I/O naming rules in :ref:`file_formats_io_naming_file`. .. option:: --instance_name From 7958f4beca103a299144c3aba04852796a9502ce Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Jun 2023 11:54:28 -0700 Subject: [PATCH 132/391] [doc] add figure --- .../file_formats/figures/fpga_core_wrapper.png | Bin 0 -> 67398 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/source/manual/file_formats/figures/fpga_core_wrapper.png diff --git a/docs/source/manual/file_formats/figures/fpga_core_wrapper.png b/docs/source/manual/file_formats/figures/fpga_core_wrapper.png new file mode 100644 index 0000000000000000000000000000000000000000..4278c086b89eafc3d5dbafce4f9017f003280db3 GIT binary patch literal 67398 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%0A4|Hv;9yR=cAWEOu8$>}sN$h0HC;KUq5%#Ej6eBn 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%0AW=6%f$PiTLZ^m zwbHRy$=eu}%*e9)30!Rog>NpzjJq#)GYYOB--UP=xc{s1m$l@xxR}EbzjAg`Sg5Mc z2${y!O@peq=UfCuQNG?p4D7RY3g{?GjA*r*oxcYi zf{`S~7P;2z5EeR+sA+Z#{t#vVYD}xDDx;l2Eav}s55-+z_lJdXao^@l;x74M>Y!!h zJ6`1_Y`LZA1+9E}MNw{i;p2$)Jil072SKI%JQOZp;C|(k-w(UhHLWxLogC*y$?H#a z&pOSQn=ajW)1@msKM`Xv{O06+|Cv2pZN$fzDihbYDjap%5&_R$gK@=EhrB3BXr93w zHf1iQv_QYufDTT7PftW?tgLmz`sx-=XaH>3@6HxyRB+Vk7q``JznHh?7z$Fm6Lsuj zFZnn)OMPPa;`fh(f#YGa#A*XCNfN)merZVRc-rX2jYz$34~Jtqs-|fv#@pj|7(@?} z8CkUUoyYx$&06TSQ+NqCIL;+Pb3jQ@i>2ffV%6E8bY4xhJGD>R=h?(wRMv(IjPjHa zM-7SNSI6G+37H?Iu)Ld|w%$QH)=2?`~9Mx4dSM}@Acx}r>u9oxop7#c(CIv!)w zIDW0E?^6Ipulz1ETPn%q3DJGe;%1s`jm4nke-KP2`}l@VpNz5ExilS>#;(O5Hj*|s zNO{E)KCCHxROSG4Yl`4=cAa;~Okc2;U=rwNi;x+t4uD>~;2U*@<>hc22dv&Ocmb<= zmPt`tZj4-T{lp&iq`rTiBU6)%6O3;5PPX|Gi_wkuDO!>#Uh0o(W30`1Q>zz`9j&g= zBRC0X@E#`aeCZ+ZLbGBcmaJ=-)sw}$l8b|sPm3DYM|hYytEyt!Fqj~^YIxdv;w9m% zC&jq*9yIenuL(QilDKtliKNnDjb-_y3bzl(=V1rwa8}h0Lpe&4A_M~Gf7pnsTFcd5 z=^rUth~I1#pUJGze4;F}k+SY2DS7-n-u0|x22~>8JTOV;|5MzxMm2G5VXeAowLp6f zf)&9>kOYX~;R9MEAPG341|f(75+o`hparj*Kuijjhmb%?1w=6^@+JsU6pbPja1s$T z4Ilw(u@C_XrFN2lKoW8%y^U*m*Sdf2oquQ6oY{MSXMg+befC;2*jF56OMWVHvc(Yh z8bgn+@I&tw%hgqG;*m}&_p?t5N(Pk=F08_Im@-(cuCAKo<_b%!Z$W)f(D^X-O+j(+ z^I9@({iFMD3X{7fWiJoWG_#MWs4eV_>w}~n|M+T5G1c_ZUe0NILp*v|-qVWOLMTD68l4Ks zAN_I>$1?3oRM(VOEu>_yp{RT-a`MT8`!QMbinKOY$3MnARm+l(zd(tD-$pdY+6$#L zfh%i)-ZT)fYchd*1eq&}h0iDR-dja8!c#jzgAron&|0_my45fIfqCR97q} z|28X=ACh%~l@QUb2=hxgo;x<5JvDA>za#vM44~vM4C>1V2JRy#Wd+>QuIiCnX@Y;j!Xw&Av5!_=7y%%ZzTCPx#K>FDnMUxTKQXEJOk5a8m0-~CtY^#qVHAgAOh>d4ye!EBwLzccmkf%bx4Pn-^uqA` zQT*Y)(2s5jv-6-gMWDLiX7_B-YwB%mQUU|kn+BOITGXK`AE6*HdRK`DZ#ov8NQ$Qq zv>);Wdg9-2a+xX<{wbcsmo_8~s67?5_vLxP(W)txyTA%-BrJ_u1OExwxiZ1N*H2x_ zdr6^%Hz8zZy|B&}zJKG9Ng^h3>vwRmJ4Bo^@plGS+Z>Rwru=KibBNrmJB{U4eu2+< zCmj81s+TM=vIFp5q}mV}8jaXEG3FfJ{}w7n$wri%8V7Se8{45m*nBD}U1bcWIDNWp2^+AN@Jeo8qwdbG;c zY(jPn3YqcP!}0Jk1m_$8So?HHB~yubSN*RD2oiLzFGw4I_N)_8={i_-eGevVdC}9i z+YX?)i}WhDCsa*$0&b~71tEZEDu+y1_U&fL{|Lv6yDHq~Ko(XLL>rDpw z8|}BLaAp#BU9+=Ii*lz1{T9F#>d2Hu7xQs*d1C0sOC!#kYK46B8VBcGnODQm+-~>X zq~PgHy)kZqm1hs^=*O^R_^mKnObfCF8~a?x&7X*)zigjcjsz~eCsJtv(^FNYaeESB zbt9Mj!cIQjL22H4Z+n&{*QHPfu96M9WWU3JVdBz#7o%WKg%wB1>EQ$vHpzm#;hzZ> zGxHtI5(u1s)dlN7_M|HmVk$jy#*g39uNyl;?`&kd9-&3M4s%jvshkoKkxP6+!- z|KG#^n!)P}1jClDuNwS9|0I8=&+*y9-`xL7{3RB}zWQol`xlp#nfemJhw`-*TsM4B zJC&V*lvA;qhkJAj3$*e2&DzefiMj$LU>$kN_N1>ml^3>izOxE0Ji1M>M>|-*7J*M%iN@ z&$C%gO34|W5c4db?EbY9|N|kY+<1>?NO$ec)0$Hgqg3d zf|J8KES`g$85s=xR8F+lwZ2IN-6&5&1f-DFFRp_zR?ZKda8MMab!wD zs#T|pGT~hg2vIxfOxHWTLr`Ab*0g@95P;MOKe(gVWEQ^y)+Ag@Ua#cGptE>+aQ|@^ zB>leEf#tgn1G*m9KQ&y$2t9iW$@Mi={=Berrwzba8A#0QU^k68-GQ#bruIsuQlTl9 zi|eH-A0pfaG}`WZ9#-dY0Pb1l3F{)XfVW4*+zCHOQHT~M=oGNoNzR9KGq%QE1(7o5 zrf0EkL(%IM>KluAJg1Bs$L#q|B9A`AiX(?}il@0cYu+)cMW+V2(4Goe78`0(1KtQgj67}m>^-=`BoZ%41J$#tsfh2o&Rj#`3vOc0R?A*V_J literal 0 HcmV?d00001 From df17f43024892be37304e20bcc925145809b0f4a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Jun 2023 12:10:37 -0700 Subject: [PATCH 133/391] [doc] update figure --- .../figures/fpga_core_wrapper.png | Bin 67398 -> 70741 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/source/manual/file_formats/figures/fpga_core_wrapper.png b/docs/source/manual/file_formats/figures/fpga_core_wrapper.png index 4278c086b89eafc3d5dbafce4f9017f003280db3..7dbe35527c43c5d44fea8d45e34123f991fbb9e7 100644 GIT binary patch literal 70741 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 QKHvk@(${>ae*ek;07Eiong9R* literal 67398 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%0A4|Hv;9yR=cAWEOu8$>}sN$h0HC;KUq5%#Ej6eBn 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%0AW=6%f$PiTLZ^m zwbHRy$=eu}%*e9)30!Rog>NpzjJq#)GYYOB--UP=xc{s1m$l@xxR}EbzjAg`Sg5Mc z2${y!O@peq=UfCuQNG?p4D7RY3g{?GjA*r*oxcYi zf{`S~7P;2z5EeR+sA+Z#{t#vVYD}xDDx;l2Eav}s55-+z_lJdXao^@l;x74M>Y!!h zJ6`1_Y`LZA1+9E}MNw{i;p2$)Jil072SKI%JQOZp;C|(k-w(UhHLWxLogC*y$?H#a z&pOSQn=ajW)1@msKM`Xv{O06+|Cv2pZN$fzDihbYDjap%5&_R$gK@=EhrB3BXr93w zHf1iQv_QYufDTT7PftW?tgLmz`sx-=XaH>3@6HxyRB+Vk7q``JznHh?7z$Fm6Lsuj zFZnn)OMPPa;`fh(f#YGa#A*XCNfN)merZVRc-rX2jYz$34~Jtqs-|fv#@pj|7(@?} z8CkUUoyYx$&06TSQ+NqCIL;+Pb3jQ@i>2ffV%6E8bY4xhJGD>R=h?(wRMv(IjPjHa zM-7SNSI6G+37H?Iu)Ld|w%$QH)=2?`~9Mx4dSM}@Acx}r>u9oxop7#c(CIv!)w zIDW0E?^6Ipulz1ETPn%q3DJGe;%1s`jm4nke-KP2`}l@VpNz5ExilS>#;(O5Hj*|s zNO{E)KCCHxROSG4Yl`4=cAa;~Okc2;U=rwNi;x+t4uD>~;2U*@<>hc22dv&Ocmb<= zmPt`tZj4-T{lp&iq`rTiBU6)%6O3;5PPX|Gi_wkuDO!>#Uh0o(W30`1Q>zz`9j&g= zBRC0X@E#`aeCZ+ZLbGBcmaJ=-)sw}$l8b|sPm3DYM|hYytEyt!Fqj~^YIxdv;w9m% zC&jq*9yIenuL(QilDKtliKNnDjb-_y3bzl(=V1rwa8}h0Lpe&4A_M~Gf7pnsTFcd5 z=^rUth~I1#pUJGze4;F}k+SY2DS7-n-u0|x22~>8JTOV;|5MzxMm2G5VXeAowLp6f zf)&9>kOYX~;R9MEAPG341|f(75+o`hparj*Kuijjhmb%?1w=6^@+JsU6pbPja1s$T z4Ilw(u@C_XrFN2lKoW8%y^U*m*Sdf2oquQ6oY{MSXMg+befC;2*jF56OMWVHvc(Yh z8bgn+@I&tw%hgqG;*m}&_p?t5N(Pk=F08_Im@-(cuCAKo<_b%!Z$W)f(D^X-O+j(+ z^I9@({iFMD3X{7fWiJoWG_#MWs4eV_>w}~n|M+T5G1c_ZUe0NILp*v|-qVWOLMTD68l4Ks zAN_I>$1?3oRM(VOEu>_yp{RT-a`MT8`!QMbinKOY$3MnARm+l(zd(tD-$pdY+6$#L zfh%i)-ZT)fYchd*1eq&}h0iDR-dja8!c#jzgAron&|0_my45fIfqCR97q} z|28X=ACh%~l@QUb2=hxgo;x<5JvDA>za#vM44~vM4C>1V2JRy#Wd+>QuIiCnX@Y;j!Xw&Av5!_=7y%%ZzTCPx#K>FDnMUxTKQXEJOk5a8m0-~CtY^#qVHAgAOh>d4ye!EBwLzccmkf%bx4Pn-^uqA` zQT*Y)(2s5jv-6-gMWDLiX7_B-YwB%mQUU|kn+BOITGXK`AE6*HdRK`DZ#ov8NQ$Qq zv>);Wdg9-2a+xX<{wbcsmo_8~s67?5_vLxP(W)txyTA%-BrJ_u1OExwxiZ1N*H2x_ zdr6^%Hz8zZy|B&}zJKG9Ng^h3>vwRmJ4Bo^@plGS+Z>Rwru=KibBNrmJB{U4eu2+< zCmj81s+TM=vIFp5q}mV}8jaXEG3FfJ{}w7n$wri%8V7Se8{45m*nBD}U1bcWIDNWp2^+AN@Jeo8qwdbG;c zY(jPn3YqcP!}0Jk1m_$8So?HHB~yubSN*RD2oiLzFGw4I_N)_8={i_-eGevVdC}9i z+YX?)i}WhDCsa*$0&b~71tEZEDu+y1_U&fL{|Lv6yDHq~Ko(XLL>rDpw z8|}BLaAp#BU9+=Ii*lz1{T9F#>d2Hu7xQs*d1C0sOC!#kYK46B8VBcGnODQm+-~>X zq~PgHy)kZqm1hs^=*O^R_^mKnObfCF8~a?x&7X*)zigjcjsz~eCsJtv(^FNYaeESB zbt9Mj!cIQjL22H4Z+n&{*QHPfu96M9WWU3JVdBz#7o%WKg%wB1>EQ$vHpzm#;hzZ> zGxHtI5(u1s)dlN7_M|HmVk$jy#*g39uNyl;?`&kd9-&3M4s%jvshkoKkxP6+!- z|KG#^n!)P}1jClDuNwS9|0I8=&+*y9-`xL7{3RB}zWQol`xlp#nfemJhw`-*TsM4B zJC&V*lvA;qhkJAj3$*e2&DzefiMj$LU>$kN_N1>ml^3>izOxE0Ji1M>M>|-*7J*M%iN@ z&$C%gO34|W5c4db?EbY9|N|kY+<1>?NO$ec)0$Hgqg3d zf|J8KES`g$85s=xR8F+lwZ2IN-6&5&1f-DFFRp_zR?ZKda8MMab!wD zs#T|pGT~hg2vIxfOxHWTLr`Ab*0g@95P;MOKe(gVWEQ^y)+Ag@Ua#cGptE>+aQ|@^ zB>leEf#tgn1G*m9KQ&y$2t9iW$@Mi={=Berrwzba8A#0QU^k68-GQ#bruIsuQlTl9 zi|eH-A0pfaG}`WZ9#-dY0Pb1l3F{)XfVW4*+zCHOQHT~M=oGNoNzR9KGq%QE1(7o5 zrf0EkL(%IM>KluAJg1Bs$L#q|B9A`AiX(?}il@0cYu+)cMW+V2(4Goe78`0(1KtQgj67}m>^-=`BoZ%41J$#tsfh2o&Rj#`3vOc0R?A*V_J From 7974ea4426fe177dfed8f414f4c96f1d1bbc5f4a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Jun 2023 12:11:00 -0700 Subject: [PATCH 134/391] [doc] update with example --- .../manual/file_formats/io_naming_file.rst | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/docs/source/manual/file_formats/io_naming_file.rst b/docs/source/manual/file_formats/io_naming_file.rst index d733be45a..7494fe3af 100644 --- a/docs/source/manual/file_formats/io_naming_file.rst +++ b/docs/source/manual/file_formats/io_naming_file.rst @@ -15,9 +15,11 @@ Under the root node ````, naming rules can be defined line-by-line throug .. code-block:: xml - + +.. note:: If you do not need to rename a port of an FPGA fabric, there is no need to define it explicitly in the naming rules. OpenFPGA can infer it. + Please be aware of the following restrictions: .. note:: Please note that when naming rules should be applied to a port at its full size. For example, given a port of ``in[0:31]``, naming rules should cover all the 32 bits. @@ -26,6 +28,9 @@ Please be aware of the following restrictions: .. warning:: Port grouping is **NOT** supported yet. For example, there are ports ``b[0:7]`` and ``c[0:7]`` from the FPGA fabric, it can **NOT** be grouped to a port ``bnc[0:15]`` at the top-level wrapper. +Syntax +`````` + Detailed syntax are presented as follows. .. option:: top_name="" @@ -58,8 +63,36 @@ Detailed syntax are presented as follows. .. option:: direction="" - Direction can be ``input``|``output``|``inout``. Only applicable to dummy ports. For example, + Direction can be ``input`` | ``output`` | ``inout``. Only applicable to dummy ports. For example, .. code-block:: xml direction="input" + +Example +``````` + +Fig. :numref:`fig_fpga_core_wrapper` shows an example of a top-level wrapper with naming rules, which is built on top of an existing FPGA core fabric. +There is a dummy input port at the top-level wrapper. + +.. _fig_fpga_core_wrapper: + +.. figure:: figures/fpga_core_wrapper.png + :width: 100% + :alt: Illustration of a top-level wrapper on an existing FPGA core fabric + + Example of a top-level wrapper: how it interfaces between SoC and an existing FPGA core fabric + +The I/O naming in the Fig. :numref:`fig_fpga_core_wrapper`` can be described in the following XML: + +.. code-block:: xml + + + + + + + + + +Note that since port ``reset[0:0]`` require no name changes, it is not required to be defined in the XML. From 0112411101c9f34bda281a467d60254c5979e86e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Jun 2023 12:26:41 -0700 Subject: [PATCH 135/391] [doc] hotfix --- docs/source/manual/file_formats/io_naming_file.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/manual/file_formats/io_naming_file.rst b/docs/source/manual/file_formats/io_naming_file.rst index 7494fe3af..6cf9a7bc5 100644 --- a/docs/source/manual/file_formats/io_naming_file.rst +++ b/docs/source/manual/file_formats/io_naming_file.rst @@ -72,7 +72,7 @@ Detailed syntax are presented as follows. Example ``````` -Fig. :numref:`fig_fpga_core_wrapper` shows an example of a top-level wrapper with naming rules, which is built on top of an existing FPGA core fabric. +:numref:`fig_fpga_core_wrapper` shows an example of a top-level wrapper with naming rules, which is built on top of an existing FPGA core fabric. There is a dummy input port at the top-level wrapper. .. _fig_fpga_core_wrapper: @@ -83,7 +83,7 @@ There is a dummy input port at the top-level wrapper. Example of a top-level wrapper: how it interfaces between SoC and an existing FPGA core fabric -The I/O naming in the Fig. :numref:`fig_fpga_core_wrapper`` can be described in the following XML: +The I/O naming in the :numref:`fig_fpga_core_wrapper` can be described in the following XML: .. code-block:: xml From ddfb0c4afd4eb2aebca085ef713ed08defaa8b25 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Jun 2023 15:06:11 -0700 Subject: [PATCH 136/391] [core] now mock fpga top supports fpga core wrapper --- .../base/openfpga_verilog_command_template.h | 2 +- openfpga/src/base/openfpga_verilog_template.h | 7 +- openfpga/src/fpga_verilog/verilog_api.cpp | 5 +- openfpga/src/fpga_verilog/verilog_api.h | 1 + .../verilog_mock_fpga_wrapper.cpp | 115 ++++++++++++++---- .../fpga_verilog/verilog_mock_fpga_wrapper.h | 3 +- .../utils/fabric_global_port_info_utils.cpp | 16 ++- 7 files changed, 118 insertions(+), 31 deletions(-) diff --git a/openfpga/src/base/openfpga_verilog_command_template.h b/openfpga/src/base/openfpga_verilog_command_template.h index 5c9531c37..fa40faff9 100644 --- a/openfpga/src/base/openfpga_verilog_command_template.h +++ b/openfpga/src/base/openfpga_verilog_command_template.h @@ -291,7 +291,7 @@ ShellCommandId add_write_mock_fpga_wrapper_command_template( /* add an option '--top_module'*/ CommandOptionId top_module_opt = - shell_cmd.add_option("dut_module", false, + shell_cmd.add_option("top_module", false, "specify the top-level module name to be used in the " "wrapper, which matters the I/O names. Can be either " "fpga_top or fpga_core. By default, it is fpga_top."); diff --git a/openfpga/src/base/openfpga_verilog_template.h b/openfpga/src/base/openfpga_verilog_template.h index 6c7fc746b..d5b41d8e1 100644 --- a/openfpga/src/base/openfpga_verilog_template.h +++ b/openfpga/src/base/openfpga_verilog_template.h @@ -228,6 +228,7 @@ template int write_mock_fpga_wrapper_template(const T& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context) { CommandOptionId opt_output_dir = cmd.option("file"); + CommandOptionId opt_top_module = cmd.option("top_module"); CommandOptionId opt_pcf = cmd.option("pin_constraints_file"); CommandOptionId opt_bgf = cmd.option("bus_group_file"); CommandOptionId opt_explicit_port_mapping = @@ -249,6 +250,10 @@ int write_mock_fpga_wrapper_template(const T& openfpga_ctx, const Command& cmd, options.set_time_stamp(!cmd_context.option_enable(cmd, opt_no_time_stamp)); options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); + if (true == cmd_context.option_enable(cmd, opt_top_module)) { + options.set_dut_module(cmd_context.option_value(cmd, opt_top_module)); + } + if (true == cmd_context.option_enable(cmd, opt_default_net_type)) { options.set_default_net_type( cmd_context.option_value(cmd, opt_default_net_type)); @@ -271,7 +276,7 @@ int write_mock_fpga_wrapper_template(const T& openfpga_ctx, const Command& cmd, return fpga_verilog_mock_fpga_wrapper( openfpga_ctx.module_graph(), g_vpr_ctx.atom(), g_vpr_ctx.placement(), pin_constraints, bus_group, openfpga_ctx.io_location_map(), - openfpga_ctx.fabric_global_port_info(), + openfpga_ctx.io_name_map(), openfpga_ctx.fabric_global_port_info(), openfpga_ctx.vpr_netlist_annotation(), options); } diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 4690aa2e0..e5c58b5b4 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -237,6 +237,7 @@ int fpga_verilog_mock_fpga_wrapper( const ModuleManager &module_manager, const AtomContext &atom_ctx, const PlacementContext &place_ctx, const PinConstraints &pin_constraints, const BusGroup &bus_group, const IoLocationMap &io_location_map, + const IoNameMap &io_name_map, const FabricGlobalPortInfo &fabric_global_port_info, const VprNetlistAnnotation &netlist_annotation, const VerilogTestbenchOption &options) { @@ -261,8 +262,8 @@ int fpga_verilog_mock_fpga_wrapper( std::string netlist_file_path = src_dir_path + netlist_file_name; status = print_verilog_mock_fpga_wrapper( module_manager, fabric_global_port_info, atom_ctx, place_ctx, - pin_constraints, bus_group, io_location_map, netlist_annotation, - netlist_name, netlist_file_path, options); + pin_constraints, bus_group, io_location_map, io_name_map, + netlist_annotation, netlist_name, netlist_file_path, options); /* Add fname to the netlist name list */ NetlistId nlist_id = NetlistId::INVALID(); diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index db448aae5..3a590820c 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -75,6 +75,7 @@ int fpga_verilog_mock_fpga_wrapper( const ModuleManager& module_manager, const AtomContext& atom_ctx, const PlacementContext& place_ctx, const PinConstraints& pin_constraints, const BusGroup& bus_group, const IoLocationMap& io_location_map, + const IoNameMap& io_name_map, const FabricGlobalPortInfo& fabric_global_port_info, const VprNetlistAnnotation& netlist_annotation, const VerilogTestbenchOption& options); diff --git a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp index a28b9ffaa..018ab3d77 100644 --- a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp +++ b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.cpp @@ -44,7 +44,7 @@ static void print_verilog_mock_fpga_wrapper_connect_ios( std::fstream& fp, const ModuleManager& module_manager, const ModuleId& top_module, const AtomContext& atom_ctx, const PlacementContext& place_ctx, const IoLocationMap& io_location_map, - const PinConstraints& pin_constraints, + const IoNameMap& io_name_map, const PinConstraints& pin_constraints, const FabricGlobalPortInfo& global_ports, const VprNetlistAnnotation& netlist_annotation, const std::string& net_name_postfix, @@ -199,10 +199,17 @@ static void print_verilog_mock_fpga_wrapper_connect_ios( print_verilog_comment( fp, std::string("----- Blif Benchmark input " + block_name + " is mapped to FPGA IOPAD " + - module_mapped_io_port.get_name() + "[" + - std::to_string(io_index) + "] -----")); - print_verilog_wire_connection(fp, benchmark_io_port, - module_mapped_io_port, false); + module_mapped_io_port.to_verilog_string() + " -----")); + /* Consider possible I/O naming rules */ + BasicPort renamed_module_mapped_io_port = + io_name_map.fpga_top_port(module_mapped_io_port); + if (renamed_module_mapped_io_port.is_valid()) { + print_verilog_wire_connection(fp, benchmark_io_port, + renamed_module_mapped_io_port, false); + } else { + print_verilog_wire_connection(fp, benchmark_io_port, + module_mapped_io_port, false); + } } else { VTR_ASSERT(AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk)); benchmark_io_port.set_name( @@ -210,10 +217,17 @@ static void print_verilog_mock_fpga_wrapper_connect_ios( print_verilog_comment( fp, std::string("----- Blif Benchmark output " + block_name + " is mapped to FPGA IOPAD " + - module_mapped_io_port.get_name() + "[" + - std::to_string(io_index) + "] -----")); - print_verilog_wire_connection(fp, module_mapped_io_port, - benchmark_io_port, false); + module_mapped_io_port.to_verilog_string() + " -----")); + /* Consider possible I/O naming rules */ + BasicPort renamed_module_mapped_io_port = + io_name_map.fpga_top_port(module_mapped_io_port); + if (renamed_module_mapped_io_port.is_valid()) { + print_verilog_wire_connection(fp, renamed_module_mapped_io_port, + benchmark_io_port, false); + } else { + print_verilog_wire_connection(fp, module_mapped_io_port, + benchmark_io_port, false); + } } /* Mark this I/O has been used/wired */ @@ -250,8 +264,16 @@ static void print_verilog_mock_fpga_wrapper_connect_ios( std::vector default_values(module_unused_io_port.get_width(), unused_io_value); - print_verilog_wire_constant_values(fp, module_unused_io_port, - default_values); + /* Consider possible I/O naming rules */ + BasicPort renamed_module_unused_io_port = + io_name_map.fpga_top_port(module_unused_io_port); + if (renamed_module_unused_io_port.is_valid()) { + print_verilog_wire_constant_values(fp, renamed_module_unused_io_port, + default_values); + } else { + print_verilog_wire_constant_values(fp, module_unused_io_port, + default_values); + } } /* Add an empty line as a splitter */ @@ -267,7 +289,7 @@ static void print_verilog_mock_fpga_wrapper_connect_ios( static int print_verilog_mock_fpga_wrapper_connect_global_ports( std::fstream& fp, const ModuleManager& module_manager, const ModuleId& top_module, const PinConstraints& pin_constraints, - const FabricGlobalPortInfo& fabric_global_ports, + const FabricGlobalPortInfo& fabric_global_ports, const IoNameMap& io_name_map, const std::vector& benchmark_clock_port_names) { /* Validate the file stream */ valid_file_stream(fp); @@ -330,8 +352,16 @@ static int print_verilog_mock_fpga_wrapper_connect_global_ports( clock_name_to_connect += std::string(APPINST_PORT_POSTFIX); BasicPort benchmark_clock_pin(clock_name_to_connect, 1); - print_verilog_wire_connection(fp, benchmark_clock_pin, module_clock_pin, - false); + /* If io naming is applicable, just consider the renaming port */ + BasicPort actual_module_clock_pin = + io_name_map.fpga_top_port(module_clock_pin); + if (!actual_module_clock_pin.is_valid()) { + print_verilog_wire_connection(fp, benchmark_clock_pin, + module_clock_pin, false); + } else { + print_verilog_wire_connection(fp, benchmark_clock_pin, + actual_module_clock_pin, false); + } } /* Finish, go to the next */ continue; @@ -363,8 +393,16 @@ static int print_verilog_mock_fpga_wrapper_connect_global_ports( if ((false == pin_constraints.unconstrained_net(constrained_net_name)) && (false == pin_constraints.unmapped_net(constrained_net_name))) { BasicPort benchmark_pin(constrained_net_name, 1); - print_verilog_wire_connection(fp, benchmark_pin, module_global_pin, - false); + /* If io naming is applicable, just consider the renaming port */ + BasicPort actual_module_global_pin = + io_name_map.fpga_top_port(module_global_pin); + if (!actual_module_global_pin.is_valid()) { + print_verilog_wire_connection(fp, benchmark_pin, module_global_pin, + false); + } else { + print_verilog_wire_connection(fp, benchmark_pin, + actual_module_global_pin, false); + } } } } @@ -407,7 +445,7 @@ int print_verilog_mock_fpga_wrapper( const ModuleManager& module_manager, const FabricGlobalPortInfo& global_ports, const AtomContext& atom_ctx, const PlacementContext& place_ctx, const PinConstraints& pin_constraints, const BusGroup& bus_group, - const IoLocationMap& io_location_map, + const IoLocationMap& io_location_map, const IoNameMap& io_name_map, const VprNetlistAnnotation& netlist_annotation, const std::string& circuit_name, const std::string& verilog_fname, const VerilogTestbenchOption& options) { @@ -434,9 +472,37 @@ int print_verilog_mock_fpga_wrapper( print_verilog_file_header(fp, title, options.time_stamp()); /* Find the top_module */ - ModuleId top_module = - module_manager.find_module(generate_fpga_top_module_name()); - VTR_ASSERT(true == module_manager.valid_module_id(top_module)); + ModuleId top_module = module_manager.find_module(options.dut_module()); + if (!module_manager.valid_module_id(top_module)) { + VTR_LOG_ERROR( + "Unable to find the DUT module '%s'. Please check if you create " + "dedicated module when building the fabric!\n", + options.dut_module().c_str()); + return CMD_EXEC_FATAL_ERROR; + } + /* Note that we always need the core module as it contains the original port + * names before possible renaming at top-level module. If there is no core + * module, it means that the current top module is the core module */ + ModuleId core_module = + module_manager.find_module(generate_fpga_core_module_name()); + if (!module_manager.valid_module_id(core_module)) { + core_module = top_module; + } + + /* Precheck on the top module and decide if we need to consider I/O naming + * - If we do have a fpga_core module added, and dut is fpga_top, we need a + * I/O naming + * - If we do NOT have a fpga_core module added, and dut is fpga_top, we do + * NOT need a I/O naming + * - If we do have a fpga_core module added, and dut is fpga_core, we do NOT + * need a I/O naming + * - If we do NOT have a fpga_core module added, and dut is fpga_core, it + * should error out earlier. + */ + bool require_io_naming = false; + if (top_module != core_module) { + require_io_naming = true; + } /* Print module declaration */ print_verilog_module_declaration(fp, module_manager, top_module, @@ -466,16 +532,17 @@ int print_verilog_mock_fpga_wrapper( /* Connect FPGA top module global ports to constant or benchmark global * signals! */ status = print_verilog_mock_fpga_wrapper_connect_global_ports( - fp, module_manager, top_module, pin_constraints, global_ports, - benchmark_clock_port_names); + fp, module_manager, core_module, pin_constraints, global_ports, + require_io_naming ? io_name_map : IoNameMap(), benchmark_clock_port_names); if (CMD_EXEC_FATAL_ERROR == status) { return status; } /* Connect I/Os to benchmark I/Os or constant driver */ print_verilog_mock_fpga_wrapper_connect_ios( - fp, module_manager, top_module, atom_ctx, place_ctx, io_location_map, - pin_constraints, global_ports, netlist_annotation, std::string(), + fp, module_manager, core_module, atom_ctx, place_ctx, io_location_map, + require_io_naming ? io_name_map : IoNameMap(), pin_constraints, + global_ports, netlist_annotation, std::string(), std::string(APPINST_PORT_POSTFIX), std::string(APPINST_PORT_POSTFIX), benchmark_clock_port_names, (size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE); diff --git a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.h b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.h index 62f2f5d71..4632811a6 100644 --- a/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.h +++ b/openfpga/src/fpga_verilog/verilog_mock_fpga_wrapper.h @@ -13,6 +13,7 @@ #include "config_protocol.h" #include "fabric_global_port_info.h" #include "io_location_map.h" +#include "io_name_map.h" #include "module_manager.h" #include "pin_constraints.h" #include "verilog_testbench_options.h" @@ -30,7 +31,7 @@ int print_verilog_mock_fpga_wrapper( const ModuleManager& module_manager, const FabricGlobalPortInfo& global_ports, const AtomContext& atom_ctx, const PlacementContext& place_ctx, const PinConstraints& pin_constraints, const BusGroup& bus_group, - const IoLocationMap& io_location_map, + const IoLocationMap& io_location_map, const IoNameMap& io_name_map, const VprNetlistAnnotation& netlist_annotation, const std::string& circuit_name, const std::string& verilog_fname, const VerilogTestbenchOption& options); diff --git a/openfpga/src/utils/fabric_global_port_info_utils.cpp b/openfpga/src/utils/fabric_global_port_info_utils.cpp index 533df628f..aaba4b123 100644 --- a/openfpga/src/utils/fabric_global_port_info_utils.cpp +++ b/openfpga/src/utils/fabric_global_port_info_utils.cpp @@ -74,10 +74,16 @@ bool port_is_fabric_global_reset_port( const FabricGlobalPortInfo& fabric_global_port_info, const ModuleManager& module_manager, const BasicPort& port) { /* Find the top_module: the fabric global ports are always part of the ports - * of the top module */ + * of the top/core module. If there is a core module, we should consider core + * only */ ModuleId top_module = module_manager.find_module(generate_fpga_top_module_name()); VTR_ASSERT(true == module_manager.valid_module_id(top_module)); + ModuleId core_module = + module_manager.find_module(generate_fpga_core_module_name()); + if (module_manager.valid_module_id(core_module)) { + top_module = core_module; + } for (const FabricGlobalPortId& fabric_global_port_id : fabric_global_port_info.global_ports()) { @@ -107,10 +113,16 @@ FabricGlobalPortId find_fabric_global_port( const FabricGlobalPortInfo& fabric_global_port_info, const ModuleManager& module_manager, const BasicPort& port) { /* Find the top_module: the fabric global ports are always part of the ports - * of the top module */ + * of the top/core module. If there is a core module, we should consider core + * only */ ModuleId top_module = module_manager.find_module(generate_fpga_top_module_name()); VTR_ASSERT(true == module_manager.valid_module_id(top_module)); + ModuleId core_module = + module_manager.find_module(generate_fpga_core_module_name()); + if (module_manager.valid_module_id(core_module)) { + top_module = core_module; + } for (const FabricGlobalPortId& fabric_global_port_id : fabric_global_port_info.global_ports()) { From 270d6f933b7c2997010b889c48be945ecb56d455 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Jun 2023 15:26:50 -0700 Subject: [PATCH 137/391] [test] add a new testcase to validate mock wrapper --- .../mock_wrapper_example_script.openfpga | 3 +- .../mock_wrapper_bgf/config/task.conf | 1 + .../config/task.conf | 1 + .../config/counter8_bus_group.xml | 12 +++++ .../config/pin_constraints_reset.xml | 7 +++ .../mock_wrapper_fpga_core/config/task.conf | 52 +++++++++++++++++++ .../config/wrapper_io_naming.xml | 11 ++++ .../config/task.conf | 1 + .../mock_wrapper_pcf/config/task.conf | 1 + 9 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/counter8_bus_group.xml create mode 100644 openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/pin_constraints_reset.xml create mode 100644 openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/wrapper_io_naming.xml diff --git a/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga index feed9d0e7..b1095060b 100644 --- a/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/mock_wrapper_example_script.openfpga @@ -22,6 +22,7 @@ lut_truth_table_fixup # - Enabled compression on routing architecture modules # - Enable pin duplication on grid modules build_fabric --compress_routing #--verbose +${OPENFPGA_MOCK_WRAPPER_ADD_FPGA_CORE} # Write the fabric hierarchy of module graph to a file # This is used by hierarchical PnR flows @@ -44,7 +45,7 @@ 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_mock_fpga_wrapper --file ./SRC ${OPENFPGA_MOCK_WRAPPER_OPTIONS} ${OPENFPGA_MOCK_WRAPPER_BGF} ${OPENFPGA_MOCK_WRAPPER_PCF} +write_mock_fpga_wrapper --file ./SRC ${OPENFPGA_MOCK_WRAPPER_OPTIONS} ${OPENFPGA_MOCK_WRAPPER_BGF} ${OPENFPGA_MOCK_WRAPPER_PCF} # Write the Verilog testbench for FPGA fabric # - We suggest the use of same output directory as fabric Verilog netlists diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf index 1b5e4a371..ed90fa347 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_bgf/config/task.conf @@ -23,6 +23,7 @@ openfpga_repack_design_constraints= openfpga_mock_wrapper_options=--explicit_port_mapping openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= +openfpga_mock_wrapper_add_fpga_core= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf index ce8a09b7f..efda62297 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_explicit_port_mapping/config/task.conf @@ -23,6 +23,7 @@ openfpga_repack_design_constraints= openfpga_mock_wrapper_options=--explicit_port_mapping openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= +openfpga_mock_wrapper_add_fpga_core= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/counter8_bus_group.xml b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/counter8_bus_group.xml new file mode 100644 index 000000000..a0fd22f77 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/counter8_bus_group.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/pin_constraints_reset.xml b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/pin_constraints_reset.xml new file mode 100644 index 000000000..abcf209f6 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/pin_constraints_reset.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/task.conf new file mode 100644 index 000000000..9647f9374 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/task.conf @@ -0,0 +1,52 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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/mock_wrapper_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_dpram8K_dsp36_fracff_40nm_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml +openfpga_repack_design_constraints= +openfpga_mock_wrapper_options=--explicit_port_mapping +openfpga_mock_wrapper_bgf= +openfpga_mock_wrapper_pcf= +openfpga_mock_wrapper_add_fpga_core=add_fpga_core_to_fabric --io_naming ${PATH:TASK_DIR}/config/wrapper_io_naming.xml + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/counters/counter_8bit_async_reset/counter.v + +[SYNTHESIS_PARAM] +# Yosys script parameters +bench_yosys_cell_sim_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_cell_sim.v +bench_yosys_dff_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_fracff_40nm_dff_map.v +bench_yosys_bram_map_rules_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_bram.txt +bench_yosys_bram_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_bram_map.v +bench_yosys_dsp_map_verilog_common=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_yosys_techlib/k6_frac_N10_tileable_adder_chain_dpram8K_dsp36_40nm_dsp_map.v +bench_yosys_dsp_map_parameters_common=-D DSP_A_MAXWIDTH=36 -D DSP_B_MAXWIDTH=36 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_NAME=mult_36x36 +bench_read_verilog_options_common = -nolatches +bench_yosys_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_dff_flow.ys +bench_yosys_rewrite_common=${PATH:OPENFPGA_PATH}/openfpga_flow/misc/ys_tmpl_yosys_vpr_flow_with_rewrite.ys + +bench0_top = counter +bench0_openfpga_mock_wrapper_pcf=-pcf ${PATH:TASK_DIR}/config/pin_constraints_reset.xml +bench0_openfpga_mock_wrapper_bgf=-bgf ${PATH:TASK_DIR}/config/counter8_bus_group.xml + +[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/mock_wrapper/mock_wrapper_fpga_core/config/wrapper_io_naming.xml b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/wrapper_io_naming.xml new file mode 100644 index 000000000..616ce00dd --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_fpga_core/config/wrapper_io_naming.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf index 6e6d87b9f..e05f55e87 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_implicit_port_mapping/config/task.conf @@ -23,6 +23,7 @@ openfpga_repack_design_constraints= openfpga_mock_wrapper_options= openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= +openfpga_mock_wrapper_add_fpga_core= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml diff --git a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf index 3352a51bf..3e2a95a3a 100644 --- a/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/mock_wrapper/mock_wrapper_pcf/config/task.conf @@ -23,6 +23,7 @@ openfpga_repack_design_constraints=--design_constraints ${PATH:TASK_DIR}/config/ openfpga_mock_wrapper_options= openfpga_mock_wrapper_bgf= openfpga_mock_wrapper_pcf= +openfpga_mock_wrapper_add_fpga_core= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_GlobalTile4Clk_40nm.xml From 51e1547634860b043f4f6d6719b2727d0816a3ee Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 26 Jun 2023 15:32:16 -0700 Subject: [PATCH 138/391] [test] hotfix --- .../fpga_core_wrapper_naming_rules/config/task.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/task.conf index 5f23481c8..87a7f4877 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fpga_core_wrapper_naming_rules/config/task.conf @@ -22,6 +22,7 @@ openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulatio openfpga_vpr_device_layout= openfpga_fast_configuration= openfpga_wrapper_io_naming_rules=--io_naming ${PATH:TASK_DIR}/config/wrapper_io_naming.xml +openfpga_testbench_dut_module= [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml From 94899517942dd316b8d9103b80e0828f9c40274c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 06:59:32 +0000 Subject: [PATCH 139/391] Bump yosys from `f9257d3` to `596da3f` Bumps [yosys](https://github.com/YosysHQ/yosys) from `f9257d3` to `596da3f`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/f9257d319271917aac30f7bfb4f5b154ff559b18...596da3f2a64905237a013746e506bbfc16b8ccec) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index f9257d319..596da3f2a 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit f9257d319271917aac30f7bfb4f5b154ff559b18 +Subproject commit 596da3f2a64905237a013746e506bbfc16b8ccec From 37329d90bc9a0976041aa03f125998fd155bb107 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Jun 2023 00:02:39 +0000 Subject: [PATCH 140/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 8080c2671..4ccd3bd16 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1131 +1.2.1169 From 1b9aeab2a77ab052adf2ecf93315455f36691d83 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 3 Jul 2023 15:02:23 -0700 Subject: [PATCH 141/391] [lib] reorganize the source files of libfabrickey --- libs/libfabrickey/CMakeLists.txt | 4 ++-- libs/libfabrickey/src/{ => base}/fabric_key.cpp | 0 libs/libfabrickey/src/{ => base}/fabric_key.h | 0 libs/libfabrickey/src/{ => base}/fabric_key_fwd.h | 0 libs/libfabrickey/src/{ => io}/read_xml_fabric_key.cpp | 0 libs/libfabrickey/src/{ => io}/read_xml_fabric_key.h | 0 libs/libfabrickey/src/{ => io}/write_xml_fabric_key.cpp | 0 libs/libfabrickey/src/{ => io}/write_xml_fabric_key.h | 0 8 files changed, 2 insertions(+), 2 deletions(-) rename libs/libfabrickey/src/{ => base}/fabric_key.cpp (100%) rename libs/libfabrickey/src/{ => base}/fabric_key.h (100%) rename libs/libfabrickey/src/{ => base}/fabric_key_fwd.h (100%) rename libs/libfabrickey/src/{ => io}/read_xml_fabric_key.cpp (100%) rename libs/libfabrickey/src/{ => io}/read_xml_fabric_key.h (100%) rename libs/libfabrickey/src/{ => io}/write_xml_fabric_key.cpp (100%) rename libs/libfabrickey/src/{ => io}/write_xml_fabric_key.h (100%) diff --git a/libs/libfabrickey/CMakeLists.txt b/libs/libfabrickey/CMakeLists.txt index 2dffe8fd5..ac43927ac 100644 --- a/libs/libfabrickey/CMakeLists.txt +++ b/libs/libfabrickey/CMakeLists.txt @@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 3.9) project("libfabrickey") file(GLOB_RECURSE EXEC_SOURCES test/*.cpp) -file(GLOB_RECURSE LIB_SOURCES src/*.cpp) -file(GLOB_RECURSE LIB_HEADERS src/*.h) +file(GLOB_RECURSE LIB_SOURCES src/*/*.cpp) +file(GLOB_RECURSE LIB_HEADERS src/*/*.h) files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS) #Remove test executable from library diff --git a/libs/libfabrickey/src/fabric_key.cpp b/libs/libfabrickey/src/base/fabric_key.cpp similarity index 100% rename from libs/libfabrickey/src/fabric_key.cpp rename to libs/libfabrickey/src/base/fabric_key.cpp diff --git a/libs/libfabrickey/src/fabric_key.h b/libs/libfabrickey/src/base/fabric_key.h similarity index 100% rename from libs/libfabrickey/src/fabric_key.h rename to libs/libfabrickey/src/base/fabric_key.h diff --git a/libs/libfabrickey/src/fabric_key_fwd.h b/libs/libfabrickey/src/base/fabric_key_fwd.h similarity index 100% rename from libs/libfabrickey/src/fabric_key_fwd.h rename to libs/libfabrickey/src/base/fabric_key_fwd.h diff --git a/libs/libfabrickey/src/read_xml_fabric_key.cpp b/libs/libfabrickey/src/io/read_xml_fabric_key.cpp similarity index 100% rename from libs/libfabrickey/src/read_xml_fabric_key.cpp rename to libs/libfabrickey/src/io/read_xml_fabric_key.cpp diff --git a/libs/libfabrickey/src/read_xml_fabric_key.h b/libs/libfabrickey/src/io/read_xml_fabric_key.h similarity index 100% rename from libs/libfabrickey/src/read_xml_fabric_key.h rename to libs/libfabrickey/src/io/read_xml_fabric_key.h diff --git a/libs/libfabrickey/src/write_xml_fabric_key.cpp b/libs/libfabrickey/src/io/write_xml_fabric_key.cpp similarity index 100% rename from libs/libfabrickey/src/write_xml_fabric_key.cpp rename to libs/libfabrickey/src/io/write_xml_fabric_key.cpp diff --git a/libs/libfabrickey/src/write_xml_fabric_key.h b/libs/libfabrickey/src/io/write_xml_fabric_key.h similarity index 100% rename from libs/libfabrickey/src/write_xml_fabric_key.h rename to libs/libfabrickey/src/io/write_xml_fabric_key.h From 93158bdc62e6f65e12494850db05190db03bca08 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 3 Jul 2023 15:53:22 -0700 Subject: [PATCH 142/391] [lib] adding subkey feature --- libs/libfabrickey/src/base/fabric_key.cpp | 24 +++++++++++++++++++++ libs/libfabrickey/src/base/fabric_key.h | 23 ++++++++++++++++++++ libs/libfabrickey/src/base/fabric_key_fwd.h | 4 ++++ 3 files changed, 51 insertions(+) diff --git a/libs/libfabrickey/src/base/fabric_key.cpp b/libs/libfabrickey/src/base/fabric_key.cpp index f9c84103d..0ae52b821 100644 --- a/libs/libfabrickey/src/base/fabric_key.cpp +++ b/libs/libfabrickey/src/base/fabric_key.cpp @@ -39,6 +39,20 @@ FabricKey::fabric_word_line_bank_range FabricKey::wl_banks( wl_bank_ids_[region_id].end()); } +FabricKey::fabric_key_module_range FabricKey::modules() const { + return vtr::make_range(sub_key_module_ids_.begin(), + sub_key_module_ids_.end()); +} + +FabricKey::fabric_sub_key_range FabricKey::sub_keys( + const FabricKeyModuleId& module_id) const { + VTR_ASSERT(valid_module_id(module_id)); + return vtr::make_range( + sub_key_ids_.begin() + size_t(module_sub_keys_[module_id][0]), + sub_key_ids_.begin() + size_t(module_sub_keys_[module_id][0]) + + size_t(module_sub_keys_[module_id].size() - 1)); +} + /************************************************************************ * Public Accessors : Basic data query ***********************************************************************/ @@ -297,3 +311,13 @@ bool FabricKey::valid_wl_bank_id(const FabricRegionId& region_id, return (size_t(bank_id) < wl_bank_ids_[region_id].size()) && (bank_id == wl_bank_ids_[region_id][bank_id]); } + +bool FabricKey::valid_module_id(const FabricKeyModuleId& module_id) const { + return (size_t(module_id) < sub_key_module_ids_.size()) && + (module_id == sub_key_module_ids_[module_id]); +} + +bool FabricKey::valid_sub_key_id(const FabricSubKeyId& sub_key_id) const { + return (size_t(sub_key_id) < sub_key_ids_.size()) && + (sub_key_id == sub_key_ids_[sub_key_id]); +} diff --git a/libs/libfabrickey/src/base/fabric_key.h b/libs/libfabrickey/src/base/fabric_key.h index 53c9f89ac..3089b8638 100644 --- a/libs/libfabrickey/src/base/fabric_key.h +++ b/libs/libfabrickey/src/base/fabric_key.h @@ -47,12 +47,18 @@ class FabricKey { typedef vtr::vector::const_iterator fabric_word_line_bank_iterator; + typedef vtr::vector::const_iterator + fabric_sub_key_iterator; + typedef vtr::vector::const_iterator + fabric_key_module_iterator; /* Create range */ typedef vtr::Range fabric_region_range; typedef vtr::Range fabric_key_range; typedef vtr::Range fabric_bit_line_bank_range; typedef vtr::Range fabric_word_line_bank_range; + typedef vtr::Range fabric_sub_key_range; + typedef vtr::Range fabric_key_module_range; public: /* Constructors */ FabricKey(); @@ -62,6 +68,8 @@ class FabricKey { fabric_region_range regions() const; fabric_bit_line_bank_range bl_banks(const FabricRegionId& region_id) const; fabric_word_line_bank_range wl_banks(const FabricRegionId& region_id) const; + fabric_key_module_range modules() const; + fabric_sub_key_range sub_keys(const FabricKeyModuleId& module_id) const; public: /* Public Accessors: Basic data query */ /* Access all the keys of a region */ @@ -156,8 +164,11 @@ class FabricKey { const FabricBitLineBankId& bank_id) const; bool valid_wl_bank_id(const FabricRegionId& region_id, const FabricWordLineBankId& bank_id) const; + bool valid_module_id(const FabricKeyModuleId& module_id) const; + bool valid_sub_key_id(const FabricSubKeyId& sub_key_id) const; private: /* Internal data */ + /* ---- Top-level keys and regions ---- */ /* Unique ids for each region */ vtr::vector region_ids_; @@ -199,6 +210,18 @@ class FabricKey { vtr::vector>> wl_bank_data_ports_; + + /* ---- List of sub modules ---- */ + vtr::vector sub_key_module_ids_; + vtr::vector sub_key_module_names_; + vtr::vector> module_sub_keys_; + std::map module2subkey_lookup_; + + /* ---- Sub keys ---- */ + vtr::vector sub_key_ids_; + vtr::vector sub_key_names_; + vtr::vector sub_key_values_; + vtr::vector sub_key_alias_; }; #endif diff --git a/libs/libfabrickey/src/base/fabric_key_fwd.h b/libs/libfabrickey/src/base/fabric_key_fwd.h index 0c282092f..628f849d4 100644 --- a/libs/libfabrickey/src/base/fabric_key_fwd.h +++ b/libs/libfabrickey/src/base/fabric_key_fwd.h @@ -16,11 +16,15 @@ struct fabric_region_id_tag; struct fabric_key_id_tag; struct fabric_bit_line_bank_id_tag; struct fabric_word_line_bank_id_tag; +struct fabric_sub_key_id_tag; +struct fabric_key_module_id_tag; typedef vtr::StrongId FabricRegionId; typedef vtr::StrongId FabricKeyId; typedef vtr::StrongId FabricBitLineBankId; typedef vtr::StrongId FabricWordLineBankId; +typedef vtr::StrongId FabricSubKeyId; +typedef vtr::StrongId FabricKeyModuleId; /* Short declaration of class */ class FabricKey; From 20acce924e667545a621407f9feac0f90c2e530e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jul 2023 07:00:23 +0000 Subject: [PATCH 143/391] Bump yosys from `596da3f` to `14d50a1` Bumps [yosys](https://github.com/YosysHQ/yosys) from `596da3f` to `14d50a1`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/596da3f2a64905237a013746e506bbfc16b8ccec...14d50a176d59a5eac95a57a01f9e933297251d5b) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 596da3f2a..14d50a176 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 596da3f2a64905237a013746e506bbfc16b8ccec +Subproject commit 14d50a176d59a5eac95a57a01f9e933297251d5b From c2020d6cef691697d501f42fe1c13c14d7f5e213 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 4 Jul 2023 21:04:21 -0700 Subject: [PATCH 144/391] [lib] now use constants in xml io for fabric key --- .../src/io/fabric_key_xml_constants.h | 26 +++++ .../src/io/read_xml_fabric_key.cpp | 108 ++++++++++++------ .../src/io/write_xml_fabric_key.cpp | 57 +++++---- 3 files changed, 132 insertions(+), 59 deletions(-) create mode 100644 libs/libfabrickey/src/io/fabric_key_xml_constants.h diff --git a/libs/libfabrickey/src/io/fabric_key_xml_constants.h b/libs/libfabrickey/src/io/fabric_key_xml_constants.h new file mode 100644 index 000000000..f827f7c7d --- /dev/null +++ b/libs/libfabrickey/src/io/fabric_key_xml_constants.h @@ -0,0 +1,26 @@ +#ifndef FABRIC_KEY_XML_CONSTANTS_H +#define FABRIC_KEY_XML_CONSTANTS_H + +/* Constants required by XML parser */ +constexpr const char* XML_FABRIC_KEY_ROOT_NAME = "fabric_key"; +constexpr const char* XML_FABRIC_KEY_REGION_NODE_NAME = "region"; +constexpr const char* XML_FABRIC_KEY_REGION_ATTRIBUTE_ID_NAME = "id"; +constexpr const char* XML_FABRIC_KEY_KEY_NODE_NAME = "key"; +constexpr const char* XML_FABRIC_KEY_KEY_ATTRIBUTE_ID_NAME = "id"; +constexpr const char* XML_FABRIC_KEY_KEY_ATTRIBUTE_ALIAS_NAME = "alias"; +constexpr const char* XML_FABRIC_KEY_KEY_ATTRIBUTE_NAME_NAME = "name"; +constexpr const char* XML_FABRIC_KEY_KEY_ATTRIBUTE_VALUE_NAME = "value"; +constexpr const char* XML_FABRIC_KEY_KEY_ATTRIBUTE_COLUMN_NAME = "column"; +constexpr const char* XML_FABRIC_KEY_KEY_ATTRIBUTE_ROW_NAME = "row"; +constexpr const char* XML_FABRIC_KEY_BL_SHIFT_REGISTER_BANKS_NODE_NAME = + "bl_shift_register_banks"; +constexpr const char* XML_FABRIC_KEY_WL_SHIFT_REGISTER_BANKS_NODE_NAME = + "wl_shift_register_banks"; +constexpr const char* XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME = + "bank"; +constexpr const char* + XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_ID_NAME = "id"; +constexpr const char* + XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_RANGE_NAME = "range"; + +#endif diff --git a/libs/libfabrickey/src/io/read_xml_fabric_key.cpp b/libs/libfabrickey/src/io/read_xml_fabric_key.cpp index 3ae4d792f..7341d175a 100644 --- a/libs/libfabrickey/src/io/read_xml_fabric_key.cpp +++ b/libs/libfabrickey/src/io/read_xml_fabric_key.cpp @@ -19,6 +19,7 @@ /* Headers from libarchfpga */ #include "arch_error.h" +#include "fabric_key_xml_constants.h" #include "read_xml_fabric_key.h" #include "read_xml_util.h" @@ -30,7 +31,10 @@ static void read_xml_region_key(pugi::xml_node& xml_component_key, FabricKey& fabric_key, const FabricRegionId& fabric_region) { /* Find the id of component key */ - const size_t& id = get_attribute(xml_component_key, "id", loc_data).as_int(); + const size_t& id = + get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_ID_NAME, + loc_data) + .as_int(); if (false == fabric_key.valid_key_id(FabricKeyId(id))) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_component_key), @@ -41,9 +45,10 @@ static void read_xml_region_key(pugi::xml_node& xml_component_key, VTR_ASSERT_SAFE(true == fabric_key.valid_key_id(FabricKeyId(id))); /* If we have an alias, set the value as well */ - const std::string& alias = get_attribute(xml_component_key, "alias", loc_data, - pugiutil::ReqOpt::OPTIONAL) - .as_string(); + const std::string& alias = + get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_ALIAS_NAME, + loc_data, pugiutil::ReqOpt::OPTIONAL) + .as_string(); if (!alias.empty()) { fabric_key.set_key_alias(FabricKeyId(id), alias); } @@ -57,10 +62,12 @@ static void read_xml_region_key(pugi::xml_node& xml_component_key, } const std::string& name = - get_attribute(xml_component_key, "name", loc_data, required_name_value) + get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_NAME_NAME, + loc_data, required_name_value) .as_string(); const size_t& value = - get_attribute(xml_component_key, "value", loc_data, required_name_value) + get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_VALUE_NAME, + loc_data, required_name_value) .as_int(); fabric_key.set_key_name(FabricKeyId(id), name); @@ -69,10 +76,12 @@ static void read_xml_region_key(pugi::xml_node& xml_component_key, /* Parse coordinates */ vtr::Point coord; - coord.set_x(get_attribute(xml_component_key, "column", loc_data, + coord.set_x(get_attribute(xml_component_key, + XML_FABRIC_KEY_KEY_ATTRIBUTE_COLUMN_NAME, loc_data, pugiutil::ReqOpt::OPTIONAL) .as_int(-1)); - coord.set_y(get_attribute(xml_component_key, "row", loc_data, + coord.set_y(get_attribute(xml_component_key, + XML_FABRIC_KEY_KEY_ATTRIBUTE_ROW_NAME, loc_data, pugiutil::ReqOpt::OPTIONAL) .as_int(-1)); if (fabric_key.valid_key_coordinate(coord)) { @@ -88,8 +97,11 @@ static void read_xml_region_bl_shift_register_bank( pugi::xml_node& xml_bank, const pugiutil::loc_data& loc_data, FabricKey& fabric_key, const FabricRegionId& fabric_region) { /* Find the id of the bank */ - FabricBitLineBankId bank_id = - FabricBitLineBankId(get_attribute(xml_bank, "id", loc_data).as_int()); + FabricBitLineBankId bank_id = FabricBitLineBankId( + get_attribute(xml_bank, + XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_ID_NAME, + loc_data) + .as_int()); if (!fabric_key.valid_bl_bank_id(fabric_region, bank_id)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_bank), @@ -101,7 +113,10 @@ static void read_xml_region_bl_shift_register_bank( /* Parse the ports */ std::string data_ports = - get_attribute(xml_bank, "range", loc_data).as_string(); + get_attribute(xml_bank, + XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_RANGE_NAME, + loc_data) + .as_string(); /* Split with ',' if we have multiple ports */ openfpga::StringToken tokenizer(data_ports); for (const std::string& data_port : tokenizer.split(',')) { @@ -117,8 +132,9 @@ static void read_xml_region_bl_shift_register_bank( static void read_xml_region_bl_shift_register_banks( pugi::xml_node& xml_bl_bank, const pugiutil::loc_data& loc_data, FabricKey& fabric_key, const FabricRegionId& fabric_region) { - size_t num_banks = - count_children(xml_bl_bank, "bank", loc_data, pugiutil::ReqOpt::OPTIONAL); + size_t num_banks = count_children( + xml_bl_bank, XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME, loc_data, + pugiutil::ReqOpt::OPTIONAL); fabric_key.reserve_bl_shift_register_banks(fabric_region, num_banks); for (size_t ibank = 0; ibank < num_banks; ++ibank) { @@ -127,8 +143,10 @@ static void read_xml_region_bl_shift_register_banks( for (pugi::xml_node xml_bank : xml_bl_bank.children()) { /* Error out if the XML child has an invalid name! */ - if (xml_bank.name() != std::string("bank")) { - bad_tag(xml_bank, loc_data, xml_bl_bank, {"bank"}); + if (xml_bank.name() != + std::string(XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME)) { + bad_tag(xml_bank, loc_data, xml_bl_bank, + {XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME}); } read_xml_region_bl_shift_register_bank(xml_bank, loc_data, fabric_key, fabric_region); @@ -143,8 +161,11 @@ static void read_xml_region_wl_shift_register_bank( pugi::xml_node& xml_bank, const pugiutil::loc_data& loc_data, FabricKey& fabric_key, const FabricRegionId& fabric_region) { /* Find the id of the bank */ - FabricWordLineBankId bank_id = - FabricWordLineBankId(get_attribute(xml_bank, "id", loc_data).as_int()); + FabricWordLineBankId bank_id = FabricWordLineBankId( + get_attribute(xml_bank, + XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_ID_NAME, + loc_data) + .as_int()); if (!fabric_key.valid_wl_bank_id(fabric_region, bank_id)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_bank), @@ -156,7 +177,10 @@ static void read_xml_region_wl_shift_register_bank( /* Parse the ports */ std::string data_ports = - get_attribute(xml_bank, "range", loc_data).as_string(); + get_attribute(xml_bank, + XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_RANGE_NAME, + loc_data) + .as_string(); /* Split with ',' if we have multiple ports */ openfpga::StringToken tokenizer(data_ports); for (const std::string& data_port : tokenizer.split(',')) { @@ -172,8 +196,9 @@ static void read_xml_region_wl_shift_register_bank( static void read_xml_region_wl_shift_register_banks( pugi::xml_node& xml_wl_bank, const pugiutil::loc_data& loc_data, FabricKey& fabric_key, const FabricRegionId& fabric_region) { - size_t num_banks = - count_children(xml_wl_bank, "bank", loc_data, pugiutil::ReqOpt::OPTIONAL); + size_t num_banks = count_children( + xml_wl_bank, XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME, loc_data, + pugiutil::ReqOpt::OPTIONAL); fabric_key.reserve_wl_shift_register_banks(fabric_region, num_banks); for (size_t ibank = 0; ibank < num_banks; ++ibank) { @@ -182,8 +207,10 @@ static void read_xml_region_wl_shift_register_banks( for (pugi::xml_node xml_bank : xml_wl_bank.children()) { /* Error out if the XML child has an invalid name! */ - if (xml_bank.name() != std::string("bank")) { - bad_tag(xml_bank, loc_data, xml_wl_bank, {"bank"}); + if (xml_bank.name() != + std::string(XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME)) { + bad_tag(xml_bank, loc_data, xml_wl_bank, + {XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME}); } read_xml_region_wl_shift_register_bank(xml_bank, loc_data, fabric_key, fabric_region); @@ -197,8 +224,9 @@ static void read_xml_fabric_region(pugi::xml_node& xml_region, const pugiutil::loc_data& loc_data, FabricKey& fabric_key) { /* Find the unique id for the region */ - const FabricRegionId& region_id = - FabricRegionId(get_attribute(xml_region, "id", loc_data).as_int()); + const FabricRegionId& region_id = FabricRegionId( + get_attribute(xml_region, XML_FABRIC_KEY_REGION_ATTRIBUTE_ID_NAME, loc_data) + .as_int()); if (false == fabric_key.valid_region_id(region_id)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_region), "Invalid region id '%lu' (in total %lu regions)!\n", @@ -207,13 +235,14 @@ static void read_xml_fabric_region(pugi::xml_node& xml_region, VTR_ASSERT_SAFE(true == fabric_key.valid_region_id(region_id)); /* Reserve memory space for the keys in the region */ - size_t num_keys = - count_children(xml_region, "key", loc_data, pugiutil::ReqOpt::OPTIONAL); + size_t num_keys = count_children(xml_region, XML_FABRIC_KEY_KEY_NODE_NAME, + loc_data, pugiutil::ReqOpt::OPTIONAL); fabric_key.reserve_region_keys(region_id, num_keys); /* Parse the key for this region */ if (0 < num_keys) { - pugi::xml_node xml_key = get_first_child(xml_region, "key", loc_data); + pugi::xml_node xml_key = + get_first_child(xml_region, XML_FABRIC_KEY_KEY_NODE_NAME, loc_data); while (xml_key) { read_xml_region_key(xml_key, loc_data, fabric_key, region_id); xml_key = xml_key.next_sibling(xml_key.name()); @@ -221,16 +250,16 @@ static void read_xml_fabric_region(pugi::xml_node& xml_region, } /* Parse the BL shift register bank for this region */ - pugi::xml_node xml_bl_bank = - get_single_child(xml_region, "bl_shift_register_banks", loc_data, - pugiutil::ReqOpt::OPTIONAL); + pugi::xml_node xml_bl_bank = get_single_child( + xml_region, XML_FABRIC_KEY_BL_SHIFT_REGISTER_BANKS_NODE_NAME, loc_data, + pugiutil::ReqOpt::OPTIONAL); read_xml_region_bl_shift_register_banks(xml_bl_bank, loc_data, fabric_key, region_id); /* Parse the WL shift register bank for this region */ - pugi::xml_node xml_wl_bank = - get_single_child(xml_region, "wl_shift_register_banks", loc_data, - pugiutil::ReqOpt::OPTIONAL); + pugi::xml_node xml_wl_bank = get_single_child( + xml_region, XML_FABRIC_KEY_WL_SHIFT_REGISTER_BANKS_NODE_NAME, loc_data, + pugiutil::ReqOpt::OPTIONAL); read_xml_region_wl_shift_register_banks(xml_wl_bank, loc_data, fabric_key, region_id); } @@ -250,7 +279,8 @@ FabricKey read_xml_fabric_key(const char* key_fname) { try { loc_data = pugiutil::load_xml(doc, key_fname); - pugi::xml_node xml_root = get_single_child(doc, "fabric_key", loc_data); + pugi::xml_node xml_root = + get_single_child(doc, XML_FABRIC_KEY_ROOT_NAME, loc_data); size_t num_regions = std::distance(xml_root.children().begin(), xml_root.children().end()); @@ -265,8 +295,9 @@ FabricKey read_xml_fabric_key(const char* key_fname) { for (pugi::xml_node xml_region : xml_root.children()) { /* Error out if the XML child has an invalid name! */ - if (xml_region.name() != std::string("region")) { - bad_tag(xml_region, loc_data, xml_root, {"region"}); + if (xml_region.name() != std::string(XML_FABRIC_KEY_REGION_NODE_NAME)) { + bad_tag(xml_region, loc_data, xml_root, + {XML_FABRIC_KEY_REGION_NODE_NAME}); } num_keys += std::distance(xml_region.children().begin(), xml_region.children().end()); @@ -282,8 +313,9 @@ FabricKey read_xml_fabric_key(const char* key_fname) { */ for (pugi::xml_node xml_region : xml_root.children()) { /* Error out if the XML child has an invalid name! */ - if (xml_region.name() != std::string("region")) { - bad_tag(xml_region, loc_data, xml_root, {"region"}); + if (xml_region.name() != std::string(XML_FABRIC_KEY_REGION_NODE_NAME)) { + bad_tag(xml_region, loc_data, xml_root, + {XML_FABRIC_KEY_REGION_NODE_NAME}); } read_xml_fabric_region(xml_region, loc_data, fabric_key); } diff --git a/libs/libfabrickey/src/io/write_xml_fabric_key.cpp b/libs/libfabrickey/src/io/write_xml_fabric_key.cpp index 133c05ddd..049e7a36f 100644 --- a/libs/libfabrickey/src/io/write_xml_fabric_key.cpp +++ b/libs/libfabrickey/src/io/write_xml_fabric_key.cpp @@ -18,6 +18,7 @@ #include "write_xml_utils.h" /* Headers from fabrickey library */ +#include "fabric_key_xml_constants.h" #include "write_xml_fabric_key.h" /******************************************************************** @@ -36,27 +37,31 @@ static int write_xml_fabric_component_key(std::fstream& fp, } openfpga::write_tab_to_file(fp, 2); - fp << " coord = fabric_key.key_coordinate(component_key); if (fabric_key.valid_key_coordinate(coord)) { - write_xml_attribute(fp, "column", coord.x()); - write_xml_attribute(fp, "row", coord.y()); + write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_COLUMN_NAME, + coord.x()); + write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_ROW_NAME, coord.y()); } fp << "/>" @@ -86,14 +91,16 @@ static int write_xml_fabric_bl_shift_register_banks( /* Write the root node */ openfpga::write_tab_to_file(fp, 2); - fp << "" + fp << "<" << XML_FABRIC_KEY_BL_SHIFT_REGISTER_BANKS_NODE_NAME << ">" << "\n"; for (const auto& bank : fabric_key.bl_banks(region)) { openfpga::write_tab_to_file(fp, 3); - fp << "" << "\n"; } openfpga::write_tab_to_file(fp, 2); - fp << "" + fp << "" << "\n"; return 0; @@ -137,14 +146,16 @@ static int write_xml_fabric_wl_shift_register_banks( /* Write the root node */ openfpga::write_tab_to_file(fp, 2); - fp << "" + fp << "<" << XML_FABRIC_KEY_WL_SHIFT_REGISTER_BANKS_NODE_NAME << ">" << "\n"; for (const auto& bank : fabric_key.wl_banks(region)) { openfpga::write_tab_to_file(fp, 3); - fp << "" << "\n"; } openfpga::write_tab_to_file(fp, 2); - fp << "" + fp << "" << "\n"; return 0; @@ -186,7 +199,7 @@ int write_xml_fabric_key(const char* fname, const FabricKey& fabric_key) { openfpga::check_file_stream(fname, fp); /* Write the root node */ - fp << "" + fp << "<" << XML_FABRIC_KEY_ROOT_NAME << ">" << "\n"; int err_code = 0; @@ -194,7 +207,9 @@ int write_xml_fabric_key(const char* fname, const FabricKey& fabric_key) { /* Write region by region */ for (const FabricRegionId& region : fabric_key.regions()) { openfpga::write_tab_to_file(fp, 1); - fp << "\n"; /* Write shift register banks */ @@ -210,12 +225,12 @@ int write_xml_fabric_key(const char* fname, const FabricKey& fabric_key) { } openfpga::write_tab_to_file(fp, 1); - fp << "" + fp << "" << "\n"; } /* Finish writing the root node */ - fp << "" + fp << "" << "\n"; /* Close the file stream */ From fb5f59c17984d786ecd0a79772c9625cf1531da9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 00:02:44 +0000 Subject: [PATCH 145/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 4ccd3bd16..11be7f271 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1169 +1.2.1173 From ed25cf0dc42e44d5d618b60d3840fae2966ed558 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 5 Jul 2023 21:18:33 -0700 Subject: [PATCH 146/391] [lib] developing sub key io and APIs --- libs/libfabrickey/src/base/fabric_key.h | 13 +- .../src/io/fabric_key_xml_constants.h | 2 + .../src/io/read_xml_fabric_key.cpp | 190 +++++++++++++++--- 3 files changed, 172 insertions(+), 33 deletions(-) diff --git a/libs/libfabrickey/src/base/fabric_key.h b/libs/libfabrickey/src/base/fabric_key.h index 3089b8638..1765868f1 100644 --- a/libs/libfabrickey/src/base/fabric_key.h +++ b/libs/libfabrickey/src/base/fabric_key.h @@ -123,11 +123,8 @@ class FabricKey { /* Configure attributes of a key */ void set_key_name(const FabricKeyId& key_id, const std::string& name); - void set_key_value(const FabricKeyId& key_id, const size_t& value); - void set_key_alias(const FabricKeyId& key_id, const std::string& alias); - void set_key_coordinate(const FabricKeyId& key_id, const vtr::Point& coord); @@ -155,6 +152,16 @@ class FabricKey { const FabricRegionId& region_id, const FabricWordLineBankId& bank_id, const openfpga::BasicPort& data_port); + /* Reserve a number of keys to be memory efficent */ + void reserve_module_keys(const FabricKeyModuleId& module_id, const size_t& num_keys); + /* Create a new key and add it to the library, return an id */ + FabricModuleId create_module(const std::string& name); + FabricSubKeyId create_module_key(const FabricKeyModuleId& module_id); + /* Configure attributes of a sub key */ + void set_sub_key_name(const FabricSubKeyId& key_id, const std::string& name); + void set_sub_key_value(const FabricSubKeyId& key_id, const size_t& value); + void set_sub_key_alias(const FabricSubKeyId& key_id, const std::string& alias); + public: /* Public invalidators/validators */ bool valid_region_id(const FabricRegionId& region_id) const; bool valid_key_id(const FabricKeyId& key_id) const; diff --git a/libs/libfabrickey/src/io/fabric_key_xml_constants.h b/libs/libfabrickey/src/io/fabric_key_xml_constants.h index f827f7c7d..536a9b4f8 100644 --- a/libs/libfabrickey/src/io/fabric_key_xml_constants.h +++ b/libs/libfabrickey/src/io/fabric_key_xml_constants.h @@ -3,6 +3,8 @@ /* Constants required by XML parser */ constexpr const char* XML_FABRIC_KEY_ROOT_NAME = "fabric_key"; +constexpr const char* XML_FABRIC_KEY_MODULE_NODE_NAME = "module"; +constexpr const char* XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME = "name"; constexpr const char* XML_FABRIC_KEY_REGION_NODE_NAME = "region"; constexpr const char* XML_FABRIC_KEY_REGION_ATTRIBUTE_ID_NAME = "id"; constexpr const char* XML_FABRIC_KEY_KEY_NODE_NAME = "key"; diff --git a/libs/libfabrickey/src/io/read_xml_fabric_key.cpp b/libs/libfabrickey/src/io/read_xml_fabric_key.cpp index 7341d175a..8a523235a 100644 --- a/libs/libfabrickey/src/io/read_xml_fabric_key.cpp +++ b/libs/libfabrickey/src/io/read_xml_fabric_key.cpp @@ -16,6 +16,7 @@ /* Headers from openfpga util library */ #include "openfpga_port_parser.h" #include "openfpga_tokenizer.h" +#include "openfpga_reserved_words.h" /* Headers from libarchfpga */ #include "arch_error.h" @@ -23,6 +24,59 @@ #include "read_xml_fabric_key.h" #include "read_xml_util.h" +/******************************************************************** + * Parse XML codes of a to an object of FabricKey + *******************************************************************/ +static void read_xml_module_key(pugi::xml_node& xml_component_key, + const pugiutil::loc_data& loc_data, + FabricKey& fabric_key, + const FabricKeyModuleId& module_id) { + /* Find the id of component key */ + const size_t& id = + get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_ID_NAME, + loc_data) + .as_int(); + + FabricSubKeyId sub_key_id = fabric_key.sub_keys(module_id)[id]; + + if (false == fabric_key.valid_sub_key_id(sub_key_id)) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_component_key), + "Invalid 'id' attribute '%d' (in total %lu keys)!\n", id, + fabric_key.sub_keys(module_id).size()); + } + + VTR_ASSERT_SAFE(true == fabric_key.valid_sub_key_id(sub_key_id)); + + /* If we have an alias, set the value as well */ + const std::string& alias = + get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_ALIAS_NAME, + loc_data, pugiutil::ReqOpt::OPTIONAL) + .as_string(); + if (!alias.empty()) { + fabric_key.set_sub_key_alias(sub_key_id, alias); + } + + /* If we have the alias set, name and valus are optional then + * Otherwise, they are mandatory attributes + */ + pugiutil::ReqOpt required_name_value = pugiutil::ReqOpt::OPTIONAL; + if (true == alias.empty()) { + required_name_value = pugiutil::ReqOpt::REQUIRED; + } + + const std::string& name = + get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_NAME_NAME, + loc_data, required_name_value) + .as_string(); + const size_t& value = + get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_VALUE_NAME, + loc_data, required_name_value) + .as_int(); + + fabric_key.set_sub_key_name(sub_key_id, name); + fabric_key.set_sub_key_value(sub_key_id, value); +} + /******************************************************************** * Parse XML codes of a to an object of FabricKey *******************************************************************/ @@ -264,6 +318,101 @@ static void read_xml_fabric_region(pugi::xml_node& xml_region, region_id); } +/******************************************************************** + * Parse XML codes of a which is a top-level module + *******************************************************************/ +static void read_xml_fabric_key_top_module(pugi::xml_node& xml_module, + const pugiutil::loc_data& loc_data, + FabricKey& fabric_key) { + size_t num_regions = + std::distance(xml_root.children().begin(), xml_root.children().end()); + /* Reserve memory space for the region */ + fabric_key.reserve_regions(num_regions); + for (size_t iregion = 0; iregion < num_regions; ++iregion) { + fabric_key.create_region(); + } + + /* Reserve memory space for the keys */ + size_t num_keys = 0; + + for (pugi::xml_node xml_region : xml_root.children()) { + /* Error out if the XML child has an invalid name! */ + if (xml_region.name() != std::string(XML_FABRIC_KEY_REGION_NODE_NAME)) { + bad_tag(xml_region, loc_data, xml_root, + {XML_FABRIC_KEY_REGION_NODE_NAME}); + } + num_keys += std::distance(xml_region.children().begin(), + xml_region.children().end()); + } + + fabric_key.reserve_keys(num_keys); + for (size_t ikey = 0; ikey < num_keys; ++ikey) { + fabric_key.create_key(); + } + + /* Iterate over the children under this node, + * each child should be named after circuit_model + */ + for (pugi::xml_node xml_region : xml_root.children()) { + /* Error out if the XML child has an invalid name! */ + if (xml_region.name() != std::string(XML_FABRIC_KEY_REGION_NODE_NAME)) { + bad_tag(xml_region, loc_data, xml_root, + {XML_FABRIC_KEY_REGION_NODE_NAME}); + } + read_xml_fabric_region(xml_region, loc_data, fabric_key); + } +} + +/******************************************************************** + * Parse XML codes of a which is a regular module + *******************************************************************/ +static void read_xml_fabric_key_module(pugi::xml_node& xml_module, + const pugiutil::loc_data& loc_data, + FabricKey& fabric_key) { + std::string name = + get_attribute(xml_module, + XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME, + loc_data).as_string(); + FabricKeyModuleId module_id = fabric_key.create_module(name); + + /* Parse all the sub keys */ + size_t num_keys = std::distance(xml_module.children().begin(), xml_module.children().end()); + + /* Reserve for better memory efficiency */ + fabric_key.reserve_module_keys(module_id, num_keys); + for (size_t ikey = 0; ikey < num_keys; ++ikey) { + fabric_key.create_module_key(module_id); + } + + for (pugi::xml_node xml_key : xml_module.children()) { + /* Error out if the XML child has an invalid name! */ + if (xml_key.name() != std::string(XML_FABRIC_KEY_KEY_NODE_NAME)) { + bad_tag(xml_key, loc_data, xml_module, + {XML_FABRIC_KEY_KEY_NODE_NAME}); + } + read_xml_module_key(xml_key, loc_data, fabric_key, module_id); + } +} + +/******************************************************************** + * Parse XML codes of a to an object of FabricKey + * - For top-level module, we expect a fixed name. If so, we use a special parser + * - For regular module, we follow regular parser + *******************************************************************/ +static void read_xml_fabric_key_by_modules(pugi::xml_node& xml_module, + const pugiutil::loc_data& loc_data, + FabricKey& fabric_key) { + std::string name = + get_attribute(xml_module, + XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME, + loc_data).as_string(); + if (name == std::string(FPGA_TOP_MODULE_NAME)) { + read_xml_fabric_key_top_module(xml_module, loc_data, fabric_key); + } else { + read_xml_fabric_key_module(xml_module, loc_data, fabric_key); + } +} + /******************************************************************** * Parse XML codes about to an object of FabricKey *******************************************************************/ @@ -282,43 +431,24 @@ FabricKey read_xml_fabric_key(const char* key_fname) { pugi::xml_node xml_root = get_single_child(doc, XML_FABRIC_KEY_ROOT_NAME, loc_data); - size_t num_regions = + /* Under the root node, we expect only modules */ + size_t num_modules = std::distance(xml_root.children().begin(), xml_root.children().end()); - /* Reserve memory space for the region */ - fabric_key.reserve_regions(num_regions); - for (size_t iregion = 0; iregion < num_regions; ++iregion) { - fabric_key.create_region(); - } - /* Reserve memory space for the keys */ - size_t num_keys = 0; + /* Reserve for memory efficiency */ + fabric_key.reserve_modules(num_modules); - for (pugi::xml_node xml_region : xml_root.children()) { + /* Walk through the modules, fill keys one by one */ + for (pugi::xml_node xml_module : xml_root.children()) { /* Error out if the XML child has an invalid name! */ - if (xml_region.name() != std::string(XML_FABRIC_KEY_REGION_NODE_NAME)) { - bad_tag(xml_region, loc_data, xml_root, - {XML_FABRIC_KEY_REGION_NODE_NAME}); + if (xml_module.name() != std::string(XML_FABRIC_KEY_MODULE_NODE_NAME)) { + bad_tag(xml_module, loc_data, xml_root, + {XML_FABRIC_KEY_MODULE_NODE_NAME}); } - num_keys += std::distance(xml_region.children().begin(), - xml_region.children().end()); + /* Parse fabric keys by module */ + read_xml_fabric_keys_by_module(xml_module, loc_data, fabric_key); } - fabric_key.reserve_keys(num_keys); - for (size_t ikey = 0; ikey < num_keys; ++ikey) { - fabric_key.create_key(); - } - - /* Iterate over the children under this node, - * each child should be named after circuit_model - */ - for (pugi::xml_node xml_region : xml_root.children()) { - /* Error out if the XML child has an invalid name! */ - if (xml_region.name() != std::string(XML_FABRIC_KEY_REGION_NODE_NAME)) { - bad_tag(xml_region, loc_data, xml_root, - {XML_FABRIC_KEY_REGION_NODE_NAME}); - } - read_xml_fabric_region(xml_region, loc_data, fabric_key); - } } catch (pugiutil::XmlError& e) { archfpga_throw(key_fname, e.line(), "%s", e.what()); } From 82a60d64e341618ac3ce8ff7b6670c067fbd9d7c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 5 Jul 2023 23:53:16 -0700 Subject: [PATCH 147/391] [lib] add api to fabric key --- libs/libfabrickey/src/base/fabric_key.cpp | 51 +++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/libs/libfabrickey/src/base/fabric_key.cpp b/libs/libfabrickey/src/base/fabric_key.cpp index 0ae52b821..09299c337 100644 --- a/libs/libfabrickey/src/base/fabric_key.cpp +++ b/libs/libfabrickey/src/base/fabric_key.cpp @@ -277,6 +277,57 @@ void FabricKey::add_data_port_to_wl_shift_register_bank( wl_bank_data_ports_[region_id][bank_id].push_back(data_port); } +void FabricKey::reserve_module_keys(const FabricKeyModuleId& module_id, const size_t& num_keys) { + VTR_ASSERT(valid_module_id(module_id)); + module_sub_keys_[module_id].reserve(num_keys); + sub_key_ids_.reserve(sub_key_ids_.size() + num_keys); + sub_key_names_.reserve(sub_key_names_.size() + num_keys); + sub_key_values_.reserve(sub_key_values_.size() + num_keys); + sub_key_alias_.reserve(sub_key_alias_.size() + num_keys); +} + +FabricKeyModuleId FabricKey::create_module(const std::string& name) { + /* Ensure name is not duplicated */ + auto result = module2subkey_lookup_.find(name); + if (result != module2subkey_lookup_.end()) { + return FabricKeyModuleId::INVALID(); /* Return an invalid id */ + } + /* Create a new id */ + FabricKeyModuleId module_id = FabricKeyModuleId(sub_key_module_ids_.size()); + sub_key_module_names_.push_back(name); + module_sub_keys_.emplace_back(); + /* Register in lookup */ + module2subkey_lookup_[name] = module_id; + return module_id; +} + +FabricSubKeyId FabricKey::create_module_key(const FabricKeyModuleId& module_id) { + VTR_ASSERT(valid_module_id(module_id)); + /* Create a new id */ + FabricSubKeyId key_id = FabricSubKeyId(sub_key_ids_.size()); + sub_key_names_.emplace_back(); + sub_key_values_.emplace_back(); + sub_key_alias_.emplace_back(); + /* Add the new id to module */ + module_sub_keys_.emplace_back(key_id); + return key_id; +} + +void FabricKey::set_sub_key_name(const FabricSubKeyId& key_id, const std::string& name) { + VTR_ASSERT(valid_sub_key_id(key_id)); + sub_key_names_[key_id] = name; +} + +void FabricKey::set_sub_key_value(const FabricSubKeyId& key_id, const size_t& value) { + VTR_ASSERT(valid_sub_key_id(key_id)); + sub_key_values_[key_id] = value; +} + +void FabricKey::set_sub_key_alias(const FabricSubKeyId& key_id, const std::string& alias) { + VTR_ASSERT(valid_sub_key_id(key_id)); + sub_key_alias_[key_id] = alias; +} + /************************************************************************ * Internal invalidators/validators ***********************************************************************/ From 6c623d60f918b057aba1c6556bea5519d3106a98 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 6 Jul 2023 11:16:36 -0700 Subject: [PATCH 148/391] [lib] code format --- libs/libfabrickey/src/base/fabric_key.cpp | 15 +++++--- libs/libfabrickey/src/base/fabric_key.h | 12 +++---- .../src/io/read_xml_fabric_key.cpp | 35 ++++++++++--------- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/libs/libfabrickey/src/base/fabric_key.cpp b/libs/libfabrickey/src/base/fabric_key.cpp index 09299c337..3caf0ac29 100644 --- a/libs/libfabrickey/src/base/fabric_key.cpp +++ b/libs/libfabrickey/src/base/fabric_key.cpp @@ -277,7 +277,8 @@ void FabricKey::add_data_port_to_wl_shift_register_bank( wl_bank_data_ports_[region_id][bank_id].push_back(data_port); } -void FabricKey::reserve_module_keys(const FabricKeyModuleId& module_id, const size_t& num_keys) { +void FabricKey::reserve_module_keys(const FabricKeyModuleId& module_id, + const size_t& num_keys) { VTR_ASSERT(valid_module_id(module_id)); module_sub_keys_[module_id].reserve(num_keys); sub_key_ids_.reserve(sub_key_ids_.size() + num_keys); @@ -301,7 +302,8 @@ FabricKeyModuleId FabricKey::create_module(const std::string& name) { return module_id; } -FabricSubKeyId FabricKey::create_module_key(const FabricKeyModuleId& module_id) { +FabricSubKeyId FabricKey::create_module_key( + const FabricKeyModuleId& module_id) { VTR_ASSERT(valid_module_id(module_id)); /* Create a new id */ FabricSubKeyId key_id = FabricSubKeyId(sub_key_ids_.size()); @@ -313,17 +315,20 @@ FabricSubKeyId FabricKey::create_module_key(const FabricKeyModuleId& module_id) return key_id; } -void FabricKey::set_sub_key_name(const FabricSubKeyId& key_id, const std::string& name) { +void FabricKey::set_sub_key_name(const FabricSubKeyId& key_id, + const std::string& name) { VTR_ASSERT(valid_sub_key_id(key_id)); sub_key_names_[key_id] = name; } -void FabricKey::set_sub_key_value(const FabricSubKeyId& key_id, const size_t& value) { +void FabricKey::set_sub_key_value(const FabricSubKeyId& key_id, + const size_t& value) { VTR_ASSERT(valid_sub_key_id(key_id)); sub_key_values_[key_id] = value; } -void FabricKey::set_sub_key_alias(const FabricSubKeyId& key_id, const std::string& alias) { +void FabricKey::set_sub_key_alias(const FabricSubKeyId& key_id, + const std::string& alias) { VTR_ASSERT(valid_sub_key_id(key_id)); sub_key_alias_[key_id] = alias; } diff --git a/libs/libfabrickey/src/base/fabric_key.h b/libs/libfabrickey/src/base/fabric_key.h index 1765868f1..4f7e6a9de 100644 --- a/libs/libfabrickey/src/base/fabric_key.h +++ b/libs/libfabrickey/src/base/fabric_key.h @@ -153,14 +153,16 @@ class FabricKey { const openfpga::BasicPort& data_port); /* Reserve a number of keys to be memory efficent */ - void reserve_module_keys(const FabricKeyModuleId& module_id, const size_t& num_keys); + void reserve_module_keys(const FabricKeyModuleId& module_id, + const size_t& num_keys); /* Create a new key and add it to the library, return an id */ FabricModuleId create_module(const std::string& name); FabricSubKeyId create_module_key(const FabricKeyModuleId& module_id); /* Configure attributes of a sub key */ void set_sub_key_name(const FabricSubKeyId& key_id, const std::string& name); void set_sub_key_value(const FabricSubKeyId& key_id, const size_t& value); - void set_sub_key_alias(const FabricSubKeyId& key_id, const std::string& alias); + void set_sub_key_alias(const FabricSubKeyId& key_id, + const std::string& alias); public: /* Public invalidators/validators */ bool valid_region_id(const FabricRegionId& region_id) const; @@ -178,25 +180,19 @@ class FabricKey { /* ---- Top-level keys and regions ---- */ /* Unique ids for each region */ vtr::vector region_ids_; - /* Key ids for each region */ vtr::vector> region_key_ids_; /* Unique ids for each key */ vtr::vector key_ids_; - /* Names for each key */ vtr::vector key_names_; - /* Values for each key */ vtr::vector key_values_; - /* Values for each key */ vtr::vector> key_coordinates_; - /* Region for each key */ vtr::vector key_regions_; - /* Optional alias for each key, with which a key can also be represented */ vtr::vector key_alias_; diff --git a/libs/libfabrickey/src/io/read_xml_fabric_key.cpp b/libs/libfabrickey/src/io/read_xml_fabric_key.cpp index 8a523235a..038ca4ae7 100644 --- a/libs/libfabrickey/src/io/read_xml_fabric_key.cpp +++ b/libs/libfabrickey/src/io/read_xml_fabric_key.cpp @@ -15,8 +15,8 @@ /* Headers from openfpga util library */ #include "openfpga_port_parser.h" -#include "openfpga_tokenizer.h" #include "openfpga_reserved_words.h" +#include "openfpga_tokenizer.h" /* Headers from libarchfpga */ #include "arch_error.h" @@ -341,8 +341,8 @@ static void read_xml_fabric_key_top_module(pugi::xml_node& xml_module, bad_tag(xml_region, loc_data, xml_root, {XML_FABRIC_KEY_REGION_NODE_NAME}); } - num_keys += std::distance(xml_region.children().begin(), - xml_region.children().end()); + num_keys += + std::distance(xml_region.children().begin(), xml_region.children().end()); } fabric_key.reserve_keys(num_keys); @@ -369,14 +369,15 @@ static void read_xml_fabric_key_top_module(pugi::xml_node& xml_module, static void read_xml_fabric_key_module(pugi::xml_node& xml_module, const pugiutil::loc_data& loc_data, FabricKey& fabric_key) { - std::string name = - get_attribute(xml_module, - XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME, - loc_data).as_string(); - FabricKeyModuleId module_id = fabric_key.create_module(name); + std::string name = + get_attribute(xml_module, XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME, + loc_data) + .as_string(); + FabricKeyModuleId module_id = fabric_key.create_module(name); /* Parse all the sub keys */ - size_t num_keys = std::distance(xml_module.children().begin(), xml_module.children().end()); + size_t num_keys = + std::distance(xml_module.children().begin(), xml_module.children().end()); /* Reserve for better memory efficiency */ fabric_key.reserve_module_keys(module_id, num_keys); @@ -387,8 +388,7 @@ static void read_xml_fabric_key_module(pugi::xml_node& xml_module, for (pugi::xml_node xml_key : xml_module.children()) { /* Error out if the XML child has an invalid name! */ if (xml_key.name() != std::string(XML_FABRIC_KEY_KEY_NODE_NAME)) { - bad_tag(xml_key, loc_data, xml_module, - {XML_FABRIC_KEY_KEY_NODE_NAME}); + bad_tag(xml_key, loc_data, xml_module, {XML_FABRIC_KEY_KEY_NODE_NAME}); } read_xml_module_key(xml_key, loc_data, fabric_key, module_id); } @@ -396,16 +396,17 @@ static void read_xml_fabric_key_module(pugi::xml_node& xml_module, /******************************************************************** * Parse XML codes of a to an object of FabricKey - * - For top-level module, we expect a fixed name. If so, we use a special parser + * - For top-level module, we expect a fixed name. If so, we use a special + *parser * - For regular module, we follow regular parser *******************************************************************/ static void read_xml_fabric_key_by_modules(pugi::xml_node& xml_module, const pugiutil::loc_data& loc_data, FabricKey& fabric_key) { - std::string name = - get_attribute(xml_module, - XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME, - loc_data).as_string(); + std::string name = + get_attribute(xml_module, XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME, + loc_data) + .as_string(); if (name == std::string(FPGA_TOP_MODULE_NAME)) { read_xml_fabric_key_top_module(xml_module, loc_data, fabric_key); } else { @@ -432,7 +433,7 @@ FabricKey read_xml_fabric_key(const char* key_fname) { get_single_child(doc, XML_FABRIC_KEY_ROOT_NAME, loc_data); /* Under the root node, we expect only modules */ - size_t num_modules = + size_t num_modules = std::distance(xml_root.children().begin(), xml_root.children().end()); /* Reserve for memory efficiency */ From 74e776f3b04c2d12ec4624b7361760c3acb85d34 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 6 Jul 2023 11:57:22 -0700 Subject: [PATCH 149/391] [lib] syntax errors and now fabric key is under the namespace of openfpga --- libs/libfabrickey/src/base/fabric_key.cpp | 19 ++++++++---- libs/libfabrickey/src/base/fabric_key.h | 26 ++++++++++------ libs/libfabrickey/src/base/fabric_key_fwd.h | 4 +++ .../src/io/fabric_key_xml_constants.h | 4 +++ .../src/io/read_xml_fabric_key.cpp | 31 ++++++++++--------- .../libfabrickey/src/io/read_xml_fabric_key.h | 5 +++ .../src/io/write_xml_fabric_key.cpp | 4 +++ .../src/io/write_xml_fabric_key.h | 5 +++ libs/libfabrickey/test/test_fabric_key.cpp | 4 +-- 9 files changed, 70 insertions(+), 32 deletions(-) diff --git a/libs/libfabrickey/src/base/fabric_key.cpp b/libs/libfabrickey/src/base/fabric_key.cpp index 3caf0ac29..3f6b9b266 100644 --- a/libs/libfabrickey/src/base/fabric_key.cpp +++ b/libs/libfabrickey/src/base/fabric_key.cpp @@ -5,6 +5,8 @@ #include "vtr_assert.h" #include "vtr_log.h" +namespace openfpga { // Begin namespace openfpga + /************************************************************************ * Member functions for class FabricKey ***********************************************************************/ @@ -44,13 +46,10 @@ FabricKey::fabric_key_module_range FabricKey::modules() const { sub_key_module_ids_.end()); } -FabricKey::fabric_sub_key_range FabricKey::sub_keys( +std::vector FabricKey::sub_keys( const FabricKeyModuleId& module_id) const { VTR_ASSERT(valid_module_id(module_id)); - return vtr::make_range( - sub_key_ids_.begin() + size_t(module_sub_keys_[module_id][0]), - sub_key_ids_.begin() + size_t(module_sub_keys_[module_id][0]) + - size_t(module_sub_keys_[module_id].size() - 1)); + return module_sub_keys_[module_id]; } /************************************************************************ @@ -277,6 +276,12 @@ void FabricKey::add_data_port_to_wl_shift_register_bank( wl_bank_data_ports_[region_id][bank_id].push_back(data_port); } +void FabricKey::reserve_modules(const size_t& num_modules) { + sub_key_module_ids_.reserve(num_modules); + sub_key_module_names_.reserve(num_modules); + module_sub_keys_.reserve(num_modules); +} + void FabricKey::reserve_module_keys(const FabricKeyModuleId& module_id, const size_t& num_keys) { VTR_ASSERT(valid_module_id(module_id)); @@ -311,7 +316,7 @@ FabricSubKeyId FabricKey::create_module_key( sub_key_values_.emplace_back(); sub_key_alias_.emplace_back(); /* Add the new id to module */ - module_sub_keys_.emplace_back(key_id); + module_sub_keys_[module_id].emplace_back(key_id); return key_id; } @@ -377,3 +382,5 @@ bool FabricKey::valid_sub_key_id(const FabricSubKeyId& sub_key_id) const { return (size_t(sub_key_id) < sub_key_ids_.size()) && (sub_key_id == sub_key_ids_[sub_key_id]); } + +} // End of namespace openfpga diff --git a/libs/libfabrickey/src/base/fabric_key.h b/libs/libfabrickey/src/base/fabric_key.h index 4f7e6a9de..3bfedd4ea 100644 --- a/libs/libfabrickey/src/base/fabric_key.h +++ b/libs/libfabrickey/src/base/fabric_key.h @@ -16,6 +16,8 @@ #include "fabric_key_fwd.h" #include "openfpga_port.h" +namespace openfpga { // Begin namespace openfpga + /******************************************************************** * A data structure to describe a secure key for fabric organization * A fabric may consist of multiple regions @@ -69,7 +71,8 @@ class FabricKey { fabric_bit_line_bank_range bl_banks(const FabricRegionId& region_id) const; fabric_word_line_bank_range wl_banks(const FabricRegionId& region_id) const; fabric_key_module_range modules() const; - fabric_sub_key_range sub_keys(const FabricKeyModuleId& module_id) const; + std::vector sub_keys( + const FabricKeyModuleId& module_id) const; public: /* Public Accessors: Basic data query */ /* Access all the keys of a region */ @@ -92,12 +95,12 @@ class FabricKey { /* Return a list of data ports which will be driven by a BL shift register * bank */ - std::vector bl_bank_data_ports( + std::vector bl_bank_data_ports( const FabricRegionId& region_id, const FabricBitLineBankId& bank_id) const; /* Return a list of data ports which will be driven by a WL shift register * bank */ - std::vector wl_bank_data_ports( + std::vector wl_bank_data_ports( const FabricRegionId& region_id, const FabricWordLineBankId& bank_id) const; public: /* Public Mutators: model-related */ @@ -141,7 +144,7 @@ class FabricKey { /* Add a data port to a given BL shift register bank */ void add_data_port_to_bl_shift_register_bank( const FabricRegionId& region_id, const FabricBitLineBankId& bank_id, - const openfpga::BasicPort& data_port); + const BasicPort& data_port); /* Create a new shift register bank for WLs and return an id */ FabricWordLineBankId create_wl_shift_register_bank( @@ -150,13 +153,14 @@ class FabricKey { /* Add a data port to a given WL shift register bank */ void add_data_port_to_wl_shift_register_bank( const FabricRegionId& region_id, const FabricWordLineBankId& bank_id, - const openfpga::BasicPort& data_port); + const BasicPort& data_port); /* Reserve a number of keys to be memory efficent */ + void reserve_modules(const size_t& num_modules); void reserve_module_keys(const FabricKeyModuleId& module_id, const size_t& num_keys); /* Create a new key and add it to the library, return an id */ - FabricModuleId create_module(const std::string& name); + FabricKeyModuleId create_module(const std::string& name); FabricSubKeyId create_module_key(const FabricKeyModuleId& module_id); /* Configure attributes of a sub key */ void set_sub_key_name(const FabricSubKeyId& key_id, const std::string& name); @@ -201,8 +205,8 @@ class FabricKey { vtr::vector> bl_bank_ids_; /* Data ports to be connected to each BL shift register bank */ - vtr::vector>> + vtr::vector>> bl_bank_data_ports_; /* Unique ids for each WL shift register bank */ @@ -210,8 +214,8 @@ class FabricKey { vtr::vector> wl_bank_ids_; /* Data ports to be connected to each WL shift register bank */ - vtr::vector>> + vtr::vector>> wl_bank_data_ports_; /* ---- List of sub modules ---- */ @@ -227,4 +231,6 @@ class FabricKey { vtr::vector sub_key_alias_; }; +} // End of namespace openfpga + #endif diff --git a/libs/libfabrickey/src/base/fabric_key_fwd.h b/libs/libfabrickey/src/base/fabric_key_fwd.h index 628f849d4..937977c16 100644 --- a/libs/libfabrickey/src/base/fabric_key_fwd.h +++ b/libs/libfabrickey/src/base/fabric_key_fwd.h @@ -12,6 +12,8 @@ #include "vtr_strong_id.h" +namespace openfpga { // Begin namespace openfpga + struct fabric_region_id_tag; struct fabric_key_id_tag; struct fabric_bit_line_bank_id_tag; @@ -29,4 +31,6 @@ typedef vtr::StrongId FabricKeyModuleId; /* Short declaration of class */ class FabricKey; +} // End of namespace openfpga + #endif diff --git a/libs/libfabrickey/src/io/fabric_key_xml_constants.h b/libs/libfabrickey/src/io/fabric_key_xml_constants.h index 536a9b4f8..726c8f265 100644 --- a/libs/libfabrickey/src/io/fabric_key_xml_constants.h +++ b/libs/libfabrickey/src/io/fabric_key_xml_constants.h @@ -1,6 +1,8 @@ #ifndef FABRIC_KEY_XML_CONSTANTS_H #define FABRIC_KEY_XML_CONSTANTS_H +namespace openfpga { // Begin namespace openfpga + /* Constants required by XML parser */ constexpr const char* XML_FABRIC_KEY_ROOT_NAME = "fabric_key"; constexpr const char* XML_FABRIC_KEY_MODULE_NODE_NAME = "module"; @@ -25,4 +27,6 @@ constexpr const char* constexpr const char* XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_RANGE_NAME = "range"; +} // End of namespace openfpga + #endif diff --git a/libs/libfabrickey/src/io/read_xml_fabric_key.cpp b/libs/libfabrickey/src/io/read_xml_fabric_key.cpp index 038ca4ae7..a89dffd3d 100644 --- a/libs/libfabrickey/src/io/read_xml_fabric_key.cpp +++ b/libs/libfabrickey/src/io/read_xml_fabric_key.cpp @@ -24,6 +24,8 @@ #include "read_xml_fabric_key.h" #include "read_xml_util.h" +namespace openfpga { // Begin namespace openfpga + /******************************************************************** * Parse XML codes of a to an object of FabricKey *******************************************************************/ @@ -32,10 +34,9 @@ static void read_xml_module_key(pugi::xml_node& xml_component_key, FabricKey& fabric_key, const FabricKeyModuleId& module_id) { /* Find the id of component key */ - const size_t& id = - get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_ID_NAME, - loc_data) - .as_int(); + size_t id = get_attribute(xml_component_key, + XML_FABRIC_KEY_KEY_ATTRIBUTE_ID_NAME, loc_data) + .as_int(); FabricSubKeyId sub_key_id = fabric_key.sub_keys(module_id)[id]; @@ -325,7 +326,7 @@ static void read_xml_fabric_key_top_module(pugi::xml_node& xml_module, const pugiutil::loc_data& loc_data, FabricKey& fabric_key) { size_t num_regions = - std::distance(xml_root.children().begin(), xml_root.children().end()); + std::distance(xml_module.children().begin(), xml_module.children().end()); /* Reserve memory space for the region */ fabric_key.reserve_regions(num_regions); for (size_t iregion = 0; iregion < num_regions; ++iregion) { @@ -335,10 +336,10 @@ static void read_xml_fabric_key_top_module(pugi::xml_node& xml_module, /* Reserve memory space for the keys */ size_t num_keys = 0; - for (pugi::xml_node xml_region : xml_root.children()) { + for (pugi::xml_node xml_region : xml_module.children()) { /* Error out if the XML child has an invalid name! */ if (xml_region.name() != std::string(XML_FABRIC_KEY_REGION_NODE_NAME)) { - bad_tag(xml_region, loc_data, xml_root, + bad_tag(xml_region, loc_data, xml_module, {XML_FABRIC_KEY_REGION_NODE_NAME}); } num_keys += @@ -353,10 +354,10 @@ static void read_xml_fabric_key_top_module(pugi::xml_node& xml_module, /* Iterate over the children under this node, * each child should be named after circuit_model */ - for (pugi::xml_node xml_region : xml_root.children()) { + for (pugi::xml_node xml_region : xml_module.children()) { /* Error out if the XML child has an invalid name! */ if (xml_region.name() != std::string(XML_FABRIC_KEY_REGION_NODE_NAME)) { - bad_tag(xml_region, loc_data, xml_root, + bad_tag(xml_region, loc_data, xml_module, {XML_FABRIC_KEY_REGION_NODE_NAME}); } read_xml_fabric_region(xml_region, loc_data, fabric_key); @@ -400,14 +401,14 @@ static void read_xml_fabric_key_module(pugi::xml_node& xml_module, *parser * - For regular module, we follow regular parser *******************************************************************/ -static void read_xml_fabric_key_by_modules(pugi::xml_node& xml_module, - const pugiutil::loc_data& loc_data, - FabricKey& fabric_key) { +static void read_xml_fabric_keys_by_modules(pugi::xml_node& xml_module, + const pugiutil::loc_data& loc_data, + FabricKey& fabric_key) { std::string name = get_attribute(xml_module, XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME, loc_data) .as_string(); - if (name == std::string(FPGA_TOP_MODULE_NAME)) { + if (name == std::string(openfpga::FPGA_TOP_MODULE_NAME)) { read_xml_fabric_key_top_module(xml_module, loc_data, fabric_key); } else { read_xml_fabric_key_module(xml_module, loc_data, fabric_key); @@ -447,7 +448,7 @@ FabricKey read_xml_fabric_key(const char* key_fname) { {XML_FABRIC_KEY_MODULE_NODE_NAME}); } /* Parse fabric keys by module */ - read_xml_fabric_keys_by_module(xml_module, loc_data, fabric_key); + read_xml_fabric_keys_by_modules(xml_module, loc_data, fabric_key); } } catch (pugiutil::XmlError& e) { @@ -456,3 +457,5 @@ FabricKey read_xml_fabric_key(const char* key_fname) { return fabric_key; } + +} // End of namespace openfpga diff --git a/libs/libfabrickey/src/io/read_xml_fabric_key.h b/libs/libfabrickey/src/io/read_xml_fabric_key.h index c017aba6f..49ebbab34 100644 --- a/libs/libfabrickey/src/io/read_xml_fabric_key.h +++ b/libs/libfabrickey/src/io/read_xml_fabric_key.h @@ -9,6 +9,11 @@ /******************************************************************** * Function declaration *******************************************************************/ + +namespace openfpga { // Begin namespace openfpga + FabricKey read_xml_fabric_key(const char* key_fname); +} // End of namespace openfpga + #endif diff --git a/libs/libfabrickey/src/io/write_xml_fabric_key.cpp b/libs/libfabrickey/src/io/write_xml_fabric_key.cpp index 049e7a36f..9a6d0e29b 100644 --- a/libs/libfabrickey/src/io/write_xml_fabric_key.cpp +++ b/libs/libfabrickey/src/io/write_xml_fabric_key.cpp @@ -21,6 +21,8 @@ #include "fabric_key_xml_constants.h" #include "write_xml_fabric_key.h" +namespace openfpga { // Begin namespace openfpga + /******************************************************************** * A writer to output a component key to XML format * @@ -238,3 +240,5 @@ int write_xml_fabric_key(const char* fname, const FabricKey& fabric_key) { return err_code; } + +} // End of namespace openfpga diff --git a/libs/libfabrickey/src/io/write_xml_fabric_key.h b/libs/libfabrickey/src/io/write_xml_fabric_key.h index a47c2602e..0e7d8e35b 100644 --- a/libs/libfabrickey/src/io/write_xml_fabric_key.h +++ b/libs/libfabrickey/src/io/write_xml_fabric_key.h @@ -11,6 +11,11 @@ /******************************************************************** * Function declaration *******************************************************************/ + +namespace openfpga { // Begin namespace openfpga + int write_xml_fabric_key(const char* fname, const FabricKey& fabric_key); +} // End of namespace openfpga + #endif diff --git a/libs/libfabrickey/test/test_fabric_key.cpp b/libs/libfabrickey/test/test_fabric_key.cpp index 014c53bef..7d412f64f 100644 --- a/libs/libfabrickey/test/test_fabric_key.cpp +++ b/libs/libfabrickey/test/test_fabric_key.cpp @@ -16,14 +16,14 @@ int main(int argc, const char** argv) { VTR_ASSERT((2 == argc) || (3 == argc)); /* Parse the fabric key from an XML file */ - FabricKey test_key = read_xml_fabric_key(argv[1]); + openfpga::FabricKey test_key = openfpga::read_xml_fabric_key(argv[1]); VTR_LOG("Read the fabric key from an XML file: %s.\n", argv[1]); /* Output the circuit library to an XML file * This is optional only used when there is a second argument */ if (3 <= argc) { - write_xml_fabric_key(argv[2], test_key); + openfpga::write_xml_fabric_key(argv[2], test_key); VTR_LOG("Echo the fabric key to an XML file: %s.\n", argv[2]); } } From 85f9899588b47d9bd0c5cab412e36183f666200a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 6 Jul 2023 16:30:36 -0700 Subject: [PATCH 150/391] [lib] fixed some bugs and now fabric key io is working --- libs/libfabrickey/src/base/fabric_key.cpp | 25 +++ libs/libfabrickey/src/base/fabric_key.h | 9 +- .../src/io/write_xml_fabric_key.cpp | 162 ++++++++++++++---- 3 files changed, 163 insertions(+), 33 deletions(-) diff --git a/libs/libfabrickey/src/base/fabric_key.cpp b/libs/libfabrickey/src/base/fabric_key.cpp index 3f6b9b266..b80d87204 100644 --- a/libs/libfabrickey/src/base/fabric_key.cpp +++ b/libs/libfabrickey/src/base/fabric_key.cpp @@ -100,6 +100,29 @@ std::vector FabricKey::wl_bank_data_ports( return wl_bank_data_ports_[region_id][bank_id]; } +std::string FabricKey::module_name(const FabricKeyModuleId& module_id) const { + VTR_ASSERT(valid_module_id(module_id)); + return sub_key_module_names_[module_id]; +} + +std::string FabricKey::sub_key_name(const FabricSubKeyId& key_id) const { + /* validate the key_id */ + VTR_ASSERT(valid_sub_key_id(key_id)); + return sub_key_names_[key_id]; +} + +size_t FabricKey::sub_key_value(const FabricSubKeyId& key_id) const { + /* validate the key_id */ + VTR_ASSERT(valid_sub_key_id(key_id)); + return sub_key_values_[key_id]; +} + +std::string FabricKey::sub_key_alias(const FabricSubKeyId& key_id) const { + /* validate the key_id */ + VTR_ASSERT(valid_sub_key_id(key_id)); + return sub_key_alias_[key_id]; +} + /************************************************************************ * Public Mutators ***********************************************************************/ @@ -300,6 +323,7 @@ FabricKeyModuleId FabricKey::create_module(const std::string& name) { } /* Create a new id */ FabricKeyModuleId module_id = FabricKeyModuleId(sub_key_module_ids_.size()); + sub_key_module_ids_.push_back(module_id); sub_key_module_names_.push_back(name); module_sub_keys_.emplace_back(); /* Register in lookup */ @@ -312,6 +336,7 @@ FabricSubKeyId FabricKey::create_module_key( VTR_ASSERT(valid_module_id(module_id)); /* Create a new id */ FabricSubKeyId key_id = FabricSubKeyId(sub_key_ids_.size()); + sub_key_ids_.push_back(key_id); sub_key_names_.emplace_back(); sub_key_values_.emplace_back(); sub_key_alias_.emplace_back(); diff --git a/libs/libfabrickey/src/base/fabric_key.h b/libs/libfabrickey/src/base/fabric_key.h index 3bfedd4ea..7afd43af7 100644 --- a/libs/libfabrickey/src/base/fabric_key.h +++ b/libs/libfabrickey/src/base/fabric_key.h @@ -77,16 +77,12 @@ class FabricKey { public: /* Public Accessors: Basic data query */ /* Access all the keys of a region */ std::vector region_keys(const FabricRegionId& region_id) const; - /* Access the name of a key */ std::string key_name(const FabricKeyId& key_id) const; - /* Access the value of a key */ size_t key_value(const FabricKeyId& key_id) const; - /* Access the alias of a key */ std::string key_alias(const FabricKeyId& key_id) const; - /* Access the coordinate of a key */ vtr::Point key_coordinate(const FabricKeyId& key_id) const; @@ -103,6 +99,11 @@ class FabricKey { std::vector wl_bank_data_ports( const FabricRegionId& region_id, const FabricWordLineBankId& bank_id) const; + std::string module_name(const FabricKeyModuleId& module_id) const; + std::string sub_key_name(const FabricSubKeyId& key_id) const; + size_t sub_key_value(const FabricSubKeyId& key_id) const; + std::string sub_key_alias(const FabricSubKeyId& key_id) const; + public: /* Public Mutators: model-related */ /* Reserve a number of regions to be memory efficent */ void reserve_regions(const size_t& num_regions); diff --git a/libs/libfabrickey/src/io/write_xml_fabric_key.cpp b/libs/libfabrickey/src/io/write_xml_fabric_key.cpp index 9a6d0e29b..25fd51616 100644 --- a/libs/libfabrickey/src/io/write_xml_fabric_key.cpp +++ b/libs/libfabrickey/src/io/write_xml_fabric_key.cpp @@ -13,6 +13,7 @@ /* Headers from openfpga util library */ #include "openfpga_digest.h" +#include "openfpga_reserved_words.h" /* Headers from arch openfpga library */ #include "write_xml_utils.h" @@ -23,6 +24,48 @@ namespace openfpga { // Begin namespace openfpga +/******************************************************************** + * A writer to output a component sub key to XML format + * + * Return 0 if successful + * Return 1 if there are more serious bugs in the architecture + * Return 2 if fail when creating files + *******************************************************************/ +static int write_xml_fabric_component_sub_key( + std::fstream& fp, const FabricKey& fabric_key, + const FabricSubKeyId& component_key, const size_t& key_idx, + const size_t& level) { + /* Validate the file stream */ + if (false == openfpga::valid_file_stream(fp)) { + return 2; + } + + openfpga::write_tab_to_file(fp, level); + fp << "<" << XML_FABRIC_KEY_KEY_NODE_NAME; + + if (false == fabric_key.valid_sub_key_id(component_key)) { + return 1; + } + + write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_ID_NAME, key_idx); + if (!fabric_key.sub_key_name(component_key).empty()) { + write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_NAME_NAME, + fabric_key.sub_key_name(component_key).c_str()); + } + write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_VALUE_NAME, + fabric_key.sub_key_value(component_key)); + + if (!fabric_key.sub_key_alias(component_key).empty()) { + write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_ALIAS_NAME, + fabric_key.sub_key_alias(component_key).c_str()); + } + + fp << "/>" + << "\n"; + + return 0; +} + /******************************************************************** * A writer to output a component key to XML format * @@ -80,7 +123,8 @@ static int write_xml_fabric_component_key(std::fstream& fp, * Return 2 if fail when creating files *******************************************************************/ static int write_xml_fabric_bl_shift_register_banks( - std::fstream& fp, const FabricKey& fabric_key, const FabricRegionId& region) { + std::fstream& fp, const FabricKey& fabric_key, const FabricRegionId& region, + const size_t& level) { /* Validate the file stream */ if (false == openfpga::valid_file_stream(fp)) { return 2; @@ -92,12 +136,12 @@ static int write_xml_fabric_bl_shift_register_banks( } /* Write the root node */ - openfpga::write_tab_to_file(fp, 2); + openfpga::write_tab_to_file(fp, level); fp << "<" << XML_FABRIC_KEY_BL_SHIFT_REGISTER_BANKS_NODE_NAME << ">" << "\n"; for (const auto& bank : fabric_key.bl_banks(region)) { - openfpga::write_tab_to_file(fp, 3); + openfpga::write_tab_to_file(fp, level + 1); fp << "<" << XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME; write_xml_attribute( @@ -120,7 +164,7 @@ static int write_xml_fabric_bl_shift_register_banks( << "\n"; } - openfpga::write_tab_to_file(fp, 2); + openfpga::write_tab_to_file(fp, level); fp << "" << "\n"; @@ -135,7 +179,8 @@ static int write_xml_fabric_bl_shift_register_banks( * Return 2 if fail when creating files *******************************************************************/ static int write_xml_fabric_wl_shift_register_banks( - std::fstream& fp, const FabricKey& fabric_key, const FabricRegionId& region) { + std::fstream& fp, const FabricKey& fabric_key, const FabricRegionId& region, + const size_t& level) { /* Validate the file stream */ if (false == openfpga::valid_file_stream(fp)) { return 2; @@ -147,12 +192,12 @@ static int write_xml_fabric_wl_shift_register_banks( } /* Write the root node */ - openfpga::write_tab_to_file(fp, 2); + openfpga::write_tab_to_file(fp, level); fp << "<" << XML_FABRIC_KEY_WL_SHIFT_REGISTER_BANKS_NODE_NAME << ">" << "\n"; for (const auto& bank : fabric_key.wl_banks(region)) { - openfpga::write_tab_to_file(fp, 3); + openfpga::write_tab_to_file(fp, level + 1); fp << "<" << XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME; write_xml_attribute( @@ -175,13 +220,83 @@ static int write_xml_fabric_wl_shift_register_banks( << "\n"; } - openfpga::write_tab_to_file(fp, 2); + openfpga::write_tab_to_file(fp, level); fp << "" << "\n"; return 0; } +/* Write keys under the top-level module to a file */ +static int write_xml_top_module_keys(std::fstream& fp, + const FabricKey& fabric_key, + const size_t& level) { + int err_code = 0; + /* Write the module declaration */ + openfpga::write_tab_to_file(fp, level); + fp << "<" << XML_FABRIC_KEY_MODULE_NODE_NAME << " " + << XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME << "=\"" + << FPGA_TOP_MODULE_NAME << "\"" + << ">\n"; + + /* Write region by region */ + for (const FabricRegionId& region : fabric_key.regions()) { + openfpga::write_tab_to_file(fp, level + 1); + fp << "<" << XML_FABRIC_KEY_REGION_NODE_NAME << " " + << XML_FABRIC_KEY_REGION_ATTRIBUTE_ID_NAME << "=\"" << size_t(region) + << "\"" + << ">\n"; + + /* Write shift register banks */ + write_xml_fabric_bl_shift_register_banks(fp, fabric_key, region, level + 2); + write_xml_fabric_wl_shift_register_banks(fp, fabric_key, region, level + 2); + + /* Write component by component */ + for (const FabricKeyId& key : fabric_key.region_keys(region)) { + err_code = write_xml_fabric_component_key(fp, fabric_key, key); + if (0 != err_code) { + return err_code; + } + } + + openfpga::write_tab_to_file(fp, level + 1); + fp << "" + << "\n"; + } + + fp << "\n"; + + return err_code; +} + +/* Write keys under the a given module to a file */ +static int write_xml_module_keys(std::fstream& fp, const FabricKey& fabric_key, + const FabricKeyModuleId& module_id, + const size_t& level) { + int err_code = 0; + /* Write the module declaration */ + openfpga::write_tab_to_file(fp, level); + fp << "<" << XML_FABRIC_KEY_MODULE_NODE_NAME << " " + << XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME << "=\"" + << fabric_key.module_name(module_id) << "\"" + << ">\n"; + + /* Write component by component */ + size_t key_idx = 0; + for (const FabricSubKeyId& key : fabric_key.sub_keys(module_id)) { + err_code = write_xml_fabric_component_sub_key(fp, fabric_key, key, key_idx, + level + 1); + if (0 != err_code) { + return err_code; + } + key_idx++; + } + + fp << "\n"; + + return err_code; +} + /******************************************************************** * A writer to output a fabric key to XML format * @@ -206,29 +321,18 @@ int write_xml_fabric_key(const char* fname, const FabricKey& fabric_key) { int err_code = 0; - /* Write region by region */ - for (const FabricRegionId& region : fabric_key.regions()) { - openfpga::write_tab_to_file(fp, 1); - fp << "<" << XML_FABRIC_KEY_REGION_NODE_NAME << " " - << XML_FABRIC_KEY_REGION_ATTRIBUTE_ID_NAME << "=\"" << size_t(region) - << "\"" - << ">\n"; + /* Write the top-level module */ + err_code = write_xml_top_module_keys(fp, fabric_key, 1); + if (0 != err_code) { + return err_code; + } - /* Write shift register banks */ - write_xml_fabric_bl_shift_register_banks(fp, fabric_key, region); - write_xml_fabric_wl_shift_register_banks(fp, fabric_key, region); - - /* Write component by component */ - for (const FabricKeyId& key : fabric_key.region_keys(region)) { - err_code = write_xml_fabric_component_key(fp, fabric_key, key); - if (0 != err_code) { - return err_code; - } + /* Write regular modules */ + for (FabricKeyModuleId module_id : fabric_key.modules()) { + err_code = write_xml_module_keys(fp, fabric_key, module_id, 1); + if (0 != err_code) { + return err_code; } - - openfpga::write_tab_to_file(fp, 1); - fp << "" - << "\n"; } /* Finish writing the root node */ From d0831507c0926e7e06918714ddf3f71b7c14af4d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 6 Jul 2023 19:21:45 -0700 Subject: [PATCH 151/391] [lib] format fabric key writer --- libs/libfabrickey/key_examples/key_example.xml | 16 +++++++++------- .../libfabrickey/src/io/write_xml_fabric_key.cpp | 9 ++++++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/libs/libfabrickey/key_examples/key_example.xml b/libs/libfabrickey/key_examples/key_example.xml index b9b0aa814..00d44276f 100644 --- a/libs/libfabrickey/key_examples/key_example.xml +++ b/libs/libfabrickey/key_examples/key_example.xml @@ -1,9 +1,11 @@ - - - - - - - + + + + + + + + + diff --git a/libs/libfabrickey/src/io/write_xml_fabric_key.cpp b/libs/libfabrickey/src/io/write_xml_fabric_key.cpp index 25fd51616..a3bb10888 100644 --- a/libs/libfabrickey/src/io/write_xml_fabric_key.cpp +++ b/libs/libfabrickey/src/io/write_xml_fabric_key.cpp @@ -75,13 +75,14 @@ static int write_xml_fabric_component_sub_key( *******************************************************************/ static int write_xml_fabric_component_key(std::fstream& fp, const FabricKey& fabric_key, - const FabricKeyId& component_key) { + const FabricKeyId& component_key, + const size_t& level) { /* Validate the file stream */ if (false == openfpga::valid_file_stream(fp)) { return 2; } - openfpga::write_tab_to_file(fp, 2); + openfpga::write_tab_to_file(fp, level); fp << "<" << XML_FABRIC_KEY_KEY_NODE_NAME; if (false == fabric_key.valid_key_id(component_key)) { @@ -253,7 +254,7 @@ static int write_xml_top_module_keys(std::fstream& fp, /* Write component by component */ for (const FabricKeyId& key : fabric_key.region_keys(region)) { - err_code = write_xml_fabric_component_key(fp, fabric_key, key); + err_code = write_xml_fabric_component_key(fp, fabric_key, key, level + 2); if (0 != err_code) { return err_code; } @@ -264,6 +265,7 @@ static int write_xml_top_module_keys(std::fstream& fp, << "\n"; } + openfpga::write_tab_to_file(fp, level); fp << "\n"; return err_code; @@ -292,6 +294,7 @@ static int write_xml_module_keys(std::fstream& fp, const FabricKey& fabric_key, key_idx++; } + openfpga::write_tab_to_file(fp, level); fp << "\n"; return err_code; From d3109ee88b21a346139612f1aabcd8e047617e3a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 6 Jul 2023 21:53:22 -0700 Subject: [PATCH 152/391] [core] developing configurable children reloading from fabric key --- openfpga/src/fabric/build_top_module.cpp | 7 ++ openfpga/src/utils/module_manager_utils.cpp | 112 ++++++++++++++++++++ openfpga/src/utils/module_manager_utils.h | 6 ++ 3 files changed, 125 insertions(+) diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index 395ca26f2..ff7fcac66 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -542,6 +542,13 @@ int build_top_module( if (CMD_EXEC_FATAL_ERROR == status) { return status; } + + /* 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); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } } /* Shuffle the configurable children in a random sequence */ diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index 90dbc2b41..5727f71de 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -8,6 +8,7 @@ #include /* Headers from vtrutil library */ +#include "command_exit_codes.h" #include "vtr_assert.h" #include "vtr_log.h" @@ -2531,4 +2532,115 @@ void add_module_bus_nets( * *******************************************************************/ +/******************************************************************** + * Compare the configurable children list with a given list of fabric sub-keys + * Return true if exact naming-matches are found + * When searching for matching, we consider + * - alias is treated as No. 1 reference + * - the pair as No. 2 reference + *******************************************************************/ +static bool submodule_memory_modules_match_fabric_key( + ModuleManager& module_manager, const ModuleId& module_id, + 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(); + size_t len_fabric_sub_key = fabric_key.sub_keys(key_module_id).size(); + if (len_module_memory != len_fabric_sub_key) { + return false; + } + /* Now walk through the child one by one */ + for (size_t ikey = 0; ikey < len_module_memory; ++ikey) { + FabricSubKeyId key_id = fabric_key.sub_keys(key_module_id)[ikey]; + std::pair inst_info(ModuleId::INVALID(), 0); + /* Try to match the alias */ + if (!fabric_key.sub_key_alias(key_id).empty()) { + if (!fabric_key.sub_key_name(key_id).empty()) { + inst_info.first = + module_manager.find_module(fabric_key.sub_key_name(key_id)); + inst_info.second = module_manager.instance_id( + module_id, inst_info.first, fabric_key.sub_key_alias(key_id)); + } else { + inst_info = find_module_manager_instance_module_info( + module_manager, module_id, fabric_key.sub_key_alias(key_id)); + } + } else { + inst_info.first = + module_manager.find_module(fabric_key.sub_key_name(key_id)); + inst_info.second = fabric_key.sub_key_value(key_id); + } + if (inst_info.first != + module_manager.configurable_children(module_id)[ikey] || + inst_info.second != + module_manager.configurable_child_instances(module_id)[ikey]) { + return false; + } + } + return true; +} + +/******************************************************************** + * Load and update the configurable children of a given module (not a top-level + *module) Compare the configurable children list with fabric sub-keys. + * - If match, nothing should be done + * - If not match, + * - remove the nets related to configurable children + * - rebuild the configurable children list + * - add the nets related to configurable children + *******************************************************************/ +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) { + int status = CMD_EXEC_SUCCESS; + /* Compare the configurable children list */ + if (submodule_memory_modules_match_fabric_key(module_manager, module_id, + fabric_key, key_module_id)) { + return CMD_EXEC_SUCCESS; + } + /* TODO: Do not match, now remove all the nets for the configurable children + */ + /* TODO: Overwrite the configurable children list */ + /* TODO: Create the nets for the new list of configurable children */ + return status; +} + +/******************************************************************** + * Load and update the configurable children of a given list of modules (not a + *top-level module) + *******************************************************************/ +int load_submodules_memory_modules_from_fabric_key( + ModuleManager& module_manager, const CircuitLibrary& circuit_lib, + const ConfigProtocol& config_protocol, const FabricKey& fabric_key) { + int status = CMD_EXEC_SUCCESS; + for (FabricKeyModuleId key_module_id : fabric_key.modules()) { + std::string module_name = fabric_key.module_name(key_module_id); + /* Ensure this is not a top module! */ + if (module_name == std::string(FPGA_TOP_MODULE_NAME)) { + VTR_LOG_ERROR( + "Expect a non-top-level name for the sub-module '%s' in fabric key!\n", + module_name.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + ModuleId module_id = module_manager.find_module(module_name); + if (module_id) { + /* 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); + if (status == CMD_EXEC_FATAL_ERROR) { + return status; + } + } else { + /* Not a valid module, report error */ + VTR_LOG_ERROR( + "The sub-module '%s' in fabric key is not a valid module in FPGA " + "fabric!\n", + module_name.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + } + return status; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/utils/module_manager_utils.h b/openfpga/src/utils/module_manager_utils.h index bd4e43130..12061f014 100644 --- a/openfpga/src/utils/module_manager_utils.h +++ b/openfpga/src/utils/module_manager_utils.h @@ -20,7 +20,9 @@ /* Headers from readarchopenfpga library */ #include "circuit_library.h" #include "circuit_types.h" +#include "config_protocol.h" #include "decoder_library.h" +#include "fabric_key.h" #include "module_manager.h" #include "vpr_device_annotation.h" @@ -183,6 +185,10 @@ void add_module_bus_nets( const ModulePortId& src_module_port_id, const ModuleId& des_module_id, const size_t& des_instance_id, const ModulePortId& des_module_port_id); +int load_submodules_memory_modules_from_fabric_key( + ModuleManager& module_manager, const CircuitLibrary& circuit_lib, + const ConfigProtocol& config_protocol, const FabricKey& fabric_key); + } /* end namespace openfpga */ #endif From a1b13b8e122e21af95dd2b6f2824932e28484280 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 6 Jul 2023 22:47:57 -0700 Subject: [PATCH 153/391] [core] overload submodule configurable children from fabric key --- openfpga/src/utils/module_manager_utils.cpp | 81 ++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index 5727f71de..35e674aa1 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -2579,6 +2579,79 @@ static bool submodule_memory_modules_match_fabric_key( return true; } +/******************************************************************** + * Update the configurable children list based on fabric key definitions + *******************************************************************/ +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 FabricKey& fabric_key, const FabricKeyModuleId& key_module_id) { + /* Reset the configurable children */ + 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); + /* Try to match the alias */ + if (!fabric_key.sub_key_alias(key_id).empty()) { + if (!fabric_key.sub_key_name(key_id).empty()) { + inst_info.first = + module_manager.find_module(fabric_key.sub_key_name(key_id)); + inst_info.second = module_manager.instance_id( + module_id, inst_info.first, fabric_key.sub_key_alias(key_id)); + } else { + inst_info = find_module_manager_instance_module_info( + module_manager, module_id, fabric_key.sub_key_alias(key_id)); + } + } else { + inst_info.first = + module_manager.find_module(fabric_key.sub_key_name(key_id)); + inst_info.second = fabric_key.sub_key_value(key_id); + } + if (false == module_manager.valid_module_id(inst_info.first)) { + if (!fabric_key.sub_key_alias(key_id).empty()) { + VTR_LOG_ERROR("Invalid key alias '%s'!\n", + fabric_key.sub_key_alias(key_id).c_str()); + } else { + VTR_LOG_ERROR("Invalid key name '%s'!\n", + fabric_key.sub_key_name(key_id).c_str()); + } + return CMD_EXEC_FATAL_ERROR; + } + + if (false == module_manager.valid_module_instance_id( + module_id, inst_info.first, inst_info.second)) { + if (!fabric_key.sub_key_alias(key_id).empty()) { + VTR_LOG_ERROR("Invalid key alias '%s'!\n", + fabric_key.sub_key_alias(key_id).c_str()); + } else { + VTR_LOG_ERROR("Invalid key value '%ld'!\n", inst_info.second); + } + return CMD_EXEC_FATAL_ERROR; + } + + /* If the the child has not configuration bits, error out */ + if (0 == find_module_num_config_bits( + module_manager, inst_info.first, circuit_lib, + config_protocol.memory_model(), config_protocol.type())) { + if (!fabric_key.sub_key_alias(key_id).empty()) { + VTR_LOG_ERROR( + "Invalid key alias '%s' which has zero configuration bits!\n", + fabric_key.sub_key_alias(key_id).c_str()); + } else { + VTR_LOG_ERROR( + "Invalid key name '%s' which has zero configuration bits!\n", + fabric_key.sub_key_name(key_id).c_str()); + } + return CMD_EXEC_FATAL_ERROR; + } + + /* 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()); + } + return CMD_EXEC_SUCCESS; +} + /******************************************************************** * Load and update the configurable children of a given module (not a top-level *module) Compare the configurable children list with fabric sub-keys. @@ -2600,7 +2673,13 @@ static int load_and_update_submodule_memory_modules_from_fabric_key( } /* TODO: Do not match, now remove all the nets for the configurable children */ - /* TODO: Overwrite the configurable children list */ + /* Overwrite the configurable children list */ + status = update_submodule_memory_modules_from_fabric_key( + module_manager, module_id, circuit_lib, config_protocol, fabric_key, + key_module_id); + if (status == CMD_EXEC_FATAL_ERROR) { + return status; + } /* TODO: Create the nets for the new list of configurable children */ return status; } From d3aa4c53d0a4886a9633e6096d1822e7920cb168 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 7 Jul 2023 14:51:21 -0700 Subject: [PATCH 154/391] [core] now support rebuild configuarable children for ccff submodules --- openfpga/src/fabric/module_manager.cpp | 9 + openfpga/src/fabric/module_manager.h | 4 + openfpga/src/utils/module_manager_utils.cpp | 341 +++++++++++++++++++- 3 files changed, 352 insertions(+), 2 deletions(-) diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index 24efd51b2..9b35670cd 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -1320,6 +1320,15 @@ void ModuleManager::clear_io_children(const ModuleId& parent_module) { io_child_coordinates_[parent_module].clear(); } +void ModuleManager::clear_module_net_sinks(const ModuleId& parent_module, + const ModuleNetId& net) { + VTR_ASSERT(valid_module_net_id(parent_module, net)); + net_sink_ids_[parent_module][net].clear(); + net_sink_terminal_ids_[parent_module][net].clear(); + net_sink_instance_ids_[parent_module][net].clear(); + net_sink_pin_ids_[parent_module][net].clear(); +} + /****************************************************************************** * Private validators/invalidators ******************************************************************************/ diff --git a/openfpga/src/fabric/module_manager.h b/openfpga/src/fabric/module_manager.h index dd0f299dc..ce936add9 100644 --- a/openfpga/src/fabric/module_manager.h +++ b/openfpga/src/fabric/module_manager.h @@ -469,6 +469,10 @@ class ModuleManager { */ void clear_io_children(const ModuleId& parent_module); + /* Remove all the sinks for a given net under a module */ + void clear_module_net_sinks(const ModuleId& parent_module, + const ModuleNetId& net); + public: /* Public validators/invalidators */ bool valid_module_id(const ModuleId& module) const; bool valid_module_port_id(const ModuleId& module, diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index 35e674aa1..175218c81 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -2652,6 +2652,334 @@ static bool update_submodule_memory_modules_from_fabric_key( return CMD_EXEC_SUCCESS; } +/******************************************************************** + * Remove the nets around the configuration chain (ccff_head and ccff_tails) + *******************************************************************/ +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) { + for (size_t mem_index = 0; + mem_index < module_manager.configurable_children(parent_module).size(); + ++mem_index) { + ModuleId net_src_module_id; + size_t net_src_instance_id; + ModulePortId net_src_port_id; + + if (0 == mem_index) { + /* Find the port name of configuration chain head */ + std::string src_port_name = + generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_INPUT); + net_src_module_id = parent_module; + net_src_instance_id = 0; + net_src_port_id = + module_manager.find_module_port(net_src_module_id, src_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]; + net_src_instance_id = module_manager.configurable_child_instances( + parent_module)[mem_index - 1]; + net_src_port_id = + module_manager.find_module_port(net_src_module_id, src_port_name); + } + + /* Get the pin id for source port */ + BasicPort net_src_port = + module_manager.module_port(net_src_module_id, net_src_port_id); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { + /* Find the net from which the source node is driving */ + ModuleNetId net = module_manager.module_instance_port_net( + parent_module, net_src_module_id, net_src_instance_id, net_src_port_id, + net_src_port.pins()[pin_id]); + /* Remove the net including sources and sinks */ + module_manager.clear_module_net_sinks(parent_module, net); + } + } + + /* For the last memory module: + * net source is the configuration chain tail of the previous memory module + * net sink is the configuration chain tail of the primitive module + */ + /* 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(); + size_t net_src_instance_id = + module_manager.configurable_child_instances(parent_module).back(); + ModulePortId net_src_port_id = + module_manager.find_module_port(net_src_module_id, src_port_name); + + /* Get the pin id for source port */ + BasicPort net_src_port = + module_manager.module_port(net_src_module_id, net_src_port_id); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { + /* Find the net from which the source node is driving */ + ModuleNetId net = module_manager.module_instance_port_net( + parent_module, net_src_module_id, net_src_instance_id, + net_src_port_id, net_src_port.pins()[pin_id]); + /* Remove the net including sources and sinks */ + module_manager.clear_module_net_sinks(parent_module, net); + } +} + +/******************************************************************** + * Remove the nets around the configurable children for a given module which + *should be in CMOS type + *******************************************************************/ +static int remove_submodule_nets_cmos_memory_config_bus( + ModuleManager& module_manager, const ModuleId& module_id, + const e_config_protocol_type& sram_orgz_type) { + switch (sram_orgz_type) { + case CONFIG_MEM_SCAN_CHAIN: { + remove_submodule_nets_cmos_memory_chain_config_bus(module_manager, module_id, + sram_orgz_type); + break; + } + case CONFIG_MEM_STANDALONE: + case CONFIG_MEM_QL_MEMORY_BANK: + /* TODO: + add_module_nets_cmos_memory_bank_bl_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); + add_module_nets_cmos_memory_bank_wl_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); + add_module_nets_cmos_memory_bank_wl_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR); + */ + break; + case CONFIG_MEM_MEMORY_BANK: + /* TODO: + add_module_nets_cmos_flatten_memory_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); + add_module_nets_cmos_flatten_memory_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); + */ + break; + case CONFIG_MEM_FRAME_BASED: + /* TODO: + add_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, + parent_module); + */ + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid type of SRAM organization!\n"); + exit(1); + } +} + +/******************************************************************** + * Remove the nets around the configurable children for a given module + *******************************************************************/ +static int remove_submodule_configurable_children_nets( + ModuleManager& module_manager, const ModuleId& module_id, + const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol) { + switch (circuit_lib.design_tech_type(config_protocol.memory_model())) { + case CIRCUIT_MODEL_DESIGN_CMOS: + remove_submodule_nets_cmos_memory_config_bus(module_manager, module_id, + config_protocol.type()); + break; + case CIRCUIT_MODEL_DESIGN_RRAM: + /* TODO: */ + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid type of memory design technology!\n"); + exit(1); + } +} + +/******************************************************************** + * Rebuild the nets(only sinks) around the configuration chain (ccff_head and + *ccff_tails) + *******************************************************************/ +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) { + for (size_t mem_index = 0; + mem_index < module_manager.configurable_children(parent_module).size(); + ++mem_index) { + ModuleId net_src_module_id; + size_t net_src_instance_id; + ModulePortId net_src_port_id; + + ModuleId net_sink_module_id; + size_t net_sink_instance_id; + ModulePortId net_sink_port_id; + + if (0 == mem_index) { + /* Find the port name of configuration chain head */ + std::string src_port_name = + generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_INPUT); + net_src_module_id = parent_module; + net_src_instance_id = 0; + 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]; + net_sink_instance_id = + module_manager.configurable_child_instances(parent_module)[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]; + net_src_instance_id = module_manager.configurable_child_instances( + parent_module)[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]; + net_sink_instance_id = + module_manager.configurable_child_instances(parent_module)[mem_index]; + net_sink_port_id = + module_manager.find_module_port(net_sink_module_id, sink_port_name); + } + + /* Get the pin id for source port */ + BasicPort net_src_port = + module_manager.module_port(net_src_module_id, net_src_port_id); + /* Get the pin id for sink port */ + BasicPort net_sink_port = + module_manager.module_port(net_sink_module_id, net_sink_port_id); + /* Port sizes of source and sink should match */ + VTR_ASSERT(net_src_port.get_width() == net_sink_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { + /* Create a net and add source and sink to it */ + ModuleNetId net = create_module_source_pin_net( + module_manager, parent_module, net_src_module_id, net_src_instance_id, + net_src_port_id, net_src_port.pins()[pin_id]); + /* Add net sink */ + module_manager.add_module_net_sink(parent_module, net, net_sink_module_id, + net_sink_instance_id, net_sink_port_id, + net_sink_port.pins()[pin_id]); + } + } + + /* For the last memory module: + * net source is the configuration chain tail of the previous memory module + * net sink is the configuration chain tail of the primitive module + */ + /* 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(); + size_t net_src_instance_id = + module_manager.configurable_child_instances(parent_module).back(); + ModulePortId 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_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_OUTPUT); + ModuleId net_sink_module_id = parent_module; + size_t net_sink_instance_id = 0; + ModulePortId net_sink_port_id = + module_manager.find_module_port(net_sink_module_id, sink_port_name); + + /* Get the pin id for source port */ + BasicPort net_src_port = + module_manager.module_port(net_src_module_id, net_src_port_id); + /* Get the pin id for sink port */ + BasicPort net_sink_port = + module_manager.module_port(net_sink_module_id, net_sink_port_id); + /* Port sizes of source and sink should match */ + VTR_ASSERT(net_src_port.get_width() == net_sink_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { + /* Create a net and add source and sink to it */ + ModuleNetId net = create_module_source_pin_net( + module_manager, parent_module, net_src_module_id, net_src_instance_id, + net_src_port_id, net_src_port.pins()[pin_id]); + /* Add net sink */ + module_manager.add_module_net_sink(parent_module, net, net_sink_module_id, + net_sink_instance_id, net_sink_port_id, + net_sink_port.pins()[pin_id]); + } +} + +/******************************************************************** + * Rebuild the nets around the configurable children for a given module which + *should be in CMOS type + *******************************************************************/ +static int rebuild_submodule_nets_cmos_memory_config_bus( + ModuleManager& module_manager, const ModuleId& module_id, + const e_config_protocol_type& sram_orgz_type) { + switch (sram_orgz_type) { + case CONFIG_MEM_SCAN_CHAIN: { + rebuild_submodule_nets_cmos_memory_chain_config_bus( + module_manager, module_id, sram_orgz_type); + break; + } + case CONFIG_MEM_STANDALONE: + case CONFIG_MEM_QL_MEMORY_BANK: + /* TODO: + add_module_nets_cmos_memory_bank_bl_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); + add_module_nets_cmos_memory_bank_wl_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); + add_module_nets_cmos_memory_bank_wl_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR); + */ + break; + case CONFIG_MEM_MEMORY_BANK: + /* TODO: + add_module_nets_cmos_flatten_memory_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); + add_module_nets_cmos_flatten_memory_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); + */ + break; + case CONFIG_MEM_FRAME_BASED: + /* TODO: + add_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, + parent_module); + */ + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid type of SRAM organization!\n"); + exit(1); + } +} + +/******************************************************************** + * Rebuild the nets(only sinks) around the configurable children for a given + *module + *******************************************************************/ +static int rebuild_submodule_configurable_children_nets( + ModuleManager& module_manager, const ModuleId& module_id, + const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol) { + switch (circuit_lib.design_tech_type(config_protocol.memory_model())) { + case CIRCUIT_MODEL_DESIGN_CMOS: + rebuild_submodule_nets_cmos_memory_config_bus(module_manager, module_id, + config_protocol.type()); + break; + case CIRCUIT_MODEL_DESIGN_RRAM: + /* TODO: */ + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid type of memory design technology!\n"); + exit(1); + } +} + /******************************************************************** * Load and update the configurable children of a given module (not a top-level *module) Compare the configurable children list with fabric sub-keys. @@ -2671,8 +2999,12 @@ static int load_and_update_submodule_memory_modules_from_fabric_key( fabric_key, key_module_id)) { return CMD_EXEC_SUCCESS; } - /* TODO: Do not match, now remove all the nets for the configurable children - */ + /* 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); + 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, @@ -2681,6 +3013,11 @@ static int load_and_update_submodule_memory_modules_from_fabric_key( 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); + if (status == CMD_EXEC_FATAL_ERROR) { + return status; + } return status; } From 433391eec41e63ec8f5da068a71a53d687184589 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 7 Jul 2023 15:03:03 -0700 Subject: [PATCH 155/391] [core] move new functions to a separated source file --- openfpga/src/fabric/build_top_module.cpp | 1 + .../src/utils/module_manager_memory_utils.cpp | 563 ++++++++++++++++++ .../src/utils/module_manager_memory_utils.h | 41 ++ openfpga/src/utils/module_manager_utils.cpp | 527 ---------------- openfpga/src/utils/module_manager_utils.h | 4 - 5 files changed, 605 insertions(+), 531 deletions(-) create mode 100644 openfpga/src/utils/module_manager_memory_utils.cpp create mode 100644 openfpga/src/utils/module_manager_memory_utils.h diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index ff7fcac66..1ef0b518c 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -22,6 +22,7 @@ #include "build_top_module_memory_bank.h" #include "build_top_module_utils.h" #include "command_exit_codes.h" +#include "module_manager_memory_utils.h" #include "module_manager_utils.h" #include "openfpga_device_grid_utils.h" #include "openfpga_naming.h" diff --git a/openfpga/src/utils/module_manager_memory_utils.cpp b/openfpga/src/utils/module_manager_memory_utils.cpp new file mode 100644 index 000000000..918b27e8f --- /dev/null +++ b/openfpga/src/utils/module_manager_memory_utils.cpp @@ -0,0 +1,563 @@ +/****************************************************************************** + * This files includes most utilized functions + * for data structures for module management. + ******************************************************************************/ + +#include +#include +#include + +/* Headers from vtrutil library */ +#include "command_exit_codes.h" +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgautil library */ +#include "build_decoder_modules.h" +#include "circuit_library_utils.h" +#include "decoder_library_utils.h" +#include "memory_utils.h" +#include "module_manager_memory_utils.h" +#include "module_manager_utils.h" +#include "openfpga_naming.h" +#include "openfpga_port.h" +#include "openfpga_reserved_words.h" +#include "pb_type_utils.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Compare the configurable children list with a given list of fabric sub-keys + * Return true if exact naming-matches are found + * When searching for matching, we consider + * - alias is treated as No. 1 reference + * - the pair as No. 2 reference + *******************************************************************/ +static bool submodule_memory_modules_match_fabric_key( + ModuleManager& module_manager, const ModuleId& module_id, + 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(); + size_t len_fabric_sub_key = fabric_key.sub_keys(key_module_id).size(); + if (len_module_memory != len_fabric_sub_key) { + return false; + } + /* Now walk through the child one by one */ + for (size_t ikey = 0; ikey < len_module_memory; ++ikey) { + FabricSubKeyId key_id = fabric_key.sub_keys(key_module_id)[ikey]; + std::pair inst_info(ModuleId::INVALID(), 0); + /* Try to match the alias */ + if (!fabric_key.sub_key_alias(key_id).empty()) { + if (!fabric_key.sub_key_name(key_id).empty()) { + inst_info.first = + module_manager.find_module(fabric_key.sub_key_name(key_id)); + inst_info.second = module_manager.instance_id( + module_id, inst_info.first, fabric_key.sub_key_alias(key_id)); + } else { + inst_info = find_module_manager_instance_module_info( + module_manager, module_id, fabric_key.sub_key_alias(key_id)); + } + } else { + inst_info.first = + module_manager.find_module(fabric_key.sub_key_name(key_id)); + inst_info.second = fabric_key.sub_key_value(key_id); + } + if (inst_info.first != + module_manager.configurable_children(module_id)[ikey] || + inst_info.second != + module_manager.configurable_child_instances(module_id)[ikey]) { + return false; + } + } + return true; +} + +/******************************************************************** + * Update the configurable children list based on fabric key definitions + *******************************************************************/ +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 FabricKey& fabric_key, const FabricKeyModuleId& key_module_id) { + /* Reset the configurable children */ + 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); + /* Try to match the alias */ + if (!fabric_key.sub_key_alias(key_id).empty()) { + if (!fabric_key.sub_key_name(key_id).empty()) { + inst_info.first = + module_manager.find_module(fabric_key.sub_key_name(key_id)); + inst_info.second = module_manager.instance_id( + module_id, inst_info.first, fabric_key.sub_key_alias(key_id)); + } else { + inst_info = find_module_manager_instance_module_info( + module_manager, module_id, fabric_key.sub_key_alias(key_id)); + } + } else { + inst_info.first = + module_manager.find_module(fabric_key.sub_key_name(key_id)); + inst_info.second = fabric_key.sub_key_value(key_id); + } + if (false == module_manager.valid_module_id(inst_info.first)) { + if (!fabric_key.sub_key_alias(key_id).empty()) { + VTR_LOG_ERROR("Invalid key alias '%s'!\n", + fabric_key.sub_key_alias(key_id).c_str()); + } else { + VTR_LOG_ERROR("Invalid key name '%s'!\n", + fabric_key.sub_key_name(key_id).c_str()); + } + return CMD_EXEC_FATAL_ERROR; + } + + if (false == module_manager.valid_module_instance_id( + module_id, inst_info.first, inst_info.second)) { + if (!fabric_key.sub_key_alias(key_id).empty()) { + VTR_LOG_ERROR("Invalid key alias '%s'!\n", + fabric_key.sub_key_alias(key_id).c_str()); + } else { + VTR_LOG_ERROR("Invalid key value '%ld'!\n", inst_info.second); + } + return CMD_EXEC_FATAL_ERROR; + } + + /* If the the child has not configuration bits, error out */ + if (0 == find_module_num_config_bits( + module_manager, inst_info.first, circuit_lib, + config_protocol.memory_model(), config_protocol.type())) { + if (!fabric_key.sub_key_alias(key_id).empty()) { + VTR_LOG_ERROR( + "Invalid key alias '%s' which has zero configuration bits!\n", + fabric_key.sub_key_alias(key_id).c_str()); + } else { + VTR_LOG_ERROR( + "Invalid key name '%s' which has zero configuration bits!\n", + fabric_key.sub_key_name(key_id).c_str()); + } + return CMD_EXEC_FATAL_ERROR; + } + + /* 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()); + } + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Remove the nets around the configuration chain (ccff_head and ccff_tails) + *******************************************************************/ +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) { + for (size_t mem_index = 0; + mem_index < module_manager.configurable_children(parent_module).size(); + ++mem_index) { + ModuleId net_src_module_id; + size_t net_src_instance_id; + ModulePortId net_src_port_id; + + if (0 == mem_index) { + /* Find the port name of configuration chain head */ + std::string src_port_name = + generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_INPUT); + net_src_module_id = parent_module; + net_src_instance_id = 0; + net_src_port_id = + module_manager.find_module_port(net_src_module_id, src_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]; + net_src_instance_id = module_manager.configurable_child_instances( + parent_module)[mem_index - 1]; + net_src_port_id = + module_manager.find_module_port(net_src_module_id, src_port_name); + } + + /* Get the pin id for source port */ + BasicPort net_src_port = + module_manager.module_port(net_src_module_id, net_src_port_id); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { + /* Find the net from which the source node is driving */ + ModuleNetId net = module_manager.module_instance_port_net( + parent_module, net_src_module_id, net_src_instance_id, net_src_port_id, + net_src_port.pins()[pin_id]); + /* Remove the net including sources and sinks */ + module_manager.clear_module_net_sinks(parent_module, net); + } + } + + /* For the last memory module: + * net source is the configuration chain tail of the previous memory module + * net sink is the configuration chain tail of the primitive module + */ + /* 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(); + size_t net_src_instance_id = + module_manager.configurable_child_instances(parent_module).back(); + ModulePortId net_src_port_id = + module_manager.find_module_port(net_src_module_id, src_port_name); + + /* Get the pin id for source port */ + BasicPort net_src_port = + module_manager.module_port(net_src_module_id, net_src_port_id); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { + /* Find the net from which the source node is driving */ + ModuleNetId net = module_manager.module_instance_port_net( + parent_module, net_src_module_id, net_src_instance_id, net_src_port_id, + net_src_port.pins()[pin_id]); + /* Remove the net including sources and sinks */ + module_manager.clear_module_net_sinks(parent_module, net); + } + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Remove the nets around the configurable children for a given module which + *should be in CMOS type + *******************************************************************/ +static int remove_submodule_nets_cmos_memory_config_bus( + ModuleManager& module_manager, const ModuleId& module_id, + const e_config_protocol_type& sram_orgz_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); + break; + } + case CONFIG_MEM_STANDALONE: + case CONFIG_MEM_QL_MEMORY_BANK: + /* TODO: + add_module_nets_cmos_memory_bank_bl_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); + add_module_nets_cmos_memory_bank_wl_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); + add_module_nets_cmos_memory_bank_wl_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR); + */ + break; + case CONFIG_MEM_MEMORY_BANK: + /* TODO: + add_module_nets_cmos_flatten_memory_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); + add_module_nets_cmos_flatten_memory_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); + */ + break; + case CONFIG_MEM_FRAME_BASED: + /* TODO: + add_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, + parent_module); + */ + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid type of SRAM organization!\n"); + return CMD_EXEC_FATAL_ERROR; + } + return CMD_EXEC_FATAL_ERROR; +} + +/******************************************************************** + * Remove the nets around the configurable children for a given module + *******************************************************************/ +static int remove_submodule_configurable_children_nets( + ModuleManager& module_manager, const ModuleId& module_id, + const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol) { + 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()); + break; + case CIRCUIT_MODEL_DESIGN_RRAM: + /* TODO: */ + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid type of memory design technology!\n"); + return CMD_EXEC_FATAL_ERROR; + } + return CMD_EXEC_FATAL_ERROR; +} + +/******************************************************************** + * Rebuild the nets(only sinks) around the configuration chain (ccff_head and + *ccff_tails) + *******************************************************************/ +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) { + for (size_t mem_index = 0; + mem_index < module_manager.configurable_children(parent_module).size(); + ++mem_index) { + ModuleId net_src_module_id; + size_t net_src_instance_id; + ModulePortId net_src_port_id; + + ModuleId net_sink_module_id; + size_t net_sink_instance_id; + ModulePortId net_sink_port_id; + + if (0 == mem_index) { + /* Find the port name of configuration chain head */ + std::string src_port_name = + generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_INPUT); + net_src_module_id = parent_module; + net_src_instance_id = 0; + 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]; + net_sink_instance_id = + module_manager.configurable_child_instances(parent_module)[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]; + net_src_instance_id = module_manager.configurable_child_instances( + parent_module)[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]; + net_sink_instance_id = + module_manager.configurable_child_instances(parent_module)[mem_index]; + net_sink_port_id = + module_manager.find_module_port(net_sink_module_id, sink_port_name); + } + + /* Get the pin id for source port */ + BasicPort net_src_port = + module_manager.module_port(net_src_module_id, net_src_port_id); + /* Get the pin id for sink port */ + BasicPort net_sink_port = + module_manager.module_port(net_sink_module_id, net_sink_port_id); + /* Port sizes of source and sink should match */ + VTR_ASSERT(net_src_port.get_width() == net_sink_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { + /* Create a net and add source and sink to it */ + ModuleNetId net = create_module_source_pin_net( + module_manager, parent_module, net_src_module_id, net_src_instance_id, + net_src_port_id, net_src_port.pins()[pin_id]); + /* Add net sink */ + module_manager.add_module_net_sink(parent_module, net, net_sink_module_id, + net_sink_instance_id, net_sink_port_id, + net_sink_port.pins()[pin_id]); + } + } + + /* For the last memory module: + * net source is the configuration chain tail of the previous memory module + * net sink is the configuration chain tail of the primitive module + */ + /* 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(); + size_t net_src_instance_id = + module_manager.configurable_child_instances(parent_module).back(); + ModulePortId 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_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_OUTPUT); + ModuleId net_sink_module_id = parent_module; + size_t net_sink_instance_id = 0; + ModulePortId net_sink_port_id = + module_manager.find_module_port(net_sink_module_id, sink_port_name); + + /* Get the pin id for source port */ + BasicPort net_src_port = + module_manager.module_port(net_src_module_id, net_src_port_id); + /* Get the pin id for sink port */ + BasicPort net_sink_port = + module_manager.module_port(net_sink_module_id, net_sink_port_id); + /* Port sizes of source and sink should match */ + VTR_ASSERT(net_src_port.get_width() == net_sink_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { + /* Create a net and add source and sink to it */ + ModuleNetId net = create_module_source_pin_net( + module_manager, parent_module, net_src_module_id, net_src_instance_id, + net_src_port_id, net_src_port.pins()[pin_id]); + /* Add net sink */ + module_manager.add_module_net_sink(parent_module, net, net_sink_module_id, + net_sink_instance_id, net_sink_port_id, + net_sink_port.pins()[pin_id]); + } + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Rebuild the nets around the configurable children for a given module which + *should be in CMOS type + *******************************************************************/ +static int rebuild_submodule_nets_cmos_memory_config_bus( + ModuleManager& module_manager, const ModuleId& module_id, + const e_config_protocol_type& sram_orgz_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); + break; + } + case CONFIG_MEM_STANDALONE: + case CONFIG_MEM_QL_MEMORY_BANK: + /* TODO: + add_module_nets_cmos_memory_bank_bl_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); + add_module_nets_cmos_memory_bank_wl_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); + add_module_nets_cmos_memory_bank_wl_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR); + */ + break; + case CONFIG_MEM_MEMORY_BANK: + /* TODO: + add_module_nets_cmos_flatten_memory_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); + add_module_nets_cmos_flatten_memory_config_bus( + module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); + */ + break; + case CONFIG_MEM_FRAME_BASED: + /* TODO: + add_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, + parent_module); + */ + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid type of SRAM organization!\n"); + return CMD_EXEC_FATAL_ERROR; + } + return CMD_EXEC_FATAL_ERROR; +} + +/******************************************************************** + * Rebuild the nets(only sinks) around the configurable children for a given + *module + *******************************************************************/ +static int rebuild_submodule_configurable_children_nets( + ModuleManager& module_manager, const ModuleId& module_id, + const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol) { + 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()); + break; + case CIRCUIT_MODEL_DESIGN_RRAM: + /* TODO: */ + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid type of memory design technology!\n"); + return CMD_EXEC_FATAL_ERROR; + } + return CMD_EXEC_FATAL_ERROR; +} + +/******************************************************************** + * Load and update the configurable children of a given module (not a top-level + *module) Compare the configurable children list with fabric sub-keys. + * - If match, nothing should be done + * - If not match, + * - remove the nets related to configurable children + * - rebuild the configurable children list + * - add the nets related to configurable children + *******************************************************************/ +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) { + int status = CMD_EXEC_SUCCESS; + /* Compare the configurable children list */ + if (submodule_memory_modules_match_fabric_key(module_manager, module_id, + fabric_key, key_module_id)) { + return CMD_EXEC_SUCCESS; + } + /* 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); + 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, + 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); + if (status == CMD_EXEC_FATAL_ERROR) { + return status; + } + return status; +} + +/******************************************************************** + * Load and update the configurable children of a given list of modules (not a + *top-level module) + *******************************************************************/ +int load_submodules_memory_modules_from_fabric_key( + ModuleManager& module_manager, const CircuitLibrary& circuit_lib, + const ConfigProtocol& config_protocol, const FabricKey& fabric_key) { + int status = CMD_EXEC_SUCCESS; + for (FabricKeyModuleId key_module_id : fabric_key.modules()) { + std::string module_name = fabric_key.module_name(key_module_id); + /* Ensure this is not a top module! */ + if (module_name == std::string(FPGA_TOP_MODULE_NAME)) { + VTR_LOG_ERROR( + "Expect a non-top-level name for the sub-module '%s' in fabric key!\n", + module_name.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + ModuleId module_id = module_manager.find_module(module_name); + if (module_id) { + /* 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); + if (status == CMD_EXEC_FATAL_ERROR) { + return status; + } + } else { + /* Not a valid module, report error */ + VTR_LOG_ERROR( + "The sub-module '%s' in fabric key is not a valid module in FPGA " + "fabric!\n", + module_name.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + } + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/utils/module_manager_memory_utils.h b/openfpga/src/utils/module_manager_memory_utils.h new file mode 100644 index 000000000..a0573bc98 --- /dev/null +++ b/openfpga/src/utils/module_manager_memory_utils.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * This files includes declarations for most utilized functions + * for data structures for module management. + ******************************************************************************/ +#ifndef MODULE_MANAGER_MEMORY_UTILS_H +#define MODULE_MANAGER_MEMORY_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include + +/* Headers from readarch library */ +#include "physical_types.h" + +/* Headers from openfpgautil library */ +#include "openfpga_port.h" + +/* Headers from readarchopenfpga library */ +#include "circuit_library.h" +#include "circuit_types.h" +#include "config_protocol.h" +#include "decoder_library.h" +#include "fabric_key.h" +#include "module_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +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); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index 175218c81..37bbdd2cb 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -2532,531 +2532,4 @@ void add_module_bus_nets( * *******************************************************************/ -/******************************************************************** - * Compare the configurable children list with a given list of fabric sub-keys - * Return true if exact naming-matches are found - * When searching for matching, we consider - * - alias is treated as No. 1 reference - * - the pair as No. 2 reference - *******************************************************************/ -static bool submodule_memory_modules_match_fabric_key( - ModuleManager& module_manager, const ModuleId& module_id, - 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(); - size_t len_fabric_sub_key = fabric_key.sub_keys(key_module_id).size(); - if (len_module_memory != len_fabric_sub_key) { - return false; - } - /* Now walk through the child one by one */ - for (size_t ikey = 0; ikey < len_module_memory; ++ikey) { - FabricSubKeyId key_id = fabric_key.sub_keys(key_module_id)[ikey]; - std::pair inst_info(ModuleId::INVALID(), 0); - /* Try to match the alias */ - if (!fabric_key.sub_key_alias(key_id).empty()) { - if (!fabric_key.sub_key_name(key_id).empty()) { - inst_info.first = - module_manager.find_module(fabric_key.sub_key_name(key_id)); - inst_info.second = module_manager.instance_id( - module_id, inst_info.first, fabric_key.sub_key_alias(key_id)); - } else { - inst_info = find_module_manager_instance_module_info( - module_manager, module_id, fabric_key.sub_key_alias(key_id)); - } - } else { - inst_info.first = - module_manager.find_module(fabric_key.sub_key_name(key_id)); - inst_info.second = fabric_key.sub_key_value(key_id); - } - if (inst_info.first != - module_manager.configurable_children(module_id)[ikey] || - inst_info.second != - module_manager.configurable_child_instances(module_id)[ikey]) { - return false; - } - } - return true; -} - -/******************************************************************** - * Update the configurable children list based on fabric key definitions - *******************************************************************/ -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 FabricKey& fabric_key, const FabricKeyModuleId& key_module_id) { - /* Reset the configurable children */ - 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); - /* Try to match the alias */ - if (!fabric_key.sub_key_alias(key_id).empty()) { - if (!fabric_key.sub_key_name(key_id).empty()) { - inst_info.first = - module_manager.find_module(fabric_key.sub_key_name(key_id)); - inst_info.second = module_manager.instance_id( - module_id, inst_info.first, fabric_key.sub_key_alias(key_id)); - } else { - inst_info = find_module_manager_instance_module_info( - module_manager, module_id, fabric_key.sub_key_alias(key_id)); - } - } else { - inst_info.first = - module_manager.find_module(fabric_key.sub_key_name(key_id)); - inst_info.second = fabric_key.sub_key_value(key_id); - } - if (false == module_manager.valid_module_id(inst_info.first)) { - if (!fabric_key.sub_key_alias(key_id).empty()) { - VTR_LOG_ERROR("Invalid key alias '%s'!\n", - fabric_key.sub_key_alias(key_id).c_str()); - } else { - VTR_LOG_ERROR("Invalid key name '%s'!\n", - fabric_key.sub_key_name(key_id).c_str()); - } - return CMD_EXEC_FATAL_ERROR; - } - - if (false == module_manager.valid_module_instance_id( - module_id, inst_info.first, inst_info.second)) { - if (!fabric_key.sub_key_alias(key_id).empty()) { - VTR_LOG_ERROR("Invalid key alias '%s'!\n", - fabric_key.sub_key_alias(key_id).c_str()); - } else { - VTR_LOG_ERROR("Invalid key value '%ld'!\n", inst_info.second); - } - return CMD_EXEC_FATAL_ERROR; - } - - /* If the the child has not configuration bits, error out */ - if (0 == find_module_num_config_bits( - module_manager, inst_info.first, circuit_lib, - config_protocol.memory_model(), config_protocol.type())) { - if (!fabric_key.sub_key_alias(key_id).empty()) { - VTR_LOG_ERROR( - "Invalid key alias '%s' which has zero configuration bits!\n", - fabric_key.sub_key_alias(key_id).c_str()); - } else { - VTR_LOG_ERROR( - "Invalid key name '%s' which has zero configuration bits!\n", - fabric_key.sub_key_name(key_id).c_str()); - } - return CMD_EXEC_FATAL_ERROR; - } - - /* 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()); - } - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Remove the nets around the configuration chain (ccff_head and ccff_tails) - *******************************************************************/ -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) { - for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module).size(); - ++mem_index) { - ModuleId net_src_module_id; - size_t net_src_instance_id; - ModulePortId net_src_port_id; - - if (0 == mem_index) { - /* Find the port name of configuration chain head */ - std::string src_port_name = - generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_INPUT); - net_src_module_id = parent_module; - net_src_instance_id = 0; - net_src_port_id = - module_manager.find_module_port(net_src_module_id, src_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]; - net_src_instance_id = module_manager.configurable_child_instances( - parent_module)[mem_index - 1]; - net_src_port_id = - module_manager.find_module_port(net_src_module_id, src_port_name); - } - - /* Get the pin id for source port */ - BasicPort net_src_port = - module_manager.module_port(net_src_module_id, net_src_port_id); - - /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { - /* Find the net from which the source node is driving */ - ModuleNetId net = module_manager.module_instance_port_net( - parent_module, net_src_module_id, net_src_instance_id, net_src_port_id, - net_src_port.pins()[pin_id]); - /* Remove the net including sources and sinks */ - module_manager.clear_module_net_sinks(parent_module, net); - } - } - - /* For the last memory module: - * net source is the configuration chain tail of the previous memory module - * net sink is the configuration chain tail of the primitive module - */ - /* 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(); - size_t net_src_instance_id = - module_manager.configurable_child_instances(parent_module).back(); - ModulePortId net_src_port_id = - module_manager.find_module_port(net_src_module_id, src_port_name); - - /* Get the pin id for source port */ - BasicPort net_src_port = - module_manager.module_port(net_src_module_id, net_src_port_id); - - /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { - /* Find the net from which the source node is driving */ - ModuleNetId net = module_manager.module_instance_port_net( - parent_module, net_src_module_id, net_src_instance_id, - net_src_port_id, net_src_port.pins()[pin_id]); - /* Remove the net including sources and sinks */ - module_manager.clear_module_net_sinks(parent_module, net); - } -} - -/******************************************************************** - * Remove the nets around the configurable children for a given module which - *should be in CMOS type - *******************************************************************/ -static int remove_submodule_nets_cmos_memory_config_bus( - ModuleManager& module_manager, const ModuleId& module_id, - const e_config_protocol_type& sram_orgz_type) { - switch (sram_orgz_type) { - case CONFIG_MEM_SCAN_CHAIN: { - remove_submodule_nets_cmos_memory_chain_config_bus(module_manager, module_id, - sram_orgz_type); - break; - } - case CONFIG_MEM_STANDALONE: - case CONFIG_MEM_QL_MEMORY_BANK: - /* TODO: - add_module_nets_cmos_memory_bank_bl_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); - add_module_nets_cmos_memory_bank_wl_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); - add_module_nets_cmos_memory_bank_wl_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR); - */ - break; - case CONFIG_MEM_MEMORY_BANK: - /* TODO: - add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); - add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); - */ - break; - case CONFIG_MEM_FRAME_BASED: - /* TODO: - add_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, - parent_module); - */ - break; - default: - VTR_LOGF_ERROR(__FILE__, __LINE__, - "Invalid type of SRAM organization!\n"); - exit(1); - } -} - -/******************************************************************** - * Remove the nets around the configurable children for a given module - *******************************************************************/ -static int remove_submodule_configurable_children_nets( - ModuleManager& module_manager, const ModuleId& module_id, - const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol) { - switch (circuit_lib.design_tech_type(config_protocol.memory_model())) { - case CIRCUIT_MODEL_DESIGN_CMOS: - remove_submodule_nets_cmos_memory_config_bus(module_manager, module_id, - config_protocol.type()); - break; - case CIRCUIT_MODEL_DESIGN_RRAM: - /* TODO: */ - break; - default: - VTR_LOGF_ERROR(__FILE__, __LINE__, - "Invalid type of memory design technology!\n"); - exit(1); - } -} - -/******************************************************************** - * Rebuild the nets(only sinks) around the configuration chain (ccff_head and - *ccff_tails) - *******************************************************************/ -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) { - for (size_t mem_index = 0; - mem_index < module_manager.configurable_children(parent_module).size(); - ++mem_index) { - ModuleId net_src_module_id; - size_t net_src_instance_id; - ModulePortId net_src_port_id; - - ModuleId net_sink_module_id; - size_t net_sink_instance_id; - ModulePortId net_sink_port_id; - - if (0 == mem_index) { - /* Find the port name of configuration chain head */ - std::string src_port_name = - generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_INPUT); - net_src_module_id = parent_module; - net_src_instance_id = 0; - 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]; - net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[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]; - net_src_instance_id = module_manager.configurable_child_instances( - parent_module)[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]; - net_sink_instance_id = - module_manager.configurable_child_instances(parent_module)[mem_index]; - net_sink_port_id = - module_manager.find_module_port(net_sink_module_id, sink_port_name); - } - - /* Get the pin id for source port */ - BasicPort net_src_port = - module_manager.module_port(net_src_module_id, net_src_port_id); - /* Get the pin id for sink port */ - BasicPort net_sink_port = - module_manager.module_port(net_sink_module_id, net_sink_port_id); - /* Port sizes of source and sink should match */ - VTR_ASSERT(net_src_port.get_width() == net_sink_port.get_width()); - - /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { - /* Create a net and add source and sink to it */ - ModuleNetId net = create_module_source_pin_net( - module_manager, parent_module, net_src_module_id, net_src_instance_id, - net_src_port_id, net_src_port.pins()[pin_id]); - /* Add net sink */ - module_manager.add_module_net_sink(parent_module, net, net_sink_module_id, - net_sink_instance_id, net_sink_port_id, - net_sink_port.pins()[pin_id]); - } - } - - /* For the last memory module: - * net source is the configuration chain tail of the previous memory module - * net sink is the configuration chain tail of the primitive module - */ - /* 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(); - size_t net_src_instance_id = - module_manager.configurable_child_instances(parent_module).back(); - ModulePortId 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_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_OUTPUT); - ModuleId net_sink_module_id = parent_module; - size_t net_sink_instance_id = 0; - ModulePortId net_sink_port_id = - module_manager.find_module_port(net_sink_module_id, sink_port_name); - - /* Get the pin id for source port */ - BasicPort net_src_port = - module_manager.module_port(net_src_module_id, net_src_port_id); - /* Get the pin id for sink port */ - BasicPort net_sink_port = - module_manager.module_port(net_sink_module_id, net_sink_port_id); - /* Port sizes of source and sink should match */ - VTR_ASSERT(net_src_port.get_width() == net_sink_port.get_width()); - - /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { - /* Create a net and add source and sink to it */ - ModuleNetId net = create_module_source_pin_net( - module_manager, parent_module, net_src_module_id, net_src_instance_id, - net_src_port_id, net_src_port.pins()[pin_id]); - /* Add net sink */ - module_manager.add_module_net_sink(parent_module, net, net_sink_module_id, - net_sink_instance_id, net_sink_port_id, - net_sink_port.pins()[pin_id]); - } -} - -/******************************************************************** - * Rebuild the nets around the configurable children for a given module which - *should be in CMOS type - *******************************************************************/ -static int rebuild_submodule_nets_cmos_memory_config_bus( - ModuleManager& module_manager, const ModuleId& module_id, - const e_config_protocol_type& sram_orgz_type) { - switch (sram_orgz_type) { - case CONFIG_MEM_SCAN_CHAIN: { - rebuild_submodule_nets_cmos_memory_chain_config_bus( - module_manager, module_id, sram_orgz_type); - break; - } - case CONFIG_MEM_STANDALONE: - case CONFIG_MEM_QL_MEMORY_BANK: - /* TODO: - add_module_nets_cmos_memory_bank_bl_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); - add_module_nets_cmos_memory_bank_wl_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); - add_module_nets_cmos_memory_bank_wl_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WLR); - */ - break; - case CONFIG_MEM_MEMORY_BANK: - /* TODO: - add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); - add_module_nets_cmos_flatten_memory_config_bus( - module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); - */ - break; - case CONFIG_MEM_FRAME_BASED: - /* TODO: - add_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, - parent_module); - */ - break; - default: - VTR_LOGF_ERROR(__FILE__, __LINE__, - "Invalid type of SRAM organization!\n"); - exit(1); - } -} - -/******************************************************************** - * Rebuild the nets(only sinks) around the configurable children for a given - *module - *******************************************************************/ -static int rebuild_submodule_configurable_children_nets( - ModuleManager& module_manager, const ModuleId& module_id, - const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol) { - switch (circuit_lib.design_tech_type(config_protocol.memory_model())) { - case CIRCUIT_MODEL_DESIGN_CMOS: - rebuild_submodule_nets_cmos_memory_config_bus(module_manager, module_id, - config_protocol.type()); - break; - case CIRCUIT_MODEL_DESIGN_RRAM: - /* TODO: */ - break; - default: - VTR_LOGF_ERROR(__FILE__, __LINE__, - "Invalid type of memory design technology!\n"); - exit(1); - } -} - -/******************************************************************** - * Load and update the configurable children of a given module (not a top-level - *module) Compare the configurable children list with fabric sub-keys. - * - If match, nothing should be done - * - If not match, - * - remove the nets related to configurable children - * - rebuild the configurable children list - * - add the nets related to configurable children - *******************************************************************/ -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) { - int status = CMD_EXEC_SUCCESS; - /* Compare the configurable children list */ - if (submodule_memory_modules_match_fabric_key(module_manager, module_id, - fabric_key, key_module_id)) { - return CMD_EXEC_SUCCESS; - } - /* 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); - 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, - 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); - if (status == CMD_EXEC_FATAL_ERROR) { - return status; - } - return status; -} - -/******************************************************************** - * Load and update the configurable children of a given list of modules (not a - *top-level module) - *******************************************************************/ -int load_submodules_memory_modules_from_fabric_key( - ModuleManager& module_manager, const CircuitLibrary& circuit_lib, - const ConfigProtocol& config_protocol, const FabricKey& fabric_key) { - int status = CMD_EXEC_SUCCESS; - for (FabricKeyModuleId key_module_id : fabric_key.modules()) { - std::string module_name = fabric_key.module_name(key_module_id); - /* Ensure this is not a top module! */ - if (module_name == std::string(FPGA_TOP_MODULE_NAME)) { - VTR_LOG_ERROR( - "Expect a non-top-level name for the sub-module '%s' in fabric key!\n", - module_name.c_str()); - return CMD_EXEC_FATAL_ERROR; - } - ModuleId module_id = module_manager.find_module(module_name); - if (module_id) { - /* 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); - if (status == CMD_EXEC_FATAL_ERROR) { - return status; - } - } else { - /* Not a valid module, report error */ - VTR_LOG_ERROR( - "The sub-module '%s' in fabric key is not a valid module in FPGA " - "fabric!\n", - module_name.c_str()); - return CMD_EXEC_FATAL_ERROR; - } - } - return status; -} - } /* end namespace openfpga */ diff --git a/openfpga/src/utils/module_manager_utils.h b/openfpga/src/utils/module_manager_utils.h index 12061f014..8a3566df9 100644 --- a/openfpga/src/utils/module_manager_utils.h +++ b/openfpga/src/utils/module_manager_utils.h @@ -185,10 +185,6 @@ void add_module_bus_nets( const ModulePortId& src_module_port_id, const ModuleId& des_module_id, const size_t& des_instance_id, const ModulePortId& des_module_port_id); -int load_submodules_memory_modules_from_fabric_key( - ModuleManager& module_manager, const CircuitLibrary& circuit_lib, - const ConfigProtocol& config_protocol, const FabricKey& fabric_key); - } /* end namespace openfpga */ #endif From 3de4d3fc0984e82b50737dc377a4c57cc2a8a5d0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 8 Jul 2023 18:12:51 -0700 Subject: [PATCH 156/391] [core] add a new command 'write_fabric_key' and now writer supports module-level keys --- .../src/base/openfpga_build_fabric_template.h | 36 +++++++-- .../base/openfpga_setup_command_template.h | 44 +++++++++++ openfpga/src/fabric/fabric_key_writer.cpp | 79 +++++++++++++++++-- openfpga/src/fabric/fabric_key_writer.h | 3 +- 4 files changed, 150 insertions(+), 12 deletions(-) diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index ba8db6e2c..2e5578c01 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -157,11 +157,11 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd, std::string fkey_fname = cmd_context.option_value(cmd, opt_write_fabric_key); VTR_ASSERT(false == fkey_fname.empty()); - curr_status = - write_fabric_key_to_xml_file(openfpga_ctx.module_graph(), fkey_fname, - openfpga_ctx.arch().config_protocol, - openfpga_ctx.blwl_shift_register_banks(), - cmd_context.option_enable(cmd, opt_verbose)); + curr_status = write_fabric_key_to_xml_file( + openfpga_ctx.module_graph(), fkey_fname, + openfpga_ctx.arch().config_protocol, + openfpga_ctx.blwl_shift_register_banks(), false, + cmd_context.option_enable(cmd, opt_verbose)); /* If there is any error, final status cannot be overwritten by a success * flag */ if (CMD_EXEC_SUCCESS != curr_status) { @@ -172,6 +172,32 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd, return final_status; } +/******************************************************************** + * Write fabric key of the module graph for FPGA device to a file + *******************************************************************/ +template +int write_fabric_key_template(const T& openfpga_ctx, const Command& cmd, + const CommandContext& cmd_context) { + CommandOptionId opt_verbose = cmd.option("verbose"); + CommandOptionId opt_include_module_keys = cmd.option("include_module_keys"); + + /* Check the option '--file' is enabled or not + * Actually, it must be enabled as the shell interface will check + * before reaching this fuction + */ + CommandOptionId opt_file = cmd.option("file"); + VTR_ASSERT(true == cmd_context.option_enable(cmd, opt_file)); + VTR_ASSERT(false == cmd_context.option_value(cmd, opt_file).empty()); + + /* Write fabric key to a file */ + return write_fabric_key_to_xml_file( + openfpga_ctx.module_graph(), cmd_context.option_value(cmd, opt_file), + openfpga_ctx.arch().config_protocol, + openfpga_ctx.blwl_shift_register_banks(), + cmd_context.option_enable(cmd, opt_include_module_keys), + cmd_context.option_enable(cmd, opt_verbose)); +} + /******************************************************************** * Write hierarchy of the module graph for FPGA device to a file *******************************************************************/ diff --git a/openfpga/src/base/openfpga_setup_command_template.h b/openfpga/src/base/openfpga_setup_command_template.h index 845873e7e..403372c4e 100644 --- a/openfpga/src/base/openfpga_setup_command_template.h +++ b/openfpga/src/base/openfpga_setup_command_template.h @@ -738,6 +738,40 @@ ShellCommandId add_add_fpga_core_to_fabric_command_template( return shell_cmd_id; } +/******************************************************************** + * - Add a command to Shell environment: write_fabric_key + * - Add associated options + * - Add command dependency + *******************************************************************/ +template +ShellCommandId add_write_fabric_key_command_template( + openfpga::Shell& shell, const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds, const bool& hidden) { + Command shell_cmd("write_fabric_key"); + /* Add an option '--file' in short '-f'*/ + CommandOptionId opt_file = + shell_cmd.add_option("file", true, "file path to the fabric key XML"); + shell_cmd.set_option_short_name(opt_file, "f"); + shell_cmd.set_option_require_value(opt_file, openfpga::OPT_STRING); + + /* Add an option '--include_module_keys'*/ + shell_cmd.add_option("include_module_keys", false, + "Include module-level keys"); + shell_cmd.add_option("verbose", false, "Show verbose outputs"); + + /* Add command to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command( + shell_cmd, "write fabric key of the FPGA fabric to file", hidden); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_const_execute_function(shell_cmd_id, + write_fabric_key_template); + + /* Add command dependency to the Shell */ + shell.set_command_dependency(shell_cmd_id, dependent_cmds); + + return shell_cmd_id; +} + template void add_setup_command_templates(openfpga::Shell& shell, const bool& hidden = false) { @@ -927,6 +961,16 @@ void add_setup_command_templates(openfpga::Shell& shell, shell, openfpga_setup_cmd_class, add_fpga_core_to_fabric_dependent_cmds, hidden); + /******************************** + * Command 'write_fabric_key' + */ + /* The 'write_fabric_key' command should NOT be executed before + * 'build_fabric' */ + std::vector write_fabric_key_dependent_cmds; + write_fabric_key_dependent_cmds.push_back(build_fabric_cmd_id); + add_write_fabric_key_command_template( + shell, openfpga_setup_cmd_class, write_fabric_key_dependent_cmds, hidden); + /******************************** * Command 'write_fabric_hierarchy' */ diff --git a/openfpga/src/fabric/fabric_key_writer.cpp b/openfpga/src/fabric/fabric_key_writer.cpp index d5e7ef978..9a7e41791 100644 --- a/openfpga/src/fabric/fabric_key_writer.cpp +++ b/openfpga/src/fabric/fabric_key_writer.cpp @@ -7,6 +7,7 @@ #include "vtr_time.h" /* Headers from openfpgautil library */ +#include "command_exit_codes.h" #include "openfpga_digest.h" /* Headers from archopenfpga library */ @@ -18,6 +19,51 @@ /* begin namespace openfpga */ namespace openfpga { +/*************************************************************************************** + * Add module-level keys to fabric key + ***************************************************************************************/ +static int add_module_keys_to_fabric_key(const ModuleManager& module_manager, + const ModuleId& curr_module, + FabricKey& fabric_key) { + /* Bypass top-level module */ + std::string module_name = module_manager.module_name(curr_module); + if (module_name == generate_fpga_top_module_name() || + module_name == generate_fpga_core_module_name()) { + return CMD_EXEC_SUCCESS; + } + /* Bypass modules which does not have any configurable children */ + if (module_manager.configurable_children(curr_module).empty()) { + return CMD_EXEC_SUCCESS; + } + /* Now create the module and add subkey one by one */ + FabricKeyModuleId key_module_id = fabric_key.create_module(module_name); + if (!key_module_id) { + return CMD_EXEC_FATAL_ERROR; + } + size_t num_config_child = + module_manager.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]; + size_t child_instance = + module_manager.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, + module_manager.module_name(child_module)); + fabric_key.set_sub_key_value(sub_key, child_instance); + + if (false == + module_manager.instance_name(curr_module, child_module, child_instance) + .empty()) { + fabric_key.set_sub_key_alias( + sub_key, module_manager.instance_name(curr_module, child_module, + child_instance)); + } + } + return CMD_EXEC_SUCCESS; +} + /*************************************************************************************** * Write the fabric key of top module to an XML file * We will use the writer API in libfabrickey @@ -29,7 +75,9 @@ namespace openfpga { int write_fabric_key_to_xml_file( const ModuleManager& module_manager, const std::string& fname, const ConfigProtocol& config_protocol, - const MemoryBankShiftRegisterBanks& blwl_sr_banks, const bool& verbose) { + const MemoryBankShiftRegisterBanks& blwl_sr_banks, + const bool& include_module_keys, const bool& verbose) { + int err_code = CMD_EXEC_SUCCESS; std::string timer_message = std::string("Write fabric key to XML file '") + fname + std::string("'"); @@ -47,10 +95,18 @@ int write_fabric_key_to_xml_file( /* Find top-level module */ std::string top_module_name = generate_fpga_top_module_name(); ModuleId top_module = module_manager.find_module(top_module_name); - if (true != module_manager.valid_module_id(top_module)) { - VTR_LOGV_ERROR(verbose, "Unable to find the top-level module '%s'!\n", - top_module_name.c_str()); - return 1; + std::string core_module_name = generate_fpga_core_module_name(); + ModuleId core_module = module_manager.find_module(core_module_name); + if (!module_manager.valid_module_id(top_module) && + !module_manager.valid_module_id(core_module)) { + VTR_LOGV_ERROR( + verbose, "Unable to find the top-level/core-level module '%s' or '%s'!\n", + top_module_name.c_str(), core_module_name.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + if (module_manager.valid_module_id(top_module) && + module_manager.valid_module_id(core_module)) { + top_module = core_module; } /* Build a fabric key database by visiting all the configurable children */ @@ -165,8 +221,19 @@ int write_fabric_key_to_xml_file( VTR_LOGV(verbose, "Created %lu regions and %lu keys for the top module %s.\n", num_regions, num_keys, top_module_name.c_str()); + /* Output module subkeys if specified */ + if (include_module_keys) { + for (ModuleId submodule : module_manager.modules()) { + err_code = + add_module_keys_to_fabric_key(module_manager, submodule, fabric_key); + if (err_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } + } + /* Call the XML writer for fabric key */ - int err_code = write_xml_fabric_key(fname.c_str(), fabric_key); + err_code = write_xml_fabric_key(fname.c_str(), fabric_key); return err_code; } diff --git a/openfpga/src/fabric/fabric_key_writer.h b/openfpga/src/fabric/fabric_key_writer.h index 34327d307..77413b812 100644 --- a/openfpga/src/fabric/fabric_key_writer.h +++ b/openfpga/src/fabric/fabric_key_writer.h @@ -20,7 +20,8 @@ namespace openfpga { int write_fabric_key_to_xml_file( const ModuleManager& module_manager, const std::string& fname, const ConfigProtocol& config_protocol, - const MemoryBankShiftRegisterBanks& blwl_sr_banks, const bool& verbose); + const MemoryBankShiftRegisterBanks& blwl_sr_banks, + const bool& include_module_keys, const bool& verbose); } /* end namespace openfpga */ From 124514c80e1ba4e6179f803dadfd97bcc3c87d2f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 8 Jul 2023 18:26:05 -0700 Subject: [PATCH 157/391] [test] update fabric key to new syntax --- ...N4_1x1_qlbanksr_multi_chain_sample_key.xml | 48 +++++---- ..._N4_2x2_multi_region_qlbank_sample_key.xml | 76 ++++++------- ...4_2x2_multi_region_qlbanksr_sample_key.xml | 100 +++++++++--------- .../k4_N4_2x2_multi_region_sample_key.xml | 44 ++++---- .../k4_N4_2x2_qlbank_sample_key.xml | 72 +++++++------ .../fabric_keys/k4_N4_2x2_sample_key.xml | 72 +++++++------ 6 files changed, 212 insertions(+), 200 deletions(-) diff --git a/openfpga_flow/fabric_keys/k4_N4_1x1_qlbanksr_multi_chain_sample_key.xml b/openfpga_flow/fabric_keys/k4_N4_1x1_qlbanksr_multi_chain_sample_key.xml index 0cb89d6fe..e012b32d3 100644 --- a/openfpga_flow/fabric_keys/k4_N4_1x1_qlbanksr_multi_chain_sample_key.xml +++ b/openfpga_flow/fabric_keys/k4_N4_1x1_qlbanksr_multi_chain_sample_key.xml @@ -1,25 +1,27 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/fabric_keys/k4_N4_2x2_multi_region_qlbank_sample_key.xml b/openfpga_flow/fabric_keys/k4_N4_2x2_multi_region_qlbank_sample_key.xml index 94b3b6090..ccb7417c7 100644 --- a/openfpga_flow/fabric_keys/k4_N4_2x2_multi_region_qlbank_sample_key.xml +++ b/openfpga_flow/fabric_keys/k4_N4_2x2_multi_region_qlbank_sample_key.xml @@ -1,39 +1,41 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/fabric_keys/k4_N4_2x2_multi_region_qlbanksr_sample_key.xml b/openfpga_flow/fabric_keys/k4_N4_2x2_multi_region_qlbanksr_sample_key.xml index 9bf434709..bcbb1e9ee 100644 --- a/openfpga_flow/fabric_keys/k4_N4_2x2_multi_region_qlbanksr_sample_key.xml +++ b/openfpga_flow/fabric_keys/k4_N4_2x2_multi_region_qlbanksr_sample_key.xml @@ -1,51 +1,53 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/fabric_keys/k4_N4_2x2_multi_region_sample_key.xml b/openfpga_flow/fabric_keys/k4_N4_2x2_multi_region_sample_key.xml index 33e632b84..91293ca3d 100644 --- a/openfpga_flow/fabric_keys/k4_N4_2x2_multi_region_sample_key.xml +++ b/openfpga_flow/fabric_keys/k4_N4_2x2_multi_region_sample_key.xml @@ -1,23 +1,25 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/fabric_keys/k4_N4_2x2_qlbank_sample_key.xml b/openfpga_flow/fabric_keys/k4_N4_2x2_qlbank_sample_key.xml index 1de56c747..ea9f98aae 100644 --- a/openfpga_flow/fabric_keys/k4_N4_2x2_qlbank_sample_key.xml +++ b/openfpga_flow/fabric_keys/k4_N4_2x2_qlbank_sample_key.xml @@ -1,37 +1,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/fabric_keys/k4_N4_2x2_sample_key.xml b/openfpga_flow/fabric_keys/k4_N4_2x2_sample_key.xml index 2ce5bfa52..d5d6e9f9f 100644 --- a/openfpga_flow/fabric_keys/k4_N4_2x2_sample_key.xml +++ b/openfpga_flow/fabric_keys/k4_N4_2x2_sample_key.xml @@ -1,37 +1,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 88502124655726169d142d0d8c3dac9a03fdedf2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 8 Jul 2023 19:10:46 -0700 Subject: [PATCH 158/391] [doc] document new command and update file format about fabric_key --- .../source/manual/file_formats/fabric_key.rst | 326 +++++++++--------- .../openfpga_commands/setup_commands.rst | 23 ++ 2 files changed, 190 insertions(+), 159 deletions(-) diff --git a/docs/source/manual/file_formats/fabric_key.rst b/docs/source/manual/file_formats/fabric_key.rst index b3088aa6b..3e49233ae 100644 --- a/docs/source/manual/file_formats/fabric_key.rst +++ b/docs/source/manual/file_formats/fabric_key.rst @@ -23,60 +23,62 @@ The following example shows how to define multiple configuration regions in the .. code-block:: xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -111,41 +113,43 @@ This key contains only ``alias`` which is easy to craft. .. code-block:: xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The following shows another example of a fabric key generate by OpenFPGA for a 2 :math:`\times` 2 FPGA. @@ -154,41 +158,43 @@ This key contains only ``name`` and ``value`` which is fast to parse. .. code-block:: xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The following shows another example of a fabric key generate by OpenFPGA for a 2 :math:`\times` 2 FPGA using memory bank. @@ -197,41 +203,43 @@ This key contains only ``name``, ``value``, ``row`` and ``column``. .. code-block:: xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BL Shift Register Banks 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 7f891eecd..04f2e25cb 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst @@ -269,6 +269,8 @@ build_fabric Output current fabric key to an XML file. For example, ``--write_fabric_key fpga_2x2.xml`` See details in :ref:`file_formats_fabric_key`. + .. warning:: This option will be deprecated. Use :ref:`cmd_write_fabric_key`` as a replacement. + .. option:: --frame_view Create only frame views of the module graph. When enabled, top-level module will not include any nets. This option is made for save runtime and memory. @@ -281,6 +283,27 @@ build_fabric .. note:: This is a must-run command before launching FPGA-Verilog, FPGA-Bitstream, FPGA-SDC and FPGA-SPICE +.. _cmd_write_fabric_key: + +write_fabric_key +~~~~~~~~~~~~~~~~ + + Output current fabric key to an XML file. For example, ``write_fabric_key --file fpga_2x2.xml`` See details in :ref:`file_formats_fabric_key`. + + .. note:: This command can output module-level keys while the ``--write_fabric_key`` option in command ``build_fabric`` does NOT support! Strongly recommend to use this command to obtain fabric key. + + .. option:: --file or -f + + Specify the file name. For example, ``--file fabric_key_echo.xml``. + + .. option:: --include_module_keys + + Output module-level keys to the file. + + .. option:: --verbose + + Show verbose log + .. _cmd_add_fpga_core_to_fabric: add_fpga_core_to_fabric From 05a49278edb4b916cee5a5bef61df35c52082b37 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 8 Jul 2023 21:37:00 -0700 Subject: [PATCH 159/391] [doc] update file format --- docs/source/manual/file_formats/fabric_key.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/source/manual/file_formats/fabric_key.rst b/docs/source/manual/file_formats/fabric_key.rst index 3e49233ae..e35c8d844 100644 --- a/docs/source/manual/file_formats/fabric_key.rst +++ b/docs/source/manual/file_formats/fabric_key.rst @@ -5,6 +5,19 @@ Fabric Key (.xml) A fabric key follows an XML format. As shown in the following XML code, the key file includes the organization of configurable blocks in the top-level FPGA fabric. +Configurable Module +^^^^^^^^^^^^^^^^^^^ + +Fabric key can be applied to various modules. Each module can be a top-level FPGA fabric, or a submodule of the FPGA fabric. + +.. option:: + + Under each module, a set of keys can be defined. Note that for the top-level FPGA fabric, not only keys but also regions and shift-register banks can be defined. For non-top-level module, only keys are allowed. + + - ``name`` indicates the unique name of a valid module in FPGA fabric. Note that ``fpga_top`` is the considered as the module name of the top-level FPGA fabric. + + .. note:: ``fpga_core`` is not applicable to fabric key. + Configurable Region ^^^^^^^^^^^^^^^^^^^ From 930d98f2afa0266054932ad0d2948f18c3a15a1d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 8 Jul 2023 21:52:16 -0700 Subject: [PATCH 160/391] [test] deploy new tests --- .../fabric_keys/k4_N4_2x2_sample_subkey.xml | 61 +++++++++++++++++++ .../regression_test_scripts/basic_reg_test.sh | 1 + .../config/task.conf | 39 ++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 openfpga_flow/fabric_keys/k4_N4_2x2_sample_subkey.xml create mode 100644 openfpga_flow/tasks/basic_tests/fabric_key/load_external_subkey_cc_fpga/config/task.conf diff --git a/openfpga_flow/fabric_keys/k4_N4_2x2_sample_subkey.xml b/openfpga_flow/fabric_keys/k4_N4_2x2_sample_subkey.xml new file mode 100644 index 000000000..ea1c6e27a --- /dev/null +++ b/openfpga_flow/fabric_keys/k4_N4_2x2_sample_subkey.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index d860f9771..f0a9c9f06 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -116,6 +116,7 @@ run-task basic_tests/fabric_key/generate_random_key $@ run-task basic_tests/fabric_key/generate_random_key_ql_memory_bank $@ run-task basic_tests/fabric_key/load_external_key $@ run-task basic_tests/fabric_key/load_external_key_cc_fpga $@ +run-task basic_tests/fabric_key/load_external_subkey_cc_fpga $@ run-task basic_tests/fabric_key/load_external_key_multi_region_cc_fpga $@ run-task basic_tests/fabric_key/load_external_key_qlbank_fpga $@ run-task basic_tests/fabric_key/load_external_key_multi_region_qlbank_fpga $@ diff --git a/openfpga_flow/tasks/basic_tests/fabric_key/load_external_subkey_cc_fpga/config/task.conf b/openfpga_flow/tasks/basic_tests/fabric_key/load_external_subkey_cc_fpga/config/task.conf new file mode 100644 index 000000000..c2fd428d7 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/fabric_key/load_external_subkey_cc_fpga/config/task.conf @@ -0,0 +1,39 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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=vpr_blif + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/generate_secure_fabric_from_key_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 +external_fabric_key_file=${PATH:OPENFPGA_PATH}/openfpga_flow/fabric_keys/k4_N4_2x2_sample_subkey.xml +openfpga_vpr_device_layout=2x2 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.blif + +[SYNTHESIS_PARAM] +bench0_top = and2 +bench0_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.act +bench0_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v +bench0_chan_width = 300 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= +#vpr_fpga_verilog_formal_verification_top_netlist= From 7382ee30e288784f6ed0ebfea777b995a5d1917d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 9 Jul 2023 10:30:32 -0700 Subject: [PATCH 161/391] [doc] typo --- .../manual/openfpga_shell/openfpga_commands/setup_commands.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 04f2e25cb..fc552656c 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst @@ -269,7 +269,7 @@ build_fabric Output current fabric key to an XML file. For example, ``--write_fabric_key fpga_2x2.xml`` See details in :ref:`file_formats_fabric_key`. - .. warning:: This option will be deprecated. Use :ref:`cmd_write_fabric_key`` as a replacement. + .. warning:: This option will be deprecated. Use :ref:`cmd_write_fabric_key` as a replacement. .. option:: --frame_view From 7fafb29572f5394657a77c250e988dbf63c9d61a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 14 Jul 2023 10:23:36 -0700 Subject: [PATCH 162/391] [ci] now cell lib test use ubunut 22.04 --- .github/workflows/cell_lib_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cell_lib_test.yml b/.github/workflows/cell_lib_test.yml index 619dc5251..7d5b08984 100644 --- a/.github/workflows/cell_lib_test.yml +++ b/.github/workflows/cell_lib_test.yml @@ -13,7 +13,7 @@ jobs: # Test the RTL compilation compatibility verilog: name: RTL compilation and tests - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Cancel previous uses: styfle/cancel-workflow-action@0.9.1 @@ -25,7 +25,7 @@ jobs: - name: Install Dependencies run: | - sudo bash .github/workflows/install_dependencies_run.sh + sudo bash .github/workflows/install_dependencies_run_ubuntu22p04.sh - name: Dump tool versions run: | From bb36e72c7775e279b1cdbf1ee027d4f4bb7f84a7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 14 Jul 2023 10:27:24 -0700 Subject: [PATCH 163/391] [ci] now use apt update before installing dependencies --- .github/workflows/install_dependencies_run.sh | 2 ++ .github/workflows/install_dependencies_run_ubuntu22p04.sh | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/install_dependencies_run.sh b/.github/workflows/install_dependencies_run.sh index ee8be9676..3d086d45f 100644 --- a/.github/workflows/install_dependencies_run.sh +++ b/.github/workflows/install_dependencies_run.sh @@ -1,3 +1,5 @@ +# Update as required by some packages +apt-get update apt-get install --no-install-recommends -y \ libdatetime-perl libc6 libffi-dev libgcc1 libreadline8 libstdc++6 \ libtcl8.6 tcl python3.8 python3-pip zlib1g libbz2-1.0 \ diff --git a/.github/workflows/install_dependencies_run_ubuntu22p04.sh b/.github/workflows/install_dependencies_run_ubuntu22p04.sh index a9973d0c8..491f8020f 100644 --- a/.github/workflows/install_dependencies_run_ubuntu22p04.sh +++ b/.github/workflows/install_dependencies_run_ubuntu22p04.sh @@ -1,3 +1,5 @@ +# Update as required by some packages +apt-get update apt-get install --no-install-recommends -y \ libdatetime-perl libc6 libffi-dev libgcc1 libreadline8 libstdc++6 \ libtcl8.6 tcl python3-pip zlib1g libbz2-1.0 \ From c58035dbd46637b66a9d3562eb8d17a352a0a3f8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 14 Jul 2023 11:01:04 -0700 Subject: [PATCH 164/391] [core] start developing option --group_tile for build_fabric --- .../src/base/openfpga_build_fabric_template.h | 19 +++++++++++++++++++ .../base/openfpga_setup_command_template.h | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index 2e5578c01..dae619276 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -17,6 +17,7 @@ #include "globals.h" #include "openfpga_naming.h" #include "read_xml_fabric_key.h" +#include "read_xml_tile_config.h" #include "read_xml_io_name_map.h" #include "vtr_log.h" #include "vtr_time.h" @@ -98,6 +99,7 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd, cmd.option("generate_random_fabric_key"); 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_verbose = cmd.option("verbose"); if (true == cmd_context.option_enable(cmd, opt_compress_routing)) { @@ -125,6 +127,23 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd, VTR_LOG("\n"); + /* Build tile-level information: + * - This feature only supports when compress routing is enabled + * - Read the tile organization configuration file + * - Build tile info + */ + TileConfig tile_config; + if (cmd_context.option_enable(cmd, opt_group_tile)) { + if (!cmd_context.option_enable(cmd, opt_compress_routing)) { + VTR_LOG_ERROR("Group tile is applicable only when compress routing is enabled!\n"); + return CMD_EXEC_FATAL_ERROR; + } + curr_status = read_xml_tile_config(cmd_context.option_value(cmd, opt_group_file).c_str(), tile_config); + if (CMD_EXEC_SUCCESS != curr_status) { + return CMD_EXEC_FATAL_ERROR; + } + } + curr_status = build_device_module_graph( openfpga_ctx.mutable_module_graph(), openfpga_ctx.mutable_decoder_lib(), openfpga_ctx.mutable_blwl_shift_register_banks(), diff --git a/openfpga/src/base/openfpga_setup_command_template.h b/openfpga/src/base/openfpga_setup_command_template.h index 403372c4e..9267531ad 100644 --- a/openfpga/src/base/openfpga_setup_command_template.h +++ b/openfpga/src/base/openfpga_setup_command_template.h @@ -405,6 +405,11 @@ ShellCommandId add_build_fabric_command_template( "write_fabric_key", false, "output current fabric key to a file"); shell_cmd.set_option_require_value(opt_write_fkey, openfpga::OPT_STRING); + /* Add an option '--group_tile' */ + CommandOptionId opt_group_tile = shell_cmd.add_option( + "group_tile", false, "group programmable blocks and routing blocks into tiles. This helps to reduce the number of blocks at top-level"); + shell_cmd.set_option_require_value(opt_group_tile, openfpga::OPT_STRING); + /* 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 " From 3bc959dcec56dec8c484de32ecb7e3fc26cef38b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 14 Jul 2023 12:13:31 -0700 Subject: [PATCH 165/391] [lib] create tile config lib and start integration to core --- libs/CMakeLists.txt | 1 + libs/libtileconfig/CMakeLists.txt | 37 +++++++++ .../example/tile_config_example.xml | 1 + libs/libtileconfig/src/base/tile_config.cpp | 80 +++++++++++++++++++ libs/libtileconfig/src/base/tile_config.h | 60 ++++++++++++++ .../src/io/read_xml_tile_config.cpp | 55 +++++++++++++ .../src/io/read_xml_tile_config.h | 21 +++++ .../src/io/tile_config_xml_constants.h | 8 ++ .../src/io/write_xml_tile_config.cpp | 60 ++++++++++++++ .../src/io/write_xml_tile_config.h | 20 +++++ .../libtileconfig/test/xml_io_tile_config.cpp | 37 +++++++++ openfpga/CMakeLists.txt | 1 + .../src/base/openfpga_build_fabric_template.h | 2 +- 13 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 libs/libtileconfig/CMakeLists.txt create mode 100644 libs/libtileconfig/example/tile_config_example.xml create mode 100644 libs/libtileconfig/src/base/tile_config.cpp create mode 100644 libs/libtileconfig/src/base/tile_config.h create mode 100644 libs/libtileconfig/src/io/read_xml_tile_config.cpp create mode 100644 libs/libtileconfig/src/io/read_xml_tile_config.h create mode 100644 libs/libtileconfig/src/io/tile_config_xml_constants.h create mode 100644 libs/libtileconfig/src/io/write_xml_tile_config.cpp create mode 100644 libs/libtileconfig/src/io/write_xml_tile_config.h create mode 100644 libs/libtileconfig/test/xml_io_tile_config.cpp diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 9a8c23e43..ab8e6dbd8 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory(libfpgabitstream) add_subdirectory(libpcf) add_subdirectory(libbusgroup) add_subdirectory(libionamemap) +add_subdirectory(libtileconfig) diff --git a/libs/libtileconfig/CMakeLists.txt b/libs/libtileconfig/CMakeLists.txt new file mode 100644 index 000000000..96d2da1d2 --- /dev/null +++ b/libs/libtileconfig/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.9) + +project("libtileconfig") + +file(GLOB_RECURSE EXEC_SOURCES test/*.cpp) +file(GLOB_RECURSE LIB_SOURCES src/*/*.cpp) +file(GLOB_RECURSE LIB_HEADERS src/*/*.h) +files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS) + +#Remove test executable from library +list(REMOVE_ITEM LIB_SOURCES ${EXEC_SOURCES}) + +#Create the library +add_library(libtileconfig STATIC + ${LIB_HEADERS} + ${LIB_SOURCES}) +target_include_directories(libtileconfig PUBLIC ${LIB_INCLUDE_DIRS}) +set_target_properties(libtileconfig PROPERTIES PREFIX "") #Avoid extra 'lib' prefix + +#Specify link-time dependancies +target_link_libraries(libtileconfig + libarchopenfpga + libopenfpgautil + libopenfpgashell + libvtrutil + libpugiutil) + +#Create the test executable +foreach(testsourcefile ${EXEC_SOURCES}) + # Use a simple string replace, to cut off .cpp. + get_filename_component(testname ${testsourcefile} NAME_WE) + add_executable(${testname} ${testsourcefile}) + # Make sure the library is linked to each test executable + target_link_libraries(${testname} libtileconfig) +endforeach(testsourcefile ${EXEC_SOURCES}) + +install(TARGETS libtileconfig DESTINATION bin) diff --git a/libs/libtileconfig/example/tile_config_example.xml b/libs/libtileconfig/example/tile_config_example.xml new file mode 100644 index 000000000..1a1f3f6e8 --- /dev/null +++ b/libs/libtileconfig/example/tile_config_example.xml @@ -0,0 +1 @@ + diff --git a/libs/libtileconfig/src/base/tile_config.cpp b/libs/libtileconfig/src/base/tile_config.cpp new file mode 100644 index 000000000..35f13a2c7 --- /dev/null +++ b/libs/libtileconfig/src/base/tile_config.cpp @@ -0,0 +1,80 @@ +/****************************************************************************** + * Memember functions for data structure TileConfig + ******************************************************************************/ +#include "tile_config.h" + +#include + +#include "command_exit_codes.h" +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* begin namespace openfpga */ +namespace openfpga { + +TileConfig::TileConfig() { + style_ = TileConfig::e_style::NUM_TYPES; /* Deposit an invalid value */ + STYLE_STRING_ = {"top_left", "top_right", "bottom_left", "bottom_right", "custom"}; +} + +/************************************************** + * Public Accessors + *************************************************/ +TileConfig::e_style TileConfig::style() const { + return style_; +} + +std::string TileConfig::style_to_string() const { + return style2str(style_); +} + +bool TileConfig::is_valid() const { + return style_ != TileConfig::e_style::NUM_TYPES; +} + +int TileConfig::set_style(const std::string& value) { + style_ = str2style(value); + return valid_style(style_); +} + +std::string TileConfig::style_all2str() const { + std::string full_types = "["; + for (int itype = size_t(TileConfig::e_style::TOP_LEFT); + itype != size_t(TileConfig::e_style::NUM_TYPES); ++itype) { + full_types += std::string(STYLE_STRING_[itype]) + std::string("|"); + } + full_types.pop_back(); + full_types += "]"; + return full_types; +} + +TileConfig::e_style TileConfig::str2style( + const std::string& style_str, const bool& verbose) const { + for (int itype = size_t(TileConfig::e_style::TOP_LEFT); + itype != size_t(TileConfig::e_style::NUM_TYPES); ++itype) { + if (style_str == std::string(STYLE_STRING_[itype])) { + return static_cast(itype); + } + } + VTR_LOGV_ERROR(verbose, "Invalid style for tile configuration! Expect %s\n", + style_all2str().c_str()); + return TileConfig::e_style::NUM_TYPES; +} + +std::string TileConfig::style2str(const TileConfig::e_style& style, + const bool& verbose) const { + if (!valid_style(style)) { + VTR_LOGV_ERROR(verbose, "Invalid style for tile configuration! Expect %s\n", + style_all2str().c_str()); + return std::string(); + } + return std::string(STYLE_STRING_[size_t(style)]); +} + +bool TileConfig::valid_style( + const TileConfig::e_style& style) const { + return style != TileConfig::e_style::NUM_TYPES; +} + +} /* end namespace openfpga */ diff --git a/libs/libtileconfig/src/base/tile_config.h b/libs/libtileconfig/src/base/tile_config.h new file mode 100644 index 000000000..e4e1f3d40 --- /dev/null +++ b/libs/libtileconfig/src/base/tile_config.h @@ -0,0 +1,60 @@ +#ifndef TILE_CONFIG_H +#define TILE_CONFIG_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +#include +#include + +/* Begin namespace openfpga */ +namespace openfpga { + +/** + * @brief tile configuration is a data structure to represent how programmable blocks and routing blocks are grouped into tiles + */ +class TileConfig { + public: /* Types */ + enum class e_style { TOP_LEFT = 0, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, CUSTOM, NUM_TYPES }; + public: /* Constructors */ + TileConfig(); + + public: /* Public accessors */ + /** @brief Get all the fpga style */ + e_style style() const; + std::string style_to_string() const; + + public: /* Public mutators */ + /** @brief Create the one-on-one mapping between an port of fpga_top and + * fpga_core. Return 0 for success, return 1 for fail */ + int set_style(const std::string& value); + + public: /* Public utility */ + /** @brief identify if the tile config is valid or not */ + bool is_valid() const; + /** @brief Parse the style from string to valid type. Parser + * error can be turned on */ + e_style str2style(const std::string& style_str, + const bool& verbose = false) const; + /** @brief Output the string representing style */ + std::string style2str(const e_style& style, + const bool& verbose = false) const; + /** @brief Validate the style */ + bool valid_style( + const e_style& style) const; + + private: /* Internal utility */ + /* Generate a string include all the valid style + * Useful for printing debugging messages */ + std::string style_all2str() const; + + private: /* Internal Data */ + e_style style_; + /* Constants */ + std::array + STYLE_STRING_; +}; + +} /* End namespace openfpga*/ + +#endif diff --git a/libs/libtileconfig/src/io/read_xml_tile_config.cpp b/libs/libtileconfig/src/io/read_xml_tile_config.cpp new file mode 100644 index 000000000..4bf093d99 --- /dev/null +++ b/libs/libtileconfig/src/io/read_xml_tile_config.cpp @@ -0,0 +1,55 @@ +/******************************************************************** + * This file includes the top-level function of this library + * which reads an XML of clock network file to the associated + * data structures + *******************************************************************/ +#include + +/* Headers from pugi XML library */ +#include "pugixml.hpp" +#include "pugixml_util.hpp" + +/* Headers from vtr util library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from libarchfpga */ +#include "arch_error.h" +#include "command_exit_codes.h" +#include "tile_config_xml_constants.h" +#include "read_xml_tile_config.h" +#include "read_xml_util.h" + +namespace openfpga { // Begin namespace openfpga + +/******************************************************************** + * Parse XML codes about to an object of ClockNetwork + *******************************************************************/ +int read_xml_tile_config(const char* fname, TileConfig& tile_config) { + vtr::ScopedStartFinishTimer timer("Read tile configuration rules"); + + int status = CMD_EXEC_SUCCESS; + + /* Parse the file */ + pugi::xml_document doc; + pugiutil::loc_data loc_data; + + try { + loc_data = pugiutil::load_xml(doc, fname); + + pugi::xml_node xml_root = + get_single_child(doc, XML_TILE_CONFIG_ROOT_NAME, loc_data); + + std::string style = + get_attribute(xml_root, XML_TILE_CONFIG_ATTRIBUTE_STYLE_NAME, loc_data) + .as_string(); + tile_config.set_style(style); + } catch (pugiutil::XmlError& e) { + archfpga_throw(fname, e.line(), "%s", e.what()); + } + + return status; +} + +} // End of namespace openfpga diff --git a/libs/libtileconfig/src/io/read_xml_tile_config.h b/libs/libtileconfig/src/io/read_xml_tile_config.h new file mode 100644 index 000000000..2c84b5538 --- /dev/null +++ b/libs/libtileconfig/src/io/read_xml_tile_config.h @@ -0,0 +1,21 @@ +#ifndef READ_XML_TILE_CONFIG_H +#define READ_XML_TILE_CONFIG_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "tile_config.h" +#include "pugixml.hpp" +#include "pugixml_util.hpp" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +namespace openfpga { // Begin namespace openfpga + +int read_xml_tile_config(const char* fname, TileConfig& tile_config); + +} // End of namespace openfpga + +#endif diff --git a/libs/libtileconfig/src/io/tile_config_xml_constants.h b/libs/libtileconfig/src/io/tile_config_xml_constants.h new file mode 100644 index 000000000..583aa8d3c --- /dev/null +++ b/libs/libtileconfig/src/io/tile_config_xml_constants.h @@ -0,0 +1,8 @@ +#ifndef TILE_CONFIG_XML_CONSTANTS_H +#define TILE_CONFIG_XML_CONSTANTS_H + +/* Constants required by XML parser */ +constexpr const char* XML_TILE_CONFIG_ROOT_NAME = "tiles"; +constexpr const char* XML_TILE_CONFIG_ATTRIBUTE_STYLE_NAME = "style"; + +#endif diff --git a/libs/libtileconfig/src/io/write_xml_tile_config.cpp b/libs/libtileconfig/src/io/write_xml_tile_config.cpp new file mode 100644 index 000000000..528f2131b --- /dev/null +++ b/libs/libtileconfig/src/io/write_xml_tile_config.cpp @@ -0,0 +1,60 @@ +/******************************************************************** + * This file includes functions that outputs a clock network object to XML + *format + *******************************************************************/ +/* Headers from system goes first */ +#include +#include + +/* Headers from vtr util library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from arch openfpga library */ +#include "openfpga_digest.h" +#include "write_xml_utils.h" + +/* Headers from pin constraint library */ +#include "tile_config_xml_constants.h" +#include "write_xml_tile_config.h" + +namespace openfpga { // Begin namespace openfpga + +/******************************************************************** + * A writer to output an object to XML format + * + * Return 0 if successful + * Return 1 if there are more serious bugs in the architecture + * Return 2 if fail when creating files + *******************************************************************/ +int write_xml_tile_config(const char* fname, const TileConfig& tile_config) { + vtr::ScopedStartFinishTimer timer("Write tile configuration rules"); + + /* Create a file handler */ + std::fstream fp; + /* Open the file stream */ + fp.open(std::string(fname), std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + openfpga::check_file_stream(fname, fp); + + int err_code = 0; + + /* Write the root node */ + fp << "<" << XML_TILE_CONFIG_ROOT_NAME; + + write_xml_attribute(fp, XML_TILE_CONFIG_ATTRIBUTE_STYLE_NAME, + tile_config.style_to_string().c_str()); + /* Finish writing the root node */ + fp << "/>" + << "\n"; + + + /* Close the file stream */ + fp.close(); + + return err_code; +} + +} // End of namespace openfpga diff --git a/libs/libtileconfig/src/io/write_xml_tile_config.h b/libs/libtileconfig/src/io/write_xml_tile_config.h new file mode 100644 index 000000000..f0456db38 --- /dev/null +++ b/libs/libtileconfig/src/io/write_xml_tile_config.h @@ -0,0 +1,20 @@ +#ifndef WRITE_XML_TILE_CONFIG_H +#define WRITE_XML_TILE_CONFIG_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +#include "tile_config.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ +namespace openfpga { // Begin namespace openfpga + +int write_xml_tile_config(const char* fname, const TileConfig& tile_config); + +} // End of namespace openfpga + +#endif diff --git a/libs/libtileconfig/test/xml_io_tile_config.cpp b/libs/libtileconfig/test/xml_io_tile_config.cpp new file mode 100644 index 000000000..bd4a15c9d --- /dev/null +++ b/libs/libtileconfig/test/xml_io_tile_config.cpp @@ -0,0 +1,37 @@ +/******************************************************************** + * Unit test functions to validate the correctness of + * 1. parser of data structures + * 2. writer of data structures + *******************************************************************/ +/* Headers from vtrutils */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from readarchopenfpga */ +#include "read_xml_tile_config.h" +#include "write_xml_tile_config.h" + +int main(int argc, const char** argv) { + /* Ensure we have only one or two argument */ + VTR_ASSERT((2 == argc) || (3 == argc)); + + int status = 0; + + /* Parse the circuit library from an XML file */ + openfpga::TileConfig tile_config; + status = openfpga::read_xml_tile_config(argv[1], tile_config); + if (status != 0) { + return status; + } + VTR_LOG("Parsed tile configuration from XML file: %s\n", argv[1]); + + /* Output the bus group to an XML file + * This is optional only used when there is a second argument + */ + if (3 <= argc) { + status = openfpga::write_xml_tile_config(argv[2], tile_config); + VTR_LOG("Write the tile configuration to an XML file: %s.\n", argv[2]); + } + + return status; +} diff --git a/openfpga/CMakeLists.txt b/openfpga/CMakeLists.txt index 7c6efe83a..bea2fa94d 100644 --- a/openfpga/CMakeLists.txt +++ b/openfpga/CMakeLists.txt @@ -42,6 +42,7 @@ target_link_libraries(libopenfpga libvtrutil libbusgroup libionamemap + libtileconfig libpugixml libvpr) diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index dae619276..a8a81adb1 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -138,7 +138,7 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd, VTR_LOG_ERROR("Group tile is applicable only when compress routing is enabled!\n"); return CMD_EXEC_FATAL_ERROR; } - curr_status = read_xml_tile_config(cmd_context.option_value(cmd, opt_group_file).c_str(), tile_config); + curr_status = read_xml_tile_config(cmd_context.option_value(cmd, opt_group_tile).c_str(), tile_config); if (CMD_EXEC_SUCCESS != curr_status) { return CMD_EXEC_FATAL_ERROR; } From 091ac88c7e495c526dbb8ceeaebb8adafb3d788e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 14 Jul 2023 12:16:40 -0700 Subject: [PATCH 166/391] [lib] code format --- libs/libtileconfig/src/base/tile_config.cpp | 22 ++++++++----------- libs/libtileconfig/src/base/tile_config.h | 21 ++++++++++++------ .../src/io/read_xml_tile_config.cpp | 4 ++-- .../src/io/read_xml_tile_config.h | 2 +- .../src/io/write_xml_tile_config.cpp | 1 - .../src/base/openfpga_build_fabric_template.h | 8 ++++--- .../base/openfpga_setup_command_template.h | 4 +++- 7 files changed, 34 insertions(+), 28 deletions(-) diff --git a/libs/libtileconfig/src/base/tile_config.cpp b/libs/libtileconfig/src/base/tile_config.cpp index 35f13a2c7..49fa8e52f 100644 --- a/libs/libtileconfig/src/base/tile_config.cpp +++ b/libs/libtileconfig/src/base/tile_config.cpp @@ -14,20 +14,17 @@ namespace openfpga { TileConfig::TileConfig() { - style_ = TileConfig::e_style::NUM_TYPES; /* Deposit an invalid value */ - STYLE_STRING_ = {"top_left", "top_right", "bottom_left", "bottom_right", "custom"}; + style_ = TileConfig::e_style::NUM_TYPES; /* Deposit an invalid value */ + STYLE_STRING_ = {"top_left", "top_right", "bottom_left", "bottom_right", + "custom"}; } /************************************************** * Public Accessors *************************************************/ -TileConfig::e_style TileConfig::style() const { - return style_; -} +TileConfig::e_style TileConfig::style() const { return style_; } -std::string TileConfig::style_to_string() const { - return style2str(style_); -} +std::string TileConfig::style_to_string() const { return style2str(style_); } bool TileConfig::is_valid() const { return style_ != TileConfig::e_style::NUM_TYPES; @@ -35,7 +32,7 @@ bool TileConfig::is_valid() const { int TileConfig::set_style(const std::string& value) { style_ = str2style(value); - return valid_style(style_); + return valid_style(style_); } std::string TileConfig::style_all2str() const { @@ -49,8 +46,8 @@ std::string TileConfig::style_all2str() const { return full_types; } -TileConfig::e_style TileConfig::str2style( - const std::string& style_str, const bool& verbose) const { +TileConfig::e_style TileConfig::str2style(const std::string& style_str, + const bool& verbose) const { for (int itype = size_t(TileConfig::e_style::TOP_LEFT); itype != size_t(TileConfig::e_style::NUM_TYPES); ++itype) { if (style_str == std::string(STYLE_STRING_[itype])) { @@ -72,8 +69,7 @@ std::string TileConfig::style2str(const TileConfig::e_style& style, return std::string(STYLE_STRING_[size_t(style)]); } -bool TileConfig::valid_style( - const TileConfig::e_style& style) const { +bool TileConfig::valid_style(const TileConfig::e_style& style) const { return style != TileConfig::e_style::NUM_TYPES; } diff --git a/libs/libtileconfig/src/base/tile_config.h b/libs/libtileconfig/src/base/tile_config.h index e4e1f3d40..41dd8acd9 100644 --- a/libs/libtileconfig/src/base/tile_config.h +++ b/libs/libtileconfig/src/base/tile_config.h @@ -4,18 +4,27 @@ /******************************************************************** * Include header files required by the data structure definition *******************************************************************/ -#include #include +#include /* Begin namespace openfpga */ namespace openfpga { /** - * @brief tile configuration is a data structure to represent how programmable blocks and routing blocks are grouped into tiles + * @brief tile configuration is a data structure to represent how programmable + * blocks and routing blocks are grouped into tiles */ class TileConfig { public: /* Types */ - enum class e_style { TOP_LEFT = 0, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, CUSTOM, NUM_TYPES }; + enum class e_style { + TOP_LEFT = 0, + TOP_RIGHT, + BOTTOM_LEFT, + BOTTOM_RIGHT, + CUSTOM, + NUM_TYPES + }; + public: /* Constructors */ TileConfig(); @@ -40,8 +49,7 @@ class TileConfig { std::string style2str(const e_style& style, const bool& verbose = false) const; /** @brief Validate the style */ - bool valid_style( - const e_style& style) const; + bool valid_style(const e_style& style) const; private: /* Internal utility */ /* Generate a string include all the valid style @@ -51,8 +59,7 @@ class TileConfig { private: /* Internal Data */ e_style style_; /* Constants */ - std::array - STYLE_STRING_; + std::array STYLE_STRING_; }; } /* End namespace openfpga*/ diff --git a/libs/libtileconfig/src/io/read_xml_tile_config.cpp b/libs/libtileconfig/src/io/read_xml_tile_config.cpp index 4bf093d99..f332f5688 100644 --- a/libs/libtileconfig/src/io/read_xml_tile_config.cpp +++ b/libs/libtileconfig/src/io/read_xml_tile_config.cpp @@ -17,9 +17,9 @@ /* Headers from libarchfpga */ #include "arch_error.h" #include "command_exit_codes.h" -#include "tile_config_xml_constants.h" #include "read_xml_tile_config.h" #include "read_xml_util.h" +#include "tile_config_xml_constants.h" namespace openfpga { // Begin namespace openfpga @@ -40,7 +40,7 @@ int read_xml_tile_config(const char* fname, TileConfig& tile_config) { pugi::xml_node xml_root = get_single_child(doc, XML_TILE_CONFIG_ROOT_NAME, loc_data); - + std::string style = get_attribute(xml_root, XML_TILE_CONFIG_ATTRIBUTE_STYLE_NAME, loc_data) .as_string(); diff --git a/libs/libtileconfig/src/io/read_xml_tile_config.h b/libs/libtileconfig/src/io/read_xml_tile_config.h index 2c84b5538..7e80f612e 100644 --- a/libs/libtileconfig/src/io/read_xml_tile_config.h +++ b/libs/libtileconfig/src/io/read_xml_tile_config.h @@ -4,9 +4,9 @@ /******************************************************************** * Include header files that are required by function declaration *******************************************************************/ -#include "tile_config.h" #include "pugixml.hpp" #include "pugixml_util.hpp" +#include "tile_config.h" /******************************************************************** * Function declaration diff --git a/libs/libtileconfig/src/io/write_xml_tile_config.cpp b/libs/libtileconfig/src/io/write_xml_tile_config.cpp index 528f2131b..48e1254df 100644 --- a/libs/libtileconfig/src/io/write_xml_tile_config.cpp +++ b/libs/libtileconfig/src/io/write_xml_tile_config.cpp @@ -50,7 +50,6 @@ int write_xml_tile_config(const char* fname, const TileConfig& tile_config) { fp << "/>" << "\n"; - /* Close the file stream */ fp.close(); diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index a8a81adb1..2fc4b3d69 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -17,8 +17,8 @@ #include "globals.h" #include "openfpga_naming.h" #include "read_xml_fabric_key.h" -#include "read_xml_tile_config.h" #include "read_xml_io_name_map.h" +#include "read_xml_tile_config.h" #include "vtr_log.h" #include "vtr_time.h" @@ -135,10 +135,12 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd, TileConfig tile_config; if (cmd_context.option_enable(cmd, opt_group_tile)) { if (!cmd_context.option_enable(cmd, opt_compress_routing)) { - VTR_LOG_ERROR("Group tile is applicable only when compress routing is enabled!\n"); + VTR_LOG_ERROR( + "Group tile is applicable only when compress routing is enabled!\n"); return CMD_EXEC_FATAL_ERROR; } - curr_status = read_xml_tile_config(cmd_context.option_value(cmd, opt_group_tile).c_str(), tile_config); + curr_status = read_xml_tile_config( + cmd_context.option_value(cmd, opt_group_tile).c_str(), tile_config); if (CMD_EXEC_SUCCESS != curr_status) { return CMD_EXEC_FATAL_ERROR; } diff --git a/openfpga/src/base/openfpga_setup_command_template.h b/openfpga/src/base/openfpga_setup_command_template.h index 9267531ad..12d34c00d 100644 --- a/openfpga/src/base/openfpga_setup_command_template.h +++ b/openfpga/src/base/openfpga_setup_command_template.h @@ -407,7 +407,9 @@ ShellCommandId add_build_fabric_command_template( /* Add an option '--group_tile' */ CommandOptionId opt_group_tile = shell_cmd.add_option( - "group_tile", false, "group programmable blocks and routing blocks into tiles. This helps to reduce the number of blocks at top-level"); + "group_tile", false, + "group programmable blocks and routing blocks into tiles. This helps to " + "reduce the number of blocks at top-level"); shell_cmd.set_option_require_value(opt_group_tile, openfpga::OPT_STRING); /* Add an option '--generate_random_fabric_key' */ From 3b506fc213a83e3bb8e281cdf3422a735f42371c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 15 Jul 2023 00:02:34 +0000 Subject: [PATCH 167/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 11be7f271..e198acba6 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1173 +1.2.1200 From c2ef5ca4089984f5f2b202bea3e6b914980c6241 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 14 Jul 2023 22:48:44 -0700 Subject: [PATCH 168/391] [core] developing top-left style tile info --- openfpga/src/annotation/fabric_tile.cpp | 89 ++++++++++++ openfpga/src/annotation/fabric_tile.h | 73 ++++++++++ openfpga/src/annotation/fabric_tile_fwd.h | 23 +++ .../src/base/openfpga_build_fabric_template.h | 2 +- openfpga/src/fabric/build_device_module.cpp | 15 +- openfpga/src/fabric/build_device_module.h | 4 +- openfpga/src/fabric/build_fabric_tile.cpp | 133 ++++++++++++++++++ openfpga/src/fabric/build_fabric_tile.h | 26 ++++ 8 files changed, 362 insertions(+), 3 deletions(-) create mode 100644 openfpga/src/annotation/fabric_tile.cpp create mode 100644 openfpga/src/annotation/fabric_tile.h create mode 100644 openfpga/src/annotation/fabric_tile_fwd.h create mode 100644 openfpga/src/fabric/build_fabric_tile.cpp create mode 100644 openfpga/src/fabric/build_fabric_tile.h diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp new file mode 100644 index 000000000..1d3754d90 --- /dev/null +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -0,0 +1,89 @@ +/************************************************************************ + * Member functions for class FabricTile + ***********************************************************************/ +#include "fabric_tile.h" + +#include "vtr_assert.h" +#include "vtr_log.h" + +/* namespace openfpga begins */ +namespace openfpga { + +FabricTile::FabricTile(const DeviceRRGSB& device_rr_gsb) + : device_rr_gsb_(device_rr_gsb) {} + +vtr::Point FabricTile::tile_coordinate( + const FabricTileId& tile_id) const { + VTR_ASSERT(valid_tile_id(tile_id)); + return coords_[tile_id]; +} + +FabricTileId FabricTile::unique_tile(const vtr::Point& coord) const { + /* Return invalid Id when out of range! */ + if (coord.x() < unique_tile_ids_.size()) { + if (coord.y() < unique_tile_ids_[coord.x()].size()) { + return unique_tile_ids_[coord.x()][coord.y()]; + } + } + return FabricTileId::INVALID(); +} + +FabricTileId FabricTile::create_tile() { + FabricTileId tile_id = FabricTileId(ids_.size()); + ids_.push_back(tile_id); + coords_.emplace_back(); + pb_coords_.emplace_back(); + cbx_coords_.emplace_back(); + cby_coords_.emplace_back(); + sb_coords_.emplace_back(); + + return tile_id; +} + +void set_tile_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord) { + VTR_ASSERT(valid_tile_id(tile_id)); + coords_[tile_id] = coord; +} + +void add_pb_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord) { + VTR_ASSERT(valid_tile_id(tile_id)); + pb_coords_[tile_id] = coord; +} + +void add_cbx_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord) { + VTR_ASSERT(valid_tile_id(tile_id)); + /* Ensure the coordinates are not duplicated */ + auto result = std::find(cbx_coords_.begin(), cbx_coords_.end(), coord); + if (result == cbx_coords_.end()) { + cbx_coords_[tile_id].push_back(coord); + } +} + +void add_cby_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord) { + VTR_ASSERT(valid_tile_id(tile_id)); + /* Ensure the coordinates are not duplicated */ + auto result = std::find(cby_coords_.begin(), cby_coords_.end(), coord); + if (result == cby_coords_.end()) { + cby_coords_[tile_id].push_back(coord); + } +} + +void add_sb_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord) { + VTR_ASSERT(valid_tile_id(tile_id)); + /* Ensure the coordinates are not duplicated */ + auto result = std::find(sb_coords_.begin(), sb_coords_.end(), coord); + if (result == sb_coords_.end()) { + sb_coords_[tile_id].push_back(coord); + } +} + +bool FabricTileId::valid_tile_id(const FabricTileId& tile_id) const { + return (size_t(tile_id) < ids_.size()) && (tile_id == ids_[tile_id]); +} + +} /* End namespace openfpga*/ diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h new file mode 100644 index 000000000..43a6488a0 --- /dev/null +++ b/openfpga/src/annotation/fabric_tile.h @@ -0,0 +1,73 @@ +#ifndef FABRIC_TILE_H +#define FABRIC_TILE_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +#include + +#include "device_rr_gsb.h" +#include "fabric_tile_fwd.h" +#include "vtr_geometry.h" +#include "vtr_vector.h" + +/* namespace openfpga begins */ +namespace openfpga { + +/******************************************************************** + * Object models the tiles in an FPGA fabric + * This includes: + * 1. a collection of tiles, each which contains a programmable block and + *surrounding routing blocks + * 2. a collection of unique tiles + *******************************************************************/ +class FabricTile { + public: /* Contructors */ + FabricTile(const DeviceRRGSB& device_rr_gsb); + + public: /* Accessors */ + vtr::Point tile_coordinate(const FabricTileId& tile_id) const; + /** @brief With a given coordinate, find the id of the unique tile (which is + * the same as the tile in structure) */ + FabricTileId unique_tile(const vtr::Point& coord) const; + + public: /* Mutators */ + FabricTileId create_tile(); + void set_tile_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord); + void add_pb_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord); + void add_cbx_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord); + void add_cby_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord); + void add_sb_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord); + /** @brief Build a list of unique tiles by comparing the coordinates in + * DeviceRRGSB */ + void build_unique_tiles(); + /** @brief Clear all the content */ + void clear(); + + private: /* Validators */ + bool valid_tile_id(const FabricTileId& tile_id) const; + + private: /* Internal builders */ + private: /* Internal Data */ + vtr::vector ids_; + vtr::vector> coords_; + /* Coordinates w.r.t. RRGSB */ + vtr::vector> pb_coords_; + vtr::vector>> cbx_coords_; + vtr::vector>> cby_coords_; + vtr::vector>> sb_coords_; + std::vector> + unique_tile_ids_; /* Use [x][y] to get the id of the unique tile with a + given coordinate */ + /* Cached data */ + const DeviceRRGSB& device_rr_gsb_; +}; + +} /* End namespace openfpga*/ + +#endif diff --git a/openfpga/src/annotation/fabric_tile_fwd.h b/openfpga/src/annotation/fabric_tile_fwd.h new file mode 100644 index 000000000..ee51de07a --- /dev/null +++ b/openfpga/src/annotation/fabric_tile_fwd.h @@ -0,0 +1,23 @@ +/************************************************** + * This file includes only declarations for + * the data structures for fabric tiles + * Please refer to fabric_tiles.h for more details + *************************************************/ +#ifndef FABRIC_TILE_FWD_H +#define FABRIC_TILE_FWD_H + +#include "vtr_strong_id.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/* Strong Ids for ModuleManager */ +struct fabric_tile_id_tag; + +typedef vtr::StrongId FabricTileId; + +class FabricTile; + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index 2fc4b3d69..21c3216f4 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -153,7 +153,7 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd, cmd_context.option_enable(cmd, opt_frame_view), cmd_context.option_enable(cmd, opt_compress_routing), cmd_context.option_enable(cmd, opt_duplicate_grid_pin), - predefined_fabric_key, + predefined_fabric_key, tile_config, cmd_context.option_enable(cmd, opt_gen_random_fabric_key), cmd_context.option_enable(cmd, opt_verbose)); diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index 4a6313870..5edbdec9e 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -35,7 +35,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 bool& generate_random_fabric_key, const bool& verbose) { + const TileConfig& tile_config, const bool& generate_random_fabric_key, + const bool& verbose) { vtr::ScopedStartFinishTimer timer("Build fabric module graph"); int status = CMD_EXEC_SUCCESS; @@ -99,6 +100,18 @@ int build_device_module_graph( openfpga_ctx.arch().config_protocol.type(), sram_model, verbose); } + /* Build tile modules if defined */ + FabricTile fabric_tile(openfpga_ctx.device_rr_gsb()); + if (!tile_config.empty()) { + /* TODO: Build detailed tile-level information */ + status = build_fabric_tile(fabric_tile, tile_config, vpr_device_ctx.grid, openfpga_ctx.device_rr_gsb()); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + /* TODO: Build the modules */ + // build_tile_modules(module_manager, fabric_tile); + } + /* Build FPGA fabric top-level module */ status = build_top_module( module_manager, decoder_lib, blwl_sr_banks, openfpga_ctx.arch().circuit_lib, diff --git a/openfpga/src/fabric/build_device_module.h b/openfpga/src/fabric/build_device_module.h index add422e5d..513982aa9 100644 --- a/openfpga/src/fabric/build_device_module.h +++ b/openfpga/src/fabric/build_device_module.h @@ -7,6 +7,7 @@ #include "fabric_key.h" #include "io_name_map.h" #include "openfpga_context.h" +#include "tile_config.h" #include "vpr_context.h" /******************************************************************** @@ -22,7 +23,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 bool& generate_random_fabric_key, const bool& verbose); + const TileConfig& tile_config, const bool& generate_random_fabric_key, + const bool& verbose); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_fabric_tile.cpp b/openfpga/src/fabric/build_fabric_tile.cpp new file mode 100644 index 000000000..83f94c523 --- /dev/null +++ b/openfpga/src/fabric/build_fabric_tile.cpp @@ -0,0 +1,133 @@ +/******************************************************************** + * This file includes functions that are used to build the location + * map information for the top-level module of the FPGA fabric + * It helps OpenFPGA to link the I/O port index in top-level module + * to the VPR I/O mapping results + *******************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "command_exit_codes.h" +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from vpr library */ +#include "build_fabric_tile.h" +#include "openfpga_naming.h" +#include "openfpga_reserved_words.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Build tiles by following the top-level style. + * - The programmble block, e.g., clb, is placed on the top-left corner + * - The connection blocks and switch block are placed on the right and bottom + *sides + *******************************************************************/ +static int build_fabric_tile_top_left(FabricTile& fabric_tile, const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb) { + int status_code = CMD_EXEC_SUCCESS; + + /* Walk through all the device rr_gsb and create tile one by one */ + for (size_t ix = 0; ix < grids.width(); ++ix) { + for (size_t iy = 0; iy < grids.height(); ++iy) { + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(ix, iy); + bool skip_add_pb = false; + vtr::Point curr_tile_coord(ix, iy); + FabricTileId curr_tile_id = FabricTileId::INVALID(); + /* For EMPTY grid, routing blocks may still be required if there is a gsb + */ + if (true == is_empty_type(phy_tile_type)) { + skip_add_pb = true; + /* Need to create a new tile here */ + curr_tile_id = fabric_tile.create_tile(); + fabric_tile.set_coordinate(curr_tile_coord); + } + /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(ix, iy)) || + (0 < grids.get_height_offset(ix, iy))) { + /* Find the root of this grid, the instance id should be valid. + * We just copy it here + */ + vtr::Point root_tile_coord( + ix - grids.get_width_offset(ix, iy), + iy - grids.get_height_offset(ix, iy)); + skip_add_pb = true; + curr_tile_id = fabric_tile.find_tile(root_tile_coord); + } else { + /* Need to create a new tile here */ + curr_tile_id = fabric_tile.create_tile(); + fabric_tile.set_coordinate(curr_tile_coord); + } + + /* Ensure that we have a valid id */ + if (fabric_tile.valid_tile_id(curr_tile_id)) { + VTR_LOG_ERROR("Failed to get a valid id for tile[%lu][%lu]!\n", ix, iy); + return CMD_EXEC_FATAL_ERROR; + } + + /* Add components: pb, cbx, cby, and sb if exists */ + if (!skip_add_pb) { + fabric_tile.add_pb_coordinate(curr_tile_id, curr_tile_coord); + } + /* The gsb coordinate is different than the grid coordinate when the + * top-left style is considered + * + * +----------+ +----------+ + * | Grid | | CBx | + * | [x][y] | | [x][y] | + * +----------+ +----------+ + * +----------+ +----------+ + * | CBy | | SB | + * | [x][y-1] | | [x][y-1] | + * +----------+ +----------+ + * + */ + vtr::Point curr_gsb_coord(ix, iy - 1); + if (!device_rr_gsb.is_gsb_exist(curr_gsb_coord)) { + continue; + } + const RRGSB& curr_rr_gsb = device_rr_gsb.get_gsb(curr_gsb_coord); + if (curr_rr_gsb.is_cb_exist(CHANX)) { + fabric_tile.add_cbx_coordinate(curr_tile_id, + curr_rr_gsb.get_cb_coordinate(CHANX)); + } + if (curr_rr_gsb.is_cb_exist(CHANY)) { + fabric_tile.add_cby_coordinate(curr_tile_id, + curr_rr_gsb.get_cb_coordinate(CHANY)); + } + if (curr_rr_gsb.is_sb_exist()) { + fabric_tile.add_sb_coordinate(curr_tile_id, + curr_rr_gsb.get_sb_coordinate()); + } + } + } + + return status_code; +} + +/******************************************************************** + * Build tile-level information for a given FPGA fabric, w.r.t. to configuration + *******************************************************************/ +int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb) { + vtr::ScopedStartFinishTimer timer( + "Build tile-level information for the FPGA fabric"); + + int status_code = CMD_EXEC_SUCCESS; + + /* Depending on the selected style, follow different approaches */ + if (tile_config.style() == TileConfig::e_style::TOP_LEFT) { + status_code = build_fabric_tile_style_top_left(fabric_tile, grids, device_rr_gsb); + } else { + /* Error out for styles that are not supported yet! */ + VTR_LOG_ERROR("Tile style '%s' is not supported yet!\n", + tile_config.style_to_string().c_str()); + status_code = CMD_EXEC_FATAL_ERROR; + } + + return status_code; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_fabric_tile.h b/openfpga/src/fabric/build_fabric_tile.h new file mode 100644 index 000000000..b70f66f07 --- /dev/null +++ b/openfpga/src/fabric/build_fabric_tile.h @@ -0,0 +1,26 @@ +#ifndef BUILD_FABRIC_TILE_H +#define BUILD_FABRIC_TILE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ + +#include + +#include "fabric_tile.h" +#include "tile_config.h" +#include "device_grid.h" +#include "device_rr_gsb.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb); + +} /* end namespace openfpga */ + +#endif From 924622f5e522bed59b86ac043985ecf0242b40db Mon Sep 17 00:00:00 2001 From: Chung Shien Chai Date: Sat, 15 Jul 2023 17:46:43 -0700 Subject: [PATCH 169/391] Issue 1248 - fix bug bintoi_charvec() --- libs/libopenfpgautil/src/openfpga_decode.cpp | 32 +++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/libs/libopenfpgautil/src/openfpga_decode.cpp b/libs/libopenfpgautil/src/openfpga_decode.cpp index 988bec456..6e69b230b 100644 --- a/libs/libopenfpgautil/src/openfpga_decode.cpp +++ b/libs/libopenfpgautil/src/openfpga_decode.cpp @@ -2,7 +2,6 @@ * This file includes functions that are used to decode integer to binary *vectors or the reverse operation ***************************************************************************************/ -#include /* Headers from vtrutil library */ #include "openfpga_decode.h" @@ -109,12 +108,27 @@ std::string combine_two_1hot_str(const std::string& code1, * Output: * index | 0 | 1 | 2 * ret | 0 | 0 | 1 +* +* ToDo: Need to revisit and change all the feature that call to this function +* Had studied the code, should be safe to make the change +* Apparently we only want to store 0 or 1 (binary vector) +* Yet we store it in a vector on size_t (8 Bytes) +* We are using 8x memory that we supposed to +* uint8_t is good enough +* Not a bug, but is a serious unoptimized issue ********************************************************************/ std::vector itobin_vec(const size_t& in_int, const size_t& bin_len) { + /* bin_len must be in valid range*/ + VTR_ASSERT(bin_len > 0 && bin_len <= 64); std::vector ret(bin_len, 0); /* Make sure we do not have any overflow! */ - VTR_ASSERT((in_int < pow(2., bin_len))); + /* If the length is 64 bits, in_int can be any number since this is the max bit length, and BTW pow(2, 64) + itself should be zero (from 64bits size_t perspective). Once we fix the bintoi_charvec() bug, without + this change, it will cause assertion */ + if (bin_len < 64) { + VTR_ASSERT(in_int < (size_t(1) << bin_len)); + } size_t temp = in_int; for (size_t i = 0; i < bin_len; i++) { @@ -140,10 +154,17 @@ std::vector itobin_vec(const size_t& in_int, const size_t& bin_len) { * which has a smaller memory footprint than size_t ********************************************************************/ std::vector itobin_charvec(const size_t& in_int, const size_t& bin_len) { + /* bin_len must be in valid range*/ + VTR_ASSERT(bin_len > 0 && bin_len <= 64); std::vector ret(bin_len, '0'); /* Make sure we do not have any overflow! */ - VTR_ASSERT((in_int < pow(2., bin_len))); + /* If the length is 64 bits, in_int can be any number since this is the max bit length, and BTW pow(2, 64) + itself should be zero (from 64bits size_t perspective). Once we fix the bintoi_charvec() bug, without + this change, it will cause assertion */ + if (bin_len < 64) { + VTR_ASSERT(in_int < (size_t(1) << bin_len)); + } size_t temp = in_int; for (size_t i = 0; i < bin_len; i++) { @@ -170,11 +191,14 @@ std::vector itobin_charvec(const size_t& in_int, const size_t& bin_len) { * which has a smaller memory footprint than size_t ********************************************************************/ size_t bintoi_charvec(const std::vector& bin) { + /* bin.size() must be in valid range*/ + VTR_ASSERT(bin.size() > 0 && bin.size() <= 64); + size_t ret = 0; for (size_t i = 0; i < bin.size(); ++i) { if ('1' == bin[i]) { - ret += pow(2., i); + ret |= ((size_t)(1) << i); } } From ea8d1287890e454b696eafb7a9c300e2efa37aaa Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 15 Jul 2023 20:29:21 -0700 Subject: [PATCH 170/391] [core] syntax --- openfpga/src/annotation/fabric_tile.cpp | 128 +++++++++++++++----- openfpga/src/annotation/fabric_tile.h | 20 ++- openfpga/src/fabric/build_device_module.cpp | 9 +- openfpga/src/fabric/build_fabric_tile.cpp | 22 ++-- openfpga/src/fabric/build_fabric_tile.h | 8 +- 5 files changed, 136 insertions(+), 51 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index 1d3754d90..f6a036c7e 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -9,8 +9,7 @@ /* namespace openfpga begins */ namespace openfpga { -FabricTile::FabricTile(const DeviceRRGSB& device_rr_gsb) - : device_rr_gsb_(device_rr_gsb) {} +FabricTile::FabricTile(const vtr::Point& max_coord) { init(max_coord); } vtr::Point FabricTile::tile_coordinate( const FabricTileId& tile_id) const { @@ -28,61 +27,128 @@ FabricTileId FabricTile::unique_tile(const vtr::Point& coord) const { return FabricTileId::INVALID(); } -FabricTileId FabricTile::create_tile() { +FabricTileId FabricTile::find_tile(const vtr::Point& coord) const { + if (coord.x() >= tile_coord2id_lookup_.size()) { + VTR_LOG_ERROR( + "Tile coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + coord.x(), coord.y(), tile_coord2id_lookup_.size(), + tile_coord2id_lookup_[0].size()); + return FabricTileId::INVALID(); + } + if (coord.y() >= tile_coord2id_lookup_[coord.x()].size()) { + VTR_LOG_ERROR( + "Tile coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + coord.x(), coord.y(), tile_coord2id_lookup_.size(), + tile_coord2id_lookup_[0].size()); + return FabricTileId::INVALID(); + } + return tile_coord2id_lookup_[coord.x()][coord.y()]; +} + +FabricTileId FabricTile::create_tile(const vtr::Point& coord) { FabricTileId tile_id = FabricTileId(ids_.size()); ids_.push_back(tile_id); - coords_.emplace_back(); + coords_.push_back(coord); pb_coords_.emplace_back(); cbx_coords_.emplace_back(); cby_coords_.emplace_back(); sb_coords_.emplace_back(); - return tile_id; + /* Register in fast look-up */ + if (register_tile_in_lookup(tile_id, coord)) { + return tile_id; + } + return FabricTileId::INVALID(); } -void set_tile_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord) { +void FabricTile::init(const vtr::Point& max_coord) { + tile_coord2id_lookup_.resize(max_coord.x()); + for (size_t ix = 0; ix < max_coord.x(); ++ix) { + tile_coord2id_lookup_[ix].resize(max_coord.y(), FabricTileId::INVALID()); + } + unique_tile_ids_.resize(max_coord.x()); + for (size_t ix = 0; ix < max_coord.x(); ++ix) { + unique_tile_ids_[ix].resize(max_coord.y(), FabricTileId::INVALID()); + } +} + +bool FabricTile::register_tile_in_lookup(const FabricTileId& tile_id, + const vtr::Point& coord) { + if (coord.x() >= tile_coord2id_lookup_.size()) { + VTR_LOG_ERROR( + "Fast look-up has not been re-allocated properly! Given x='%lu' exceeds " + "the upper-bound '%lu'!\n", + coord.x(), tile_coord2id_lookup_.size()); + return false; + } + if (coord.y() >= tile_coord2id_lookup_[coord.x()].size()) { + VTR_LOG_ERROR( + "Fast look-up has not been re-allocated properly! Given y='%lu' exceeds " + "the upper-bound '%lu'!\n", + coord.y(), tile_coord2id_lookup_[coord.x()].size()); + return false; + } + /* Throw error if this coord is already registered! */ + if (tile_coord2id_lookup_[coord.x()][coord.y()]) { + VTR_LOG_ERROR("Tile at [%lu][%lu] has already been registered!\n"); + return false; + } + tile_coord2id_lookup_[coord.x()][coord.y()] = tile_id; + + return true; +} + +void FabricTile::invalidate_tile_in_lookup(const vtr::Point& coord) { + tile_coord2id_lookup_[coord.x()][coord.y()] = FabricTileId::INVALID(); +} + +bool FabricTile::set_tile_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord) { VTR_ASSERT(valid_tile_id(tile_id)); + /* Invalidate previous coordinate in look-up */ + invalidate_tile_in_lookup(coords_[tile_id]); + /* update coordinate */ coords_[tile_id] = coord; + /* Register in fast look-up */ + return register_tile_in_lookup(tile_id, coord); } -void add_pb_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord) { +void FabricTile::add_pb_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord) { VTR_ASSERT(valid_tile_id(tile_id)); pb_coords_[tile_id] = coord; } -void add_cbx_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord) { +void FabricTile::add_cbx_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord) { VTR_ASSERT(valid_tile_id(tile_id)); - /* Ensure the coordinates are not duplicated */ - auto result = std::find(cbx_coords_.begin(), cbx_coords_.end(), coord); - if (result == cbx_coords_.end()) { - cbx_coords_[tile_id].push_back(coord); - } + cbx_coords_[tile_id].push_back(coord); } -void add_cby_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord) { +void FabricTile::add_cby_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord) { VTR_ASSERT(valid_tile_id(tile_id)); - /* Ensure the coordinates are not duplicated */ - auto result = std::find(cby_coords_.begin(), cby_coords_.end(), coord); - if (result == cby_coords_.end()) { - cby_coords_[tile_id].push_back(coord); - } + cby_coords_[tile_id].push_back(coord); } -void add_sb_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord) { +void FabricTile::add_sb_coordinate(const FabricTileId& tile_id, + const vtr::Point& coord) { VTR_ASSERT(valid_tile_id(tile_id)); - /* Ensure the coordinates are not duplicated */ - auto result = std::find(sb_coords_.begin(), sb_coords_.end(), coord); - if (result == sb_coords_.end()) { - sb_coords_[tile_id].push_back(coord); - } + sb_coords_[tile_id].push_back(coord); } -bool FabricTileId::valid_tile_id(const FabricTileId& tile_id) const { +void FabricTile::clear() { + ids_.clear(); + coords_.clear(); + pb_coords_.clear(); + cbx_coords_.clear(); + cby_coords_.clear(); + sb_coords_.clear(); + tile_coord2id_lookup_.clear(); + unique_tile_ids_.clear(); +} + +bool FabricTile::valid_tile_id(const FabricTileId& tile_id) const { return (size_t(tile_id) < ids_.size()) && (tile_id == ids_[tile_id]); } diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h index 43a6488a0..88d5a5eb7 100644 --- a/openfpga/src/annotation/fabric_tile.h +++ b/openfpga/src/annotation/fabric_tile.h @@ -23,17 +23,19 @@ namespace openfpga { *******************************************************************/ class FabricTile { public: /* Contructors */ - FabricTile(const DeviceRRGSB& device_rr_gsb); + FabricTile(const vtr::Point& max_coord); public: /* Accessors */ vtr::Point tile_coordinate(const FabricTileId& tile_id) const; /** @brief With a given coordinate, find the id of the unique tile (which is * the same as the tile in structure) */ FabricTileId unique_tile(const vtr::Point& coord) const; + /** @brief Find the tile info with a given coordinate */ + FabricTileId find_tile(const vtr::Point& coord) const; public: /* Mutators */ - FabricTileId create_tile(); - void set_tile_coordinate(const FabricTileId& tile_id, + FabricTileId create_tile(const vtr::Point& coord); + bool set_tile_coordinate(const FabricTileId& tile_id, const vtr::Point& coord); void add_pb_coordinate(const FabricTileId& tile_id, const vtr::Point& coord); @@ -48,11 +50,17 @@ class FabricTile { void build_unique_tiles(); /** @brief Clear all the content */ void clear(); + /** @brief Initialize the data with a given range. Used by constructors */ + void init(const vtr::Point& max_coord); - private: /* Validators */ + public: /* Validators */ bool valid_tile_id(const FabricTileId& tile_id) const; private: /* Internal builders */ + void invalidate_tile_in_lookup(const vtr::Point& coord); + bool register_tile_in_lookup(const FabricTileId& tile_id, + const vtr::Point& coord); + private: /* Internal Data */ vtr::vector ids_; vtr::vector> coords_; @@ -61,11 +69,11 @@ class FabricTile { vtr::vector>> cbx_coords_; vtr::vector>> cby_coords_; vtr::vector>> sb_coords_; + /* A fast lookup to spot tile by coordinate */ + std::vector> tile_coord2id_lookup_; std::vector> unique_tile_ids_; /* Use [x][y] to get the id of the unique tile with a given coordinate */ - /* Cached data */ - const DeviceRRGSB& device_rr_gsb_; }; } /* End namespace openfpga*/ diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index 5edbdec9e..c21b540ed 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -12,6 +12,7 @@ #include "build_decoder_modules.h" #include "build_device_module.h" #include "build_essential_modules.h" +#include "build_fabric_tile.h" #include "build_grid_modules.h" #include "build_lut_modules.h" #include "build_memory_modules.h" @@ -101,10 +102,12 @@ int build_device_module_graph( } /* Build tile modules if defined */ - FabricTile fabric_tile(openfpga_ctx.device_rr_gsb()); - if (!tile_config.empty()) { + FabricTile fabric_tile(vtr::Point(vpr_device_ctx.grid.width(), + vpr_device_ctx.grid.height())); + if (!tile_config.is_valid()) { /* TODO: Build detailed tile-level information */ - status = build_fabric_tile(fabric_tile, tile_config, vpr_device_ctx.grid, openfpga_ctx.device_rr_gsb()); + status = build_fabric_tile(fabric_tile, tile_config, vpr_device_ctx.grid, + openfpga_ctx.device_rr_gsb()); if (CMD_EXEC_FATAL_ERROR == status) { return status; } diff --git a/openfpga/src/fabric/build_fabric_tile.cpp b/openfpga/src/fabric/build_fabric_tile.cpp index 83f94c523..81f4359e5 100644 --- a/openfpga/src/fabric/build_fabric_tile.cpp +++ b/openfpga/src/fabric/build_fabric_tile.cpp @@ -27,7 +27,9 @@ namespace openfpga { * - The connection blocks and switch block are placed on the right and bottom *sides *******************************************************************/ -static int build_fabric_tile_top_left(FabricTile& fabric_tile, const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb) { +static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, + const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb) { int status_code = CMD_EXEC_SUCCESS; /* Walk through all the device rr_gsb and create tile one by one */ @@ -36,14 +38,17 @@ static int build_fabric_tile_top_left(FabricTile& fabric_tile, const DeviceGrid& t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(ix, iy); bool skip_add_pb = false; vtr::Point curr_tile_coord(ix, iy); + vtr::Point curr_gsb_coord(ix, iy - 1); FabricTileId curr_tile_id = FabricTileId::INVALID(); /* For EMPTY grid, routing blocks may still be required if there is a gsb */ if (true == is_empty_type(phy_tile_type)) { skip_add_pb = true; + if (!device_rr_gsb.is_gsb_exist(curr_gsb_coord)) { + continue; + } /* Need to create a new tile here */ - curr_tile_id = fabric_tile.create_tile(); - fabric_tile.set_coordinate(curr_tile_coord); + curr_tile_id = fabric_tile.create_tile(curr_tile_coord); } /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ if ((0 < grids.get_width_offset(ix, iy)) || @@ -58,8 +63,7 @@ static int build_fabric_tile_top_left(FabricTile& fabric_tile, const DeviceGrid& curr_tile_id = fabric_tile.find_tile(root_tile_coord); } else { /* Need to create a new tile here */ - curr_tile_id = fabric_tile.create_tile(); - fabric_tile.set_coordinate(curr_tile_coord); + curr_tile_id = fabric_tile.create_tile(curr_tile_coord); } /* Ensure that we have a valid id */ @@ -85,7 +89,6 @@ static int build_fabric_tile_top_left(FabricTile& fabric_tile, const DeviceGrid& * +----------+ +----------+ * */ - vtr::Point curr_gsb_coord(ix, iy - 1); if (!device_rr_gsb.is_gsb_exist(curr_gsb_coord)) { continue; } @@ -111,7 +114,9 @@ static int build_fabric_tile_top_left(FabricTile& fabric_tile, const DeviceGrid& /******************************************************************** * Build tile-level information for a given FPGA fabric, w.r.t. to configuration *******************************************************************/ -int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb) { +int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, + const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb) { vtr::ScopedStartFinishTimer timer( "Build tile-level information for the FPGA fabric"); @@ -119,7 +124,8 @@ int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, co /* Depending on the selected style, follow different approaches */ if (tile_config.style() == TileConfig::e_style::TOP_LEFT) { - status_code = build_fabric_tile_style_top_left(fabric_tile, grids, device_rr_gsb); + status_code = + build_fabric_tile_style_top_left(fabric_tile, grids, device_rr_gsb); } else { /* Error out for styles that are not supported yet! */ VTR_LOG_ERROR("Tile style '%s' is not supported yet!\n", diff --git a/openfpga/src/fabric/build_fabric_tile.h b/openfpga/src/fabric/build_fabric_tile.h index b70f66f07..1c5ab66a1 100644 --- a/openfpga/src/fabric/build_fabric_tile.h +++ b/openfpga/src/fabric/build_fabric_tile.h @@ -7,10 +7,10 @@ #include -#include "fabric_tile.h" -#include "tile_config.h" #include "device_grid.h" #include "device_rr_gsb.h" +#include "fabric_tile.h" +#include "tile_config.h" /******************************************************************** * Function declaration @@ -19,7 +19,9 @@ /* begin namespace openfpga */ namespace openfpga { -int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb); +int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, + const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb); } /* end namespace openfpga */ From 98c598cec2c1d17a21e1fc908a682bbb61f0ec63 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 15 Jul 2023 22:54:33 -0700 Subject: [PATCH 171/391] [core] unique tile identifier done --- openfpga/src/annotation/device_rr_gsb.cpp | 57 ++++++++++------- openfpga/src/annotation/device_rr_gsb.h | 7 +++ openfpga/src/annotation/fabric_tile.cpp | 76 +++++++++++++++++++++-- openfpga/src/annotation/fabric_tile.h | 16 ++++- openfpga/src/fabric/build_fabric_tile.cpp | 12 +++- 5 files changed, 136 insertions(+), 32 deletions(-) diff --git a/openfpga/src/annotation/device_rr_gsb.cpp b/openfpga/src/annotation/device_rr_gsb.cpp index 9811bdcab..7374ab793 100644 --- a/openfpga/src/annotation/device_rr_gsb.cpp +++ b/openfpga/src/annotation/device_rr_gsb.cpp @@ -145,34 +145,14 @@ const RRGSB& DeviceRRGSB::get_cb_unique_module(const t_rr_type& cb_type, /* Give a coordinate of a rr switch block, and return its unique mirror */ const RRGSB& DeviceRRGSB::get_cb_unique_module( const t_rr_type& cb_type, const vtr::Point& coordinate) const { - VTR_ASSERT(validate_cb_type(cb_type)); - VTR_ASSERT(validate_coordinate(coordinate)); - size_t cb_unique_module_id; - - switch (cb_type) { - case CHANX: - cb_unique_module_id = - cbx_unique_module_id_[coordinate.x()][coordinate.y()]; - break; - case CHANY: - cb_unique_module_id = - cby_unique_module_id_[coordinate.x()][coordinate.y()]; - break; - default: - VTR_LOG_ERROR("Invalid type of connection block!\n"); - exit(1); - } - - return get_cb_unique_module(cb_type, cb_unique_module_id); + return get_cb_unique_module(cb_type, + get_cb_unique_module_index(cb_type, coordinate)); } /* Give a coordinate of a rr switch block, and return its unique mirror */ const RRGSB& DeviceRRGSB::get_sb_unique_module( const vtr::Point& coordinate) const { - VTR_ASSERT(validate_coordinate(coordinate)); - size_t sb_unique_module_id = - sb_unique_module_id_[coordinate.x()][coordinate.y()]; - return get_sb_unique_module(sb_unique_module_id); + return get_sb_unique_module(get_sb_unique_module_index(coordinate)); } /************************************************************************ @@ -550,4 +530,35 @@ bool DeviceRRGSB::validate_cb_type(const t_rr_type& cb_type) const { return ((CHANX == cb_type) || (CHANY == cb_type)); } +size_t DeviceRRGSB::get_sb_unique_module_index( + const vtr::Point& coordinate) const { + VTR_ASSERT(validate_coordinate(coordinate)); + size_t sb_unique_module_id = + sb_unique_module_id_[coordinate.x()][coordinate.y()]; + return sb_unique_module_id; +} + +size_t DeviceRRGSB::get_cb_unique_module_index( + const t_rr_type& cb_type, const vtr::Point& coordinate) const { + VTR_ASSERT(validate_cb_type(cb_type)); + VTR_ASSERT(validate_coordinate(coordinate)); + size_t cb_unique_module_id; + + switch (cb_type) { + case CHANX: + cb_unique_module_id = + cbx_unique_module_id_[coordinate.x()][coordinate.y()]; + break; + case CHANY: + cb_unique_module_id = + cby_unique_module_id_[coordinate.x()][coordinate.y()]; + break; + default: + VTR_LOG_ERROR("Invalid type of connection block!\n"); + exit(1); + } + + return cb_unique_module_id; +} + } /* End namespace openfpga*/ diff --git a/openfpga/src/annotation/device_rr_gsb.h b/openfpga/src/annotation/device_rr_gsb.h index 5873bb192..bd190cec5 100644 --- a/openfpga/src/annotation/device_rr_gsb.h +++ b/openfpga/src/annotation/device_rr_gsb.h @@ -59,6 +59,13 @@ class DeviceRRGSB { size_t get_num_cb_unique_module(const t_rr_type& cb_type) const; /* get the number of unique mirrors of CBs */ bool is_gsb_exist(const vtr::Point coord) const; + /* Get the index of the unique Switch block module with a given GSB + * coordinate. Note: Do NOT use sb coordinate!!! */ + size_t get_sb_unique_module_index(const vtr::Point& coordinate) const; + /* Get the index of the unique Connection block module with a given GSB + * coordinate. Note: Do NOT use sb coordinate!!! */ + size_t get_cb_unique_module_index(const t_rr_type& cb_type, + const vtr::Point& coordinate) const; public: /* Mutators */ void reserve( diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index f6a036c7e..e2d7f1251 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -19,9 +19,9 @@ vtr::Point FabricTile::tile_coordinate( FabricTileId FabricTile::unique_tile(const vtr::Point& coord) const { /* Return invalid Id when out of range! */ - if (coord.x() < unique_tile_ids_.size()) { - if (coord.y() < unique_tile_ids_[coord.x()].size()) { - return unique_tile_ids_[coord.x()][coord.y()]; + if (coord.x() < tile_coord2unique_tile_ids_.size()) { + if (coord.y() < tile_coord2unique_tile_ids_[coord.x()].size()) { + return tile_coord2unique_tile_ids_[coord.x()][coord.y()]; } } return FabricTileId::INVALID(); @@ -66,9 +66,10 @@ void FabricTile::init(const vtr::Point& max_coord) { for (size_t ix = 0; ix < max_coord.x(); ++ix) { tile_coord2id_lookup_[ix].resize(max_coord.y(), FabricTileId::INVALID()); } - unique_tile_ids_.resize(max_coord.x()); + tile_coord2unique_tile_ids_.resize(max_coord.x()); for (size_t ix = 0; ix < max_coord.x(); ++ix) { - unique_tile_ids_[ix].resize(max_coord.y(), FabricTileId::INVALID()); + tile_coord2unique_tile_ids_[ix].resize(max_coord.y(), + FabricTileId::INVALID()); } } @@ -145,6 +146,7 @@ void FabricTile::clear() { cby_coords_.clear(); sb_coords_.clear(); tile_coord2id_lookup_.clear(); + tile_coord2unique_tile_ids_.clear(); unique_tile_ids_.clear(); } @@ -152,4 +154,68 @@ bool FabricTile::valid_tile_id(const FabricTileId& tile_id) const { return (size_t(tile_id) < ids_.size()) && (tile_id == ids_[tile_id]); } +bool FabricTile::equivalent_tile(const FabricTileId& tile_a, + const FabricTileId& tile_b, + const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb) const { + /* The pb of two tiles should be the same, otherwise not equivalent */ + if (grids.get_physical_type(pb_coords_[tile_a].x(), pb_coords_[tile_a].y()) != + grids.get_physical_type(pb_coords_[tile_b].x(), pb_coords_[tile_b].y())) { + return false; + } + /* The number of cbx, cby and sb blocks should be the same */ + if (cbx_coords_[tile_a].size() != cbx_coords_[tile_b].size() || + cby_coords_[tile_a].size() != cby_coords_[tile_b].size() || + sb_coords_[tile_a].size() != sb_coords_[tile_b].size()) { + return false; + } + /* Each CBx should have the same unique modules in the device rr_gsb */ + for (size_t iblk = 0; iblk < cbx_coords_[tile_a].size(); ++iblk) { + if (device_rr_gsb.get_cb_unique_module_index(CHANX, + cbx_coords_[tile_a][iblk]) != + device_rr_gsb.get_cb_unique_module_index(CHANX, + cbx_coords_[tile_b][iblk])) { + return false; + } + } + for (size_t iblk = 0; iblk < cby_coords_[tile_a].size(); ++iblk) { + if (device_rr_gsb.get_cb_unique_module_index(CHANY, + cby_coords_[tile_a][iblk]) != + device_rr_gsb.get_cb_unique_module_index(CHANY, + cby_coords_[tile_b][iblk])) { + return false; + } + } + for (size_t iblk = 0; iblk < sb_coords_[tile_a].size(); ++iblk) { + if (device_rr_gsb.get_sb_unique_module_index(sb_coords_[tile_a][iblk]) != + device_rr_gsb.get_sb_unique_module_index(sb_coords_[tile_b][iblk])) { + return false; + } + } + return true; +} + +int FabricTile::build_unique_tiles(const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb) { + for (size_t ix = 0; ix < grids.width(); ++ix) { + for (size_t iy = 0; iy < grids.height(); ++iy) { + bool is_unique_tile = true; + for (FabricTileId unique_tile_id : unique_tile_ids_) { + if (equivalent_tile(tile_coord2id_lookup_[ix][iy], unique_tile_id, + grids, device_rr_gsb)) { + is_unique_tile = false; + tile_coord2unique_tile_ids_[ix][iy] = unique_tile_id; + break; + } + } + /* Update list if this is a unique tile */ + if (is_unique_tile) { + unique_tile_ids_.push_back(tile_coord2unique_tile_ids_[ix][iy]); + tile_coord2unique_tile_ids_[ix][iy] = tile_coord2id_lookup_[ix][iy]; + } + } + } + return 0; +} + } /* End namespace openfpga*/ diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h index 88d5a5eb7..e2eda4054 100644 --- a/openfpga/src/annotation/fabric_tile.h +++ b/openfpga/src/annotation/fabric_tile.h @@ -6,6 +6,7 @@ *******************************************************************/ #include +#include "device_grid.h" #include "device_rr_gsb.h" #include "fabric_tile_fwd.h" #include "vtr_geometry.h" @@ -52,10 +53,20 @@ class FabricTile { void clear(); /** @brief Initialize the data with a given range. Used by constructors */ void init(const vtr::Point& max_coord); + /** @brief Identify the number of unique tiles and keep in the lookup */ + int build_unique_tiles(const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb); public: /* Validators */ bool valid_tile_id(const FabricTileId& tile_id) const; + private: /* Internal validators */ + /** @brief Identify if two tile are equivalent in their sub-modules, including + * pb, cbx, cby and sb */ + bool equivalent_tile(const FabricTileId& tile_a, const FabricTileId& tile_b, + const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb) const; + private: /* Internal builders */ void invalidate_tile_in_lookup(const vtr::Point& coord); bool register_tile_in_lookup(const FabricTileId& tile_id, @@ -72,8 +83,9 @@ class FabricTile { /* A fast lookup to spot tile by coordinate */ std::vector> tile_coord2id_lookup_; std::vector> - unique_tile_ids_; /* Use [x][y] to get the id of the unique tile with a - given coordinate */ + tile_coord2unique_tile_ids_; /* Use [x][y] to get the id of the unique tile + with a given coordinate */ + std::vector unique_tile_ids_; }; } /* End namespace openfpga*/ diff --git a/openfpga/src/fabric/build_fabric_tile.cpp b/openfpga/src/fabric/build_fabric_tile.cpp index 81f4359e5..2b52e532e 100644 --- a/openfpga/src/fabric/build_fabric_tile.cpp +++ b/openfpga/src/fabric/build_fabric_tile.cpp @@ -95,11 +95,11 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, const RRGSB& curr_rr_gsb = device_rr_gsb.get_gsb(curr_gsb_coord); if (curr_rr_gsb.is_cb_exist(CHANX)) { fabric_tile.add_cbx_coordinate(curr_tile_id, - curr_rr_gsb.get_cb_coordinate(CHANX)); + curr_rr_gsb.get_sb_coordinate()); } if (curr_rr_gsb.is_cb_exist(CHANY)) { fabric_tile.add_cby_coordinate(curr_tile_id, - curr_rr_gsb.get_cb_coordinate(CHANY)); + curr_rr_gsb.get_sb_coordinate()); } if (curr_rr_gsb.is_sb_exist()) { fabric_tile.add_sb_coordinate(curr_tile_id, @@ -133,6 +133,14 @@ int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, status_code = CMD_EXEC_FATAL_ERROR; } + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Build unique tiles to compress the number of tile modules to be built in + * later steps */ + status_code = fabric_tile.build_unique_tiles(grids, device_rr_gsb); + return status_code; } From b2f5b493c252e1202c6337b8497bd92c9624b3cf Mon Sep 17 00:00:00 2001 From: Chung Shien Chai Date: Sun, 16 Jul 2023 13:08:40 -0700 Subject: [PATCH 172/391] Fix the cpp-format --- libs/libopenfpgautil/src/openfpga_decode.cpp | 33 +++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/libs/libopenfpgautil/src/openfpga_decode.cpp b/libs/libopenfpgautil/src/openfpga_decode.cpp index 6e69b230b..30aa2d128 100644 --- a/libs/libopenfpgautil/src/openfpga_decode.cpp +++ b/libs/libopenfpgautil/src/openfpga_decode.cpp @@ -5,6 +5,7 @@ /* Headers from vtrutil library */ #include "openfpga_decode.h" + #include "vtr_assert.h" /* begin namespace openfpga */ @@ -108,14 +109,14 @@ std::string combine_two_1hot_str(const std::string& code1, * Output: * index | 0 | 1 | 2 * ret | 0 | 0 | 1 -* -* ToDo: Need to revisit and change all the feature that call to this function -* Had studied the code, should be safe to make the change -* Apparently we only want to store 0 or 1 (binary vector) -* Yet we store it in a vector on size_t (8 Bytes) -* We are using 8x memory that we supposed to -* uint8_t is good enough -* Not a bug, but is a serious unoptimized issue + * + * ToDo: Need to revisit and change all the feature that call to this function + * Had studied the code, should be safe to make the change + * Apparently we only want to store 0 or 1 (binary vector) + * Yet we store it in a vector on size_t (8 Bytes) + * We are using 8x memory that we supposed to + * uint8_t is good enough + * Not a bug, but is a serious unoptimized issue ********************************************************************/ std::vector itobin_vec(const size_t& in_int, const size_t& bin_len) { /* bin_len must be in valid range*/ @@ -123,9 +124,10 @@ std::vector itobin_vec(const size_t& in_int, const size_t& bin_len) { std::vector ret(bin_len, 0); /* Make sure we do not have any overflow! */ - /* If the length is 64 bits, in_int can be any number since this is the max bit length, and BTW pow(2, 64) - itself should be zero (from 64bits size_t perspective). Once we fix the bintoi_charvec() bug, without - this change, it will cause assertion */ + /* If the length is 64 bits, in_int can be any number since this is the max + bit length, and BTW pow(2, 64) itself should be zero (from 64bits size_t + perspective). Once we fix the bintoi_charvec() bug, without this change, it + will cause assertion */ if (bin_len < 64) { VTR_ASSERT(in_int < (size_t(1) << bin_len)); } @@ -159,9 +161,10 @@ std::vector itobin_charvec(const size_t& in_int, const size_t& bin_len) { std::vector ret(bin_len, '0'); /* Make sure we do not have any overflow! */ - /* If the length is 64 bits, in_int can be any number since this is the max bit length, and BTW pow(2, 64) - itself should be zero (from 64bits size_t perspective). Once we fix the bintoi_charvec() bug, without - this change, it will cause assertion */ + /* If the length is 64 bits, in_int can be any number since this is the max + bit length, and BTW pow(2, 64) itself should be zero (from 64bits size_t + perspective). Once we fix the bintoi_charvec() bug, without this change, it + will cause assertion */ if (bin_len < 64) { VTR_ASSERT(in_int < (size_t(1) << bin_len)); } @@ -193,7 +196,7 @@ std::vector itobin_charvec(const size_t& in_int, const size_t& bin_len) { size_t bintoi_charvec(const std::vector& bin) { /* bin.size() must be in valid range*/ VTR_ASSERT(bin.size() > 0 && bin.size() <= 64); - + size_t ret = 0; for (size_t i = 0; i < bin.size(); ++i) { From ba4b7e35229d9342812e52f8cf599340bbecf5a6 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Jul 2023 15:18:09 -0700 Subject: [PATCH 173/391] [core] developing tile module builder --- openfpga/src/annotation/fabric_tile.cpp | 73 ++++++-- openfpga/src/annotation/fabric_tile.h | 16 +- openfpga/src/base/openfpga_naming.cpp | 7 + openfpga/src/base/openfpga_naming.h | 2 + openfpga/src/fabric/build_device_module.cpp | 8 +- .../fabric/build_fabric_io_location_map.cpp | 1 + openfpga/src/fabric/build_fabric_tile.cpp | 12 +- openfpga/src/fabric/build_tile_modules.cpp | 166 ++++++++++++++++++ openfpga/src/fabric/build_tile_modules.h | 34 ++++ .../src/utils/openfpga_device_grid_utils.cpp | 22 +++ .../src/utils/openfpga_device_grid_utils.h | 3 + 11 files changed, 314 insertions(+), 30 deletions(-) create mode 100644 openfpga/src/fabric/build_tile_modules.cpp create mode 100644 openfpga/src/fabric/build_tile_modules.h diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index e2d7f1251..f3ae34843 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -17,6 +17,33 @@ vtr::Point FabricTile::tile_coordinate( return coords_[tile_id]; } +std::vector> FabricTile::pb_coordinates( + const FabricTileId& tile_id) const { + VTR_ASSERT(valid_tile_id(tile_id)); + return pb_coords_[tile_id]; +} + +std::vector> FabricTile::cb_coordinates( + const FabricTileId& tile_id, const t_rr_type& cb_type) const { + VTR_ASSERT(valid_tile_id(tile_id)); + switch (cb_type) { + case CHANX: + return cbx_coords_[tile_id]; + case CHANY: + return cby_coords_[tile_id]; + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } + return std::vector>(); +} + +std::vector> FabricTile::sb_coordinates( + const FabricTileId& tile_id) const { + VTR_ASSERT(valid_tile_id(tile_id)); + return sb_coords_[tile_id]; +} + FabricTileId FabricTile::unique_tile(const vtr::Point& coord) const { /* Return invalid Id when out of range! */ if (coord.x() < tile_coord2unique_tile_ids_.size()) { @@ -45,6 +72,10 @@ FabricTileId FabricTile::find_tile(const vtr::Point& coord) const { return tile_coord2id_lookup_[coord.x()][coord.y()]; } +std::vector FabricTile::unique_tiles() const { + return unique_tile_ids_; +} + FabricTileId FabricTile::create_tile(const vtr::Point& coord) { FabricTileId tile_id = FabricTileId(ids_.size()); ids_.push_back(tile_id); @@ -117,19 +148,24 @@ bool FabricTile::set_tile_coordinate(const FabricTileId& tile_id, void FabricTile::add_pb_coordinate(const FabricTileId& tile_id, const vtr::Point& coord) { VTR_ASSERT(valid_tile_id(tile_id)); - pb_coords_[tile_id] = coord; + pb_coords_[tile_id].push_back(coord); } -void FabricTile::add_cbx_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord) { +void FabricTile::add_cb_coordinate(const FabricTileId& tile_id, + const t_rr_type& cb_type, + const vtr::Point& coord) { VTR_ASSERT(valid_tile_id(tile_id)); - cbx_coords_[tile_id].push_back(coord); -} - -void FabricTile::add_cby_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord) { - VTR_ASSERT(valid_tile_id(tile_id)); - cby_coords_[tile_id].push_back(coord); + switch (cb_type) { + case CHANX: + cbx_coords_[tile_id].push_back(coord); + break; + case CHANY: + cby_coords_[tile_id].push_back(coord); + break; + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } } void FabricTile::add_sb_coordinate(const FabricTileId& tile_id, @@ -158,17 +194,22 @@ bool FabricTile::equivalent_tile(const FabricTileId& tile_a, const FabricTileId& tile_b, const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb) const { - /* The pb of two tiles should be the same, otherwise not equivalent */ - if (grids.get_physical_type(pb_coords_[tile_a].x(), pb_coords_[tile_a].y()) != - grids.get_physical_type(pb_coords_[tile_b].x(), pb_coords_[tile_b].y())) { - return false; - } /* The number of cbx, cby and sb blocks should be the same */ - if (cbx_coords_[tile_a].size() != cbx_coords_[tile_b].size() || + if (pb_coords_[tile_a].size() != pb_coords_[tile_b].size() || + cbx_coords_[tile_a].size() != cbx_coords_[tile_b].size() || cby_coords_[tile_a].size() != cby_coords_[tile_b].size() || sb_coords_[tile_a].size() != sb_coords_[tile_b].size()) { return false; } + /* The pb of two tiles should be the same, otherwise not equivalent */ + for (size_t iblk = 0; iblk < pb_coords_[tile_a].size(); ++iblk) { + if (grids.get_physical_type(pb_coords_[tile_a][iblk].x(), + pb_coords_[tile_a][iblk].y()) != + grids.get_physical_type(pb_coords_[tile_b][iblk].x(), + pb_coords_[tile_b][iblk].y())) { + return false; + } + } /* Each CBx should have the same unique modules in the device rr_gsb */ for (size_t iblk = 0; iblk < cbx_coords_[tile_a].size(); ++iblk) { if (device_rr_gsb.get_cb_unique_module_index(CHANX, diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h index e2eda4054..22c5112c3 100644 --- a/openfpga/src/annotation/fabric_tile.h +++ b/openfpga/src/annotation/fabric_tile.h @@ -28,11 +28,19 @@ class FabricTile { public: /* Accessors */ vtr::Point tile_coordinate(const FabricTileId& tile_id) const; + std::vector> pb_coordinates( + const FabricTileId& tile_id) const; + std::vector> cb_coordinates( + const FabricTileId& tile_id, const t_rr_type& cb_type) const; + std::vector> sb_coordinates( + const FabricTileId& tile_id) const; /** @brief With a given coordinate, find the id of the unique tile (which is * the same as the tile in structure) */ FabricTileId unique_tile(const vtr::Point& coord) const; /** @brief Find the tile info with a given coordinate */ FabricTileId find_tile(const vtr::Point& coord) const; + /** @brief Return a list of unique tiles */ + std::vector unique_tiles() const; public: /* Mutators */ FabricTileId create_tile(const vtr::Point& coord); @@ -40,10 +48,8 @@ class FabricTile { const vtr::Point& coord); void add_pb_coordinate(const FabricTileId& tile_id, const vtr::Point& coord); - void add_cbx_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord); - void add_cby_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord); + void add_cb_coordinate(const FabricTileId& tile_id, const t_rr_type& cb_type, + const vtr::Point& coord); void add_sb_coordinate(const FabricTileId& tile_id, const vtr::Point& coord); /** @brief Build a list of unique tiles by comparing the coordinates in @@ -76,7 +82,7 @@ class FabricTile { vtr::vector ids_; vtr::vector> coords_; /* Coordinates w.r.t. RRGSB */ - vtr::vector> pb_coords_; + vtr::vector>> pb_coords_; vtr::vector>> cbx_coords_; vtr::vector>> cby_coords_; vtr::vector>> sb_coords_; diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index a53a87ade..037db2009 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -497,6 +497,13 @@ std::string generate_switch_block_module_name( std::string("_")); } +/********************************************************************* + * Generate the module name for a switch block with a given index + *********************************************************************/ +std::string generate_tile_module_name(const size_t& index) { + return std::string("tile_" + std::to_string(index)); +} + /********************************************************************* * 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 e14b54549..7651f140b 100644 --- a/openfpga/src/base/openfpga_naming.h +++ b/openfpga/src/base/openfpga_naming.h @@ -110,6 +110,8 @@ std::string generate_switch_block_module_name( std::string generate_connection_block_module_name( const t_rr_type& cb_type, const vtr::Point& coordinate); +std::string generate_tile_module_name(const size_t& index); + 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_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index c21b540ed..bb5864ae1 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -18,6 +18,7 @@ #include "build_memory_modules.h" #include "build_mux_modules.h" #include "build_routing_modules.h" +#include "build_tile_modules.h" #include "build_top_module.h" #include "build_wire_modules.h" #include "command_exit_codes.h" @@ -105,14 +106,17 @@ int build_device_module_graph( FabricTile fabric_tile(vtr::Point(vpr_device_ctx.grid.width(), vpr_device_ctx.grid.height())); if (!tile_config.is_valid()) { - /* TODO: Build detailed tile-level information */ + /* Build detailed tile-level information */ status = build_fabric_tile(fabric_tile, tile_config, vpr_device_ctx.grid, openfpga_ctx.device_rr_gsb()); if (CMD_EXEC_FATAL_ERROR == status) { return status; } /* TODO: Build the modules */ - // build_tile_modules(module_manager, fabric_tile); + build_tile_modules(module_manager, fabric_tile, vpr_device_ctx.grid, + openfpga_ctx.device_rr_gsb(), + openfpga_ctx.arch().circuit_lib, sram_model, + openfpga_ctx.arch().config_protocol.type(), verbose); } /* Build FPGA fabric top-level module */ diff --git a/openfpga/src/fabric/build_fabric_io_location_map.cpp b/openfpga/src/fabric/build_fabric_io_location_map.cpp index f099e546c..65e1a5824 100644 --- a/openfpga/src/fabric/build_fabric_io_location_map.cpp +++ b/openfpga/src/fabric/build_fabric_io_location_map.cpp @@ -114,6 +114,7 @@ IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, if (curr_io_index == io_counter.end()) { io_counter[gpio_port.get_name()] = 0; } + /* FIXME: Will cause critical bugs when tile modules are added */ io_location_map.set_io_index(coord.x(), coord.y(), subchild_coord.x(), gpio_port.get_name(), io_counter[gpio_port.get_name()]); diff --git a/openfpga/src/fabric/build_fabric_tile.cpp b/openfpga/src/fabric/build_fabric_tile.cpp index 2b52e532e..2f4c53c9f 100644 --- a/openfpga/src/fabric/build_fabric_tile.cpp +++ b/openfpga/src/fabric/build_fabric_tile.cpp @@ -93,13 +93,11 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, continue; } const RRGSB& curr_rr_gsb = device_rr_gsb.get_gsb(curr_gsb_coord); - if (curr_rr_gsb.is_cb_exist(CHANX)) { - fabric_tile.add_cbx_coordinate(curr_tile_id, - curr_rr_gsb.get_sb_coordinate()); - } - if (curr_rr_gsb.is_cb_exist(CHANY)) { - fabric_tile.add_cby_coordinate(curr_tile_id, - curr_rr_gsb.get_sb_coordinate()); + for (t_rr_type cb_type : {CHANX, CHANY}) { + if (curr_rr_gsb.is_cb_exist(cb_type)) { + fabric_tile.add_cb_coordinate(curr_tile_id, cb_type, + curr_rr_gsb.get_sb_coordinate()); + } } if (curr_rr_gsb.is_sb_exist()) { fabric_tile.add_sb_coordinate(curr_tile_id, diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp new file mode 100644 index 000000000..a3e0b6e0d --- /dev/null +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -0,0 +1,166 @@ +/******************************************************************** + * This file includes functions that are used to build the location + * map information for the top-level module of the FPGA fabric + * It helps OpenFPGA to link the I/O port index in top-level module + * to the VPR I/O mapping results + *******************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "command_exit_codes.h" +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from vpr library */ +#include "build_tile_modules.h" +#include "module_manager_utils.h" +#include "openfpga_device_grid_utils.h" +#include "openfpga_naming.h" +#include "openfpga_reserved_words.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Build all the tile modules + *******************************************************************/ +static int build_tile_module( + ModuleManager& module_manager, const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id, const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, + const CircuitModelId& sram_model, + const e_config_protocol_type& sram_orgz_type, const bool& verbose) { + int status_code = CMD_EXEC_SUCCESS; + + /* Create the module */ + std::string module_name = generate_tile_module_name(size_t(fabric_tile_id)); + VTR_LOGV(verbose, "Building tile module '%s'...\n"); + ModuleId tile_module = module_manager.add_module(module_name); + vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); + + /* Add instance of programmable block */ + for (vtr::Point grid_gsb_coord : + fabric_tile.pb_coordinates(fabric_tile_id)) { + const RRGSB& grid_rr_gsb = device_rr_gsb.get_gsb(grid_gsb_coord); + vtr::Point grid_coord = grid_rr_gsb.get_grid_coordinate(); + t_physical_tile_type_ptr phy_tile = + grids.get_physical_type(grid_coord.x(), grid_coord.y()); + /* Empty type does not require a module */ + if (!is_empty_type(phy_tile)) { + e_side grid_side = find_grid_side_by_coordinate(grids, grid_coord); + std::string pb_module_name = generate_grid_block_module_name( + std::string(GRID_MODULE_NAME_PREFIX), std::string(phy_tile->name), + is_io_type(phy_tile), grid_side); + ModuleId pb_module = module_manager.find_module(pb_module_name); + if (!pb_module) { + VTR_LOG_ERROR( + "Failed to find pb module '%s' required by tile[%lu][%lu]!\n", + pb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + } + size_t pb_instance = module_manager.num_instance(tile_module, pb_module); + module_manager.add_child_module(tile_module, pb_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); + } + VTR_LOGV(verbose, "Added programmable module '%s' to tile[%lu][%lu]\n", + pb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + } + } + + /* Add instance of connection blocks */ + for (t_rr_type cb_type : {CHANX, CHANY}) { + for (vtr::Point cb_coord : + fabric_tile.cb_coordinates(fabric_tile_id, cb_type)) { + /* get the unique module coord */ + const RRGSB& unique_rr_gsb = + device_rr_gsb.get_cb_unique_module(cb_type, cb_coord); + vtr::Point unique_cb_coord(unique_rr_gsb.get_cb_x(cb_type), + unique_rr_gsb.get_cb_y(cb_type)); + std::string cb_module_name = + generate_connection_block_module_name(cb_type, unique_cb_coord); + ModuleId cb_module = module_manager.find_module(cb_module_name); + if (!cb_module) { + VTR_LOG_ERROR( + "Failed to find connection block module '%s' required by " + "tile[%lu][%lu]!\n", + cb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + } + size_t cb_instance = module_manager.num_instance(tile_module, cb_module); + module_manager.add_child_module(tile_module, cb_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); + } + VTR_LOGV(verbose, + "Added connection block module '%s' to tile[%lu][%lu]\n", + cb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + } + } + + /* Add instance of switch blocks */ + for (vtr::Point sb_coord : + fabric_tile.sb_coordinates(fabric_tile_id)) { + /* get the unique module coord */ + const RRGSB& unique_rr_gsb = device_rr_gsb.get_sb_unique_module(sb_coord); + vtr::Point unique_sb_coord(unique_rr_gsb.get_sb_x(), + unique_rr_gsb.get_sb_y()); + std::string sb_module_name = + generate_switch_block_module_name(unique_sb_coord); + ModuleId sb_module = module_manager.find_module(sb_module_name); + if (!sb_module) { + VTR_LOG_ERROR( + "Failed to find switch block module '%s' required by tile[%lu][%lu]!\n", + sb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + } + size_t sb_instance = module_manager.num_instance(tile_module, sb_module); + module_manager.add_child_module(tile_module, sb_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); + } + VTR_LOGV(verbose, "Added switch block module '%s' to tile[%lu][%lu]\n", + sb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + } + + /* TODO: Add module nets and ports */ + + VTR_LOGV(verbose, "Done\n"); + return status_code; +} + +/******************************************************************** + * Build all the tile modules + *******************************************************************/ +int build_tile_modules(ModuleManager& module_manager, + const FabricTile& fabric_tile, const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb, + const CircuitLibrary& circuit_lib, + const CircuitModelId& sram_model, + const e_config_protocol_type& sram_orgz_type, + const bool& verbose) { + vtr::ScopedStartFinishTimer timer("Build tile modules for the FPGA fabric"); + + int status_code = CMD_EXEC_SUCCESS; + + /* Build a module for each unique tile */ + for (FabricTileId fabric_tile_id : fabric_tile.unique_tiles()) { + status_code = build_tile_module(module_manager, fabric_tile, fabric_tile_id, + grids, device_rr_gsb, circuit_lib, + sram_model, sram_orgz_type, verbose); + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } + + return status_code; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_tile_modules.h b/openfpga/src/fabric/build_tile_modules.h new file mode 100644 index 000000000..374b9bf5d --- /dev/null +++ b/openfpga/src/fabric/build_tile_modules.h @@ -0,0 +1,34 @@ +#ifndef BUILD_TILE_MODULES_H +#define BUILD_TILE_MODULES_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ + +#include + +#include "circuit_library.h" +#include "config_protocol.h" +#include "device_grid.h" +#include "device_rr_gsb.h" +#include "fabric_tile.h" +#include "module_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int build_tile_modules(ModuleManager& module_manager, + const FabricTile& fabric_tile, const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb, + const CircuitLibrary& circuit_lib, + const CircuitModelId& sram_model, + const e_config_protocol_type& sram_orgz_type, + const bool& verbose); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/utils/openfpga_device_grid_utils.cpp b/openfpga/src/utils/openfpga_device_grid_utils.cpp index 5a234b60d..0b69a5a10 100644 --- a/openfpga/src/utils/openfpga_device_grid_utils.cpp +++ b/openfpga/src/utils/openfpga_device_grid_utils.cpp @@ -13,6 +13,28 @@ /* begin namespace openfpga */ namespace openfpga { +/******************************************************************** + * Identify if a grid locates the side of an FPGA fabric + * - If on a side, return the side + * - If not, return an invalid side + *******************************************************************/ +e_side find_grid_side_by_coordinate(const DeviceGrid& grids, + const vtr::Point& coord) { + if (coord.x() == 0) { + return LEFT; + } + if (coord.y() == 0) { + return BOTTOM; + } + if (coord.x() == grids.width() - 1) { + return RIGHT; + } + if (coord.y() == grids.height() - 1) { + return TOP; + } + return NUM_SIDES; +} + /******************************************************************** * Create a list of the coordinates for the grids on the device perimeter * It follows a clockwise sequence when including the coordinates. diff --git a/openfpga/src/utils/openfpga_device_grid_utils.h b/openfpga/src/utils/openfpga_device_grid_utils.h index 4b49be150..00f0e9aab 100644 --- a/openfpga/src/utils/openfpga_device_grid_utils.h +++ b/openfpga/src/utils/openfpga_device_grid_utils.h @@ -18,6 +18,9 @@ /* begin namespace openfpga */ namespace openfpga { +e_side find_grid_side_by_coordinate(const DeviceGrid& grids, + const vtr::Point& coord); + /* A constant array to walk through FPGA border sides clockwise*/ constexpr std::array FPGA_SIDES_CLOCKWISE{TOP, RIGHT, BOTTOM, LEFT}; From f16b16c5baed5bf11decad142f7a736a06ccaf59 Mon Sep 17 00:00:00 2001 From: Ganesh Gore Date: Sun, 16 Jul 2023 21:54:30 -0600 Subject: [PATCH 174/391] Fixed binder dependecies --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0c7500540..b1901b95a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM ghcr.io/lnis-uofu/openfpga-master:8d555772 # Install node js USER root -RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - +RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - RUN curl -fsSL https://code-server.dev/install.sh | sh RUN apt-get install -y nodejs RUN apt-get install tree @@ -49,7 +49,7 @@ RUN python3 -m pip install --upgrade pip RUN python3 -m pip install --user --no-cache-dir notebook RUN python3 -m pip install --user --no-cache-dir jupyterlab RUN python3 -m pip install --user --no-cache-dir jupyterhub -RUN python3 -m pip install --user --no-cache-dir "jupyter-server<2.0.0" +RUN python3 -m pip install --user --no-cache-dir jupyter-server RUN python3 -m pip install --user --no-cache-dir jupyter-server-proxy RUN python3 -m pip install --user --no-cache-dir jupyter-vscode-proxy From aabcc25567e6b411ad8638fa4430420b81291603 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 17 Jul 2023 23:06:55 -0700 Subject: [PATCH 175/391] [core] developing tile module port and net builder --- openfpga/src/annotation/fabric_tile.cpp | 43 ++ openfpga/src/annotation/fabric_tile.h | 7 +- openfpga/src/fabric/build_device_module.cpp | 3 +- openfpga/src/fabric/build_tile_modules.cpp | 702 +++++++++++++++++- openfpga/src/fabric/build_tile_modules.h | 3 + .../src/utils/openfpga_device_grid_utils.cpp | 22 - .../src/utils/openfpga_device_grid_utils.h | 3 - 7 files changed, 750 insertions(+), 33 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index f3ae34843..b9cb7d17f 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -72,6 +72,49 @@ FabricTileId FabricTile::find_tile(const vtr::Point& coord) const { return tile_coord2id_lookup_[coord.x()][coord.y()]; } +bool FabricTile::pb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { + VTR_ASSERT(valid_tile_id(tile_id)); + for (vtr::Point curr_coord : pb_coords_[tile_id]) { + if (curr_coord == coord) { + return true; + } + } + return false; +} + +bool FabricTile::sb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { + VTR_ASSERT(valid_tile_id(tile_id)); + for (vtr::Point curr_coord : sb_coords_[tile_id]) { + if (curr_coord == coord) { + return true; + } + } + return false; +} + +bool FabricTile::cb_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord) const { + VTR_ASSERT(valid_tile_id(tile_id)); + switch (cb_type) { + case CHANX: + for (vtr::Point curr_coord : cbx_coords_[tile_id]) { + if (curr_coord == coord) { + return true; + } + } + return false; + case CHANY: + for (vtr::Point curr_coord : cby_coords_[tile_id]) { + if (curr_coord == coord) { + return true; + } + } + return false; + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } +} + std::vector FabricTile::unique_tiles() const { return unique_tile_ids_; } diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h index 22c5112c3..268f26c15 100644 --- a/openfpga/src/annotation/fabric_tile.h +++ b/openfpga/src/annotation/fabric_tile.h @@ -41,7 +41,12 @@ class FabricTile { FabricTileId find_tile(const vtr::Point& coord) const; /** @brief Return a list of unique tiles */ std::vector unique_tiles() const; - + /** @brief Check if a programmable block (with a coordinate) exists in a tile */ + bool pb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const; + /** @brief Check if a switch block (with a coordinate) exists in a tile */ + bool sb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const; + /** @brief Check if a connection block (with a coordinate) exists in a tile */ + bool cb_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord) const; public: /* Mutators */ FabricTileId create_tile(const vtr::Point& coord); bool set_tile_coordinate(const FabricTileId& tile_id, diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index bb5864ae1..9aca51353 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -114,9 +114,10 @@ int build_device_module_graph( } /* TODO: Build the modules */ build_tile_modules(module_manager, fabric_tile, vpr_device_ctx.grid, + openfpga_ctx.vpr_device_annotation(), openfpga_ctx.device_rr_gsb(), openfpga_ctx.arch().circuit_lib, sram_model, - openfpga_ctx.arch().config_protocol.type(), verbose); + openfpga_ctx.arch().config_protocol.type(), frame_view, verbose); } /* Build FPGA fabric top-level module */ diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index a3e0b6e0d..641d7d32a 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -24,14 +24,645 @@ namespace openfpga { /******************************************************************** - * Build all the tile modules + * Add module nets to connect a GSB to adjacent grid ports/pins + * as well as connection blocks + * This function will create nets for the following types of connections + * between grid output pins of Switch block and adjacent grids + * In this case, the net source is the grid pin, while the net sink + * is the switch block pin + * + * +------------+ +------------+ + * | | | | + * | Grid | | Grid | + * | [x][y+1] | | [x+1][y+1] | + * | |----+ +----| | + * +------------+ | | +------------+ + * | v v | + * | +------------+ | + * +------>| |<-----+ + * | Switch | + * | Block | + * +------>| [x][y] |<-----+ + * | +------------+ | + * | ^ ^ | + * | | | | + * +------------+ | | +------------+ + * | |----+ +-----| | + * | Grid | | Grid | + * | [x][y] | | [x+1][y] | + * | | | | + * +------------+ +------------+ + * + *******************************************************************/ +static int build_tile_module_port_and_nets_between_sb_and_pb(ModuleManager& module_manager, + const ModuleId& tile_module, + const VprDeviceAnnotation& vpr_device_annotation, + const DeviceRRGSB& device_rr_gsb, + const RRGSB& rr_gsb, + const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id, + const size_t& sb_instance, + const bool& compress_routing_hierarchy, + const bool& frame_view, + const bool& verbose) { + /* Skip those Switch blocks that do not exist */ + if (false == rr_gsb.is_sb_exist()) { + return CMD_EXEC_SUCCESS; + } + + /* We could have two different coordinators, one is the instance, the other is + * the module */ + vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), + rr_gsb.get_sb_y()); + vtr::Point module_gsb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); + + /* If we use compact routing hierarchy, we should find the unique module of + * CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + vtr::Point gsb_coord(rr_gsb.get_x(), rr_gsb.get_y()); + const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(gsb_coord); + module_gsb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_coordinate.set_y(unique_mirror.get_y()); + } + + /* This is the source cb that is added to the top module */ + const RRGSB& module_sb = device_rr_gsb.get_gsb(module_gsb_coordinate); + vtr::Point module_sb_coordinate(module_sb.get_sb_x(), + module_sb.get_sb_y()); + + /* Collect sink-related information */ + std::string sink_sb_module_name = + generate_switch_block_module_name(module_sb_coordinate); + ModuleId sink_sb_module = module_manager.find_module(sink_sb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sink_sb_module)); + size_t sink_sb_instance = sb_instance; + + /* Connect grid output pins (OPIN) to switch block grid pins */ + for (size_t side = 0; side < module_sb.get_num_sides(); ++side) { + SideManager side_manager(side); + for (size_t inode = 0; + inode < module_sb.get_num_opin_nodes(side_manager.get_side()); + ++inode) { + /* Collect source-related information */ + /* Generate the grid module name by considering if it locates on the + * border */ + vtr::Point grid_coordinate( + rr_graph.node_xlow( + rr_gsb.get_opin_node(side_manager.get_side(), inode)), + rr_graph.node_ylow( + rr_gsb.get_opin_node(side_manager.get_side(), inode))); + std::string src_grid_module_name = + generate_grid_block_module_name_in_top_module( + std::string(GRID_MODULE_NAME_PREFIX), grids, grid_coordinate); + ModuleId src_grid_module = + module_manager.find_module(src_grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(src_grid_module)); + size_t src_grid_instance = + grid_instance_ids[grid_coordinate.x()][grid_coordinate.y()]; + size_t src_grid_pin_index = rr_graph.node_pin_num( + rr_gsb.get_opin_node(side_manager.get_side(), inode)); + + t_physical_tile_type_ptr grid_type_descriptor = + grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + size_t src_grid_pin_width = + grid_type_descriptor->pin_width_offset[src_grid_pin_index]; + size_t src_grid_pin_height = + grid_type_descriptor->pin_height_offset[src_grid_pin_index]; + BasicPort src_grid_pin_info = + vpr_device_annotation.physical_tile_pin_port_info(grid_type_descriptor, + src_grid_pin_index); + VTR_ASSERT(true == src_grid_pin_info.is_valid()); + int subtile_index = vpr_device_annotation.physical_tile_pin_subtile_index( + grid_type_descriptor, src_grid_pin_index); + VTR_ASSERT(OPEN != subtile_index && + subtile_index < grid_type_descriptor->capacity); + std::string src_grid_port_name = generate_grid_port_name( + src_grid_pin_width, src_grid_pin_height, subtile_index, + get_rr_graph_single_node_side( + rr_graph, rr_gsb.get_opin_node(side_manager.get_side(), inode)), + src_grid_pin_info); + ModulePortId src_grid_port_id = + module_manager.find_module_port(src_grid_module, src_grid_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(src_grid_module, + src_grid_port_id)); + BasicPort src_grid_port = + module_manager.module_port(src_grid_module, src_grid_port_id); + + /* Check if the grid is inside the tile, if not, create ports */ + if (fabric_tile.pb_in_tile(grid_coordinate) && !frame_view) { + /* Collect sink-related information */ + vtr::Point sink_sb_port_coord( + rr_graph.node_xlow( + module_sb.get_opin_node(side_manager.get_side(), inode)), + rr_graph.node_ylow( + module_sb.get_opin_node(side_manager.get_side(), inode))); + std::string sink_sb_port_name = generate_sb_module_grid_port_name( + side_manager.get_side(), + get_rr_graph_single_node_side( + rr_graph, module_sb.get_opin_node(side_manager.get_side(), inode)), + grids, vpr_device_annotation, rr_graph, + module_sb.get_opin_node(side_manager.get_side(), inode)); + ModulePortId sink_sb_port_id = + module_manager.find_module_port(sink_sb_module, sink_sb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(sink_sb_module, + sink_sb_port_id)); + BasicPort sink_sb_port = + module_manager.module_port(sink_sb_module, sink_sb_port_id); + + /* Source and sink port should match in size */ + VTR_ASSERT(src_grid_port.get_width() == sink_sb_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_grid_port.pins().size(); ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, src_grid_module, src_grid_instance, + src_grid_port_id, src_grid_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, sink_sb_module, + sink_sb_instance, sink_sb_port_id, + sink_sb_port.pins()[pin_id]); + } + } else { + /* Create a port on the tile module and create the net if required */ + ModulePortId src_tile_port_id = module_manager.add_port(tile_module, src_grid_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_grid_port.pins().size(); ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, tile_module, 0, + src_tile_port_id, src_grid_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, sink_sb_module, + sink_sb_instance, sink_sb_port_id, + sink_sb_port.pins()[pin_id]); + } + } + } + } + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * This function will create nets for the connections + * between grid input pins and connection blocks + * In this case, the net source is the connection block pin, + * while the net sink is the grid input + * + * +------------+ +------------------+ +------------+ + * | | | | | | + * | Grid |<-----| Connection Block |----->| Grid | + * | [x][y+1] | | Y-direction | | [x+1][y+1] | + * | | | [x][y+1] | | | + * +------------+ +------------------+ +------------+ + * ^ + * | + * +------------+ +------------------+ + * | Connection | | | + * | Block | | Switch Block | + * | X-direction| | [x][y] | + * | [x][y] | | | + * +------------+ +------------------+ + * | + * v + * +------------+ + * | | + * | Grid | + * | [x][y] | + * | | + * +------------+ + * + * + * Relationship between source connection block and its unique module + * Take an example of a CBY + * + * grid_pin name should follow unique module of Grid[x][y+1] + * cb_pin name should follow unique module of CBY[x][y+1] + * + * However, instace id should follow the origin Grid and Connection block + * + * + * +------------+ +------------------+ + * | | | | + * | Grid |<------------| Connection Block | + * | [x][y+1] | | Y-direction | + * | | | [x][y+1] | + * +------------+ +------------------+ + * ^ + * || unique mirror + * +------------+ +------------------+ + * | | | | + * | Grid |<------------| Connection Block | + * | [i][j+1] | | Y-direction | + * | | | [i][j+1] | + * +------------+ +------------------+ + * + *******************************************************************/ +static int build_tile_module_port_and_nets_between_cb_and_pb(ModuleManager& module_manager, + const ModuleId& tile_module, + const VprDeviceAnnotation& vpr_device_annotation, + const DeviceRRGSB& device_rr_gsb, + const RRGSB& rr_gsb, + const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id, + const size_t& sb_instance, + const bool& compress_routing_hierarchy, + const bool& frame_view, + const bool& verbose) { + /* We could have two different coordinators, one is the instance, the other is + * the module */ + vtr::Point instance_cb_coordinate(rr_gsb.get_cb_x(cb_type), + rr_gsb.get_cb_y(cb_type)); + vtr::Point module_gsb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); + + /* Skip those Connection blocks that do not exist */ + if (false == rr_gsb.is_cb_exist(cb_type)) { + return; + } + + /* Skip if the cb does not contain any configuration bits! */ + if (true == connection_block_contain_only_routing_tracks(rr_gsb, cb_type)) { + return; + } + + /* If we use compact routing hierarchy, we should find the unique module of + * CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + vtr::Point gsb_coord(rr_gsb.get_x(), rr_gsb.get_y()); + const RRGSB& unique_mirror = + device_rr_gsb.get_cb_unique_module(cb_type, gsb_coord); + module_gsb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_coordinate.set_y(unique_mirror.get_y()); + } + + /* This is the source cb that is added to the top module */ + const RRGSB& module_cb = device_rr_gsb.get_gsb(module_gsb_coordinate); + vtr::Point module_cb_coordinate(module_cb.get_cb_x(cb_type), + module_cb.get_cb_y(cb_type)); + + /* Collect source-related information */ + std::string src_cb_module_name = + generate_connection_block_module_name(cb_type, module_cb_coordinate); + ModuleId src_cb_module = module_manager.find_module(src_cb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(src_cb_module)); + /* Instance id should follow the instance cb coordinate */ + size_t src_cb_instance = + cb_instance_ids[instance_cb_coordinate.x()][instance_cb_coordinate.y()]; + + /* Iterate over the output pins of the Connection Block */ + std::vector cb_ipin_sides = module_cb.get_cb_ipin_sides(cb_type); + for (size_t iside = 0; iside < cb_ipin_sides.size(); ++iside) { + enum e_side cb_ipin_side = cb_ipin_sides[iside]; + for (size_t inode = 0; inode < module_cb.get_num_ipin_nodes(cb_ipin_side); + ++inode) { + /* Collect source-related information */ + RRNodeId module_ipin_node = module_cb.get_ipin_node(cb_ipin_side, inode); + vtr::Point cb_src_port_coord( + rr_graph.node_xlow(module_ipin_node), + rr_graph.node_ylow(module_ipin_node)); + std::string src_cb_port_name = generate_cb_module_grid_port_name( + cb_ipin_side, grids, vpr_device_annotation, rr_graph, module_ipin_node); + ModulePortId src_cb_port_id = + module_manager.find_module_port(src_cb_module, src_cb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(src_cb_module, + src_cb_port_id)); + BasicPort src_cb_port = + module_manager.module_port(src_cb_module, src_cb_port_id); + + /* Collect sink-related information */ + /* Note that we use the instance cb pin here!!! + * because it has the correct coordinator for the grid!!! + */ + RRNodeId instance_ipin_node = rr_gsb.get_ipin_node(cb_ipin_side, inode); + vtr::Point grid_coordinate( + rr_graph.node_xlow(instance_ipin_node), + rr_graph.node_ylow(instance_ipin_node)); + std::string sink_grid_module_name = + generate_grid_block_module_name_in_top_module( + std::string(GRID_MODULE_NAME_PREFIX), grids, grid_coordinate); + ModuleId sink_grid_module = + module_manager.find_module(sink_grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sink_grid_module)); + size_t sink_grid_instance = + grid_instance_ids[grid_coordinate.x()][grid_coordinate.y()]; + size_t sink_grid_pin_index = rr_graph.node_pin_num(instance_ipin_node); + + t_physical_tile_type_ptr grid_type_descriptor = + grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + size_t sink_grid_pin_width = + grid_type_descriptor->pin_width_offset[sink_grid_pin_index]; + size_t sink_grid_pin_height = + grid_type_descriptor->pin_height_offset[sink_grid_pin_index]; + BasicPort sink_grid_pin_info = + vpr_device_annotation.physical_tile_pin_port_info(grid_type_descriptor, + sink_grid_pin_index); + VTR_ASSERT(true == sink_grid_pin_info.is_valid()); + int subtile_index = vpr_device_annotation.physical_tile_pin_subtile_index( + grid_type_descriptor, sink_grid_pin_index); + VTR_ASSERT(OPEN != subtile_index && + subtile_index < grid_type_descriptor->capacity); + std::string sink_grid_port_name = generate_grid_port_name( + sink_grid_pin_width, sink_grid_pin_height, subtile_index, + get_rr_graph_single_node_side( + rr_graph, rr_gsb.get_ipin_node(cb_ipin_side, inode)), + sink_grid_pin_info); + ModulePortId sink_grid_port_id = + module_manager.find_module_port(sink_grid_module, sink_grid_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id( + sink_grid_module, sink_grid_port_id)); + BasicPort sink_grid_port = + module_manager.module_port(sink_grid_module, sink_grid_port_id); + + /* Source and sink port should match in size */ + VTR_ASSERT(src_cb_port.get_width() == sink_grid_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, top_module, src_cb_module, src_cb_instance, + src_cb_port_id, src_cb_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink( + top_module, net, sink_grid_module, sink_grid_instance, + sink_grid_port_id, sink_grid_port.pins()[pin_id]); + } + } + } + +} + +/******************************************************************** + * This function will create nets for the connections + * between connection block and switch block pins + * Two cases should be considered: + * a. The switch block pin denotes an input of a routing track + * The net source is an output of a routing track of connection block + * while the net sink is an input of a routing track of switch block + * b. The switch block pin denotes an output of a routing track + * The net source is an output of routing track of switch block + * while the net sink is an input of a routing track of connection block + * + * +------------+ +------------------+ +------------+ + * | | | | | | + * | Grid | | Connection Block | | Grid | + * | [x][y+1] | | Y-direction | | [x+1][y+1] | + * | | | [x][y+1] | | | + * +------------+ +------------------+ +------------+ + * | ^ + * v | + * +------------+ +------------------+ +------------+ + * | Connection |----->| |----->| Connection | + * | Block | | Switch Block | | Block | + * | X-direction|<-----| [x][y] |<-----| X-direction| + * | [x][y] | | | | [x+1][y] | + * +------------+ +------------------+ +------------+ + * | ^ + * v | + * +------------+ +------------------+ +------------+ + * | | | | | | + * | Grid | | Connection Block | | Grid | + * | [x][y] | | Y-direction | | [x][y+1] | + * | | | [x][y] | | | + * +------------+ +------------------+ +------------+ + * + * Here, to achieve the purpose, we can simply iterate over the + * four sides of switch block and make connections to adjancent + * connection blocks + * + *******************************************************************/ +static int build_tile_module_port_and_nets_between_sb_and_cb() { + /* We could have two different coordinators, one is the instance, the other is + * the module */ + vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), + rr_gsb.get_sb_y()); + vtr::Point module_gsb_sb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); + + /* Skip those Switch blocks that do not exist */ + if (false == rr_gsb.is_sb_exist()) { + return; + } + + /* If we use compact routing hierarchy, we should find the unique module of + * CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + vtr::Point gsb_coord(rr_gsb.get_x(), rr_gsb.get_y()); + const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(gsb_coord); + module_gsb_sb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_sb_coordinate.set_y(unique_mirror.get_y()); + } + + /* This is the source cb that is added to the top module */ + const RRGSB& module_sb = device_rr_gsb.get_gsb(module_gsb_sb_coordinate); + vtr::Point module_sb_coordinate(module_sb.get_sb_x(), + module_sb.get_sb_y()); + std::string sb_module_name = + generate_switch_block_module_name(module_sb_coordinate); + ModuleId sb_module_id = module_manager.find_module(sb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sb_module_id)); + size_t sb_instance = + sb_instance_ids[instance_sb_coordinate.x()][instance_sb_coordinate.y()]; + + /* Connect grid output pins (OPIN) to switch block grid pins */ + for (size_t side = 0; side < module_sb.get_num_sides(); ++side) { + SideManager side_manager(side); + /* Iterate over the routing tracks on this side */ + /* Early skip: if there is no routing tracks at this side */ + if (0 == module_sb.get_chan_width(side_manager.get_side())) { + continue; + } + /* Find the Connection Block module */ + /* We find the original connection block and then spot its unique mirror! + * Do NOT use module_sb here!!! + */ + t_rr_type cb_type = + find_top_module_cb_type_by_sb_side(side_manager.get_side()); + vtr::Point instance_gsb_cb_coordinate = + find_top_module_gsb_coordinate_by_sb_side(rr_gsb, + side_manager.get_side()); + vtr::Point module_gsb_cb_coordinate = + find_top_module_gsb_coordinate_by_sb_side(rr_gsb, + side_manager.get_side()); + + /* Skip those Connection blocks that do not exist: + * 1. The CB does not exist in the device level! We should skip! + * 2. The CB does exist but we need to make sure if the GSB includes such + * CBs For TOP and LEFT side, check the existence using RRGSB method + * is_cb_exist() FOr RIGHT and BOTTOM side, find the adjacent RRGSB and then + * use is_cb_exist() + */ + if (TOP == side_manager.get_side() || LEFT == side_manager.get_side()) { + if (false == rr_gsb.is_cb_exist(cb_type)) { + continue; + } + } + + if (RIGHT == side_manager.get_side() || BOTTOM == side_manager.get_side()) { + const RRGSB& adjacent_gsb = + device_rr_gsb.get_gsb(module_gsb_cb_coordinate); + if (false == adjacent_gsb.is_cb_exist(cb_type)) { + continue; + } + } + + /* If we use compact routing hierarchy, we should find the unique module of + * CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + const RRGSB& unique_mirror = + device_rr_gsb.get_cb_unique_module(cb_type, module_gsb_cb_coordinate); + module_gsb_cb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_cb_coordinate.set_y(unique_mirror.get_y()); + } + + const RRGSB& module_cb = device_rr_gsb.get_gsb(module_gsb_cb_coordinate); + vtr::Point module_cb_coordinate(module_cb.get_cb_x(cb_type), + module_cb.get_cb_y(cb_type)); + std::string cb_module_name = + generate_connection_block_module_name(cb_type, module_cb_coordinate); + ModuleId cb_module_id = module_manager.find_module(cb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(cb_module_id)); + const RRGSB& instance_cb = + device_rr_gsb.get_gsb(instance_gsb_cb_coordinate); + vtr::Point instance_cb_coordinate(instance_cb.get_cb_x(cb_type), + instance_cb.get_cb_y(cb_type)); + size_t cb_instance = cb_instance_ids.at( + cb_type)[instance_cb_coordinate.x()][instance_cb_coordinate.y()]; + + for (size_t itrack = 0; + itrack < module_sb.get_chan_width(side_manager.get_side()); ++itrack) { + std::string sb_port_name = generate_sb_module_track_port_name( + rr_graph.node_type( + module_sb.get_chan_node(side_manager.get_side(), itrack)), + side_manager.get_side(), + module_sb.get_chan_node_direction(side_manager.get_side(), itrack)); + /* Prepare SB-related port information */ + ModulePortId sb_port_id = + module_manager.find_module_port(sb_module_id, sb_port_name); + VTR_ASSERT(true == + module_manager.valid_module_port_id(sb_module_id, sb_port_id)); + BasicPort sb_port = module_manager.module_port(sb_module_id, sb_port_id); + + /* Prepare CB-related port information */ + PORTS cb_port_direction = OUT_PORT; + /* The cb port direction should be opposite to the sb port !!! */ + if (OUT_PORT == + module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { + cb_port_direction = IN_PORT; + } else { + VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( + side_manager.get_side(), itrack)); + } + + /* Upper CB port is required if the routing tracks are on the top or right + * sides of the switch block, which indicated bottom and left sides of the + * connection blocks + */ + bool use_cb_upper_port = + (TOP == side_manager.get_side()) || (RIGHT == side_manager.get_side()); + std::string cb_port_name = generate_cb_module_track_port_name( + cb_type, cb_port_direction, use_cb_upper_port); + ModulePortId cb_port_id = + module_manager.find_module_port(cb_module_id, cb_port_name); + VTR_ASSERT(true == + module_manager.valid_module_port_id(cb_module_id, cb_port_id)); + BasicPort cb_port = module_manager.module_port(cb_module_id, cb_port_id); + + /* Configure the net source and sink: + * If sb port is an output (source), cb port is an input (sink) + * If sb port is an input (sink), cb port is an output (source) + */ + if (OUT_PORT == + module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { + ModuleNetId net = + create_module_source_pin_net(module_manager, top_module, sb_module_id, + sb_instance, sb_port_id, itrack / 2); + module_manager.add_module_net_sink(top_module, net, cb_module_id, + cb_instance, cb_port_id, itrack / 2); + } else { + VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( + side_manager.get_side(), itrack)); + ModuleNetId net = + create_module_source_pin_net(module_manager, top_module, cb_module_id, + cb_instance, cb_port_id, itrack / 2); + module_manager.add_module_net_sink(top_module, net, sb_module_id, + sb_instance, sb_port_id, itrack / 2); + } + } + } +} + +static int build_tile_port_and_nets_from_pb() { + +} + +/******************************************************************** + * Build the ports and associated nets for a tile module + * For each submodule, e.g., programmable block, connection block and switch blocks, + * we walk through their neighbours, starting from sb, cb and then programmable blocks. + * - If a neighbour is one of the submodules under this tile, we can build nets between the submodule and its neighbour. + * - If the neighbour is not under this tile, we should build ports for the tile module and then build nets to connect + * Note that if frame_view is enabled, nets are not built + *******************************************************************/ +static int build_tile_module_ports_and_nets(ModuleManager& module_manager, + const ModuleId& tile_module, + const VprDeviceAnnotation& vpr_device_annotation, + const DeviceRRGSB& device_rr_gsb, + const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id, + const std::vector& sb_instances, + const bool& frame_view, + const bool& verbose) { + int status_code = CMD_EXEC_SUCCESS; + + /* TODO: Get the submodule of Switch blocks one by one, build connections between sb and pb */ + for (size_t isb = 0; isb < fabric_tile.sb_coordinates(fabric_tile_id).size(); ++isb) { + vtr::Point sb_coord = fabric_tile.sb_coordinates(fabric_tile_id)[isb]; + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); + status_code = build_tile_module_port_and_nets_between_sb_and_pb(module_manager, + tile_module, + vpr_device_annotation, + device_rr_gsb, + rr_gsb, + fabric_tile, + fabric_tile_id, + sb_instances[isb], + true, + frame_view, + verbose); + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } + /* TODO: Get the submodule of connection blocks one by one, build connections between cb and pb */ + status_code = build_tile_module_port_and_nets_between_cb_and_pb(); + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + /* TODO: Get the submodule of connection blocks one by one, build connections between sb and cb */ + status_code = build_tile_module_port_and_nets_between_sb_and_cb(); + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + /* TODO: Create the ports from pb which only connects to adjacent sb and cbs, as well as pb */ + status_code = build_tile_port_and_nets_from_pb(); + + return status_code; +} + +/******************************************************************** + * Build a tile module + * - Add instances + * - Add ports and nets + * - Add global ports + * - Add I/O ports + * - Add configuration ports and nets *******************************************************************/ static int build_tile_module( - ModuleManager& module_manager, const FabricTile& fabric_tile, + ModuleManager& module_manager, DecoderLibrary& decoder_lib, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, const DeviceGrid& grids, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, const CircuitModelId& sram_model, - const e_config_protocol_type& sram_orgz_type, const bool& verbose) { + const e_config_protocol_type& sram_orgz_type, + const bool& frame_view, + const bool& verbose) { int status_code = CMD_EXEC_SUCCESS; /* Create the module */ @@ -49,7 +680,7 @@ static int build_tile_module( grids.get_physical_type(grid_coord.x(), grid_coord.y()); /* Empty type does not require a module */ if (!is_empty_type(phy_tile)) { - e_side grid_side = find_grid_side_by_coordinate(grids, grid_coord); + e_side grid_side = find_grid_border_side(vtr::Point(grids.width, grids.height()), grid_coord); std::string pb_module_name = generate_grid_block_module_name( std::string(GRID_MODULE_NAME_PREFIX), std::string(phy_tile->name), is_io_type(phy_tile), grid_side); @@ -58,6 +689,7 @@ static int build_tile_module( VTR_LOG_ERROR( "Failed to find pb module '%s' required by tile[%lu][%lu]!\n", pb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + return CMD_EXEC_FATAL_ERROR; } size_t pb_instance = module_manager.num_instance(tile_module, pb_module); module_manager.add_child_module(tile_module, pb_module); @@ -89,6 +721,7 @@ static int build_tile_module( "Failed to find connection block module '%s' required by " "tile[%lu][%lu]!\n", cb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + return CMD_EXEC_FATAL_ERROR; } size_t cb_instance = module_manager.num_instance(tile_module, cb_module); module_manager.add_child_module(tile_module, cb_module); @@ -105,6 +738,7 @@ static int build_tile_module( } /* Add instance of switch blocks */ + std::vector sb_instances; /* Keep tracking the instance id of each sb */ for (vtr::Point sb_coord : fabric_tile.sb_coordinates(fabric_tile_id)) { /* get the unique module coord */ @@ -118,6 +752,7 @@ static int build_tile_module( VTR_LOG_ERROR( "Failed to find switch block module '%s' required by tile[%lu][%lu]!\n", sb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + return CMD_EXEC_FATAL_ERROR; } size_t sb_instance = module_manager.num_instance(tile_module, sb_module); module_manager.add_child_module(tile_module, sb_module); @@ -128,9 +763,62 @@ static int build_tile_module( } VTR_LOGV(verbose, "Added switch block module '%s' to tile[%lu][%lu]\n", sb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + sb_instances.push_back(sb_instance); } /* TODO: Add module nets and ports */ + status_code = build_tile_module_ports_and_nets(module_manager, tile_module, vpr_device_annotation, device_rr_gsb, fabric_tile, fabric_tile_id, sb_instances, frame_view, verbose); + + /* 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, tile_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, tile_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, + tile_module); + if (0 < module_num_shared_config_bits) { + add_reserved_sram_ports_to_module_manager(module_manager, tile_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, tile_module, circuit_lib, sram_model, sram_orgz_type); + if (0 < module_num_config_bits) { + add_pb_sram_ports_to_module_manager(module_manager, tile_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(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)); + } VTR_LOGV(verbose, "Done\n"); return status_code; @@ -141,10 +829,12 @@ static int build_tile_module( *******************************************************************/ int build_tile_modules(ModuleManager& module_manager, const FabricTile& fabric_tile, const DeviceGrid& grids, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, const CircuitModelId& sram_model, const e_config_protocol_type& sram_orgz_type, + const bool& frame_view, const bool& verbose) { vtr::ScopedStartFinishTimer timer("Build tile modules for the FPGA fabric"); @@ -153,8 +843,8 @@ int build_tile_modules(ModuleManager& module_manager, /* Build a module for each unique tile */ for (FabricTileId fabric_tile_id : fabric_tile.unique_tiles()) { status_code = build_tile_module(module_manager, fabric_tile, fabric_tile_id, - grids, device_rr_gsb, circuit_lib, - sram_model, sram_orgz_type, verbose); + grids, vpr_device_annotation, device_rr_gsb, circuit_lib, + sram_model, sram_orgz_type, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } diff --git a/openfpga/src/fabric/build_tile_modules.h b/openfpga/src/fabric/build_tile_modules.h index 374b9bf5d..44876ecca 100644 --- a/openfpga/src/fabric/build_tile_modules.h +++ b/openfpga/src/fabric/build_tile_modules.h @@ -9,6 +9,7 @@ #include "circuit_library.h" #include "config_protocol.h" +#include "vpr_device_annotation.h" #include "device_grid.h" #include "device_rr_gsb.h" #include "fabric_tile.h" @@ -23,10 +24,12 @@ namespace openfpga { int build_tile_modules(ModuleManager& module_manager, const FabricTile& fabric_tile, const DeviceGrid& grids, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, const CircuitModelId& sram_model, const e_config_protocol_type& sram_orgz_type, + const bool& frame_view, const bool& verbose); } /* end namespace openfpga */ diff --git a/openfpga/src/utils/openfpga_device_grid_utils.cpp b/openfpga/src/utils/openfpga_device_grid_utils.cpp index 0b69a5a10..5a234b60d 100644 --- a/openfpga/src/utils/openfpga_device_grid_utils.cpp +++ b/openfpga/src/utils/openfpga_device_grid_utils.cpp @@ -13,28 +13,6 @@ /* begin namespace openfpga */ namespace openfpga { -/******************************************************************** - * Identify if a grid locates the side of an FPGA fabric - * - If on a side, return the side - * - If not, return an invalid side - *******************************************************************/ -e_side find_grid_side_by_coordinate(const DeviceGrid& grids, - const vtr::Point& coord) { - if (coord.x() == 0) { - return LEFT; - } - if (coord.y() == 0) { - return BOTTOM; - } - if (coord.x() == grids.width() - 1) { - return RIGHT; - } - if (coord.y() == grids.height() - 1) { - return TOP; - } - return NUM_SIDES; -} - /******************************************************************** * Create a list of the coordinates for the grids on the device perimeter * It follows a clockwise sequence when including the coordinates. diff --git a/openfpga/src/utils/openfpga_device_grid_utils.h b/openfpga/src/utils/openfpga_device_grid_utils.h index 00f0e9aab..4b49be150 100644 --- a/openfpga/src/utils/openfpga_device_grid_utils.h +++ b/openfpga/src/utils/openfpga_device_grid_utils.h @@ -18,9 +18,6 @@ /* begin namespace openfpga */ namespace openfpga { -e_side find_grid_side_by_coordinate(const DeviceGrid& grids, - const vtr::Point& coord); - /* A constant array to walk through FPGA border sides clockwise*/ constexpr std::array FPGA_SIDES_CLOCKWISE{TOP, RIGHT, BOTTOM, LEFT}; From d18530e782a7f623d3eb54895c3cdbeb4b69d64b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 06:17:49 +0000 Subject: [PATCH 176/391] Bump yosys from `14d50a1` to `25d4b3a` Bumps [yosys](https://github.com/YosysHQ/yosys) from `14d50a1` to `25d4b3a`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/14d50a176d59a5eac95a57a01f9e933297251d5b...25d4b3a5dc32ac3865d5907b5aba45f67f2dabb0) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 14d50a176..25d4b3a5d 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 14d50a176d59a5eac95a57a01f9e933297251d5b +Subproject commit 25d4b3a5dc32ac3865d5907b5aba45f67f2dabb0 From 403ed4ea6020517dd680d3240fdb2b948976009c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 18 Jul 2023 16:03:47 -0700 Subject: [PATCH 177/391] [core] still developing tile module port and net builder --- openfpga/src/annotation/fabric_tile.cpp | 50 +++- openfpga/src/annotation/fabric_tile.h | 6 + openfpga/src/fabric/build_tile_modules.cpp | 276 ++++++++++++++------- 3 files changed, 233 insertions(+), 99 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index b9cb7d17f..679ee0d1b 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -73,42 +73,68 @@ FabricTileId FabricTile::find_tile(const vtr::Point& coord) const { } bool FabricTile::pb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { + return find_pb_index_in_tile(tile_id, coord) != pb_coords_.size(); +} + +size_t FabricTile::find_pb_index_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { VTR_ASSERT(valid_tile_id(tile_id)); - for (vtr::Point curr_coord : pb_coords_[tile_id]) { + for (size_t idx = 0; idx < pb_coords_[tile_id].size(); ++idx) { + vtr::Point curr_coord = pb_coords_[tile_id][idx]; if (curr_coord == coord) { - return true; + return idx; } } - return false; + /* Not found, return an invalid index */ + return pb_coords_.size(); } bool FabricTile::sb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { + return find_sb_index_in_tile(tile_id, coord) != sb_coords_.size(); +} + +size_t FabricTile::find_sb_index_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { VTR_ASSERT(valid_tile_id(tile_id)); - for (vtr::Point curr_coord : sb_coords_[tile_id]) { + for (size_t idx = 0; idx < sb_coords_[tile_id].size(); ++idx) { + vtr::Point curr_coord = sb_coords_[tile_id][idx]; if (curr_coord == coord) { - return true; + return idx; } } - return false; + /* Not found, return an invalid index */ + return sb_coords_.size(); } bool FabricTile::cb_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord) const { + switch (cb_type) { + case CHANX: + return find_cb_index_in_tile(tile_id, cb_type, coord) == cbx_coords_.size(); + case CHANY: + return find_cb_index_in_tile(tile_id, cb_type, coord) == cby_coords_.size(); + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } +} + +size_t FabricTile::find_cb_index_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord) const { VTR_ASSERT(valid_tile_id(tile_id)); switch (cb_type) { case CHANX: - for (vtr::Point curr_coord : cbx_coords_[tile_id]) { + for (size_t idx = 0; idx < cbx_coords_[tile_id].size(); ++idx) { + vtr::Point curr_coord = cbx_coords_[tile_id][idx]; if (curr_coord == coord) { - return true; + return idx; } } - return false; + return cbx_coords_[tile_id].size(); case CHANY: - for (vtr::Point curr_coord : cby_coords_[tile_id]) { + for (size_t idx = 0; idx < cby_coords_[tile_id].size(); ++idx) { + vtr::Point curr_coord = cby_coords_[tile_id][idx]; if (curr_coord == coord) { - return true; + return idx; } } - return false; + return cby_coords_[tile_id].size(); default: VTR_LOG("Invalid type of connection block!\n"); exit(1); diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h index 268f26c15..0cc5ab24f 100644 --- a/openfpga/src/annotation/fabric_tile.h +++ b/openfpga/src/annotation/fabric_tile.h @@ -41,6 +41,12 @@ class FabricTile { FabricTileId find_tile(const vtr::Point& coord) const; /** @brief Return a list of unique tiles */ std::vector unique_tiles() const; + /** @brief Find the index of a programmable block in the internal list by a given coordinate. */ + size_t find_pb_index_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const; + /** @brief Find the index of a switch block in the internal list by a given coordinate. */ + size_t find_sb_index_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const; + /** @brief Find the index of a connection block in the internal list by a given coordinate. */ + size_t find_cb_index_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord) const; /** @brief Check if a programmable block (with a coordinate) exists in a tile */ bool pb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const; /** @brief Check if a switch block (with a coordinate) exists in a tile */ diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 641d7d32a..c728f3ca9 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -61,6 +61,7 @@ static int build_tile_module_port_and_nets_between_sb_and_pb(ModuleManager& modu const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, + const size_t& pb_instances, const size_t& sb_instance, const bool& compress_routing_hierarchy, const bool& frame_view, @@ -117,8 +118,6 @@ static int build_tile_module_port_and_nets_between_sb_and_pb(ModuleManager& modu ModuleId src_grid_module = module_manager.find_module(src_grid_module_name); VTR_ASSERT(true == module_manager.valid_module_id(src_grid_module)); - size_t src_grid_instance = - grid_instance_ids[grid_coordinate.x()][grid_coordinate.y()]; size_t src_grid_pin_index = rr_graph.node_pin_num( rr_gsb.get_opin_node(side_manager.get_side(), inode)); @@ -150,6 +149,7 @@ static int build_tile_module_port_and_nets_between_sb_and_pb(ModuleManager& modu /* Check if the grid is inside the tile, if not, create ports */ if (fabric_tile.pb_in_tile(grid_coordinate) && !frame_view) { + size_t src_grid_instance = grid_instances[fabric_tile.find_pb_index(fabric_tile_id, grid_coordinate)]; /* Collect sink-related information */ vtr::Point sink_sb_port_coord( rr_graph.node_xlow( @@ -263,7 +263,9 @@ static int build_tile_module_port_and_nets_between_cb_and_pb(ModuleManager& modu const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, - const size_t& sb_instance, + const t_rr_type& cb_type, + const std::vector& pb_instances, + const size_t& cb_instance, const bool& compress_routing_hierarchy, const bool& frame_view, const bool& verbose) { @@ -304,8 +306,7 @@ static int build_tile_module_port_and_nets_between_cb_and_pb(ModuleManager& modu ModuleId src_cb_module = module_manager.find_module(src_cb_module_name); VTR_ASSERT(true == module_manager.valid_module_id(src_cb_module)); /* Instance id should follow the instance cb coordinate */ - size_t src_cb_instance = - cb_instance_ids[instance_cb_coordinate.x()][instance_cb_coordinate.y()]; + size_t src_cb_instance = cb_instance; /* Iterate over the output pins of the Connection Block */ std::vector cb_ipin_sides = module_cb.get_cb_ipin_sides(cb_type); @@ -341,8 +342,6 @@ static int build_tile_module_port_and_nets_between_cb_and_pb(ModuleManager& modu ModuleId sink_grid_module = module_manager.find_module(sink_grid_module_name); VTR_ASSERT(true == module_manager.valid_module_id(sink_grid_module)); - size_t sink_grid_instance = - grid_instance_ids[grid_coordinate.x()][grid_coordinate.y()]; size_t sink_grid_pin_index = rr_graph.node_pin_num(instance_ipin_node); t_physical_tile_type_ptr grid_type_descriptor = @@ -371,22 +370,40 @@ static int build_tile_module_port_and_nets_between_cb_and_pb(ModuleManager& modu BasicPort sink_grid_port = module_manager.module_port(sink_grid_module, sink_grid_port_id); - /* Source and sink port should match in size */ - VTR_ASSERT(src_cb_port.get_width() == sink_grid_port.get_width()); + /* Check if the grid is inside the tile, if not, create ports */ + if (fabric_tile.pb_in_tile(grid_coordinate) && !frame_view) { + size_t sink_grid_instance = + grid_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, grid_coordinate)]; - /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); ++pin_id) { - ModuleNetId net = create_module_source_pin_net( - module_manager, top_module, src_cb_module, src_cb_instance, - src_cb_port_id, src_cb_port.pins()[pin_id]); - /* Configure the net sink */ - module_manager.add_module_net_sink( - top_module, net, sink_grid_module, sink_grid_instance, - sink_grid_port_id, sink_grid_port.pins()[pin_id]); + /* Source and sink port should match in size */ + VTR_ASSERT(src_cb_port.get_width() == sink_grid_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, src_cb_module, src_cb_instance, + src_cb_port_id, src_cb_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink( + tile_module, net, sink_grid_module, sink_grid_instance, + sink_grid_port_id, sink_grid_port.pins()[pin_id]); + } + } else { + /* Create a port on the tile module and create the net if required */ + ModulePortId sink_tile_port_id = module_manager.add_port(tile_module, src_cb_port, ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, src_cb_module, src_cb_instance, src_tile_port_id, src_cb_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, tile_module, + 0, sink_tile_port_id, + src_cb_port.pins()[pin_id]); + } } } } - + return CMD_EXEC_SUCCESS; } /******************************************************************** @@ -428,7 +445,18 @@ static int build_tile_module_port_and_nets_between_cb_and_pb(ModuleManager& modu * connection blocks * *******************************************************************/ -static int build_tile_module_port_and_nets_between_sb_and_cb() { +static int build_tile_module_port_and_nets_between_sb_and_cb(ModuleManager& module_manager, + const ModuleId& tile_module, + const VprDeviceAnnotation& vpr_device_annotation, + const DeviceRRGSB& device_rr_gsb, + const RRGSB& rr_gsb, + const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id, + const std::map>& cb_instances, + const size_t& sb_instance, + const bool& compress_routing_hierarchy, + const bool& frame_view, + const bool& verbose) { /* We could have two different coordinators, one is the instance, the other is * the module */ vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), @@ -457,8 +485,6 @@ static int build_tile_module_port_and_nets_between_sb_and_cb() { generate_switch_block_module_name(module_sb_coordinate); ModuleId sb_module_id = module_manager.find_module(sb_module_name); VTR_ASSERT(true == module_manager.valid_module_id(sb_module_id)); - size_t sb_instance = - sb_instance_ids[instance_sb_coordinate.x()][instance_sb_coordinate.y()]; /* Connect grid output pins (OPIN) to switch block grid pins */ for (size_t side = 0; side < module_sb.get_num_sides(); ++side) { @@ -522,70 +548,107 @@ static int build_tile_module_port_and_nets_between_sb_and_cb() { device_rr_gsb.get_gsb(instance_gsb_cb_coordinate); vtr::Point instance_cb_coordinate(instance_cb.get_cb_x(cb_type), instance_cb.get_cb_y(cb_type)); - size_t cb_instance = cb_instance_ids.at( - cb_type)[instance_cb_coordinate.x()][instance_cb_coordinate.y()]; - for (size_t itrack = 0; - itrack < module_sb.get_chan_width(side_manager.get_side()); ++itrack) { - std::string sb_port_name = generate_sb_module_track_port_name( - rr_graph.node_type( - module_sb.get_chan_node(side_manager.get_side(), itrack)), - side_manager.get_side(), - module_sb.get_chan_node_direction(side_manager.get_side(), itrack)); - /* Prepare SB-related port information */ - ModulePortId sb_port_id = - module_manager.find_module_port(sb_module_id, sb_port_name); - VTR_ASSERT(true == - module_manager.valid_module_port_id(sb_module_id, sb_port_id)); - BasicPort sb_port = module_manager.module_port(sb_module_id, sb_port_id); + /* Check if the grid is inside the tile, if not, create ports */ + if (fabric_tile.cb_in_tile(fabric_tile_id, cb_type, instance_cb_coordinate) && !frame_view) { + size_t cb_instance = cb_instances.at(cb_type)[fabric_tile.find_cb_index_in_tile(fabric_tile_id, cb_type, instance_cb_coordinate)]; - /* Prepare CB-related port information */ - PORTS cb_port_direction = OUT_PORT; - /* The cb port direction should be opposite to the sb port !!! */ - if (OUT_PORT == - module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { - cb_port_direction = IN_PORT; - } else { - VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( - side_manager.get_side(), itrack)); + for (size_t itrack = 0; + itrack < module_sb.get_chan_width(side_manager.get_side()); ++itrack) { + std::string sb_port_name = generate_sb_module_track_port_name( + rr_graph.node_type( + module_sb.get_chan_node(side_manager.get_side(), itrack)), + side_manager.get_side(), + module_sb.get_chan_node_direction(side_manager.get_side(), itrack)); + /* Prepare SB-related port information */ + ModulePortId sb_port_id = + module_manager.find_module_port(sb_module_id, sb_port_name); + VTR_ASSERT(true == + module_manager.valid_module_port_id(sb_module_id, sb_port_id)); + BasicPort sb_port = module_manager.module_port(sb_module_id, sb_port_id); + + /* Prepare CB-related port information */ + PORTS cb_port_direction = OUT_PORT; + /* The cb port direction should be opposite to the sb port !!! */ + if (OUT_PORT == + module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { + cb_port_direction = IN_PORT; + } else { + VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( + side_manager.get_side(), itrack)); + } + + /* Upper CB port is required if the routing tracks are on the top or right + * sides of the switch block, which indicated bottom and left sides of the + * connection blocks + */ + bool use_cb_upper_port = + (TOP == side_manager.get_side()) || (RIGHT == side_manager.get_side()); + std::string cb_port_name = generate_cb_module_track_port_name( + cb_type, cb_port_direction, use_cb_upper_port); + ModulePortId cb_port_id = + module_manager.find_module_port(cb_module_id, cb_port_name); + VTR_ASSERT(true == + module_manager.valid_module_port_id(cb_module_id, cb_port_id)); + BasicPort cb_port = module_manager.module_port(cb_module_id, cb_port_id); + + /* Configure the net source and sink: + * If sb port is an output (source), cb port is an input (sink) + * If sb port is an input (sink), cb port is an output (source) + */ + if (OUT_PORT == + module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { + ModuleNetId net = + create_module_source_pin_net(module_manager, tile_module, sb_module_id, + sb_instance, sb_port_id, itrack / 2); + module_manager.add_module_net_sink(tile_module, net, cb_module_id, + cb_instance, cb_port_id, itrack / 2); + } else { + VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( + side_manager.get_side(), itrack)); + ModuleNetId net = + create_module_source_pin_net(module_manager, tile_module, cb_module_id, + cb_instance, cb_port_id, itrack / 2); + module_manager.add_module_net_sink(tile_module, net, sb_module_id, + sb_instance, sb_port_id, itrack / 2); + } } - - /* Upper CB port is required if the routing tracks are on the top or right - * sides of the switch block, which indicated bottom and left sides of the - * connection blocks - */ - bool use_cb_upper_port = - (TOP == side_manager.get_side()) || (RIGHT == side_manager.get_side()); - std::string cb_port_name = generate_cb_module_track_port_name( - cb_type, cb_port_direction, use_cb_upper_port); - ModulePortId cb_port_id = - module_manager.find_module_port(cb_module_id, cb_port_name); - VTR_ASSERT(true == - module_manager.valid_module_port_id(cb_module_id, cb_port_id)); - BasicPort cb_port = module_manager.module_port(cb_module_id, cb_port_id); - - /* Configure the net source and sink: - * If sb port is an output (source), cb port is an input (sink) - * If sb port is an input (sink), cb port is an output (source) - */ - if (OUT_PORT == - module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { - ModuleNetId net = - create_module_source_pin_net(module_manager, top_module, sb_module_id, - sb_instance, sb_port_id, itrack / 2); - module_manager.add_module_net_sink(top_module, net, cb_module_id, - cb_instance, cb_port_id, itrack / 2); - } else { - VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( - side_manager.get_side(), itrack)); - ModuleNetId net = - create_module_source_pin_net(module_manager, top_module, cb_module_id, - cb_instance, cb_port_id, itrack / 2); - module_manager.add_module_net_sink(top_module, net, sb_module_id, - sb_instance, sb_port_id, itrack / 2); + } else { + /* Create input and output ports */ + std::string chan_input_port_name = generate_sb_module_track_port_name( + cb_type, side_manager.get_side(), IN_PORT); + /* Create a port on the tile module and create the net if required */ + ModulePortId sb_chan_input_port_id = module_manager.find_module_port(sb_module_id, chan_input_port_name); + BasicPort chan_input_port = module_manager.module_port(sb_module_id, sb_chan_input_port_id); + ModulePortId tile_chan_input_port_id = module_manager.add_port(tile_module, chan_input_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < chan_input_port.pins().size(); ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, tile_module, 0, tile_chan_input_port_id, chan_input_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, sb_module_id, + sb_instance, sb_chan_input_port_id, + chan_input_port.pins()[pin_id]); + } + + std::string chan_output_port_name = generate_sb_module_track_port_name( + cb_type, side_manager.get_side(), OUT_PORT); + /* Create a port on the tile module and create the net if required */ + ModulePortId sb_chan_output_port_id = module_manager.find_module_port(sb_module_id, chan_output_port_name); + BasicPort chan_output_port = module_manager.module_port(sb_module_id, sb_chan_output_port_id); + ModulePortId tile_chan_output_port_id = module_manager.add_port(tile_module, chan_output_port, ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < chan_output_port.pins().size(); ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, sb_module_id, sb_instance, sb_chan_output_port_id, chan_output_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, tile_module, + 0, tile_chan_output_port_id, + chan_output_port.pins()[pin_id]); } } } + return CMD_EXEC_SUCCESS; } static int build_tile_port_and_nets_from_pb() { @@ -606,6 +669,8 @@ static int build_tile_module_ports_and_nets(ModuleManager& module_manager, const DeviceRRGSB& device_rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, + const std::vector& pb_instances, + const std::map>& cb_instances, const std::vector& sb_instances, const bool& frame_view, const bool& verbose) { @@ -622,6 +687,7 @@ static int build_tile_module_ports_and_nets(ModuleManager& module_manager, rr_gsb, fabric_tile, fabric_tile_id, + pb_instances, sb_instances[isb], true, frame_view, @@ -631,14 +697,46 @@ static int build_tile_module_ports_and_nets(ModuleManager& module_manager, } } /* TODO: Get the submodule of connection blocks one by one, build connections between cb and pb */ - status_code = build_tile_module_port_and_nets_between_cb_and_pb(); - if (status_code != CMD_EXEC_SUCCESS) { - return CMD_EXEC_FATAL_ERROR; + for (t_rr_type cb_type : {CHANX, CHANY}) { + for (size_t icb = 0; icb < fabric_tile.cb_coordinates(fabric_tile_id, cb_type).size(); ++icb) { + vtr::Point cb_coord = fabric_tile.cb_coordinates(fabric_tile_id, cb_type)[icb]; + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(cb_coord); + status_code = build_tile_module_port_and_nets_between_cb_and_pb(module_manager, + tile_module, + vpr_device_annotation, + device_rr_gsb, + rr_gsb, + fabric_tile, + fabric_tile_id, + pb_instances, + cb_instances[cb_type][icb], + true, + frame_view, + verbose); + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } } /* TODO: Get the submodule of connection blocks one by one, build connections between sb and cb */ - status_code = build_tile_module_port_and_nets_between_sb_and_cb(); - if (status_code != CMD_EXEC_SUCCESS) { - return CMD_EXEC_FATAL_ERROR; + for (size_t isb = 0; isb < fabric_tile.sb_coordinates(fabric_tile_id).size(); ++isb) { + vtr::Point sb_coord = fabric_tile.sb_coordinates(fabric_tile_id)[isb]; + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); + status_code = build_tile_module_port_and_nets_between_sb_and_cb(module_manager, + tile_module, + vpr_device_annotation, + device_rr_gsb, + rr_gsb, + fabric_tile, + fabric_tile_id, + cb_instances, + sb_instances[isb], + true, + frame_view, + verbose); + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } } /* TODO: Create the ports from pb which only connects to adjacent sb and cbs, as well as pb */ status_code = build_tile_port_and_nets_from_pb(); @@ -672,6 +770,7 @@ static int build_tile_module( vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); /* Add instance of programmable block */ + std::vector pb_instances; /* Keep tracking the instance id of each pb */ for (vtr::Point grid_gsb_coord : fabric_tile.pb_coordinates(fabric_tile_id)) { const RRGSB& grid_rr_gsb = device_rr_gsb.get_gsb(grid_gsb_coord); @@ -702,9 +801,11 @@ static int build_tile_module( VTR_LOGV(verbose, "Added programmable module '%s' to tile[%lu][%lu]\n", pb_module_name.c_str(), tile_coord.x(), tile_coord.y()); } + pb_instances.push_back(pb_instance); } /* Add instance of connection blocks */ + std::map> cb_instances; /* Keep tracking the instance id of each cb */ for (t_rr_type cb_type : {CHANX, CHANY}) { for (vtr::Point cb_coord : fabric_tile.cb_coordinates(fabric_tile_id, cb_type)) { @@ -734,6 +835,7 @@ static int build_tile_module( VTR_LOGV(verbose, "Added connection block module '%s' to tile[%lu][%lu]\n", cb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + cb_instances[cb_type].push_back(cb_instance); } } @@ -767,7 +869,7 @@ static int build_tile_module( } /* TODO: Add module nets and ports */ - status_code = build_tile_module_ports_and_nets(module_manager, tile_module, vpr_device_annotation, device_rr_gsb, fabric_tile, fabric_tile_id, sb_instances, frame_view, verbose); + status_code = build_tile_module_ports_and_nets(module_manager, tile_module, vpr_device_annotation, device_rr_gsb, fabric_tile, fabric_tile_id, pb_instances, cb_instances, sb_instances, frame_view, verbose); /* Add global ports to the pb_module: * This is a much easier job after adding sub modules (instances), From 0dcec9d8e51c994f80907867f9fb4c107e845c0b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 18 Jul 2023 17:56:27 -0700 Subject: [PATCH 178/391] [core] finishing up tile module builder --- openfpga/src/fabric/build_tile_modules.cpp | 437 ++++++++++++++------- 1 file changed, 299 insertions(+), 138 deletions(-) diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index c728f3ca9..e85c8ab2a 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -148,52 +148,56 @@ static int build_tile_module_port_and_nets_between_sb_and_pb(ModuleManager& modu module_manager.module_port(src_grid_module, src_grid_port_id); /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.pb_in_tile(grid_coordinate) && !frame_view) { - size_t src_grid_instance = grid_instances[fabric_tile.find_pb_index(fabric_tile_id, grid_coordinate)]; - /* Collect sink-related information */ - vtr::Point sink_sb_port_coord( - rr_graph.node_xlow( - module_sb.get_opin_node(side_manager.get_side(), inode)), - rr_graph.node_ylow( - module_sb.get_opin_node(side_manager.get_side(), inode))); - std::string sink_sb_port_name = generate_sb_module_grid_port_name( - side_manager.get_side(), - get_rr_graph_single_node_side( - rr_graph, module_sb.get_opin_node(side_manager.get_side(), inode)), - grids, vpr_device_annotation, rr_graph, - module_sb.get_opin_node(side_manager.get_side(), inode)); - ModulePortId sink_sb_port_id = - module_manager.find_module_port(sink_sb_module, sink_sb_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id(sink_sb_module, - sink_sb_port_id)); - BasicPort sink_sb_port = - module_manager.module_port(sink_sb_module, sink_sb_port_id); + if (fabric_tile.pb_in_tile(grid_coordinate)) { + if (!frame_view) { + size_t src_grid_instance = grid_instances[fabric_tile.find_pb_index(fabric_tile_id, grid_coordinate)]; + /* Collect sink-related information */ + vtr::Point sink_sb_port_coord( + rr_graph.node_xlow( + module_sb.get_opin_node(side_manager.get_side(), inode)), + rr_graph.node_ylow( + module_sb.get_opin_node(side_manager.get_side(), inode))); + std::string sink_sb_port_name = generate_sb_module_grid_port_name( + side_manager.get_side(), + get_rr_graph_single_node_side( + rr_graph, module_sb.get_opin_node(side_manager.get_side(), inode)), + grids, vpr_device_annotation, rr_graph, + module_sb.get_opin_node(side_manager.get_side(), inode)); + ModulePortId sink_sb_port_id = + module_manager.find_module_port(sink_sb_module, sink_sb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(sink_sb_module, + sink_sb_port_id)); + BasicPort sink_sb_port = + module_manager.module_port(sink_sb_module, sink_sb_port_id); - /* Source and sink port should match in size */ - VTR_ASSERT(src_grid_port.get_width() == sink_sb_port.get_width()); + /* Source and sink port should match in size */ + VTR_ASSERT(src_grid_port.get_width() == sink_sb_port.get_width()); - /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < src_grid_port.pins().size(); ++pin_id) { - ModuleNetId net = create_module_source_pin_net( - module_manager, tile_module, src_grid_module, src_grid_instance, - src_grid_port_id, src_grid_port.pins()[pin_id]); - /* Configure the net sink */ - module_manager.add_module_net_sink(tile_module, net, sink_sb_module, - sink_sb_instance, sink_sb_port_id, - sink_sb_port.pins()[pin_id]); + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_grid_port.pins().size(); ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, src_grid_module, src_grid_instance, + src_grid_port_id, src_grid_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, sink_sb_module, + sink_sb_instance, sink_sb_port_id, + sink_sb_port.pins()[pin_id]); + } } } else { /* Create a port on the tile module and create the net if required */ ModulePortId src_tile_port_id = module_manager.add_port(tile_module, src_grid_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); - /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < src_grid_port.pins().size(); ++pin_id) { - ModuleNetId net = create_module_source_pin_net( - module_manager, tile_module, tile_module, 0, - src_tile_port_id, src_grid_port.pins()[pin_id]); - /* Configure the net sink */ - module_manager.add_module_net_sink(tile_module, net, sink_sb_module, - sink_sb_instance, sink_sb_port_id, - sink_sb_port.pins()[pin_id]); + if (!frame_view) { + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_grid_port.pins().size(); ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, tile_module, 0, + src_tile_port_id, src_grid_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, sink_sb_module, + sink_sb_instance, sink_sb_port_id, + sink_sb_port.pins()[pin_id]); + } } } } @@ -371,34 +375,38 @@ static int build_tile_module_port_and_nets_between_cb_and_pb(ModuleManager& modu module_manager.module_port(sink_grid_module, sink_grid_port_id); /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.pb_in_tile(grid_coordinate) && !frame_view) { - size_t sink_grid_instance = - grid_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, grid_coordinate)]; + if (fabric_tile.pb_in_tile(grid_coordinate)) { + if (!frame_view) { + size_t sink_grid_instance = + grid_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, grid_coordinate)]; - /* Source and sink port should match in size */ - VTR_ASSERT(src_cb_port.get_width() == sink_grid_port.get_width()); + /* Source and sink port should match in size */ + VTR_ASSERT(src_cb_port.get_width() == sink_grid_port.get_width()); - /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); ++pin_id) { - ModuleNetId net = create_module_source_pin_net( - module_manager, tile_module, src_cb_module, src_cb_instance, - src_cb_port_id, src_cb_port.pins()[pin_id]); - /* Configure the net sink */ - module_manager.add_module_net_sink( - tile_module, net, sink_grid_module, sink_grid_instance, - sink_grid_port_id, sink_grid_port.pins()[pin_id]); + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, src_cb_module, src_cb_instance, + src_cb_port_id, src_cb_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink( + tile_module, net, sink_grid_module, sink_grid_instance, + sink_grid_port_id, sink_grid_port.pins()[pin_id]); + } } } else { /* Create a port on the tile module and create the net if required */ ModulePortId sink_tile_port_id = module_manager.add_port(tile_module, src_cb_port, ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); - /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); ++pin_id) { - ModuleNetId net = create_module_source_pin_net( - module_manager, tile_module, src_cb_module, src_cb_instance, src_tile_port_id, src_cb_port.pins()[pin_id]); - /* Configure the net sink */ - module_manager.add_module_net_sink(tile_module, net, tile_module, - 0, sink_tile_port_id, - src_cb_port.pins()[pin_id]); + if (!frame_view) { + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, src_cb_module, src_cb_instance, src_tile_port_id, src_cb_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, tile_module, + 0, sink_tile_port_id, + src_cb_port.pins()[pin_id]); + } } } } @@ -550,67 +558,69 @@ static int build_tile_module_port_and_nets_between_sb_and_cb(ModuleManager& modu instance_cb.get_cb_y(cb_type)); /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.cb_in_tile(fabric_tile_id, cb_type, instance_cb_coordinate) && !frame_view) { - size_t cb_instance = cb_instances.at(cb_type)[fabric_tile.find_cb_index_in_tile(fabric_tile_id, cb_type, instance_cb_coordinate)]; + if (fabric_tile.cb_in_tile(fabric_tile_id, cb_type, instance_cb_coordinate)) { + if (!frame_view) { + size_t cb_instance = cb_instances.at(cb_type)[fabric_tile.find_cb_index_in_tile(fabric_tile_id, cb_type, instance_cb_coordinate)]; - for (size_t itrack = 0; - itrack < module_sb.get_chan_width(side_manager.get_side()); ++itrack) { - std::string sb_port_name = generate_sb_module_track_port_name( - rr_graph.node_type( - module_sb.get_chan_node(side_manager.get_side(), itrack)), - side_manager.get_side(), - module_sb.get_chan_node_direction(side_manager.get_side(), itrack)); - /* Prepare SB-related port information */ - ModulePortId sb_port_id = - module_manager.find_module_port(sb_module_id, sb_port_name); - VTR_ASSERT(true == - module_manager.valid_module_port_id(sb_module_id, sb_port_id)); - BasicPort sb_port = module_manager.module_port(sb_module_id, sb_port_id); + for (size_t itrack = 0; + itrack < module_sb.get_chan_width(side_manager.get_side()); ++itrack) { + std::string sb_port_name = generate_sb_module_track_port_name( + rr_graph.node_type( + module_sb.get_chan_node(side_manager.get_side(), itrack)), + side_manager.get_side(), + module_sb.get_chan_node_direction(side_manager.get_side(), itrack)); + /* Prepare SB-related port information */ + ModulePortId sb_port_id = + module_manager.find_module_port(sb_module_id, sb_port_name); + VTR_ASSERT(true == + module_manager.valid_module_port_id(sb_module_id, sb_port_id)); + BasicPort sb_port = module_manager.module_port(sb_module_id, sb_port_id); - /* Prepare CB-related port information */ - PORTS cb_port_direction = OUT_PORT; - /* The cb port direction should be opposite to the sb port !!! */ - if (OUT_PORT == - module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { - cb_port_direction = IN_PORT; - } else { - VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( - side_manager.get_side(), itrack)); - } + /* Prepare CB-related port information */ + PORTS cb_port_direction = OUT_PORT; + /* The cb port direction should be opposite to the sb port !!! */ + if (OUT_PORT == + module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { + cb_port_direction = IN_PORT; + } else { + VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( + side_manager.get_side(), itrack)); + } - /* Upper CB port is required if the routing tracks are on the top or right - * sides of the switch block, which indicated bottom and left sides of the - * connection blocks - */ - bool use_cb_upper_port = - (TOP == side_manager.get_side()) || (RIGHT == side_manager.get_side()); - std::string cb_port_name = generate_cb_module_track_port_name( - cb_type, cb_port_direction, use_cb_upper_port); - ModulePortId cb_port_id = - module_manager.find_module_port(cb_module_id, cb_port_name); - VTR_ASSERT(true == - module_manager.valid_module_port_id(cb_module_id, cb_port_id)); - BasicPort cb_port = module_manager.module_port(cb_module_id, cb_port_id); + /* Upper CB port is required if the routing tracks are on the top or right + * sides of the switch block, which indicated bottom and left sides of the + * connection blocks + */ + bool use_cb_upper_port = + (TOP == side_manager.get_side()) || (RIGHT == side_manager.get_side()); + std::string cb_port_name = generate_cb_module_track_port_name( + cb_type, cb_port_direction, use_cb_upper_port); + ModulePortId cb_port_id = + module_manager.find_module_port(cb_module_id, cb_port_name); + VTR_ASSERT(true == + module_manager.valid_module_port_id(cb_module_id, cb_port_id)); + BasicPort cb_port = module_manager.module_port(cb_module_id, cb_port_id); - /* Configure the net source and sink: - * If sb port is an output (source), cb port is an input (sink) - * If sb port is an input (sink), cb port is an output (source) - */ - if (OUT_PORT == - module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { - ModuleNetId net = - create_module_source_pin_net(module_manager, tile_module, sb_module_id, - sb_instance, sb_port_id, itrack / 2); - module_manager.add_module_net_sink(tile_module, net, cb_module_id, - cb_instance, cb_port_id, itrack / 2); - } else { - VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( - side_manager.get_side(), itrack)); - ModuleNetId net = - create_module_source_pin_net(module_manager, tile_module, cb_module_id, - cb_instance, cb_port_id, itrack / 2); - module_manager.add_module_net_sink(tile_module, net, sb_module_id, - sb_instance, sb_port_id, itrack / 2); + /* Configure the net source and sink: + * If sb port is an output (source), cb port is an input (sink) + * If sb port is an input (sink), cb port is an output (source) + */ + if (OUT_PORT == + module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { + ModuleNetId net = + create_module_source_pin_net(module_manager, tile_module, sb_module_id, + sb_instance, sb_port_id, itrack / 2); + module_manager.add_module_net_sink(tile_module, net, cb_module_id, + cb_instance, cb_port_id, itrack / 2); + } else { + VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( + side_manager.get_side(), itrack)); + ModuleNetId net = + create_module_source_pin_net(module_manager, tile_module, cb_module_id, + cb_instance, cb_port_id, itrack / 2); + module_manager.add_module_net_sink(tile_module, net, sb_module_id, + sb_instance, sb_port_id, itrack / 2); + } } } } else { @@ -622,13 +632,15 @@ static int build_tile_module_port_and_nets_between_sb_and_cb(ModuleManager& modu BasicPort chan_input_port = module_manager.module_port(sb_module_id, sb_chan_input_port_id); ModulePortId tile_chan_input_port_id = module_manager.add_port(tile_module, chan_input_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < chan_input_port.pins().size(); ++pin_id) { - ModuleNetId net = create_module_source_pin_net( - module_manager, tile_module, tile_module, 0, tile_chan_input_port_id, chan_input_port.pins()[pin_id]); - /* Configure the net sink */ - module_manager.add_module_net_sink(tile_module, net, sb_module_id, - sb_instance, sb_chan_input_port_id, - chan_input_port.pins()[pin_id]); + if (!frame_view) { + for (size_t pin_id = 0; pin_id < chan_input_port.pins().size(); ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, tile_module, 0, tile_chan_input_port_id, chan_input_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, sb_module_id, + sb_instance, sb_chan_input_port_id, + chan_input_port.pins()[pin_id]); + } } std::string chan_output_port_name = generate_sb_module_track_port_name( @@ -638,21 +650,162 @@ static int build_tile_module_port_and_nets_between_sb_and_cb(ModuleManager& modu BasicPort chan_output_port = module_manager.module_port(sb_module_id, sb_chan_output_port_id); ModulePortId tile_chan_output_port_id = module_manager.add_port(tile_module, chan_output_port, ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < chan_output_port.pins().size(); ++pin_id) { - ModuleNetId net = create_module_source_pin_net( - module_manager, tile_module, sb_module_id, sb_instance, sb_chan_output_port_id, chan_output_port.pins()[pin_id]); - /* Configure the net sink */ - module_manager.add_module_net_sink(tile_module, net, tile_module, - 0, tile_chan_output_port_id, - chan_output_port.pins()[pin_id]); + if (!frame_view) { + for (size_t pin_id = 0; pin_id < chan_output_port.pins().size(); ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, sb_module_id, sb_instance, sb_chan_output_port_id, chan_output_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, tile_module, + 0, tile_chan_output_port_id, + chan_output_port.pins()[pin_id]); + } } } } return CMD_EXEC_SUCCESS; } -static int build_tile_port_and_nets_from_pb() { +/******************************************************************** + * This function will create nets for the unconnected pins for a programmable block in a tile + * This function should be called after the following functions: + * - build_tile_module_port_and_nets_between_sb_and_pb() + * - build_tile_module_port_and_nets_between_cb_and_pb() + * - build_tile_module_port_and_nets_between_sb_and_cb() + * The functions above build nets/connections between the current programmable block and other routing blocks. + * However, it could happen that the programmable block is the only submodule under the tile. + * Also, it could happen that the programmable block drives or is driven by other blocks from another tile. + * As a result, we should build the nets for these unconnected pins + * + * +------------+ + * | |-->grid_xxx + * | Grid | + * | [x][y] |<--grid_xxx + * | | + * +------------+ + ********************************************************************/ +static int build_tile_port_and_nets_from_pb(ModuleManager& module_manager, + const ModuleId& tile_module, + const DeviceGrid& grids, + const VprDeviceAnnotation& vpr_device_annotation, + const vtr::Point& pb_coord, + const size_t& pb_instance, + const bool& frame_view, + const bool& verbose) { + t_physical_tile_type_ptr phy_tile = + grids.get_physical_type(pb_coord.x(), pb_coord.y()); + /* Empty type does not require a module */ + if (is_empty_type(phy_tile)) { + return CMD_EXEC_SUCCESS; + } + e_side grid_side = find_grid_border_side(vtr::Point(grids.width, grids.height()), pb_coord); + std::string pb_module_name = generate_grid_block_module_name( + std::string(GRID_MODULE_NAME_PREFIX), std::string(phy_tile->name), + is_io_type(phy_tile), grid_side); + ModuleId pb_module = module_manager.find_module(pb_module_name); + if (!pb_module) { + VTR_LOG_ERROR( + "Failed to find pb module '%s' required by tile[%lu][%lu]!\n", + pb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + return CMD_EXEC_FATAL_ERROR; + } + /* Find the pin side for I/O grids*/ + std::vector grid_pin_sides; + /* For I/O grids, we care only one side + * Otherwise, we will iterate all the 4 sides + */ + if (true == is_io_type(phy_tile)) { + grid_pin_sides = + find_grid_module_pin_sides(phy_tile, grid_side); + } else { + grid_pin_sides = {TOP, RIGHT, BOTTOM, LEFT}; + } + + /* Create a map between pin class type and grid pin direction */ + std::map pin_type2type_map; + pin_type2type_map[RECEIVER] = ModuleManager::MODULE_INPUT_PORT; + pin_type2type_map[DRIVER] = ModuleManager::MODULE_OUTPUT_PORT; + + /* Iterate over sides, height and pins */ + for (const e_side& side : grid_pin_sides) { + for (int iwidth = 0; iwidth < phy_tile->width; ++iwidth) { + for (int iheight = 0; iheight < phy_tile->height; ++iheight) { + for (int ipin = 0; ipin < phy_tile->num_pins; ++ipin) { + if (!phy_tile->pinloc[iwidth][iheight][side][ipin]) { + continue; + } + /* Reach here, it means this pin is on this side */ + int class_id = phy_tile->pin_class[ipin]; + e_pin_type pin_class_type = + phy_tile->class_inf[class_id].type; + /* Generate the pin name, + * we give a empty coordinate but it will not be used (see details in + * the function + */ + BasicPort pin_info = + vpr_device_annotation.physical_tile_pin_port_info( + phy_tile, ipin); + VTR_ASSERT(true == pin_info.is_valid()); + int subtile_index = + vpr_device_annotation.physical_tile_pin_subtile_index( + phy_tile, ipin); + VTR_ASSERT(OPEN != subtile_index && + subtile_index < phy_tile->capacity); + std::string port_name = generate_grid_port_name( + iwidth, iheight, subtile_index, side, pin_info); + BasicPort pb_port(port_name, 0, 0); + ModulePortId pb_module_port_id = module_manager.find_port(pb_module, port_name); + if (!module_manager.valid_module_port_id(pb_module, pb_module_port_id)) { + VTR_LOG_ERROR( + "Failed to find port '%s' for pb module '%s' required by tile[%lu][%lu]!\n", + pb_port.to_verilog_string().c_str(), pb_module_name.c_str(), pb_coord.x(), pb_coord.y()); + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the port from the pb module and see if it is already been driven or driving a net. if not, create a new port at the tile module */ + if (module_manager.port_type(pb_module, pb_module_port_id) == ModuleManager::e_module_port_type::MODULE_INPUT_PORT) { + for (size_t ipin = 0; ipin < pb_port.pins().size(); ++ipin) { + if (module_manager.net_sink_exist(tile_module, pb_module, pb_instance, pb_module_port_id, pb_port.pins()[ipin])) { + continue; + } + /* Create a new port and a new net */ + ModulePortId tile_module_port_id = module_manager.add_port(tile_module, pb_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); + if (!frame_view) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, tile_module, 0, tile_module_port_id, pb_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, pb_module, + pb_instance, pb_module_port_id, + pb_port.pins()[pin_id]); + } + } + } else if (module_manager.port_type(pb_module, pb_module_port_id) == ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT) { + for (size_t ipin = 0; ipin < pb_port.pins().size(); ++ipin) { + if (module_manager.net_source_exist(tile_module, pb_module, pb_instance, pb_module_port_id, pb_port.pins()[ipin])) { + continue; + } + /* Create a new port and a new net */ + ModulePortId tile_module_port_id = module_manager.add_port(tile_module, pb_port, ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); + if (!frame_view) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, pb_module, pb_instance, pb_module_port_id, pb_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, tile_module, + 0, tile_module_port_id, + pb_port.pins()[pin_id]); + } + } + } else { + VTR_LOG_ERROR( + "Expect either input or output port '%s' for pb module '%s' required by tile[%lu][%lu]!\n", + grid_port.to_verilog_string().c_str(), pb_module_name.c_str(), pb_coord.x(), pb_coord.y()); + return CMD_EXEC_FATAL_ERROR; + } + } + } + } + } + return CMD_EXEC_SUCCESS; } /******************************************************************** @@ -676,7 +829,7 @@ static int build_tile_module_ports_and_nets(ModuleManager& module_manager, const bool& verbose) { int status_code = CMD_EXEC_SUCCESS; - /* TODO: Get the submodule of Switch blocks one by one, build connections between sb and pb */ + /* Get the submodule of Switch blocks one by one, build connections between sb and pb */ for (size_t isb = 0; isb < fabric_tile.sb_coordinates(fabric_tile_id).size(); ++isb) { vtr::Point sb_coord = fabric_tile.sb_coordinates(fabric_tile_id)[isb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); @@ -696,7 +849,7 @@ static int build_tile_module_ports_and_nets(ModuleManager& module_manager, return CMD_EXEC_FATAL_ERROR; } } - /* TODO: Get the submodule of connection blocks one by one, build connections between cb and pb */ + /* Get the submodule of connection blocks one by one, build connections between cb and pb */ for (t_rr_type cb_type : {CHANX, CHANY}) { for (size_t icb = 0; icb < fabric_tile.cb_coordinates(fabric_tile_id, cb_type).size(); ++icb) { vtr::Point cb_coord = fabric_tile.cb_coordinates(fabric_tile_id, cb_type)[icb]; @@ -718,7 +871,7 @@ static int build_tile_module_ports_and_nets(ModuleManager& module_manager, } } } - /* TODO: Get the submodule of connection blocks one by one, build connections between sb and cb */ + /* Get the submodule of connection blocks one by one, build connections between sb and cb */ for (size_t isb = 0; isb < fabric_tile.sb_coordinates(fabric_tile_id).size(); ++isb) { vtr::Point sb_coord = fabric_tile.sb_coordinates(fabric_tile_id)[isb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); @@ -739,7 +892,15 @@ static int build_tile_module_ports_and_nets(ModuleManager& module_manager, } } /* TODO: Create the ports from pb which only connects to adjacent sb and cbs, as well as pb */ - status_code = build_tile_port_and_nets_from_pb(); + for (size_t ipb = 0; ipb < fabric_tile.pb_coordinates(fabric_tile_id).size(); ++ipb) { + vtr::Point pb_coord = fabric_tile.pb_coordinates(fabric_tile_id)[ipb]; + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(pb_coord); + status_code = build_tile_port_and_nets_from_pb(module_manager, tile_module, pb_coord, pb_instances[ipb], frame_view, verbose); + if (status_code != CMD_EXEC_SUCCESS) { + + return CMD_EXEC_FATAL_ERROR; + } + } return status_code; } From 5ae146bd8682a0b172f6bf8477baa1f88f29f811 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 18 Jul 2023 21:17:40 -0700 Subject: [PATCH 179/391] [core] finish up tile module builder --- openfpga/src/annotation/fabric_tile.cpp | 26 +- openfpga/src/annotation/fabric_tile.h | 32 +- openfpga/src/fabric/build_device_module.cpp | 12 +- openfpga/src/fabric/build_tile_modules.cpp | 611 +++++++++++--------- openfpga/src/fabric/build_tile_modules.h | 9 +- 5 files changed, 395 insertions(+), 295 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index 679ee0d1b..0e83e0734 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -72,11 +72,13 @@ FabricTileId FabricTile::find_tile(const vtr::Point& coord) const { return tile_coord2id_lookup_[coord.x()][coord.y()]; } -bool FabricTile::pb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { +bool FabricTile::pb_in_tile(const FabricTileId& tile_id, + const vtr::Point& coord) const { return find_pb_index_in_tile(tile_id, coord) != pb_coords_.size(); } -size_t FabricTile::find_pb_index_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { +size_t FabricTile::find_pb_index_in_tile( + const FabricTileId& tile_id, const vtr::Point& coord) const { VTR_ASSERT(valid_tile_id(tile_id)); for (size_t idx = 0; idx < pb_coords_[tile_id].size(); ++idx) { vtr::Point curr_coord = pb_coords_[tile_id][idx]; @@ -88,11 +90,13 @@ size_t FabricTile::find_pb_index_in_tile(const FabricTileId& tile_id, const vtr: return pb_coords_.size(); } -bool FabricTile::sb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { +bool FabricTile::sb_in_tile(const FabricTileId& tile_id, + const vtr::Point& coord) const { return find_sb_index_in_tile(tile_id, coord) != sb_coords_.size(); } -size_t FabricTile::find_sb_index_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { +size_t FabricTile::find_sb_index_in_tile( + const FabricTileId& tile_id, const vtr::Point& coord) const { VTR_ASSERT(valid_tile_id(tile_id)); for (size_t idx = 0; idx < sb_coords_[tile_id].size(); ++idx) { vtr::Point curr_coord = sb_coords_[tile_id][idx]; @@ -104,19 +108,25 @@ size_t FabricTile::find_sb_index_in_tile(const FabricTileId& tile_id, const vtr: return sb_coords_.size(); } -bool FabricTile::cb_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord) const { +bool FabricTile::cb_in_tile(const FabricTileId& tile_id, + const t_rr_type& cb_type, + const vtr::Point& coord) const { switch (cb_type) { case CHANX: - return find_cb_index_in_tile(tile_id, cb_type, coord) == cbx_coords_.size(); + return find_cb_index_in_tile(tile_id, cb_type, coord) == + cbx_coords_.size(); case CHANY: - return find_cb_index_in_tile(tile_id, cb_type, coord) == cby_coords_.size(); + return find_cb_index_in_tile(tile_id, cb_type, coord) == + cby_coords_.size(); default: VTR_LOG("Invalid type of connection block!\n"); exit(1); } } -size_t FabricTile::find_cb_index_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord) const { +size_t FabricTile::find_cb_index_in_tile( + const FabricTileId& tile_id, const t_rr_type& cb_type, + const vtr::Point& coord) const { VTR_ASSERT(valid_tile_id(tile_id)); switch (cb_type) { case CHANX: diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h index 0cc5ab24f..467852b38 100644 --- a/openfpga/src/annotation/fabric_tile.h +++ b/openfpga/src/annotation/fabric_tile.h @@ -41,18 +41,30 @@ class FabricTile { FabricTileId find_tile(const vtr::Point& coord) const; /** @brief Return a list of unique tiles */ std::vector unique_tiles() const; - /** @brief Find the index of a programmable block in the internal list by a given coordinate. */ - size_t find_pb_index_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const; - /** @brief Find the index of a switch block in the internal list by a given coordinate. */ - size_t find_sb_index_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const; - /** @brief Find the index of a connection block in the internal list by a given coordinate. */ - size_t find_cb_index_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord) const; - /** @brief Check if a programmable block (with a coordinate) exists in a tile */ - bool pb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const; + /** @brief Find the index of a programmable block in the internal list by a + * given coordinate. */ + size_t find_pb_index_in_tile(const FabricTileId& tile_id, + const vtr::Point& coord) const; + /** @brief Find the index of a switch block in the internal list by a given + * coordinate. */ + size_t find_sb_index_in_tile(const FabricTileId& tile_id, + const vtr::Point& coord) const; + /** @brief Find the index of a connection block in the internal list by a + * given coordinate. */ + size_t find_cb_index_in_tile(const FabricTileId& tile_id, + const t_rr_type& cb_type, + const vtr::Point& coord) const; + /** @brief Check if a programmable block (with a coordinate) exists in a tile + */ + bool pb_in_tile(const FabricTileId& tile_id, + const vtr::Point& coord) const; /** @brief Check if a switch block (with a coordinate) exists in a tile */ - bool sb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const; + bool sb_in_tile(const FabricTileId& tile_id, + const vtr::Point& coord) const; /** @brief Check if a connection block (with a coordinate) exists in a tile */ - bool cb_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord) const; + bool cb_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type, + const vtr::Point& coord) const; + public: /* Mutators */ FabricTileId create_tile(const vtr::Point& coord); bool set_tile_coordinate(const FabricTileId& tile_id, diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index 9aca51353..cb8f5e4cd 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -112,12 +112,12 @@ int build_device_module_graph( if (CMD_EXEC_FATAL_ERROR == status) { return status; } - /* TODO: Build the modules */ - build_tile_modules(module_manager, fabric_tile, vpr_device_ctx.grid, - openfpga_ctx.vpr_device_annotation(), - openfpga_ctx.device_rr_gsb(), - openfpga_ctx.arch().circuit_lib, sram_model, - openfpga_ctx.arch().config_protocol.type(), frame_view, verbose); + /* Build the modules */ + build_tile_modules( + module_manager, decoder_lib, fabric_tile, vpr_device_ctx.grid, + openfpga_ctx.vpr_device_annotation(), openfpga_ctx.device_rr_gsb(), + vpr_device_ctx.rr_graph, openfpga_ctx.arch().circuit_lib, sram_model, + openfpga_ctx.arch().config_protocol.type(), frame_view, verbose); } /* Build FPGA fabric top-level module */ diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index e85c8ab2a..08b9a4ba2 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -4,21 +4,25 @@ * It helps OpenFPGA to link the I/O port index in top-level module * to the VPR I/O mapping results *******************************************************************/ +#include "build_tile_modules.h" + #include #include -/* Headers from vtrutil library */ +#include "build_grid_module_utils.h" +#include "build_routing_module_utils.h" +#include "build_top_module_utils.h" #include "command_exit_codes.h" -#include "vtr_assert.h" -#include "vtr_log.h" -#include "vtr_time.h" - -/* Headers from vpr library */ -#include "build_tile_modules.h" #include "module_manager_utils.h" #include "openfpga_device_grid_utils.h" #include "openfpga_naming.h" #include "openfpga_reserved_words.h" +#include "openfpga_rr_graph_utils.h" +#include "openfpga_side_manager.h" +#include "rr_gsb_utils.h" +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" /* begin namespace openfpga */ namespace openfpga { @@ -54,18 +58,14 @@ namespace openfpga { * +------------+ +------------+ * *******************************************************************/ -static int build_tile_module_port_and_nets_between_sb_and_pb(ModuleManager& module_manager, - const ModuleId& tile_module, - const VprDeviceAnnotation& vpr_device_annotation, - const DeviceRRGSB& device_rr_gsb, - const RRGSB& rr_gsb, - const FabricTile& fabric_tile, - const FabricTileId& fabric_tile_id, - const size_t& pb_instances, - const size_t& sb_instance, - const bool& compress_routing_hierarchy, - const bool& frame_view, - const bool& verbose) { +static int build_tile_module_port_and_nets_between_sb_and_pb( + ModuleManager& module_manager, const ModuleId& tile_module, + const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, + const RRGSB& rr_gsb, const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id, const std::vector& pb_instances, + const size_t& sb_instance, const bool& compact_routing_hierarchy, + const bool& frame_view, const bool& verbose) { /* Skip those Switch blocks that do not exist */ if (false == rr_gsb.is_sb_exist()) { return CMD_EXEC_SUCCESS; @@ -147,56 +147,69 @@ static int build_tile_module_port_and_nets_between_sb_and_pb(ModuleManager& modu BasicPort src_grid_port = module_manager.module_port(src_grid_module, src_grid_port_id); + /* Collect sink-related information */ + vtr::Point sink_sb_port_coord( + rr_graph.node_xlow( + module_sb.get_opin_node(side_manager.get_side(), inode)), + rr_graph.node_ylow( + module_sb.get_opin_node(side_manager.get_side(), inode))); + std::string sink_sb_port_name = generate_sb_module_grid_port_name( + side_manager.get_side(), + get_rr_graph_single_node_side( + rr_graph, module_sb.get_opin_node(side_manager.get_side(), inode)), + grids, vpr_device_annotation, rr_graph, + module_sb.get_opin_node(side_manager.get_side(), inode)); + ModulePortId sink_sb_port_id = + module_manager.find_module_port(sink_sb_module, sink_sb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(sink_sb_module, + sink_sb_port_id)); + BasicPort sink_sb_port = + module_manager.module_port(sink_sb_module, sink_sb_port_id); + /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.pb_in_tile(grid_coordinate)) { + if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { if (!frame_view) { - size_t src_grid_instance = grid_instances[fabric_tile.find_pb_index(fabric_tile_id, grid_coordinate)]; - /* Collect sink-related information */ - vtr::Point sink_sb_port_coord( - rr_graph.node_xlow( - module_sb.get_opin_node(side_manager.get_side(), inode)), - rr_graph.node_ylow( - module_sb.get_opin_node(side_manager.get_side(), inode))); - std::string sink_sb_port_name = generate_sb_module_grid_port_name( - side_manager.get_side(), - get_rr_graph_single_node_side( - rr_graph, module_sb.get_opin_node(side_manager.get_side(), inode)), - grids, vpr_device_annotation, rr_graph, - module_sb.get_opin_node(side_manager.get_side(), inode)); - ModulePortId sink_sb_port_id = - module_manager.find_module_port(sink_sb_module, sink_sb_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id(sink_sb_module, - sink_sb_port_id)); - BasicPort sink_sb_port = - module_manager.module_port(sink_sb_module, sink_sb_port_id); + size_t src_grid_instance = + pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, + grid_coordinate)]; /* Source and sink port should match in size */ VTR_ASSERT(src_grid_port.get_width() == sink_sb_port.get_width()); /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < src_grid_port.pins().size(); ++pin_id) { + for (size_t pin_id = 0; pin_id < src_grid_port.pins().size(); + ++pin_id) { ModuleNetId net = create_module_source_pin_net( module_manager, tile_module, src_grid_module, src_grid_instance, src_grid_port_id, src_grid_port.pins()[pin_id]); /* Configure the net sink */ - module_manager.add_module_net_sink(tile_module, net, sink_sb_module, - sink_sb_instance, sink_sb_port_id, - sink_sb_port.pins()[pin_id]); + module_manager.add_module_net_sink( + tile_module, net, sink_sb_module, sink_sb_instance, + sink_sb_port_id, sink_sb_port.pins()[pin_id]); } } } else { - /* Create a port on the tile module and create the net if required */ - ModulePortId src_tile_port_id = module_manager.add_port(tile_module, src_grid_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); + /* Create a port on the tile module and create the net if required. + * FIXME: Create a proper name to avoid naming conflicts */ + ModulePortId src_tile_port_id = module_manager.add_port( + tile_module, src_grid_port, + ModuleManager::e_module_port_type::MODULE_INPUT_PORT); + VTR_LOGV( + verbose, + "Adding ports '%s' to tile as required by the switch block '%s'...\n", + src_grid_port.to_verilog_string().c_str(), + sink_sb_module_name.c_str()); if (!frame_view) { /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < src_grid_port.pins().size(); ++pin_id) { + for (size_t pin_id = 0; pin_id < src_grid_port.pins().size(); + ++pin_id) { ModuleNetId net = create_module_source_pin_net( - module_manager, tile_module, tile_module, 0, - src_tile_port_id, src_grid_port.pins()[pin_id]); + module_manager, tile_module, tile_module, 0, src_tile_port_id, + src_grid_port.pins()[pin_id]); /* Configure the net sink */ - module_manager.add_module_net_sink(tile_module, net, sink_sb_module, - sink_sb_instance, sink_sb_port_id, - sink_sb_port.pins()[pin_id]); + module_manager.add_module_net_sink( + tile_module, net, sink_sb_module, sink_sb_instance, + sink_sb_port_id, sink_sb_port.pins()[pin_id]); } } } @@ -260,19 +273,15 @@ static int build_tile_module_port_and_nets_between_sb_and_pb(ModuleManager& modu * +------------+ +------------------+ * *******************************************************************/ -static int build_tile_module_port_and_nets_between_cb_and_pb(ModuleManager& module_manager, - const ModuleId& tile_module, - const VprDeviceAnnotation& vpr_device_annotation, - const DeviceRRGSB& device_rr_gsb, - const RRGSB& rr_gsb, - const FabricTile& fabric_tile, - const FabricTileId& fabric_tile_id, - const t_rr_type& cb_type, - const std::vector& pb_instances, - const size_t& cb_instance, - const bool& compress_routing_hierarchy, - const bool& frame_view, - const bool& verbose) { +static int build_tile_module_port_and_nets_between_cb_and_pb( + ModuleManager& module_manager, const ModuleId& tile_module, + const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, + const RRGSB& rr_gsb, const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id, const t_rr_type& cb_type, + const std::vector& pb_instances, const size_t& cb_instance, + const bool& compact_routing_hierarchy, const bool& frame_view, + const bool& verbose) { /* We could have two different coordinators, one is the instance, the other is * the module */ vtr::Point instance_cb_coordinate(rr_gsb.get_cb_x(cb_type), @@ -281,12 +290,12 @@ static int build_tile_module_port_and_nets_between_cb_and_pb(ModuleManager& modu /* Skip those Connection blocks that do not exist */ if (false == rr_gsb.is_cb_exist(cb_type)) { - return; + return CMD_EXEC_SUCCESS; } /* Skip if the cb does not contain any configuration bits! */ if (true == connection_block_contain_only_routing_tracks(rr_gsb, cb_type)) { - return; + return CMD_EXEC_SUCCESS; } /* If we use compact routing hierarchy, we should find the unique module of @@ -375,16 +384,18 @@ static int build_tile_module_port_and_nets_between_cb_and_pb(ModuleManager& modu module_manager.module_port(sink_grid_module, sink_grid_port_id); /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.pb_in_tile(grid_coordinate)) { + if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { if (!frame_view) { size_t sink_grid_instance = - grid_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, grid_coordinate)]; + pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, + grid_coordinate)]; /* Source and sink port should match in size */ VTR_ASSERT(src_cb_port.get_width() == sink_grid_port.get_width()); /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); ++pin_id) { + for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); + ++pin_id) { ModuleNetId net = create_module_source_pin_net( module_manager, tile_module, src_cb_module, src_cb_instance, src_cb_port_id, src_cb_port.pins()[pin_id]); @@ -395,16 +406,26 @@ static int build_tile_module_port_and_nets_between_cb_and_pb(ModuleManager& modu } } } else { - /* Create a port on the tile module and create the net if required */ - ModulePortId sink_tile_port_id = module_manager.add_port(tile_module, src_cb_port, ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); + /* Create a port on the tile module and create the net if required. + * FIXME: Create a proper name to avoid naming conflicts */ + ModulePortId sink_tile_port_id = module_manager.add_port( + tile_module, src_cb_port, + ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); + VTR_LOGV(verbose, + "Adding ports '%s' to tile as required by the connection " + "block '%s'...\n", + src_cb_port.to_verilog_string().c_str(), + src_cb_module_name.c_str()); if (!frame_view) { /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); ++pin_id) { + for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); + ++pin_id) { ModuleNetId net = create_module_source_pin_net( - module_manager, tile_module, src_cb_module, src_cb_instance, src_tile_port_id, src_cb_port.pins()[pin_id]); + module_manager, tile_module, src_cb_module, src_cb_instance, + sink_tile_port_id, src_cb_port.pins()[pin_id]); /* Configure the net sink */ - module_manager.add_module_net_sink(tile_module, net, tile_module, - 0, sink_tile_port_id, + module_manager.add_module_net_sink(tile_module, net, tile_module, 0, + sink_tile_port_id, src_cb_port.pins()[pin_id]); } } @@ -453,18 +474,14 @@ static int build_tile_module_port_and_nets_between_cb_and_pb(ModuleManager& modu * connection blocks * *******************************************************************/ -static int build_tile_module_port_and_nets_between_sb_and_cb(ModuleManager& module_manager, - const ModuleId& tile_module, - const VprDeviceAnnotation& vpr_device_annotation, - const DeviceRRGSB& device_rr_gsb, - const RRGSB& rr_gsb, - const FabricTile& fabric_tile, - const FabricTileId& fabric_tile_id, - const std::map>& cb_instances, - const size_t& sb_instance, - const bool& compress_routing_hierarchy, - const bool& frame_view, - const bool& verbose) { +static int build_tile_module_port_and_nets_between_sb_and_cb( + ModuleManager& module_manager, const ModuleId& tile_module, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, + const RRGSB& rr_gsb, const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id, + const std::map>& cb_instances, + const size_t& sb_instance, const bool& compact_routing_hierarchy, + const bool& frame_view, const bool& verbose) { /* We could have two different coordinators, one is the instance, the other is * the module */ vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), @@ -473,7 +490,7 @@ static int build_tile_module_port_and_nets_between_sb_and_cb(ModuleManager& modu /* Skip those Switch blocks that do not exist */ if (false == rr_gsb.is_sb_exist()) { - return; + return CMD_EXEC_SUCCESS; } /* If we use compact routing hierarchy, we should find the unique module of @@ -558,12 +575,16 @@ static int build_tile_module_port_and_nets_between_sb_and_cb(ModuleManager& modu instance_cb.get_cb_y(cb_type)); /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.cb_in_tile(fabric_tile_id, cb_type, instance_cb_coordinate)) { + if (fabric_tile.cb_in_tile(fabric_tile_id, cb_type, + instance_cb_coordinate)) { if (!frame_view) { - size_t cb_instance = cb_instances.at(cb_type)[fabric_tile.find_cb_index_in_tile(fabric_tile_id, cb_type, instance_cb_coordinate)]; + size_t cb_instance = + cb_instances.at(cb_type)[fabric_tile.find_cb_index_in_tile( + fabric_tile_id, cb_type, instance_cb_coordinate)]; for (size_t itrack = 0; - itrack < module_sb.get_chan_width(side_manager.get_side()); ++itrack) { + itrack < module_sb.get_chan_width(side_manager.get_side()); + ++itrack) { std::string sb_port_name = generate_sb_module_track_port_name( rr_graph.node_type( module_sb.get_chan_node(side_manager.get_side(), itrack)), @@ -572,54 +593,58 @@ static int build_tile_module_port_and_nets_between_sb_and_cb(ModuleManager& modu /* Prepare SB-related port information */ ModulePortId sb_port_id = module_manager.find_module_port(sb_module_id, sb_port_name); - VTR_ASSERT(true == - module_manager.valid_module_port_id(sb_module_id, sb_port_id)); - BasicPort sb_port = module_manager.module_port(sb_module_id, sb_port_id); + VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module_id, + sb_port_id)); + BasicPort sb_port = + module_manager.module_port(sb_module_id, sb_port_id); /* Prepare CB-related port information */ PORTS cb_port_direction = OUT_PORT; /* The cb port direction should be opposite to the sb port !!! */ - if (OUT_PORT == - module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { + if (OUT_PORT == module_sb.get_chan_node_direction( + side_manager.get_side(), itrack)) { cb_port_direction = IN_PORT; } else { VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( side_manager.get_side(), itrack)); } - /* Upper CB port is required if the routing tracks are on the top or right - * sides of the switch block, which indicated bottom and left sides of the - * connection blocks + /* Upper CB port is required if the routing tracks are on the top or + * right sides of the switch block, which indicated bottom and left + * sides of the connection blocks */ - bool use_cb_upper_port = - (TOP == side_manager.get_side()) || (RIGHT == side_manager.get_side()); + bool use_cb_upper_port = (TOP == side_manager.get_side()) || + (RIGHT == side_manager.get_side()); std::string cb_port_name = generate_cb_module_track_port_name( cb_type, cb_port_direction, use_cb_upper_port); ModulePortId cb_port_id = module_manager.find_module_port(cb_module_id, cb_port_name); - VTR_ASSERT(true == - module_manager.valid_module_port_id(cb_module_id, cb_port_id)); - BasicPort cb_port = module_manager.module_port(cb_module_id, cb_port_id); + VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module_id, + cb_port_id)); + BasicPort cb_port = + module_manager.module_port(cb_module_id, cb_port_id); /* Configure the net source and sink: * If sb port is an output (source), cb port is an input (sink) * If sb port is an input (sink), cb port is an output (source) */ - if (OUT_PORT == - module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { - ModuleNetId net = - create_module_source_pin_net(module_manager, tile_module, sb_module_id, - sb_instance, sb_port_id, itrack / 2); + if (OUT_PORT == module_sb.get_chan_node_direction( + side_manager.get_side(), itrack)) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, sb_module_id, sb_instance, + sb_port_id, itrack / 2); module_manager.add_module_net_sink(tile_module, net, cb_module_id, - cb_instance, cb_port_id, itrack / 2); + cb_instance, cb_port_id, + itrack / 2); } else { VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( side_manager.get_side(), itrack)); - ModuleNetId net = - create_module_source_pin_net(module_manager, tile_module, cb_module_id, - cb_instance, cb_port_id, itrack / 2); + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, cb_module_id, cb_instance, + cb_port_id, itrack / 2); module_manager.add_module_net_sink(tile_module, net, sb_module_id, - sb_instance, sb_port_id, itrack / 2); + sb_instance, sb_port_id, + itrack / 2); } } } @@ -627,36 +652,58 @@ static int build_tile_module_port_and_nets_between_sb_and_cb(ModuleManager& modu /* Create input and output ports */ std::string chan_input_port_name = generate_sb_module_track_port_name( cb_type, side_manager.get_side(), IN_PORT); - /* Create a port on the tile module and create the net if required */ - ModulePortId sb_chan_input_port_id = module_manager.find_module_port(sb_module_id, chan_input_port_name); - BasicPort chan_input_port = module_manager.module_port(sb_module_id, sb_chan_input_port_id); - ModulePortId tile_chan_input_port_id = module_manager.add_port(tile_module, chan_input_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); + /* Create a port on the tile module and create the net if required. FIXME: + * Create a proper name to avoid naming conflicts */ + ModulePortId sb_chan_input_port_id = + module_manager.find_module_port(sb_module_id, chan_input_port_name); + BasicPort chan_input_port = + module_manager.module_port(sb_module_id, sb_chan_input_port_id); + ModulePortId tile_chan_input_port_id = module_manager.add_port( + tile_module, chan_input_port, + ModuleManager::e_module_port_type::MODULE_INPUT_PORT); + VTR_LOGV( + verbose, + "Adding ports '%s' to tile as required by the switch block '%s'...\n", + chan_input_port.to_verilog_string().c_str(), sb_module_name.c_str()); /* Create a net for each pin */ if (!frame_view) { - for (size_t pin_id = 0; pin_id < chan_input_port.pins().size(); ++pin_id) { + for (size_t pin_id = 0; pin_id < chan_input_port.pins().size(); + ++pin_id) { ModuleNetId net = create_module_source_pin_net( - module_manager, tile_module, tile_module, 0, tile_chan_input_port_id, chan_input_port.pins()[pin_id]); + module_manager, tile_module, tile_module, 0, + tile_chan_input_port_id, chan_input_port.pins()[pin_id]); /* Configure the net sink */ module_manager.add_module_net_sink(tile_module, net, sb_module_id, sb_instance, sb_chan_input_port_id, chan_input_port.pins()[pin_id]); } } - + std::string chan_output_port_name = generate_sb_module_track_port_name( cb_type, side_manager.get_side(), OUT_PORT); - /* Create a port on the tile module and create the net if required */ - ModulePortId sb_chan_output_port_id = module_manager.find_module_port(sb_module_id, chan_output_port_name); - BasicPort chan_output_port = module_manager.module_port(sb_module_id, sb_chan_output_port_id); - ModulePortId tile_chan_output_port_id = module_manager.add_port(tile_module, chan_output_port, ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); + /* Create a port on the tile module and create the net if required. FIXME: + * Create a proper name to avoid naming conflicts */ + ModulePortId sb_chan_output_port_id = + module_manager.find_module_port(sb_module_id, chan_output_port_name); + BasicPort chan_output_port = + module_manager.module_port(sb_module_id, sb_chan_output_port_id); + ModulePortId tile_chan_output_port_id = module_manager.add_port( + tile_module, chan_output_port, + ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); + VTR_LOGV( + verbose, + "Adding ports '%s' to tile as required by the switch block '%s'...\n", + chan_output_port.to_verilog_string().c_str(), sb_module_name.c_str()); /* Create a net for each pin */ if (!frame_view) { - for (size_t pin_id = 0; pin_id < chan_output_port.pins().size(); ++pin_id) { + for (size_t pin_id = 0; pin_id < chan_output_port.pins().size(); + ++pin_id) { ModuleNetId net = create_module_source_pin_net( - module_manager, tile_module, sb_module_id, sb_instance, sb_chan_output_port_id, chan_output_port.pins()[pin_id]); + module_manager, tile_module, sb_module_id, sb_instance, + sb_chan_output_port_id, chan_output_port.pins()[pin_id]); /* Configure the net sink */ - module_manager.add_module_net_sink(tile_module, net, tile_module, - 0, tile_chan_output_port_id, + module_manager.add_module_net_sink(tile_module, net, tile_module, 0, + tile_chan_output_port_id, chan_output_port.pins()[pin_id]); } } @@ -666,16 +713,17 @@ static int build_tile_module_port_and_nets_between_sb_and_cb(ModuleManager& modu } /******************************************************************** - * This function will create nets for the unconnected pins for a programmable block in a tile - * This function should be called after the following functions: + * This function will create nets for the unconnected pins for a programmable + *block in a tile This function should be called after the following functions: * - build_tile_module_port_and_nets_between_sb_and_pb() * - build_tile_module_port_and_nets_between_cb_and_pb() * - build_tile_module_port_and_nets_between_sb_and_cb() - * The functions above build nets/connections between the current programmable block and other routing blocks. - * However, it could happen that the programmable block is the only submodule under the tile. - * Also, it could happen that the programmable block drives or is driven by other blocks from another tile. - * As a result, we should build the nets for these unconnected pins - * + * The functions above build nets/connections between the current programmable + *block and other routing blocks. However, it could happen that the programmable + *block is the only submodule under the tile. Also, it could happen that the + *programmable block drives or is driven by other blocks from another tile. As a + *result, we should build the nets for these unconnected pins + * * +------------+ * | |-->grid_xxx * | Grid | @@ -683,29 +731,26 @@ static int build_tile_module_port_and_nets_between_sb_and_cb(ModuleManager& modu * | | * +------------+ ********************************************************************/ -static int build_tile_port_and_nets_from_pb(ModuleManager& module_manager, - const ModuleId& tile_module, - const DeviceGrid& grids, - const VprDeviceAnnotation& vpr_device_annotation, - const vtr::Point& pb_coord, - const size_t& pb_instance, - const bool& frame_view, - const bool& verbose) { +static int build_tile_port_and_nets_from_pb( + ModuleManager& module_manager, const ModuleId& tile_module, + const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const vtr::Point& pb_coord, const size_t& pb_instance, + const bool& frame_view, const bool& verbose) { t_physical_tile_type_ptr phy_tile = grids.get_physical_type(pb_coord.x(), pb_coord.y()); /* Empty type does not require a module */ if (is_empty_type(phy_tile)) { return CMD_EXEC_SUCCESS; } - e_side grid_side = find_grid_border_side(vtr::Point(grids.width, grids.height()), pb_coord); + e_side grid_side = find_grid_border_side( + vtr::Point(grids.width(), grids.height()), pb_coord); std::string pb_module_name = generate_grid_block_module_name( std::string(GRID_MODULE_NAME_PREFIX), std::string(phy_tile->name), is_io_type(phy_tile), grid_side); ModuleId pb_module = module_manager.find_module(pb_module_name); if (!pb_module) { - VTR_LOG_ERROR( - "Failed to find pb module '%s' required by tile[%lu][%lu]!\n", - pb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + VTR_LOG_ERROR("Failed to find pb module '%s' required by tile[%lu][%lu]!\n", + pb_module_name.c_str(), pb_coord.x(), pb_coord.y()); return CMD_EXEC_FATAL_ERROR; } @@ -715,8 +760,7 @@ static int build_tile_port_and_nets_from_pb(ModuleManager& module_manager, * Otherwise, we will iterate all the 4 sides */ if (true == is_io_type(phy_tile)) { - grid_pin_sides = - find_grid_module_pin_sides(phy_tile, grid_side); + grid_pin_sides = find_grid_module_pin_sides(phy_tile, grid_side); } else { grid_pin_sides = {TOP, RIGHT, BOTTOM, LEFT}; } @@ -735,70 +779,102 @@ static int build_tile_port_and_nets_from_pb(ModuleManager& module_manager, continue; } /* Reach here, it means this pin is on this side */ - int class_id = phy_tile->pin_class[ipin]; - e_pin_type pin_class_type = - phy_tile->class_inf[class_id].type; /* Generate the pin name, * we give a empty coordinate but it will not be used (see details in * the function */ BasicPort pin_info = - vpr_device_annotation.physical_tile_pin_port_info( - phy_tile, ipin); + vpr_device_annotation.physical_tile_pin_port_info(phy_tile, ipin); VTR_ASSERT(true == pin_info.is_valid()); int subtile_index = - vpr_device_annotation.physical_tile_pin_subtile_index( - phy_tile, ipin); + vpr_device_annotation.physical_tile_pin_subtile_index(phy_tile, + ipin); VTR_ASSERT(OPEN != subtile_index && subtile_index < phy_tile->capacity); std::string port_name = generate_grid_port_name( iwidth, iheight, subtile_index, side, pin_info); BasicPort pb_port(port_name, 0, 0); - ModulePortId pb_module_port_id = module_manager.find_port(pb_module, port_name); - if (!module_manager.valid_module_port_id(pb_module, pb_module_port_id)) { + ModulePortId pb_module_port_id = + module_manager.find_module_port(pb_module, port_name); + if (!module_manager.valid_module_port_id(pb_module, + pb_module_port_id)) { VTR_LOG_ERROR( - "Failed to find port '%s' for pb module '%s' required by tile[%lu][%lu]!\n", - pb_port.to_verilog_string().c_str(), pb_module_name.c_str(), pb_coord.x(), pb_coord.y()); + "Failed to find port '%s' for pb module '%s' required by " + "tile[%lu][%lu]!\n", + pb_port.to_verilog_string().c_str(), pb_module_name.c_str(), + pb_coord.x(), pb_coord.y()); return CMD_EXEC_FATAL_ERROR; } - /* Find the port from the pb module and see if it is already been driven or driving a net. if not, create a new port at the tile module */ - if (module_manager.port_type(pb_module, pb_module_port_id) == ModuleManager::e_module_port_type::MODULE_INPUT_PORT) { - for (size_t ipin = 0; ipin < pb_port.pins().size(); ++ipin) { - if (module_manager.net_sink_exist(tile_module, pb_module, pb_instance, pb_module_port_id, pb_port.pins()[ipin])) { + /* Find the port from the pb module and see if it is already been + * driven or driving a net. if not, create a new port at the tile + * module */ + if (module_manager.port_type(pb_module, pb_module_port_id) == + ModuleManager::e_module_port_type::MODULE_INPUT_PORT) { + for (size_t pin_id = 0; pin_id < pb_port.pins().size(); ++pin_id) { + if (module_manager.valid_module_net_id( + tile_module, + module_manager.module_instance_port_net( + tile_module, pb_module, pb_instance, pb_module_port_id, + pb_port.pins()[pin_id]))) { continue; } - /* Create a new port and a new net */ - ModulePortId tile_module_port_id = module_manager.add_port(tile_module, pb_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); + VTR_LOGV(verbose, + "Adding ports '%s' to tile as required by the " + "programmable block '%s'...\n", + pb_port.to_verilog_string().c_str(), + pb_module_name.c_str()); + /* Create a new port and a new net. FIXME: Create a proper name to + * avoid naming conflicts */ + ModulePortId tile_module_port_id = module_manager.add_port( + tile_module, pb_port, + ModuleManager::e_module_port_type::MODULE_INPUT_PORT); if (!frame_view) { ModuleNetId net = create_module_source_pin_net( - module_manager, tile_module, tile_module, 0, tile_module_port_id, pb_port.pins()[pin_id]); + module_manager, tile_module, tile_module, 0, + tile_module_port_id, pb_port.pins()[pin_id]); /* Configure the net sink */ - module_manager.add_module_net_sink(tile_module, net, pb_module, - pb_instance, pb_module_port_id, - pb_port.pins()[pin_id]); + module_manager.add_module_net_sink( + tile_module, net, pb_module, pb_instance, pb_module_port_id, + pb_port.pins()[pin_id]); } } - } else if (module_manager.port_type(pb_module, pb_module_port_id) == ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT) { - for (size_t ipin = 0; ipin < pb_port.pins().size(); ++ipin) { - if (module_manager.net_source_exist(tile_module, pb_module, pb_instance, pb_module_port_id, pb_port.pins()[ipin])) { + } else if (module_manager.port_type(pb_module, pb_module_port_id) == + ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT) { + for (size_t pin_id = 0; pin_id < pb_port.pins().size(); ++pin_id) { + if (module_manager.valid_module_net_id( + tile_module, + module_manager.module_instance_port_net( + tile_module, pb_module, pb_instance, pb_module_port_id, + pb_port.pins()[pin_id]))) { continue; } - /* Create a new port and a new net */ - ModulePortId tile_module_port_id = module_manager.add_port(tile_module, pb_port, ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); + VTR_LOGV(verbose, + "Adding ports '%s' to tile as required by the " + "programmable block '%s'...\n", + pb_port.to_verilog_string().c_str(), + pb_module_name.c_str()); + /* Create a new port and a new net. FIXME: Create a proper name to + * avoid naming conflicts */ + ModulePortId tile_module_port_id = module_manager.add_port( + tile_module, pb_port, + ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); if (!frame_view) { ModuleNetId net = create_module_source_pin_net( - module_manager, tile_module, pb_module, pb_instance, pb_module_port_id, pb_port.pins()[pin_id]); + module_manager, tile_module, pb_module, pb_instance, + pb_module_port_id, pb_port.pins()[pin_id]); /* Configure the net sink */ - module_manager.add_module_net_sink(tile_module, net, tile_module, - 0, tile_module_port_id, - pb_port.pins()[pin_id]); + module_manager.add_module_net_sink( + tile_module, net, tile_module, 0, tile_module_port_id, + pb_port.pins()[pin_id]); } } } else { VTR_LOG_ERROR( - "Expect either input or output port '%s' for pb module '%s' required by tile[%lu][%lu]!\n", - grid_port.to_verilog_string().c_str(), pb_module_name.c_str(), pb_coord.x(), pb_coord.y()); + "Expect either input or output port '%s' for pb module '%s' " + "required by tile[%lu][%lu]!\n", + pb_port.to_verilog_string().c_str(), pb_module_name.c_str(), + pb_coord.x(), pb_coord.y()); return CMD_EXEC_FATAL_ERROR; } } @@ -810,94 +886,85 @@ static int build_tile_port_and_nets_from_pb(ModuleManager& module_manager, /******************************************************************** * Build the ports and associated nets for a tile module - * For each submodule, e.g., programmable block, connection block and switch blocks, - * we walk through their neighbours, starting from sb, cb and then programmable blocks. - * - If a neighbour is one of the submodules under this tile, we can build nets between the submodule and its neighbour. - * - If the neighbour is not under this tile, we should build ports for the tile module and then build nets to connect - * Note that if frame_view is enabled, nets are not built + * For each submodule, e.g., programmable block, connection block and switch + *blocks, we walk through their neighbours, starting from sb, cb and then + *programmable blocks. + * - If a neighbour is one of the submodules under this tile, we can build nets + *between the submodule and its neighbour. + * - If the neighbour is not under this tile, we should build ports for the tile + *module and then build nets to connect Note that if frame_view is enabled, nets + *are not built *******************************************************************/ -static int build_tile_module_ports_and_nets(ModuleManager& module_manager, - const ModuleId& tile_module, - const VprDeviceAnnotation& vpr_device_annotation, - const DeviceRRGSB& device_rr_gsb, - const FabricTile& fabric_tile, - const FabricTileId& fabric_tile_id, - const std::vector& pb_instances, - const std::map>& cb_instances, - const std::vector& sb_instances, - const bool& frame_view, - const bool& verbose) { +static int build_tile_module_ports_and_nets( + ModuleManager& module_manager, const ModuleId& tile_module, + const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph_view, + const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, + const std::vector& pb_instances, + const std::map>& cb_instances, + const std::vector& sb_instances, const bool& frame_view, + const bool& verbose) { int status_code = CMD_EXEC_SUCCESS; - /* Get the submodule of Switch blocks one by one, build connections between sb and pb */ - for (size_t isb = 0; isb < fabric_tile.sb_coordinates(fabric_tile_id).size(); ++isb) { - vtr::Point sb_coord = fabric_tile.sb_coordinates(fabric_tile_id)[isb]; + /* Get the submodule of Switch blocks one by one, build connections between sb + * and pb */ + for (size_t isb = 0; isb < fabric_tile.sb_coordinates(fabric_tile_id).size(); + ++isb) { + vtr::Point sb_coord = + fabric_tile.sb_coordinates(fabric_tile_id)[isb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); - status_code = build_tile_module_port_and_nets_between_sb_and_pb(module_manager, - tile_module, - vpr_device_annotation, - device_rr_gsb, - rr_gsb, - fabric_tile, - fabric_tile_id, - pb_instances, - sb_instances[isb], - true, - frame_view, - verbose); + status_code = build_tile_module_port_and_nets_between_sb_and_pb( + module_manager, tile_module, grids, vpr_device_annotation, device_rr_gsb, + rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, pb_instances, + sb_instances[isb], true, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } } - /* Get the submodule of connection blocks one by one, build connections between cb and pb */ + /* Get the submodule of connection blocks one by one, build connections + * between cb and pb */ for (t_rr_type cb_type : {CHANX, CHANY}) { - for (size_t icb = 0; icb < fabric_tile.cb_coordinates(fabric_tile_id, cb_type).size(); ++icb) { - vtr::Point cb_coord = fabric_tile.cb_coordinates(fabric_tile_id, cb_type)[icb]; + for (size_t icb = 0; + icb < fabric_tile.cb_coordinates(fabric_tile_id, cb_type).size(); + ++icb) { + vtr::Point cb_coord = + fabric_tile.cb_coordinates(fabric_tile_id, cb_type)[icb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(cb_coord); - status_code = build_tile_module_port_and_nets_between_cb_and_pb(module_manager, - tile_module, - vpr_device_annotation, - device_rr_gsb, - rr_gsb, - fabric_tile, - fabric_tile_id, - pb_instances, - cb_instances[cb_type][icb], - true, - frame_view, - verbose); + status_code = build_tile_module_port_and_nets_between_cb_and_pb( + module_manager, tile_module, grids, vpr_device_annotation, + device_rr_gsb, rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, + cb_type, pb_instances, cb_instances.at(cb_type)[icb], true, frame_view, + verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } } } - /* Get the submodule of connection blocks one by one, build connections between sb and cb */ - for (size_t isb = 0; isb < fabric_tile.sb_coordinates(fabric_tile_id).size(); ++isb) { - vtr::Point sb_coord = fabric_tile.sb_coordinates(fabric_tile_id)[isb]; + /* Get the submodule of connection blocks one by one, build connections + * between sb and cb */ + for (size_t isb = 0; isb < fabric_tile.sb_coordinates(fabric_tile_id).size(); + ++isb) { + vtr::Point sb_coord = + fabric_tile.sb_coordinates(fabric_tile_id)[isb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); - status_code = build_tile_module_port_and_nets_between_sb_and_cb(module_manager, - tile_module, - vpr_device_annotation, - device_rr_gsb, - rr_gsb, - fabric_tile, - fabric_tile_id, - cb_instances, - sb_instances[isb], - true, - frame_view, - verbose); + status_code = build_tile_module_port_and_nets_between_sb_and_cb( + module_manager, tile_module, device_rr_gsb, rr_graph_view, rr_gsb, + fabric_tile, fabric_tile_id, cb_instances, sb_instances[isb], true, + frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } } - /* TODO: Create the ports from pb which only connects to adjacent sb and cbs, as well as pb */ - for (size_t ipb = 0; ipb < fabric_tile.pb_coordinates(fabric_tile_id).size(); ++ipb) { - vtr::Point pb_coord = fabric_tile.pb_coordinates(fabric_tile_id)[ipb]; - const RRGSB& rr_gsb = device_rr_gsb.get_gsb(pb_coord); - status_code = build_tile_port_and_nets_from_pb(module_manager, tile_module, pb_coord, pb_instances[ipb], frame_view, verbose); + /* Create the ports from pb which only connects to adjacent sb and cbs, as + * well as pb */ + for (size_t ipb = 0; ipb < fabric_tile.pb_coordinates(fabric_tile_id).size(); + ++ipb) { + vtr::Point pb_coord = + fabric_tile.pb_coordinates(fabric_tile_id)[ipb]; + status_code = build_tile_port_and_nets_from_pb( + module_manager, tile_module, grids, vpr_device_annotation, pb_coord, + pb_instances[ipb], frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { - return CMD_EXEC_FATAL_ERROR; } } @@ -914,13 +981,12 @@ static int build_tile_module_ports_and_nets(ModuleManager& module_manager, * - Add configuration ports and nets *******************************************************************/ static int build_tile_module( - ModuleManager& module_manager, DecoderLibrary& decoder_lib, const FabricTile& fabric_tile, - const FabricTileId& fabric_tile_id, const DeviceGrid& grids, - const VprDeviceAnnotation& vpr_device_annotation, - const DeviceRRGSB& device_rr_gsb, const CircuitLibrary& circuit_lib, - const CircuitModelId& sram_model, - const e_config_protocol_type& sram_orgz_type, - const bool& frame_view, + ModuleManager& module_manager, DecoderLibrary& decoder_lib, + const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, + const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph_view, + const CircuitLibrary& circuit_lib, const CircuitModelId& sram_model, + const e_config_protocol_type& sram_orgz_type, const bool& frame_view, const bool& verbose) { int status_code = CMD_EXEC_SUCCESS; @@ -931,7 +997,8 @@ static int build_tile_module( vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); /* Add instance of programmable block */ - std::vector pb_instances; /* Keep tracking the instance id of each pb */ + std::vector + pb_instances; /* Keep tracking the instance id of each pb */ for (vtr::Point grid_gsb_coord : fabric_tile.pb_coordinates(fabric_tile_id)) { const RRGSB& grid_rr_gsb = device_rr_gsb.get_gsb(grid_gsb_coord); @@ -940,7 +1007,8 @@ static int build_tile_module( grids.get_physical_type(grid_coord.x(), grid_coord.y()); /* Empty type does not require a module */ if (!is_empty_type(phy_tile)) { - e_side grid_side = find_grid_border_side(vtr::Point(grids.width, grids.height()), grid_coord); + e_side grid_side = find_grid_border_side( + vtr::Point(grids.width(), grids.height()), grid_coord); std::string pb_module_name = generate_grid_block_module_name( std::string(GRID_MODULE_NAME_PREFIX), std::string(phy_tile->name), is_io_type(phy_tile), grid_side); @@ -961,12 +1029,13 @@ static int build_tile_module( } VTR_LOGV(verbose, "Added programmable module '%s' to tile[%lu][%lu]\n", pb_module_name.c_str(), tile_coord.x(), tile_coord.y()); + pb_instances.push_back(pb_instance); } - pb_instances.push_back(pb_instance); } /* Add instance of connection blocks */ - std::map> cb_instances; /* Keep tracking the instance id of each cb */ + std::map> + cb_instances; /* Keep tracking the instance id of each cb */ for (t_rr_type cb_type : {CHANX, CHANY}) { for (vtr::Point cb_coord : fabric_tile.cb_coordinates(fabric_tile_id, cb_type)) { @@ -1001,7 +1070,8 @@ static int build_tile_module( } /* Add instance of switch blocks */ - std::vector sb_instances; /* Keep tracking the instance id of each sb */ + std::vector + sb_instances; /* Keep tracking the instance id of each sb */ for (vtr::Point sb_coord : fabric_tile.sb_coordinates(fabric_tile_id)) { /* get the unique module coord */ @@ -1029,8 +1099,11 @@ static int build_tile_module( sb_instances.push_back(sb_instance); } - /* TODO: Add module nets and ports */ - status_code = build_tile_module_ports_and_nets(module_manager, tile_module, vpr_device_annotation, device_rr_gsb, fabric_tile, fabric_tile_id, pb_instances, cb_instances, sb_instances, frame_view, verbose); + /* Add module nets and ports */ + status_code = build_tile_module_ports_and_nets( + module_manager, tile_module, grids, vpr_device_annotation, device_rr_gsb, + rr_graph_view, fabric_tile, fabric_tile_id, pb_instances, cb_instances, + sb_instances, frame_view, verbose); /* Add global ports to the pb_module: * This is a much easier job after adding sub modules (instances), @@ -1091,23 +1164,25 @@ static int build_tile_module( * Build all the tile modules *******************************************************************/ int build_tile_modules(ModuleManager& module_manager, + DecoderLibrary& decoder_lib, const FabricTile& fabric_tile, const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph_view, const CircuitLibrary& circuit_lib, const CircuitModelId& sram_model, const e_config_protocol_type& sram_orgz_type, - const bool& frame_view, - const bool& verbose) { + const bool& frame_view, const bool& verbose) { vtr::ScopedStartFinishTimer timer("Build tile modules for the FPGA fabric"); int status_code = CMD_EXEC_SUCCESS; /* Build a module for each unique tile */ for (FabricTileId fabric_tile_id : fabric_tile.unique_tiles()) { - status_code = build_tile_module(module_manager, fabric_tile, fabric_tile_id, - grids, vpr_device_annotation, device_rr_gsb, circuit_lib, - sram_model, sram_orgz_type, frame_view, verbose); + status_code = build_tile_module( + module_manager, decoder_lib, fabric_tile, fabric_tile_id, grids, + vpr_device_annotation, device_rr_gsb, rr_graph_view, circuit_lib, + sram_model, sram_orgz_type, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } diff --git a/openfpga/src/fabric/build_tile_modules.h b/openfpga/src/fabric/build_tile_modules.h index 44876ecca..0513d9850 100644 --- a/openfpga/src/fabric/build_tile_modules.h +++ b/openfpga/src/fabric/build_tile_modules.h @@ -9,11 +9,13 @@ #include "circuit_library.h" #include "config_protocol.h" -#include "vpr_device_annotation.h" +#include "decoder_library.h" #include "device_grid.h" #include "device_rr_gsb.h" #include "fabric_tile.h" #include "module_manager.h" +#include "rr_graph_view.h" +#include "vpr_device_annotation.h" /******************************************************************** * Function declaration @@ -23,14 +25,15 @@ namespace openfpga { int build_tile_modules(ModuleManager& module_manager, + DecoderLibrary& decoder_lib, const FabricTile& fabric_tile, const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph_view, const CircuitLibrary& circuit_lib, const CircuitModelId& sram_model, const e_config_protocol_type& sram_orgz_type, - const bool& frame_view, - const bool& verbose); + const bool& frame_view, const bool& verbose); } /* end namespace openfpga */ From 6607bb7e48dfae1f6badb885ce0b27803124834f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 18 Jul 2023 22:35:22 -0700 Subject: [PATCH 180/391] [core] now fpga verilog supports tile modules --- .../src/openfpga_reserved_words.h | 1 + openfpga/src/annotation/fabric_tile.cpp | 4 +- openfpga/src/annotation/fabric_tile.h | 5 +- openfpga/src/base/netlist_manager.h | 1 + .../src/base/openfpga_build_fabric_template.h | 4 +- openfpga/src/base/openfpga_context.h | 4 + openfpga/src/base/openfpga_naming.cpp | 13 ++- openfpga/src/base/openfpga_naming.h | 5 +- openfpga/src/base/openfpga_verilog_template.h | 17 ++- openfpga/src/fabric/build_device_module.cpp | 11 +- openfpga/src/fabric/build_device_module.h | 3 +- openfpga/src/fabric/build_fabric_tile.cpp | 2 + openfpga/src/fabric/build_tile_modules.cpp | 4 +- openfpga/src/fpga_verilog/verilog_api.cpp | 27 ++++- openfpga/src/fpga_verilog/verilog_api.h | 6 +- .../verilog_auxiliary_netlists.cpp | 9 ++ openfpga/src/fpga_verilog/verilog_tile.cpp | 110 ++++++++++++++++++ openfpga/src/fpga_verilog/verilog_tile.h | 29 +++++ 18 files changed, 222 insertions(+), 33 deletions(-) create mode 100644 openfpga/src/fpga_verilog/verilog_tile.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_tile.h diff --git a/libs/libopenfpgautil/src/openfpga_reserved_words.h b/libs/libopenfpgautil/src/openfpga_reserved_words.h index 11dd44ef0..f06ed9c66 100644 --- a/libs/libopenfpgautil/src/openfpga_reserved_words.h +++ b/libs/libopenfpgautil/src/openfpga_reserved_words.h @@ -68,6 +68,7 @@ constexpr const char* INV_PORT_POSTFIX = "_inv"; /* Bitstream file strings */ constexpr const char* BITSTREAM_XML_FILE_NAME_POSTFIX = "_bitstream.xml"; +constexpr const char* DEFAULT_TILE_DIR_NAME = "tile/"; constexpr const char* DEFAULT_LB_DIR_NAME = "lb/"; constexpr const char* DEFAULT_RR_DIR_NAME = "routing/"; constexpr const char* DEFAULT_SUBMODULE_DIR_NAME = "sub_module/"; diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index 0e83e0734..96144752a 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -9,8 +9,6 @@ /* namespace openfpga begins */ namespace openfpga { -FabricTile::FabricTile(const vtr::Point& max_coord) { init(max_coord); } - vtr::Point FabricTile::tile_coordinate( const FabricTileId& tile_id) const { VTR_ASSERT(valid_tile_id(tile_id)); @@ -155,6 +153,8 @@ std::vector FabricTile::unique_tiles() const { return unique_tile_ids_; } +bool FabricTile::empty() const { return ids_.empty(); } + FabricTileId FabricTile::create_tile(const vtr::Point& coord) { FabricTileId tile_id = FabricTileId(ids_.size()); ids_.push_back(tile_id); diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h index 467852b38..d67f73506 100644 --- a/openfpga/src/annotation/fabric_tile.h +++ b/openfpga/src/annotation/fabric_tile.h @@ -23,9 +23,6 @@ namespace openfpga { * 2. a collection of unique tiles *******************************************************************/ class FabricTile { - public: /* Contructors */ - FabricTile(const vtr::Point& max_coord); - public: /* Accessors */ vtr::Point tile_coordinate(const FabricTileId& tile_id) const; std::vector> pb_coordinates( @@ -64,6 +61,8 @@ class FabricTile { /** @brief Check if a connection block (with a coordinate) exists in a tile */ bool cb_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord) const; + /** @brief Identify if the fabric tile is empty: no tiles are defined */ + bool empty() const; public: /* Mutators */ FabricTileId create_tile(const vtr::Point& coord); diff --git a/openfpga/src/base/netlist_manager.h b/openfpga/src/base/netlist_manager.h index 3e895ff3e..8f94e9521 100644 --- a/openfpga/src/base/netlist_manager.h +++ b/openfpga/src/base/netlist_manager.h @@ -39,6 +39,7 @@ class NetlistManager { SUBMODULE_NETLIST, LOGIC_BLOCK_NETLIST, ROUTING_MODULE_NETLIST, + TILE_MODULE_NETLIST, TOP_MODULE_NETLIST, TESTBENCH_NETLIST, NUM_NETLIST_TYPES diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index 21c3216f4..a017e6743 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -149,8 +149,8 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd, curr_status = build_device_module_graph( openfpga_ctx.mutable_module_graph(), openfpga_ctx.mutable_decoder_lib(), openfpga_ctx.mutable_blwl_shift_register_banks(), - const_cast(openfpga_ctx), g_vpr_ctx.device(), - cmd_context.option_enable(cmd, opt_frame_view), + openfpga_ctx.mutable_fabric_tile(), const_cast(openfpga_ctx), + g_vpr_ctx.device(), cmd_context.option_enable(cmd, opt_frame_view), cmd_context.option_enable(cmd, opt_compress_routing), cmd_context.option_enable(cmd, opt_duplicate_grid_pin), predefined_fabric_key, tile_config, diff --git a/openfpga/src/base/openfpga_context.h b/openfpga/src/base/openfpga_context.h index 769bf0931..9473ecc65 100644 --- a/openfpga/src/base/openfpga_context.h +++ b/openfpga/src/base/openfpga_context.h @@ -10,6 +10,7 @@ #include "device_rr_gsb.h" #include "fabric_bitstream.h" #include "fabric_global_port_info.h" +#include "fabric_tile.h" #include "io_location_map.h" #include "io_name_map.h" #include "memory_bank_shift_register_banks.h" @@ -106,6 +107,7 @@ class OpenfpgaContext : public Context { return io_location_map_; } const openfpga::IoNameMap& io_name_map() const { return io_name_map_; } + const openfpga::FabricTile& fabric_tile() const { return fabric_tile_; } const openfpga::FabricGlobalPortInfo& fabric_global_port_info() const { return fabric_global_port_info_; } @@ -165,6 +167,7 @@ class OpenfpgaContext : public Context { return io_location_map_; } openfpga::IoNameMap& mutable_io_name_map() { return io_name_map_; } + openfpga::FabricTile& mutable_fabric_tile() { return fabric_tile_; } openfpga::FabricGlobalPortInfo& mutable_fabric_global_port_info() { return fabric_global_port_info_; } @@ -220,6 +223,7 @@ class OpenfpgaContext : public Context { openfpga::ModuleManager module_graph_; openfpga::IoLocationMap io_location_map_; openfpga::IoNameMap io_name_map_; + openfpga::FabricTile fabric_tile_; openfpga::FabricGlobalPortInfo fabric_global_port_info_; /* Bitstream database */ diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index 037db2009..e14a608e7 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -500,8 +500,17 @@ std::string generate_switch_block_module_name( /********************************************************************* * Generate the module name for a switch block with a given index *********************************************************************/ -std::string generate_tile_module_name(const size_t& index) { - return std::string("tile_" + std::to_string(index)); +std::string generate_tile_module_name(const vtr::Point& tile_coord) { + return std::string("tile_" + std::to_string(tile_coord.x()) + "__" + + std::to_string(tile_coord.y()) + "_"); +} + +/********************************************************************* + * Generate the netlist name of a grid block + **********************************************************************/ +std::string generate_tile_module_netlist_name(const std::string& block_name, + const std::string& postfix) { + return block_name + postfix; } /********************************************************************* diff --git a/openfpga/src/base/openfpga_naming.h b/openfpga/src/base/openfpga_naming.h index 7651f140b..f828de5cf 100644 --- a/openfpga/src/base/openfpga_naming.h +++ b/openfpga/src/base/openfpga_naming.h @@ -110,7 +110,10 @@ std::string generate_switch_block_module_name( std::string generate_connection_block_module_name( const t_rr_type& cb_type, const vtr::Point& coordinate); -std::string generate_tile_module_name(const size_t& index); +std::string generate_tile_module_name(const vtr::Point& tile_coord); + +std::string generate_tile_module_netlist_name(const std::string& block_name, + const std::string& postfix); std::string generate_sb_mux_instance_name(const std::string& prefix, const e_side& sb_side, diff --git a/openfpga/src/base/openfpga_verilog_template.h b/openfpga/src/base/openfpga_verilog_template.h index d5b41d8e1..32cb09ee0 100644 --- a/openfpga/src/base/openfpga_verilog_template.h +++ b/openfpga/src/base/openfpga_verilog_template.h @@ -57,16 +57,13 @@ int write_fabric_verilog_template(T& openfpga_ctx, const Command& cmd, options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); options.set_compress_routing(openfpga_ctx.flow_manager().compress_routing()); - fpga_fabric_verilog(openfpga_ctx.mutable_module_graph(), - openfpga_ctx.mutable_verilog_netlists(), - openfpga_ctx.blwl_shift_register_banks(), - openfpga_ctx.arch().circuit_lib, openfpga_ctx.mux_lib(), - openfpga_ctx.decoder_lib(), g_vpr_ctx.device(), - openfpga_ctx.vpr_device_annotation(), - openfpga_ctx.device_rr_gsb(), options); - - /* TODO: should identify the error code from internal function execution */ - return CMD_EXEC_SUCCESS; + return fpga_fabric_verilog( + openfpga_ctx.mutable_module_graph(), + openfpga_ctx.mutable_verilog_netlists(), + openfpga_ctx.blwl_shift_register_banks(), openfpga_ctx.arch().circuit_lib, + openfpga_ctx.mux_lib(), openfpga_ctx.decoder_lib(), g_vpr_ctx.device(), + openfpga_ctx.vpr_device_annotation(), openfpga_ctx.device_rr_gsb(), + openfpga_ctx.fabric_tile(), options); } /******************************************************************** diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index cb8f5e4cd..565f4944b 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -33,7 +33,7 @@ namespace openfpga { *******************************************************************/ int build_device_module_graph( ModuleManager& module_manager, DecoderLibrary& decoder_lib, - MemoryBankShiftRegisterBanks& blwl_sr_banks, + MemoryBankShiftRegisterBanks& blwl_sr_banks, FabricTile& fabric_tile, 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, @@ -103,8 +103,6 @@ int build_device_module_graph( } /* Build tile modules if defined */ - FabricTile fabric_tile(vtr::Point(vpr_device_ctx.grid.width(), - vpr_device_ctx.grid.height())); if (!tile_config.is_valid()) { /* Build detailed tile-level information */ status = build_fabric_tile(fabric_tile, tile_config, vpr_device_ctx.grid, @@ -114,9 +112,10 @@ int build_device_module_graph( } /* Build the modules */ build_tile_modules( - module_manager, decoder_lib, fabric_tile, vpr_device_ctx.grid, - openfpga_ctx.vpr_device_annotation(), openfpga_ctx.device_rr_gsb(), - vpr_device_ctx.rr_graph, openfpga_ctx.arch().circuit_lib, sram_model, + module_manager, decoder_lib, openfpga_ctx.fabric_tile(), + vpr_device_ctx.grid, openfpga_ctx.vpr_device_annotation(), + openfpga_ctx.device_rr_gsb(), vpr_device_ctx.rr_graph, + openfpga_ctx.arch().circuit_lib, sram_model, openfpga_ctx.arch().config_protocol.type(), frame_view, verbose); } diff --git a/openfpga/src/fabric/build_device_module.h b/openfpga/src/fabric/build_device_module.h index 513982aa9..6244fc7dd 100644 --- a/openfpga/src/fabric/build_device_module.h +++ b/openfpga/src/fabric/build_device_module.h @@ -5,6 +5,7 @@ * Include header files that are required by function declaration *******************************************************************/ #include "fabric_key.h" +#include "fabric_tile.h" #include "io_name_map.h" #include "openfpga_context.h" #include "tile_config.h" @@ -19,7 +20,7 @@ namespace openfpga { int build_device_module_graph( ModuleManager& module_manager, DecoderLibrary& decoder_lib, - MemoryBankShiftRegisterBanks& blwl_sr_banks, + MemoryBankShiftRegisterBanks& blwl_sr_banks, FabricTile& fabric_tile, 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, diff --git a/openfpga/src/fabric/build_fabric_tile.cpp b/openfpga/src/fabric/build_fabric_tile.cpp index 2f4c53c9f..01c85d2ea 100644 --- a/openfpga/src/fabric/build_fabric_tile.cpp +++ b/openfpga/src/fabric/build_fabric_tile.cpp @@ -120,6 +120,8 @@ int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, int status_code = CMD_EXEC_SUCCESS; + fabric_tile.init(vtr::Point(grids.width(), grids.height())); + /* Depending on the selected style, follow different approaches */ if (tile_config.style() == TileConfig::e_style::TOP_LEFT) { status_code = diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 08b9a4ba2..20a9a37ad 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -991,10 +991,10 @@ static int build_tile_module( int status_code = CMD_EXEC_SUCCESS; /* Create the module */ - std::string module_name = generate_tile_module_name(size_t(fabric_tile_id)); + vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); + std::string module_name = generate_tile_module_name(tile_coord); VTR_LOGV(verbose, "Building tile module '%s'...\n"); ModuleId tile_module = module_manager.add_module(module_name); - vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); /* Add instance of programmable block */ std::vector diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index e5c58b5b4..68eabe145 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -23,6 +23,7 @@ #include "verilog_routing.h" #include "verilog_simulation_info_writer.h" #include "verilog_submodule.h" +#include "verilog_tile.h" #include "verilog_top_module.h" #include "verilog_top_testbench.h" @@ -53,15 +54,18 @@ namespace openfpga { * We should think clearly about how to handle them for both Verilog and SPICE *generators! ********************************************************************/ -void fpga_fabric_verilog( +int fpga_fabric_verilog( ModuleManager &module_manager, NetlistManager &netlist_manager, const MemoryBankShiftRegisterBanks &blwl_sr_banks, const CircuitLibrary &circuit_lib, const MuxLibrary &mux_lib, const DecoderLibrary &decoder_lib, const DeviceContext &device_ctx, const VprDeviceAnnotation &device_annotation, - const DeviceRRGSB &device_rr_gsb, const FabricVerilogOption &options) { + const DeviceRRGSB &device_rr_gsb, const FabricTile &fabric_tile, + const FabricVerilogOption &options) { vtr::ScopedStartFinishTimer timer("Write Verilog netlists for FPGA fabric\n"); + int status_code = CMD_EXEC_SUCCESS; + std::string src_dir_path = format_dir_path(options.output_directory()); /* Create directories */ @@ -83,6 +87,13 @@ void fpga_fabric_verilog( std::string rr_dir_path = src_dir_path + std::string(DEFAULT_RR_DIR_NAME); create_directory(rr_dir_path); + /* Sub directory under SRC directory to contain all the tile netlists + */ + std::string tile_dir_path = src_dir_path + std::string(DEFAULT_TILE_DIR_NAME); + if (!fabric_tile.empty()) { + create_directory(tile_dir_path); + } + /* Print Verilog files containing preprocessing flags */ print_verilog_preprocessing_flags_netlist(std::string(src_dir_path), options); @@ -115,6 +126,16 @@ void fpga_fabric_verilog( device_ctx, device_annotation, lb_dir_path, std::string(DEFAULT_LB_DIR_NAME), options, options.verbose_output()); + /* Generate tiles */ + if (!fabric_tile.empty()) { + status_code = print_verilog_tiles( + netlist_manager, const_cast(module_manager), + tile_dir_path, fabric_tile, options); + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } + /* Generate FPGA fabric */ print_verilog_core_module(netlist_manager, const_cast(module_manager), @@ -132,6 +153,8 @@ void fpga_fabric_verilog( */ VTR_LOGV(options.verbose_output(), "Written %lu Verilog modules in total\n", module_manager.num_modules()); + + return CMD_EXEC_SUCCESS; } /******************************************************************** diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index 3a590820c..bea3ba4d7 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -16,6 +16,7 @@ #include "device_rr_gsb.h" #include "fabric_bitstream.h" #include "fabric_global_port_info.h" +#include "fabric_tile.h" #include "fabric_verilog_options.h" #include "io_location_map.h" #include "io_name_map.h" @@ -37,13 +38,14 @@ /* begin namespace openfpga */ namespace openfpga { -void fpga_fabric_verilog( +int fpga_fabric_verilog( ModuleManager& module_manager, NetlistManager& netlist_manager, const MemoryBankShiftRegisterBanks& blwl_sr_banks, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, const DecoderLibrary& decoder_lib, const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, - const DeviceRRGSB& device_rr_gsb, const FabricVerilogOption& options); + const DeviceRRGSB& device_rr_gsb, const FabricTile& fabric_tile, + const FabricVerilogOption& options); int fpga_verilog_full_testbench( const ModuleManager& module_manager, diff --git a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp index a19ee9446..8501ac6aa 100644 --- a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp +++ b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp @@ -138,6 +138,15 @@ void print_verilog_fabric_include_netlist(const NetlistManager& netlist_manager, } fp << std::endl; + /* Include all the tile modules */ + print_verilog_comment( + fp, std::string("------ Include tile module netlists -----")); + for (const NetlistId& nlist_id : + netlist_manager.netlists_by_type(NetlistManager::TILE_MODULE_NETLIST)) { + print_verilog_include_netlist(fp, netlist_manager.netlist_name(nlist_id)); + } + fp << std::endl; + /* Include FPGA top module */ print_verilog_comment( fp, std::string("------ Include fabric top-level netlists -----")); diff --git a/openfpga/src/fpga_verilog/verilog_tile.cpp b/openfpga/src/fpga_verilog/verilog_tile.cpp new file mode 100644 index 000000000..0a3465c4c --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_tile.cpp @@ -0,0 +1,110 @@ +/******************************************************************** + * This file includes functions that are used to print the top-level + * module for the FPGA fabric in Verilog format + *******************************************************************/ +#include "verilog_tile.h" + +#include +#include +#include + +#include "command_exit_codes.h" +#include "openfpga_digest.h" +#include "openfpga_naming.h" +#include "verilog_constants.h" +#include "verilog_module_writer.h" +#include "verilog_writer_utils.h" +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print the tile module for the FPGA fabric in Verilog format + *******************************************************************/ +static int print_verilog_tile_module_netlist( + NetlistManager& netlist_manager, const ModuleManager& module_manager, + const std::string& verilog_dir, const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id, const FabricVerilogOption& options) { + /* Create a module as the top-level fabric, and add it to the module manager + */ + vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); + std::string tile_module_name = generate_tile_module_name(tile_coord); + ModuleId tile_module = module_manager.find_module(tile_module_name); + if (!module_manager.valid_module_id(tile_module)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Start printing out Verilog netlists */ + /* Create the file name for Verilog netlist */ + std::string verilog_fname(generate_tile_module_netlist_name( + tile_module_name, std::string(VERILOG_NETLIST_FILE_POSTFIX))); + std::string verilog_fpath(verilog_dir + verilog_fname); + + VTR_LOG("Writing Verilog netlist '%s' for tile module '%s'...", + verilog_fpath.c_str(), tile_module_name.c_str()); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fpath, std::fstream::out | std::fstream::trunc); + + check_file_stream(verilog_fpath.c_str(), fp); + + print_verilog_file_header(fp, std::string("Tile Verilog module for FPGA"), + options.time_stamp()); + + /* Write the module content in Verilog format */ + write_verilog_module_to_file(fp, module_manager, tile_module, + options.explicit_port_mapping(), + options.default_net_type()); + + /* Add an empty line as a splitter */ + fp << std::endl; + + /* Close file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = NetlistId::INVALID(); + if (options.use_relative_path()) { + nlist_id = netlist_manager.add_netlist(verilog_fname); + } else { + nlist_id = netlist_manager.add_netlist(verilog_fpath); + } + VTR_ASSERT(nlist_id); + netlist_manager.set_netlist_type(nlist_id, + NetlistManager::TILE_MODULE_NETLIST); + + VTR_LOG("Done\n"); + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Print all the tile modules for the FPGA fabric in Verilog format + *******************************************************************/ +int print_verilog_tiles(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& verilog_dir, + const FabricTile& fabric_tile, + const FabricVerilogOption& options) { + vtr::ScopedStartFinishTimer timer("Build tile modules for the FPGA fabric"); + + int status_code = CMD_EXEC_SUCCESS; + + /* Build a module for each unique tile */ + for (FabricTileId fabric_tile_id : fabric_tile.unique_tiles()) { + status_code = print_verilog_tile_module_netlist( + netlist_manager, module_manager, verilog_dir, fabric_tile, fabric_tile_id, + options); + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } + + return status_code; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_tile.h b/openfpga/src/fpga_verilog/verilog_tile.h new file mode 100644 index 000000000..a38886136 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_tile.h @@ -0,0 +1,29 @@ +#ifndef VERILOG_TILE_H +#define VERILOG_TILE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +#include "fabric_tile.h" +#include "fabric_verilog_options.h" +#include "module_manager.h" +#include "netlist_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_verilog_tiles(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& verilog_dir, + const FabricTile& fabric_tile, + const FabricVerilogOption& options); + +} /* end namespace openfpga */ + +#endif From 1a864d33b67acddf5eb977088da0845a1a67f181 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Jul 2023 06:06:53 +0000 Subject: [PATCH 181/391] Bump yosys from `25d4b3a` to `83c9261` Bumps [yosys](https://github.com/YosysHQ/yosys) from `25d4b3a` to `83c9261`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/25d4b3a5dc32ac3865d5907b5aba45f67f2dabb0...83c9261d6ca47e7974258ae4e7f1c86f0dfa8961) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 25d4b3a5d..83c9261d6 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 25d4b3a5dc32ac3865d5907b5aba45f67f2dabb0 +Subproject commit 83c9261d6ca47e7974258ae4e7f1c86f0dfa8961 From 82fe63297a8542e23c8f22f09857b0284b5a388b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Jul 2023 11:22:36 -0700 Subject: [PATCH 182/391] [test] add a new test for top-left tile grouping --- ...ile_full_testbench_example_script.openfpga | 71 +++++++++++++++++++ .../regression_test_scripts/basic_reg_test.sh | 2 + .../homo_fabric_tile/config/task.conf | 35 +++++++++ .../homo_fabric_tile/config/tile_config.xml | 1 + 4 files changed, 109 insertions(+) create mode 100644 openfpga_flow/openfpga_shell_scripts/group_tile_full_testbench_example_script.openfpga create mode 100644 openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile/config/tile_config.xml diff --git a/openfpga_flow/openfpga_shell_scripts/group_tile_full_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/group_tile_full_testbench_example_script.openfpga new file mode 100644 index 000000000..00f0d3f0e --- /dev/null +++ b/openfpga_flow/openfpga_shell_scripts/group_tile_full_testbench_example_script.openfpga @@ -0,0 +1,71 @@ +# 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_tile ${OPENFPGA_GROUP_TILE_CONFIG_FILE} #--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 +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 f0a9c9f06..a576bdd43 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -175,6 +175,8 @@ echo -e "Testing tiles with I/O in center grid"; run-task basic_tests/tile_organization/tileable_io $@ echo -e "Testing tiles with I/O consisting of subtiles"; run-task basic_tests/tile_organization/io_subtile $@ +echo -e "Testing tile grouping on a homogeneous FPGA fabric"; +run-task basic_tests/tile_organization/homo_fabric_tile $@ echo -e "Testing global port definition from tiles"; run-task basic_tests/global_tile_ports/global_tile_clock $@ diff --git a/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile/config/task.conf b/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile/config/task.conf new file mode 100644 index 000000000..c07a08d33 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/tile_organization/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_file=${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/tile_organization/homo_fabric_tile/config/tile_config.xml b/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile/config/tile_config.xml new file mode 100644 index 000000000..1a1f3f6e8 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile/config/tile_config.xml @@ -0,0 +1 @@ + From 48e207d3e41e10ee2527c8002d1eaa04305c8c9a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Jul 2023 12:22:57 -0700 Subject: [PATCH 183/391] [core] debugging --- openfpga/src/annotation/fabric_tile.cpp | 11 +++++----- openfpga/src/fabric/build_device_module.cpp | 4 ++-- openfpga/src/fabric/build_fabric_tile.cpp | 23 ++++++++++++++------- openfpga/src/fabric/build_fabric_tile.h | 6 ++++-- openfpga/src/fabric/build_tile_modules.cpp | 2 +- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index 96144752a..9e6b16ad2 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -1,6 +1,7 @@ /************************************************************************ * Member functions for class FabricTile ***********************************************************************/ +#include "build_top_module_utils.h" #include "fabric_tile.h" #include "vtr_assert.h" @@ -282,10 +283,7 @@ bool FabricTile::equivalent_tile(const FabricTileId& tile_a, } /* The pb of two tiles should be the same, otherwise not equivalent */ for (size_t iblk = 0; iblk < pb_coords_[tile_a].size(); ++iblk) { - if (grids.get_physical_type(pb_coords_[tile_a][iblk].x(), - pb_coords_[tile_a][iblk].y()) != - grids.get_physical_type(pb_coords_[tile_b][iblk].x(), - pb_coords_[tile_b][iblk].y())) { + if (generate_grid_block_module_name_in_top_module(std::string(), grids, pb_coords_[tile_a][iblk]) != generate_grid_block_module_name_in_top_module(std::string(), grids, pb_coords_[tile_b][iblk])) { return false; } } @@ -319,6 +317,9 @@ int FabricTile::build_unique_tiles(const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb) { for (size_t ix = 0; ix < grids.width(); ++ix) { for (size_t iy = 0; iy < grids.height(); ++iy) { + if (!valid_tile_id(tile_coord2id_lookup_[ix][iy])) { + continue; /* Skip invalid tile (which does not exist) */ + } bool is_unique_tile = true; for (FabricTileId unique_tile_id : unique_tile_ids_) { if (equivalent_tile(tile_coord2id_lookup_[ix][iy], unique_tile_id, @@ -330,7 +331,7 @@ int FabricTile::build_unique_tiles(const DeviceGrid& grids, } /* Update list if this is a unique tile */ if (is_unique_tile) { - unique_tile_ids_.push_back(tile_coord2unique_tile_ids_[ix][iy]); + unique_tile_ids_.push_back(tile_coord2id_lookup_[ix][iy]); tile_coord2unique_tile_ids_[ix][iy] = tile_coord2id_lookup_[ix][iy]; } } diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index 565f4944b..f38103c8f 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -103,10 +103,10 @@ int build_device_module_graph( } /* Build tile modules if defined */ - if (!tile_config.is_valid()) { + if (tile_config.is_valid()) { /* Build detailed tile-level information */ status = build_fabric_tile(fabric_tile, tile_config, vpr_device_ctx.grid, - openfpga_ctx.device_rr_gsb()); + openfpga_ctx.device_rr_gsb(), verbose); if (CMD_EXEC_FATAL_ERROR == status) { return status; } diff --git a/openfpga/src/fabric/build_fabric_tile.cpp b/openfpga/src/fabric/build_fabric_tile.cpp index 01c85d2ea..4f8e31561 100644 --- a/openfpga/src/fabric/build_fabric_tile.cpp +++ b/openfpga/src/fabric/build_fabric_tile.cpp @@ -29,7 +29,8 @@ namespace openfpga { *******************************************************************/ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, const DeviceGrid& grids, - const DeviceRRGSB& device_rr_gsb) { + const DeviceRRGSB& device_rr_gsb, + const bool& verbose) { int status_code = CMD_EXEC_SUCCESS; /* Walk through all the device rr_gsb and create tile one by one */ @@ -45,14 +46,15 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, if (true == is_empty_type(phy_tile_type)) { skip_add_pb = true; if (!device_rr_gsb.is_gsb_exist(curr_gsb_coord)) { + VTR_LOGV(verbose, "Skip tile[%lu][%lu] as it is empty\n", curr_tile_coord.x(), curr_tile_coord.y()); continue; } /* Need to create a new tile here */ + VTR_LOGV(verbose, "Create tile[%lu][%lu] which only has routing but not a programmable block\n", curr_tile_coord.x(), curr_tile_coord.y()); curr_tile_id = fabric_tile.create_tile(curr_tile_coord); - } - /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(ix, iy)) || + } else if ((0 < grids.get_width_offset(ix, iy)) || (0 < grids.get_height_offset(ix, iy))) { + /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ /* Find the root of this grid, the instance id should be valid. * We just copy it here */ @@ -60,14 +62,16 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, ix - grids.get_width_offset(ix, iy), iy - grids.get_height_offset(ix, iy)); skip_add_pb = true; + VTR_LOGV(verbose, "Tile[%lu][%lu] contains a heterogeneous block which is rooted from tile[%lu][%lu]\n", curr_tile_coord.x(), curr_tile_coord.y(), root_tile_coord.x(), root_tile_coord.y()); curr_tile_id = fabric_tile.find_tile(root_tile_coord); } else { /* Need to create a new tile here */ + VTR_LOGV(verbose, "Create a regular tile[%lu][%lu]\n", curr_tile_coord.x(), curr_tile_coord.y()); curr_tile_id = fabric_tile.create_tile(curr_tile_coord); } /* Ensure that we have a valid id */ - if (fabric_tile.valid_tile_id(curr_tile_id)) { + if (!fabric_tile.valid_tile_id(curr_tile_id)) { VTR_LOG_ERROR("Failed to get a valid id for tile[%lu][%lu]!\n", ix, iy); return CMD_EXEC_FATAL_ERROR; } @@ -112,9 +116,11 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, /******************************************************************** * Build tile-level information for a given FPGA fabric, w.r.t. to configuration *******************************************************************/ -int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, +int build_fabric_tile(FabricTile& fabric_tile, + const TileConfig& tile_config, const DeviceGrid& grids, - const DeviceRRGSB& device_rr_gsb) { + const DeviceRRGSB& device_rr_gsb, + const bool& verbose) { vtr::ScopedStartFinishTimer timer( "Build tile-level information for the FPGA fabric"); @@ -125,7 +131,7 @@ int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, /* Depending on the selected style, follow different approaches */ if (tile_config.style() == TileConfig::e_style::TOP_LEFT) { status_code = - build_fabric_tile_style_top_left(fabric_tile, grids, device_rr_gsb); + build_fabric_tile_style_top_left(fabric_tile, grids, device_rr_gsb, verbose); } else { /* Error out for styles that are not supported yet! */ VTR_LOG_ERROR("Tile style '%s' is not supported yet!\n", @@ -140,6 +146,7 @@ int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, /* Build unique tiles to compress the number of tile modules to be built in * later steps */ status_code = fabric_tile.build_unique_tiles(grids, device_rr_gsb); + VTR_LOGV(verbose, "Extracted %lu uniques tiles from the FPGA fabric\n", fabric_tile.unique_tiles().size()); return status_code; } diff --git a/openfpga/src/fabric/build_fabric_tile.h b/openfpga/src/fabric/build_fabric_tile.h index 1c5ab66a1..c9c89adad 100644 --- a/openfpga/src/fabric/build_fabric_tile.h +++ b/openfpga/src/fabric/build_fabric_tile.h @@ -19,9 +19,11 @@ /* begin namespace openfpga */ namespace openfpga { -int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, +int build_fabric_tile(FabricTile& fabric_tile, + const TileConfig& tile_config, const DeviceGrid& grids, - const DeviceRRGSB& device_rr_gsb); + const DeviceRRGSB& device_rr_gsb, + 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 20a9a37ad..3fecea1de 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -993,7 +993,7 @@ static int build_tile_module( /* Create the module */ vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); std::string module_name = generate_tile_module_name(tile_coord); - VTR_LOGV(verbose, "Building tile module '%s'...\n"); + VTR_LOGV(verbose, "Building tile module '%s'...\n", module_name.c_str()); ModuleId tile_module = module_manager.add_module(module_name); /* Add instance of programmable block */ From d03fa92ddf38ede64b8283b759780b0a9538bbc8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Jul 2023 12:49:35 -0700 Subject: [PATCH 184/391] [core] debugging --- openfpga/src/annotation/fabric_tile.cpp | 12 ++++++------ openfpga/src/fabric/build_tile_modules.cpp | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index 9e6b16ad2..d0a6370d9 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -73,7 +73,7 @@ FabricTileId FabricTile::find_tile(const vtr::Point& coord) const { bool FabricTile::pb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { - return find_pb_index_in_tile(tile_id, coord) != pb_coords_.size(); + return !pb_coords_[tile_id].empty() && find_pb_index_in_tile(tile_id, coord) != pb_coords_[tile_id].size(); } size_t FabricTile::find_pb_index_in_tile( @@ -91,7 +91,7 @@ size_t FabricTile::find_pb_index_in_tile( bool FabricTile::sb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { - return find_sb_index_in_tile(tile_id, coord) != sb_coords_.size(); + return !sb_coords_[tile_id].empty() && find_sb_index_in_tile(tile_id, coord) != sb_coords_[tile_id].size(); } size_t FabricTile::find_sb_index_in_tile( @@ -112,11 +112,11 @@ bool FabricTile::cb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { switch (cb_type) { case CHANX: - return find_cb_index_in_tile(tile_id, cb_type, coord) == - cbx_coords_.size(); + return !cbx_coords_[tile_id].empty() && find_cb_index_in_tile(tile_id, cb_type, coord) == + cbx_coords_[tile_id].size(); case CHANY: - return find_cb_index_in_tile(tile_id, cb_type, coord) == - cby_coords_.size(); + return !cby_coords_[tile_id].empty() && find_cb_index_in_tile(tile_id, cb_type, coord) == + cby_coords_[tile_id].size(); default: VTR_LOG("Invalid type of connection block!\n"); exit(1); diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 3fecea1de..dab4005cd 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -168,6 +168,7 @@ static int build_tile_module_port_and_nets_between_sb_and_pb( /* Check if the grid is inside the tile, if not, create ports */ if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { + VTR_LOGV(verbose, "Build intra-tile nets between switch block '%s' and programmable block '%s'\n", sink_sb_module_name.c_str(), src_grid_module_name.c_str()); if (!frame_view) { size_t src_grid_instance = pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, @@ -1005,6 +1006,7 @@ static int build_tile_module( vtr::Point grid_coord = grid_rr_gsb.get_grid_coordinate(); t_physical_tile_type_ptr phy_tile = grids.get_physical_type(grid_coord.x(), grid_coord.y()); + VTR_LOGV(verbose, "Try to find pb at [%lu][%lu]\n", grid_coord.x(), grid_coord.y()); /* Empty type does not require a module */ if (!is_empty_type(phy_tile)) { e_side grid_side = find_grid_border_side( From 001b3b3f8bfbdd0ee0a5e47d6376dad09cc68f23 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Jul 2023 14:38:07 -0700 Subject: [PATCH 185/391] [core] debugging --- openfpga/src/annotation/fabric_tile.cpp | 9 ++++++++- openfpga/src/fabric/build_fabric_tile.cpp | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index d0a6370d9..4c2548dc7 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -283,9 +283,16 @@ bool FabricTile::equivalent_tile(const FabricTileId& tile_a, } /* The pb of two tiles should be the same, otherwise not equivalent */ for (size_t iblk = 0; iblk < pb_coords_[tile_a].size(); ++iblk) { - if (generate_grid_block_module_name_in_top_module(std::string(), grids, pb_coords_[tile_a][iblk]) != generate_grid_block_module_name_in_top_module(std::string(), grids, pb_coords_[tile_b][iblk])) { + if (device_rr_gsb.is_gsb_exist(pb_coords_[tile_a][iblk]) != device_rr_gsb.is_gsb_exist(pb_coords_[tile_b][iblk])) { return false; } + if (device_rr_gsb.is_gsb_exist(pb_coords_[tile_a][iblk])) { + vtr::Point tile_a_pb_coord = device_rr_gsb.get_gsb(pb_coords_[tile_a][iblk]).get_sb_coordinate(); + vtr::Point tile_b_pb_coord = device_rr_gsb.get_gsb(pb_coords_[tile_b][iblk]).get_sb_coordinate(); + if (generate_grid_block_module_name_in_top_module(std::string(), grids, tile_a_pb_coord) != generate_grid_block_module_name_in_top_module(std::string(), grids, tile_b_pb_coord)) { + return false; + } + } } /* Each CBx should have the same unique modules in the device rr_gsb */ for (size_t iblk = 0; iblk < cbx_coords_[tile_a].size(); ++iblk) { diff --git a/openfpga/src/fabric/build_fabric_tile.cpp b/openfpga/src/fabric/build_fabric_tile.cpp index 4f8e31561..328c91b7b 100644 --- a/openfpga/src/fabric/build_fabric_tile.cpp +++ b/openfpga/src/fabric/build_fabric_tile.cpp @@ -78,7 +78,7 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, /* Add components: pb, cbx, cby, and sb if exists */ if (!skip_add_pb) { - fabric_tile.add_pb_coordinate(curr_tile_id, curr_tile_coord); + fabric_tile.add_pb_coordinate(curr_tile_id, curr_gsb_coord); } /* The gsb coordinate is different than the grid coordinate when the * top-left style is considered From 778d03610ce3affed93efd3945143f2f47696925 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Jul 2023 15:27:05 -0700 Subject: [PATCH 186/391] [core] debugging --- openfpga/src/annotation/fabric_tile.cpp | 17 ++++++++++++----- openfpga/src/annotation/fabric_tile.h | 2 ++ openfpga/src/fabric/build_tile_modules.cpp | 10 +++++----- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index 4c2548dc7..abb0bff3c 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -72,21 +72,28 @@ FabricTileId FabricTile::find_tile(const vtr::Point& coord) const { } bool FabricTile::pb_in_tile(const FabricTileId& tile_id, + const DeviceRRGSB& device_rr_gsb, const vtr::Point& coord) const { - return !pb_coords_[tile_id].empty() && find_pb_index_in_tile(tile_id, coord) != pb_coords_[tile_id].size(); + return !pb_coords_[tile_id].empty() && find_pb_index_in_tile(tile_id, device_rr_gsb, coord) != pb_coords_[tile_id].size(); } size_t FabricTile::find_pb_index_in_tile( - const FabricTileId& tile_id, const vtr::Point& coord) const { + const FabricTileId& tile_id, + const DeviceRRGSB& device_rr_gsb, + const vtr::Point& coord) const { VTR_ASSERT(valid_tile_id(tile_id)); for (size_t idx = 0; idx < pb_coords_[tile_id].size(); ++idx) { - vtr::Point curr_coord = pb_coords_[tile_id][idx]; + /* Convert gsb coordinate to actual pb coordinate */ + if (!device_rr_gsb.is_gsb_exist(pb_coords_[tile_id][idx])) { + continue; + } + vtr::Point curr_coord = device_rr_gsb.get_gsb(pb_coords_[tile_id][idx]).get_grid_coordinate(); if (curr_coord == coord) { return idx; } } /* Not found, return an invalid index */ - return pb_coords_.size(); + return pb_coords_[tile_id].size(); } bool FabricTile::sb_in_tile(const FabricTileId& tile_id, @@ -104,7 +111,7 @@ size_t FabricTile::find_sb_index_in_tile( } } /* Not found, return an invalid index */ - return sb_coords_.size(); + return sb_coords_[tile_id].size(); } bool FabricTile::cb_in_tile(const FabricTileId& tile_id, diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h index d67f73506..eb97ea46b 100644 --- a/openfpga/src/annotation/fabric_tile.h +++ b/openfpga/src/annotation/fabric_tile.h @@ -41,6 +41,7 @@ class FabricTile { /** @brief Find the index of a programmable block in the internal list by a * given coordinate. */ size_t find_pb_index_in_tile(const FabricTileId& tile_id, + const DeviceRRGSB& device_rr_gsb, const vtr::Point& coord) const; /** @brief Find the index of a switch block in the internal list by a given * coordinate. */ @@ -54,6 +55,7 @@ class FabricTile { /** @brief Check if a programmable block (with a coordinate) exists in a tile */ bool pb_in_tile(const FabricTileId& tile_id, + const DeviceRRGSB& device_rr_gsb, const vtr::Point& coord) const; /** @brief Check if a switch block (with a coordinate) exists in a tile */ bool sb_in_tile(const FabricTileId& tile_id, diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index dab4005cd..8c0878bb8 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -167,11 +167,11 @@ static int build_tile_module_port_and_nets_between_sb_and_pb( module_manager.module_port(sink_sb_module, sink_sb_port_id); /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { - VTR_LOGV(verbose, "Build intra-tile nets between switch block '%s' and programmable block '%s'\n", sink_sb_module_name.c_str(), src_grid_module_name.c_str()); + if (fabric_tile.pb_in_tile(fabric_tile_id, device_rr_gsb, grid_coordinate)) { + VTR_LOGV(verbose, "Build intra-tile nets between switch block '%s' and programmable block '%s[%lu][%lu]'\n", sink_sb_module_name.c_str(), src_grid_module_name.c_str(), grid_coordinate.x(), grid_coordinate.y()); if (!frame_view) { size_t src_grid_instance = - pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, + pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, device_rr_gsb, grid_coordinate)]; /* Source and sink port should match in size */ @@ -385,10 +385,10 @@ static int build_tile_module_port_and_nets_between_cb_and_pb( module_manager.module_port(sink_grid_module, sink_grid_port_id); /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { + if (fabric_tile.pb_in_tile(fabric_tile_id, device_rr_gsb, grid_coordinate)) { if (!frame_view) { size_t sink_grid_instance = - pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, + pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, device_rr_gsb, grid_coordinate)]; /* Source and sink port should match in size */ From 0d03d7b483772104ef0c3e4334a93f72c088461b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Jul 2023 17:20:53 -0700 Subject: [PATCH 187/391] [core] now fabric tile cache both grid and gsb coord for pb --- openfpga/src/annotation/fabric_tile.cpp | 56 +++++++++++++--------- openfpga/src/annotation/fabric_tile.h | 20 ++++---- openfpga/src/fabric/build_fabric_tile.cpp | 2 +- openfpga/src/fabric/build_tile_modules.cpp | 12 ++--- 4 files changed, 51 insertions(+), 39 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index abb0bff3c..6e84b71d4 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -72,28 +72,38 @@ FabricTileId FabricTile::find_tile(const vtr::Point& coord) const { } bool FabricTile::pb_in_tile(const FabricTileId& tile_id, - const DeviceRRGSB& device_rr_gsb, - const vtr::Point& coord) const { - return !pb_coords_[tile_id].empty() && find_pb_index_in_tile(tile_id, device_rr_gsb, coord) != pb_coords_[tile_id].size(); + const vtr::Point& coord, + const bool& use_gsb_coord) const { + if (use_gsb_coord) { + return !pb_gsb_coords_[tile_id].empty() && find_pb_index_in_tile(tile_id, coord, use_gsb_coord) != pb_gsb_coords_[tile_id].size(); + } + return !pb_coords_[tile_id].empty() && find_pb_index_in_tile(tile_id, coord) != pb_coords_[tile_id].size(); } size_t FabricTile::find_pb_index_in_tile( const FabricTileId& tile_id, - const DeviceRRGSB& device_rr_gsb, - const vtr::Point& coord) const { + const vtr::Point& coord, + const bool& use_gsb_coord) const { VTR_ASSERT(valid_tile_id(tile_id)); - for (size_t idx = 0; idx < pb_coords_[tile_id].size(); ++idx) { - /* Convert gsb coordinate to actual pb coordinate */ - if (!device_rr_gsb.is_gsb_exist(pb_coords_[tile_id][idx])) { - continue; + if (use_gsb_coord) { + for (size_t idx = 0; idx < pb_gsb_coords_[tile_id].size(); ++idx) { + vtr::Point curr_coord = pb_gsb_coords_[tile_id][idx]; + if (curr_coord == coord) { + return idx; + } } - vtr::Point curr_coord = device_rr_gsb.get_gsb(pb_coords_[tile_id][idx]).get_grid_coordinate(); - if (curr_coord == coord) { - return idx; + /* Not found, return an invalid index */ + return pb_gsb_coords_[tile_id].size(); + } else { + for (size_t idx = 0; idx < pb_coords_[tile_id].size(); ++idx) { + vtr::Point curr_coord = pb_coords_[tile_id][idx]; + if (curr_coord == coord) { + return idx; + } } + /* Not found, return an invalid index */ + return pb_coords_[tile_id].size(); } - /* Not found, return an invalid index */ - return pb_coords_[tile_id].size(); } bool FabricTile::sb_in_tile(const FabricTileId& tile_id, @@ -168,6 +178,7 @@ FabricTileId FabricTile::create_tile(const vtr::Point& coord) { ids_.push_back(tile_id); coords_.push_back(coord); pb_coords_.emplace_back(); + pb_gsb_coords_.emplace_back(); cbx_coords_.emplace_back(); cby_coords_.emplace_back(); sb_coords_.emplace_back(); @@ -233,9 +244,11 @@ bool FabricTile::set_tile_coordinate(const FabricTileId& tile_id, } void FabricTile::add_pb_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord) { + const vtr::Point& coord, + const vtr::Point& gsb_coord) { VTR_ASSERT(valid_tile_id(tile_id)); pb_coords_[tile_id].push_back(coord); + pb_gsb_coords_[tile_id].push_back(gsb_coord); } void FabricTile::add_cb_coordinate(const FabricTileId& tile_id, @@ -265,6 +278,7 @@ void FabricTile::clear() { ids_.clear(); coords_.clear(); pb_coords_.clear(); + pb_gsb_coords_.clear(); cbx_coords_.clear(); cby_coords_.clear(); sb_coords_.clear(); @@ -283,6 +297,7 @@ bool FabricTile::equivalent_tile(const FabricTileId& tile_a, const DeviceRRGSB& device_rr_gsb) const { /* The number of cbx, cby and sb blocks should be the same */ if (pb_coords_[tile_a].size() != pb_coords_[tile_b].size() || + pb_gsb_coords_[tile_a].size() != pb_gsb_coords_[tile_b].size() || cbx_coords_[tile_a].size() != cbx_coords_[tile_b].size() || cby_coords_[tile_a].size() != cby_coords_[tile_b].size() || sb_coords_[tile_a].size() != sb_coords_[tile_b].size()) { @@ -290,16 +305,11 @@ bool FabricTile::equivalent_tile(const FabricTileId& tile_a, } /* The pb of two tiles should be the same, otherwise not equivalent */ for (size_t iblk = 0; iblk < pb_coords_[tile_a].size(); ++iblk) { - if (device_rr_gsb.is_gsb_exist(pb_coords_[tile_a][iblk]) != device_rr_gsb.is_gsb_exist(pb_coords_[tile_b][iblk])) { + vtr::Point tile_a_pb_coord = pb_coords_[tile_a][iblk]; + vtr::Point tile_b_pb_coord = pb_coords_[tile_b][iblk]; + if (generate_grid_block_module_name_in_top_module(std::string(), grids, tile_a_pb_coord) != generate_grid_block_module_name_in_top_module(std::string(), grids, tile_b_pb_coord)) { return false; } - if (device_rr_gsb.is_gsb_exist(pb_coords_[tile_a][iblk])) { - vtr::Point tile_a_pb_coord = device_rr_gsb.get_gsb(pb_coords_[tile_a][iblk]).get_sb_coordinate(); - vtr::Point tile_b_pb_coord = device_rr_gsb.get_gsb(pb_coords_[tile_b][iblk]).get_sb_coordinate(); - if (generate_grid_block_module_name_in_top_module(std::string(), grids, tile_a_pb_coord) != generate_grid_block_module_name_in_top_module(std::string(), grids, tile_b_pb_coord)) { - return false; - } - } } /* Each CBx should have the same unique modules in the device rr_gsb */ for (size_t iblk = 0; iblk < cbx_coords_[tile_a].size(); ++iblk) { diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h index eb97ea46b..0b2803342 100644 --- a/openfpga/src/annotation/fabric_tile.h +++ b/openfpga/src/annotation/fabric_tile.h @@ -39,10 +39,10 @@ class FabricTile { /** @brief Return a list of unique tiles */ std::vector unique_tiles() const; /** @brief Find the index of a programmable block in the internal list by a - * given coordinate. */ + * given coordinate. Note that the coord can be either the one in device grid or the one of gsb which the programmable block belongs to */ size_t find_pb_index_in_tile(const FabricTileId& tile_id, - const DeviceRRGSB& device_rr_gsb, - const vtr::Point& coord) const; + const vtr::Point& coord, + const bool& use_gsb_coord = false) const; /** @brief Find the index of a switch block in the internal list by a given * coordinate. */ size_t find_sb_index_in_tile(const FabricTileId& tile_id, @@ -52,11 +52,11 @@ class FabricTile { size_t find_cb_index_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord) const; - /** @brief Check if a programmable block (with a coordinate) exists in a tile + /** @brief Check if a programmable block (with a coordinate) exists in a tile. Note that the coord can be either the one in device grid or the one of gsb which the programmable block belongs to */ bool pb_in_tile(const FabricTileId& tile_id, - const DeviceRRGSB& device_rr_gsb, - const vtr::Point& coord) const; + const vtr::Point& coord, + const bool& use_gsb_coord = false) const; /** @brief Check if a switch block (with a coordinate) exists in a tile */ bool sb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const; @@ -71,7 +71,8 @@ class FabricTile { bool set_tile_coordinate(const FabricTileId& tile_id, const vtr::Point& coord); void add_pb_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord); + const vtr::Point& coord, + const vtr::Point& gsb_coord); void add_cb_coordinate(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord); void add_sb_coordinate(const FabricTileId& tile_id, @@ -105,8 +106,11 @@ class FabricTile { private: /* Internal Data */ vtr::vector ids_; vtr::vector> coords_; - /* Coordinates w.r.t. RRGSB */ + /* Coordinates w.r.t. RRGSB + * Note that we keep two coordinates for the programmable block: regular one (in device grid) and the one in gsb. This is to ease the lookup/search for coordinates through both device grid and gsb. Client functions need one of the them depending on the scenario. In future, once we refactor the RRGSB organization (to follow bottom-left corner style). This limitation can be resolved. + */ vtr::vector>> pb_coords_; + vtr::vector>> pb_gsb_coords_; vtr::vector>> cbx_coords_; vtr::vector>> cby_coords_; vtr::vector>> sb_coords_; diff --git a/openfpga/src/fabric/build_fabric_tile.cpp b/openfpga/src/fabric/build_fabric_tile.cpp index 328c91b7b..e985d15f1 100644 --- a/openfpga/src/fabric/build_fabric_tile.cpp +++ b/openfpga/src/fabric/build_fabric_tile.cpp @@ -78,7 +78,7 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, /* Add components: pb, cbx, cby, and sb if exists */ if (!skip_add_pb) { - fabric_tile.add_pb_coordinate(curr_tile_id, curr_gsb_coord); + fabric_tile.add_pb_coordinate(curr_tile_id, curr_tile_coord, curr_gsb_coord); } /* The gsb coordinate is different than the grid coordinate when the * top-left style is considered diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 8c0878bb8..8c7c3eb66 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -167,11 +167,11 @@ static int build_tile_module_port_and_nets_between_sb_and_pb( module_manager.module_port(sink_sb_module, sink_sb_port_id); /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.pb_in_tile(fabric_tile_id, device_rr_gsb, grid_coordinate)) { + if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { VTR_LOGV(verbose, "Build intra-tile nets between switch block '%s' and programmable block '%s[%lu][%lu]'\n", sink_sb_module_name.c_str(), src_grid_module_name.c_str(), grid_coordinate.x(), grid_coordinate.y()); if (!frame_view) { size_t src_grid_instance = - pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, device_rr_gsb, + pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, grid_coordinate)]; /* Source and sink port should match in size */ @@ -385,10 +385,10 @@ static int build_tile_module_port_and_nets_between_cb_and_pb( module_manager.module_port(sink_grid_module, sink_grid_port_id); /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.pb_in_tile(fabric_tile_id, device_rr_gsb, grid_coordinate)) { + if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { if (!frame_view) { size_t sink_grid_instance = - pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, device_rr_gsb, + pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, grid_coordinate)]; /* Source and sink port should match in size */ @@ -1000,10 +1000,8 @@ static int build_tile_module( /* Add instance of programmable block */ std::vector pb_instances; /* Keep tracking the instance id of each pb */ - for (vtr::Point grid_gsb_coord : + for (vtr::Point grid_coord : fabric_tile.pb_coordinates(fabric_tile_id)) { - const RRGSB& grid_rr_gsb = device_rr_gsb.get_gsb(grid_gsb_coord); - vtr::Point grid_coord = grid_rr_gsb.get_grid_coordinate(); t_physical_tile_type_ptr phy_tile = grids.get_physical_type(grid_coord.x(), grid_coord.y()); VTR_LOGV(verbose, "Try to find pb at [%lu][%lu]\n", grid_coord.x(), grid_coord.y()); From 2e69eebea07e467fe87e4407a93829a8ca004d22 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Jul 2023 17:23:44 -0700 Subject: [PATCH 188/391] [core] now tile module builder is working --- openfpga/src/fabric/build_tile_modules.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 8c7c3eb66..d229fe40c 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -423,7 +423,7 @@ static int build_tile_module_port_and_nets_between_cb_and_pb( ++pin_id) { ModuleNetId net = create_module_source_pin_net( module_manager, tile_module, src_cb_module, src_cb_instance, - sink_tile_port_id, src_cb_port.pins()[pin_id]); + src_cb_port_id, src_cb_port.pins()[pin_id]); /* Configure the net sink */ module_manager.add_module_net_sink(tile_module, net, tile_module, 0, sink_tile_port_id, From a06b9a0f488b8626783d53faea2f3b772d8d6757 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Jul 2023 22:22:07 -0700 Subject: [PATCH 189/391] [core] now start to develop the tile instances under the top module --- openfpga/src/annotation/fabric_tile.cpp | 34 ++++++---- openfpga/src/annotation/fabric_tile.h | 17 +++-- openfpga/src/fabric/build_fabric_tile.cpp | 37 +++++++---- openfpga/src/fabric/build_fabric_tile.h | 6 +- openfpga/src/fabric/build_tile_modules.cpp | 13 ++-- openfpga/src/fabric/build_top_module.cpp | 75 +++++++++++++++------- 6 files changed, 119 insertions(+), 63 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index 6e84b71d4..24072a5ba 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -1,9 +1,9 @@ /************************************************************************ * Member functions for class FabricTile ***********************************************************************/ -#include "build_top_module_utils.h" #include "fabric_tile.h" +#include "build_top_module_utils.h" #include "vtr_assert.h" #include "vtr_log.h" @@ -75,15 +75,17 @@ bool FabricTile::pb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord, const bool& use_gsb_coord) const { if (use_gsb_coord) { - return !pb_gsb_coords_[tile_id].empty() && find_pb_index_in_tile(tile_id, coord, use_gsb_coord) != pb_gsb_coords_[tile_id].size(); + return !pb_gsb_coords_[tile_id].empty() && + find_pb_index_in_tile(tile_id, coord, use_gsb_coord) != + pb_gsb_coords_[tile_id].size(); } - return !pb_coords_[tile_id].empty() && find_pb_index_in_tile(tile_id, coord) != pb_coords_[tile_id].size(); + return !pb_coords_[tile_id].empty() && + find_pb_index_in_tile(tile_id, coord) != pb_coords_[tile_id].size(); } -size_t FabricTile::find_pb_index_in_tile( - const FabricTileId& tile_id, - const vtr::Point& coord, - const bool& use_gsb_coord) const { +size_t FabricTile::find_pb_index_in_tile(const FabricTileId& tile_id, + const vtr::Point& coord, + const bool& use_gsb_coord) const { VTR_ASSERT(valid_tile_id(tile_id)); if (use_gsb_coord) { for (size_t idx = 0; idx < pb_gsb_coords_[tile_id].size(); ++idx) { @@ -108,7 +110,8 @@ size_t FabricTile::find_pb_index_in_tile( bool FabricTile::sb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { - return !sb_coords_[tile_id].empty() && find_sb_index_in_tile(tile_id, coord) != sb_coords_[tile_id].size(); + return !sb_coords_[tile_id].empty() && + find_sb_index_in_tile(tile_id, coord) != sb_coords_[tile_id].size(); } size_t FabricTile::find_sb_index_in_tile( @@ -129,11 +132,13 @@ bool FabricTile::cb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord) const { switch (cb_type) { case CHANX: - return !cbx_coords_[tile_id].empty() && find_cb_index_in_tile(tile_id, cb_type, coord) == - cbx_coords_[tile_id].size(); + return !cbx_coords_[tile_id].empty() && + find_cb_index_in_tile(tile_id, cb_type, coord) == + cbx_coords_[tile_id].size(); case CHANY: - return !cby_coords_[tile_id].empty() && find_cb_index_in_tile(tile_id, cb_type, coord) == - cby_coords_[tile_id].size(); + return !cby_coords_[tile_id].empty() && + find_cb_index_in_tile(tile_id, cb_type, coord) == + cby_coords_[tile_id].size(); default: VTR_LOG("Invalid type of connection block!\n"); exit(1); @@ -307,7 +312,10 @@ bool FabricTile::equivalent_tile(const FabricTileId& tile_a, for (size_t iblk = 0; iblk < pb_coords_[tile_a].size(); ++iblk) { vtr::Point tile_a_pb_coord = pb_coords_[tile_a][iblk]; vtr::Point tile_b_pb_coord = pb_coords_[tile_b][iblk]; - if (generate_grid_block_module_name_in_top_module(std::string(), grids, tile_a_pb_coord) != generate_grid_block_module_name_in_top_module(std::string(), grids, tile_b_pb_coord)) { + if (generate_grid_block_module_name_in_top_module(std::string(), grids, + tile_a_pb_coord) != + generate_grid_block_module_name_in_top_module(std::string(), grids, + tile_b_pb_coord)) { return false; } } diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h index 0b2803342..323d5d7ec 100644 --- a/openfpga/src/annotation/fabric_tile.h +++ b/openfpga/src/annotation/fabric_tile.h @@ -39,7 +39,8 @@ class FabricTile { /** @brief Return a list of unique tiles */ std::vector unique_tiles() const; /** @brief Find the index of a programmable block in the internal list by a - * given coordinate. Note that the coord can be either the one in device grid or the one of gsb which the programmable block belongs to */ + * given coordinate. Note that the coord can be either the one in device grid + * or the one of gsb which the programmable block belongs to */ size_t find_pb_index_in_tile(const FabricTileId& tile_id, const vtr::Point& coord, const bool& use_gsb_coord = false) const; @@ -52,10 +53,11 @@ class FabricTile { size_t find_cb_index_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord) const; - /** @brief Check if a programmable block (with a coordinate) exists in a tile. Note that the coord can be either the one in device grid or the one of gsb which the programmable block belongs to + /** @brief Check if a programmable block (with a coordinate) exists in a tile. + * Note that the coord can be either the one in device grid or the one of gsb + * which the programmable block belongs to */ - bool pb_in_tile(const FabricTileId& tile_id, - const vtr::Point& coord, + bool pb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord, const bool& use_gsb_coord = false) const; /** @brief Check if a switch block (with a coordinate) exists in a tile */ bool sb_in_tile(const FabricTileId& tile_id, @@ -107,7 +109,12 @@ class FabricTile { vtr::vector ids_; vtr::vector> coords_; /* Coordinates w.r.t. RRGSB - * Note that we keep two coordinates for the programmable block: regular one (in device grid) and the one in gsb. This is to ease the lookup/search for coordinates through both device grid and gsb. Client functions need one of the them depending on the scenario. In future, once we refactor the RRGSB organization (to follow bottom-left corner style). This limitation can be resolved. + * Note that we keep two coordinates for the programmable block: regular one + * (in device grid) and the one in gsb. This is to ease the lookup/search for + * coordinates through both device grid and gsb. Client functions need one of + * the them depending on the scenario. In future, once we refactor the RRGSB + * organization (to follow bottom-left corner style). This limitation can be + * resolved. */ vtr::vector>> pb_coords_; vtr::vector>> pb_gsb_coords_; diff --git a/openfpga/src/fabric/build_fabric_tile.cpp b/openfpga/src/fabric/build_fabric_tile.cpp index e985d15f1..25746876d 100644 --- a/openfpga/src/fabric/build_fabric_tile.cpp +++ b/openfpga/src/fabric/build_fabric_tile.cpp @@ -46,15 +46,19 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, if (true == is_empty_type(phy_tile_type)) { skip_add_pb = true; if (!device_rr_gsb.is_gsb_exist(curr_gsb_coord)) { - VTR_LOGV(verbose, "Skip tile[%lu][%lu] as it is empty\n", curr_tile_coord.x(), curr_tile_coord.y()); + VTR_LOGV(verbose, "Skip tile[%lu][%lu] as it is empty\n", + curr_tile_coord.x(), curr_tile_coord.y()); continue; } /* Need to create a new tile here */ - VTR_LOGV(verbose, "Create tile[%lu][%lu] which only has routing but not a programmable block\n", curr_tile_coord.x(), curr_tile_coord.y()); + VTR_LOGV(verbose, + "Create tile[%lu][%lu] which only has routing but not a " + "programmable block\n", + curr_tile_coord.x(), curr_tile_coord.y()); curr_tile_id = fabric_tile.create_tile(curr_tile_coord); } else if ((0 < grids.get_width_offset(ix, iy)) || - (0 < grids.get_height_offset(ix, iy))) { - /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ + (0 < grids.get_height_offset(ix, iy))) { + /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ /* Find the root of this grid, the instance id should be valid. * We just copy it here */ @@ -62,11 +66,16 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, ix - grids.get_width_offset(ix, iy), iy - grids.get_height_offset(ix, iy)); skip_add_pb = true; - VTR_LOGV(verbose, "Tile[%lu][%lu] contains a heterogeneous block which is rooted from tile[%lu][%lu]\n", curr_tile_coord.x(), curr_tile_coord.y(), root_tile_coord.x(), root_tile_coord.y()); + VTR_LOGV(verbose, + "Tile[%lu][%lu] contains a heterogeneous block which is " + "rooted from tile[%lu][%lu]\n", + curr_tile_coord.x(), curr_tile_coord.y(), root_tile_coord.x(), + root_tile_coord.y()); curr_tile_id = fabric_tile.find_tile(root_tile_coord); } else { /* Need to create a new tile here */ - VTR_LOGV(verbose, "Create a regular tile[%lu][%lu]\n", curr_tile_coord.x(), curr_tile_coord.y()); + VTR_LOGV(verbose, "Create a regular tile[%lu][%lu]\n", + curr_tile_coord.x(), curr_tile_coord.y()); curr_tile_id = fabric_tile.create_tile(curr_tile_coord); } @@ -78,7 +87,8 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, /* Add components: pb, cbx, cby, and sb if exists */ if (!skip_add_pb) { - fabric_tile.add_pb_coordinate(curr_tile_id, curr_tile_coord, curr_gsb_coord); + fabric_tile.add_pb_coordinate(curr_tile_id, curr_tile_coord, + curr_gsb_coord); } /* The gsb coordinate is different than the grid coordinate when the * top-left style is considered @@ -116,10 +126,8 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, /******************************************************************** * Build tile-level information for a given FPGA fabric, w.r.t. to configuration *******************************************************************/ -int build_fabric_tile(FabricTile& fabric_tile, - const TileConfig& tile_config, - const DeviceGrid& grids, - const DeviceRRGSB& device_rr_gsb, +int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, + const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb, const bool& verbose) { vtr::ScopedStartFinishTimer timer( "Build tile-level information for the FPGA fabric"); @@ -130,8 +138,8 @@ int build_fabric_tile(FabricTile& fabric_tile, /* Depending on the selected style, follow different approaches */ if (tile_config.style() == TileConfig::e_style::TOP_LEFT) { - status_code = - build_fabric_tile_style_top_left(fabric_tile, grids, device_rr_gsb, verbose); + status_code = build_fabric_tile_style_top_left(fabric_tile, grids, + device_rr_gsb, verbose); } else { /* Error out for styles that are not supported yet! */ VTR_LOG_ERROR("Tile style '%s' is not supported yet!\n", @@ -146,7 +154,8 @@ int build_fabric_tile(FabricTile& fabric_tile, /* Build unique tiles to compress the number of tile modules to be built in * later steps */ status_code = fabric_tile.build_unique_tiles(grids, device_rr_gsb); - VTR_LOGV(verbose, "Extracted %lu uniques tiles from the FPGA fabric\n", fabric_tile.unique_tiles().size()); + VTR_LOGV(verbose, "Extracted %lu uniques tiles from the FPGA fabric\n", + fabric_tile.unique_tiles().size()); return status_code; } diff --git a/openfpga/src/fabric/build_fabric_tile.h b/openfpga/src/fabric/build_fabric_tile.h index c9c89adad..16b53f2f2 100644 --- a/openfpga/src/fabric/build_fabric_tile.h +++ b/openfpga/src/fabric/build_fabric_tile.h @@ -19,10 +19,8 @@ /* begin namespace openfpga */ namespace openfpga { -int build_fabric_tile(FabricTile& fabric_tile, - const TileConfig& tile_config, - const DeviceGrid& grids, - const DeviceRRGSB& device_rr_gsb, +int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, + const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb, 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 d229fe40c..6aafad219 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -168,10 +168,14 @@ static int build_tile_module_port_and_nets_between_sb_and_pb( /* Check if the grid is inside the tile, if not, create ports */ if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { - VTR_LOGV(verbose, "Build intra-tile nets between switch block '%s' and programmable block '%s[%lu][%lu]'\n", sink_sb_module_name.c_str(), src_grid_module_name.c_str(), grid_coordinate.x(), grid_coordinate.y()); + VTR_LOGV(verbose, + "Build intra-tile nets between switch block '%s' and " + "programmable block '%s[%lu][%lu]'\n", + sink_sb_module_name.c_str(), src_grid_module_name.c_str(), + grid_coordinate.x(), grid_coordinate.y()); if (!frame_view) { size_t src_grid_instance = - pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, + pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, grid_coordinate)]; /* Source and sink port should match in size */ @@ -388,7 +392,7 @@ static int build_tile_module_port_and_nets_between_cb_and_pb( if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { if (!frame_view) { size_t sink_grid_instance = - pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, + pb_instances[fabric_tile.find_pb_index_in_tile(fabric_tile_id, grid_coordinate)]; /* Source and sink port should match in size */ @@ -1004,7 +1008,8 @@ static int build_tile_module( fabric_tile.pb_coordinates(fabric_tile_id)) { t_physical_tile_type_ptr phy_tile = grids.get_physical_type(grid_coord.x(), grid_coord.y()); - VTR_LOGV(verbose, "Try to find pb at [%lu][%lu]\n", grid_coord.x(), grid_coord.y()); + VTR_LOGV(verbose, "Try to find pb at [%lu][%lu]\n", grid_coord.x(), + grid_coord.y()); /* Empty type does not require a module */ if (!is_empty_type(phy_tile)) { e_side grid_side = find_grid_border_side( diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index 1ef0b518c..b6525bc9f 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -421,18 +421,12 @@ static void add_top_module_io_children( } /******************************************************************** - * Print the top-level module for the FPGA fabric in Verilog format - * This function will - * 1. name the top-level module - * 2. include dependent netlists - * - User defined netlists - * - Auto-generated netlists - * 3. Add the submodules to the top-level graph - * 4. Add module nets to connect datapath ports - * 5. Add module nets/submodules to connect configuration ports + * Add the fine-grained instances to the top module of FPGA fabric + * The fine-grained instances include programmable blocks, connection blocks and + *switch blocks, each of which is an instance under the top module *******************************************************************/ -int build_top_module( - ModuleManager& module_manager, DecoderLibrary& decoder_lib, +static int build_top_module_fine_grained_child_instances( + ModuleManager& module_manager, const ModuleId& top_module, MemoryBankShiftRegisterBanks& blwl_sr_banks, const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup, @@ -442,19 +436,8 @@ int build_top_module( 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 bool& generate_random_fabric_key) { - vtr::ScopedStartFinishTimer timer("Build FPGA fabric module"); - + const FabricKey& fabric_key) { int status = CMD_EXEC_SUCCESS; - - /* Create a module as the top-level fabric, and add it to the module manager - */ - std::string top_module_name = generate_fpga_top_module_name(); - ModuleId top_module = module_manager.add_module(top_module_name); - - /* Label module usage */ - module_manager.set_module_usage(top_module, ModuleManager::MODULE_TOP); - std::map> cb_instance_ids; /* Add sub modules, which are grid, SB and CBX/CBY modules as instances */ @@ -551,6 +534,52 @@ int build_top_module( return status; } } + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Print the top-level module for the FPGA fabric in Verilog format + * This function will + * 1. name the top-level module + * 2. include dependent netlists + * - User defined netlists + * - Auto-generated netlists + * 3. Add the submodules to the top-level graph + * 4. Add module nets to connect datapath ports + * 5. Add module nets/submodules to connect configuration ports + *******************************************************************/ +int build_top_module( + ModuleManager& module_manager, DecoderLibrary& decoder_lib, + MemoryBankShiftRegisterBanks& blwl_sr_banks, + const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, + const RRClockSpatialLookup& rr_clock_lookup, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, + const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, + 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 bool& generate_random_fabric_key) { + vtr::ScopedStartFinishTimer timer("Build FPGA fabric module"); + + int status = CMD_EXEC_SUCCESS; + + /* Create a module as the top-level fabric, and add it to the module manager + */ + std::string top_module_name = generate_fpga_top_module_name(); + ModuleId top_module = module_manager.add_module(top_module_name); + + /* Label module usage */ + module_manager.set_module_usage(top_module, ModuleManager::MODULE_TOP); + + status = build_top_module_fine_grained_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, config_protocol, sram_model, + frame_view, compact_routing_hierarchy, duplicate_grid_pin, fabric_key); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } /* Shuffle the configurable children in a random sequence */ if (true == generate_random_fabric_key) { From bd265334b5da4969f12ae354cd7d0feb9e84fdfe Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Jul 2023 23:26:55 -0700 Subject: [PATCH 190/391] [core] added tile instances to top module builder --- openfpga/src/fabric/build_device_module.cpp | 4 +- openfpga/src/fabric/build_top_module.cpp | 150 +++++++++++++++++- openfpga/src/fabric/build_top_module.h | 8 +- .../src/utils/openfpga_device_grid_utils.cpp | 59 +++++++ .../src/utils/openfpga_device_grid_utils.h | 3 + 5 files changed, 211 insertions(+), 13 deletions(-) diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index f38103c8f..0ba0dd167 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -127,8 +127,8 @@ int build_device_module_graph( openfpga_ctx.arch().tile_annotations, vpr_device_ctx.rr_graph, openfpga_ctx.device_rr_gsb(), openfpga_ctx.tile_direct(), openfpga_ctx.arch().arch_direct, openfpga_ctx.arch().config_protocol, - sram_model, frame_view, compress_routing, duplicate_grid_pin, fabric_key, - generate_random_fabric_key); + sram_model, fabric_tile, frame_view, compress_routing, duplicate_grid_pin, + fabric_key, generate_random_fabric_key); 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 b6525bc9f..745385cdf 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -537,6 +537,132 @@ static int build_top_module_fine_grained_child_instances( return CMD_EXEC_SUCCESS; } +/******************************************************************** + * Add a instance of a tile module to the top module + *******************************************************************/ +static size_t add_top_module_tile_instance(ModuleManager& module_manager, + const ModuleId& top_module, + const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id) { + /* Find the module name for this type of grid */ + vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); + FabricTileId unique_fabric_tile_id = fabric_tile.unique_tile(tile_coord); + vtr::Point unique_tile_coord = + fabric_tile.tile_coordinate(unique_fabric_tile_id); + std::string tile_module_name = generate_tile_module_name(unique_tile_coord); + ModuleId tile_module = module_manager.find_module(tile_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(tile_module)); + /* Record the instance id */ + size_t tile_instance = module_manager.num_instance(top_module, tile_module); + /* Add the module to top_module */ + module_manager.add_child_module(top_module, tile_module, false); + /* Set an unique name to the instance + * Note: it is your risk to gurantee the name is unique! + */ + std::string instance_name = generate_tile_module_name(tile_coord); + module_manager.set_child_instance_name(top_module, tile_module, tile_instance, + instance_name); + return tile_instance; +} + +/******************************************************************** + * Add all the tiles as sub-modules across the fabric + * Here, we will iterate over the full fabric (coordinates) + * and instanciate the tile modules + * + * Return an 2-D array of instance ids of the grid modules that + * have been added + * + * This function assumes an island-style floorplanning for FPGA fabric + * + * + * +-----------------------------------+ + * | I/O tiles | + * | TOP side | + * +-----------------------------------+ + * + * +-----------+ +-----------------------------------+ +------------+ + * | | | | | | + * | I/O tiles | | Core tiles | | I/O tiles | + * | LEFT side | | (CLB, Heterogeneous blocks, etc.) | | RIGHT side | + * | | | | | | + * +-----------+ +-----------------------------------+ +------------+ + * + * +-----------------------------------+ + * | I/O tiles | + * | BOTTOM side | + * +-----------------------------------+ + * + *******************************************************************/ +static int add_top_module_tile_instances(ModuleManager& module_manager, + const ModuleId& top_module, + vtr::Matrix& tile_instance_ids, + const DeviceGrid& grids, + const FabricTile& fabric_tile) { + vtr::ScopedStartFinishTimer timer("Add tile instances to top module"); + int status = CMD_EXEC_SUCCESS; + + /* Reserve an array for the instance ids */ + tile_instance_ids.resize({grids.width(), grids.height()}); + tile_instance_ids.fill(size_t(-1)); + + /* Instanciate I/O grids */ + /* Create the coordinate range for each side of FPGA fabric */ + std::map>> io_coordinates = + generate_perimeter_tile_coordinates(grids); + + for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { + for (const vtr::Point& io_coord : io_coordinates[io_side]) { + FabricTileId fabric_tile_id = fabric_tile.find_tile(io_coord); + if (!fabric_tile.valid_tile_id(fabric_tile_id)) { + continue; + } + /* Add a tile module to top_module*/ + tile_instance_ids[io_coord.x()][io_coord.y()] = + add_top_module_tile_instance(module_manager, top_module, fabric_tile, + fabric_tile_id); + } + } + + /* Instanciate core grids + * IMPORTANT: sequence matters here, it impacts the I/O indexing. + * We should follow the same sequence as the build_io_location_map()! + * If you change the sequence of walking through grids here, you should change + * it in the build_io_location map()! + */ + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + vtr::Point curr_coord(ix, iy); + FabricTileId fabric_tile_id = fabric_tile.find_tile(curr_coord); + if (!fabric_tile.valid_tile_id(fabric_tile_id)) { + continue; + } + /* Add a tile module to top_module*/ + tile_instance_ids[curr_coord.x()][curr_coord.y()] = + add_top_module_tile_instance(module_manager, top_module, fabric_tile, + fabric_tile_id); + } + } + return status; +} + +/******************************************************************** + * Add the tile-level instances to the top module of FPGA fabric + * and build connects between them + *******************************************************************/ +static int build_top_module_tile_child_instances( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceGrid& grids, const FabricTile& fabric_tile) { + int status = CMD_EXEC_SUCCESS; + vtr::Matrix tile_instance_ids; + status = add_top_module_tile_instances(module_manager, top_module, + tile_instance_ids, grids, fabric_tile); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + return CMD_EXEC_SUCCESS; +} + /******************************************************************** * Print the top-level module for the FPGA fabric in Verilog format * This function will @@ -557,9 +683,10 @@ int build_top_module( const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, 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 bool& generate_random_fabric_key) { + 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) { vtr::ScopedStartFinishTimer timer("Build FPGA fabric module"); int status = CMD_EXEC_SUCCESS; @@ -572,11 +699,18 @@ int build_top_module( /* Label module usage */ module_manager.set_module_usage(top_module, ModuleManager::MODULE_TOP); - status = build_top_module_fine_grained_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, config_protocol, sram_model, - frame_view, compact_routing_hierarchy, duplicate_grid_pin, fabric_key); + if (fabric_tile.empty()) { + status = build_top_module_fine_grained_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, config_protocol, sram_model, + frame_view, compact_routing_hierarchy, duplicate_grid_pin, fabric_key); + } else { + /* TODO: Build the tile instances under the top module */ + status = build_top_module_tile_child_instances(module_manager, top_module, + grids, fabric_tile); + } + if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } diff --git a/openfpga/src/fabric/build_top_module.h b/openfpga/src/fabric/build_top_module.h index 9a4fb8fe0..dbee3d6cd 100644 --- a/openfpga/src/fabric/build_top_module.h +++ b/openfpga/src/fabric/build_top_module.h @@ -15,6 +15,7 @@ #include "device_grid.h" #include "device_rr_gsb.h" #include "fabric_key.h" +#include "fabric_tile.h" #include "memory_bank_shift_register_banks.h" #include "module_manager.h" #include "rr_clock_spatial_lookup.h" @@ -40,9 +41,10 @@ int build_top_module( const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, 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 bool& generate_random_fabric_key); + 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); } /* end namespace openfpga */ diff --git a/openfpga/src/utils/openfpga_device_grid_utils.cpp b/openfpga/src/utils/openfpga_device_grid_utils.cpp index 5a234b60d..01437f48f 100644 --- a/openfpga/src/utils/openfpga_device_grid_utils.cpp +++ b/openfpga/src/utils/openfpga_device_grid_utils.cpp @@ -61,4 +61,63 @@ generate_perimeter_grid_coordinates(const DeviceGrid& grids) { return io_coordinates; } +/******************************************************************** + * Create a list of the coordinates for the tiles on the device perimeter + * It follows a clockwise sequence when including the coordinates. + * Note that different from the function for grids, this function include corner + *tiles! Detailed sequence is as follows: + * - Top-left + * - TOP side, from left most to the right + * - Top-right + * - Right side, from top to the bottom + * - Bottom-right + * - Bottom side, from right to the left + * - Bottom-left + * - Left side, from bottom to top + * + * Note: + * - This function offers a standard sequence to walk through the + * grids on the perimeter of an FPGA device + * When sequence matters, this function should be used to ensure + * consistency between functions. + *******************************************************************/ +std::map>> +generate_perimeter_tile_coordinates(const DeviceGrid& grids) { + /* Search the border side */ + /* Create the coordinate range for each side of FPGA fabric */ + std::vector fpga_sides{TOP, RIGHT, BOTTOM, LEFT}; + std::map>> io_coordinates; + + /* Top-left */ + io_coordinates[TOP].push_back(vtr::Point(0, grids.height() - 1)); + /* TOP side*/ + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + io_coordinates[TOP].push_back(vtr::Point(ix, grids.height() - 1)); + } + + /* Top-Right */ + io_coordinates[RIGHT].push_back( + vtr::Point(grids.width() - 1, grids.height() - 1)); + /* RIGHT side */ + for (size_t iy = grids.height() - 2; iy > 0; --iy) { + io_coordinates[RIGHT].push_back(vtr::Point(grids.width() - 1, iy)); + } + + /* Bottom-right */ + io_coordinates[BOTTOM].push_back(vtr::Point(grids.width() - 1, 0)); + /* BOTTOM side*/ + for (size_t ix = grids.width() - 2; ix > 0; --ix) { + io_coordinates[BOTTOM].push_back(vtr::Point(ix, 0)); + } + + /* Bottom-left */ + io_coordinates[BOTTOM].push_back(vtr::Point(0, 0)); + /* LEFT side */ + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + io_coordinates[LEFT].push_back(vtr::Point(0, iy)); + } + + return io_coordinates; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/utils/openfpga_device_grid_utils.h b/openfpga/src/utils/openfpga_device_grid_utils.h index 4b49be150..6489e3be4 100644 --- a/openfpga/src/utils/openfpga_device_grid_utils.h +++ b/openfpga/src/utils/openfpga_device_grid_utils.h @@ -24,6 +24,9 @@ constexpr std::array FPGA_SIDES_CLOCKWISE{TOP, RIGHT, BOTTOM, LEFT}; std::map>> generate_perimeter_grid_coordinates(const DeviceGrid& grids); +std::map>> +generate_perimeter_tile_coordinates(const DeviceGrid& grids); + } /* end namespace openfpga */ #endif From 6458580e3e5b0bf3e265f052862c8d8d08f434a0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 20 Jul 2023 16:59:39 -0700 Subject: [PATCH 191/391] [core] move child instance builder to a separated source file as these codes are expanding in size --- openfpga/src/fabric/build_top_module.cpp | 632 +---------------- .../build_top_module_child_instance.cpp | 666 ++++++++++++++++++ .../fabric/build_top_module_child_instance.h | 54 ++ 3 files changed, 721 insertions(+), 631 deletions(-) create mode 100644 openfpga/src/fabric/build_top_module_child_instance.cpp create mode 100644 openfpga/src/fabric/build_top_module_child_instance.h diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index 745385cdf..6b2a36d72 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -16,6 +16,7 @@ /* Headers from openfpgashell library */ #include "build_module_graph_utils.h" #include "build_top_module.h" +#include "build_top_module_child_instance.h" #include "build_top_module_connection.h" #include "build_top_module_directs.h" #include "build_top_module_memory.h" @@ -32,637 +33,6 @@ /* begin namespace openfpga */ namespace openfpga { -/******************************************************************** - * Add a instance of a grid module to the top module - *******************************************************************/ -static size_t add_top_module_grid_instance( - ModuleManager& module_manager, const ModuleId& top_module, - t_physical_tile_type_ptr grid_type, const e_side& border_side, - const vtr::Point& grid_coord) { - /* Find the module name for this type of grid */ - std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); - std::string grid_module_name = generate_grid_block_module_name( - grid_module_name_prefix, std::string(grid_type->name), - is_io_type(grid_type), border_side); - ModuleId grid_module = module_manager.find_module(grid_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); - /* Record the instance id */ - size_t grid_instance = module_manager.num_instance(top_module, grid_module); - /* Add the module to top_module */ - module_manager.add_child_module(top_module, grid_module, false); - /* Set an unique name to the instance - * Note: it is your risk to gurantee the name is unique! - */ - std::string instance_name = generate_grid_block_instance_name( - grid_module_name_prefix, std::string(grid_type->name), - is_io_type(grid_type), border_side, grid_coord); - module_manager.set_child_instance_name(top_module, grid_module, grid_instance, - instance_name); - - return grid_instance; -} - -/******************************************************************** - * Add all the grids as sub-modules across the fabric - * The grid modules are created for each unique type of grid (based - * on the type in data structure data_structure - * Here, we will iterate over the full fabric (coordinates) - * and instanciate the grid modules - * - * Return an 2-D array of instance ids of the grid modules that - * have been added - * - * This function assumes an island-style floorplanning for FPGA fabric - * - * - * +-----------------------------------+ - * | I/O grids | - * | TOP side | - * +-----------------------------------+ - * - * +-----------+ +-----------------------------------+ +------------+ - * | | | | | | - * | I/O grids | | Core grids | | I/O grids | - * | LEFT side | | (CLB, Heterogeneous blocks, etc.) | | RIGHT side | - * | | | | | | - * +-----------+ +-----------------------------------+ +------------+ - * - * +-----------------------------------+ - * | I/O grids | - * | BOTTOM side | - * +-----------------------------------+ - * - *******************************************************************/ -static vtr::Matrix add_top_module_grid_instances( - ModuleManager& module_manager, const ModuleId& top_module, - const DeviceGrid& grids) { - vtr::ScopedStartFinishTimer timer("Add grid instances to top module"); - - /* Reserve an array for the instance ids */ - vtr::Matrix grid_instance_ids({grids.width(), grids.height()}); - grid_instance_ids.fill(size_t(-1)); - - /* Instanciate I/O grids */ - /* Create the coordinate range for each side of FPGA fabric */ - std::map>> io_coordinates = - generate_perimeter_grid_coordinates(grids); - - for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { - for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { - t_physical_tile_type_ptr phy_tile_type = - grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); - /* Bypass EMPTY grid */ - if (true == is_empty_type(phy_tile_type)) { - continue; - } - /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || - (0 < grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { - /* Find the root of this grid, the instance id should be valid. - * We just copy it here - */ - vtr::Point root_grid_coord( - io_coordinate.x() - - grids.get_width_offset(io_coordinate.x(), io_coordinate.y()), - io_coordinate.y() - - grids.get_height_offset(io_coordinate.x(), io_coordinate.y())); - VTR_ASSERT(size_t(-1) != - grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); - grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = - grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]; - continue; - } - - /* Add a grid module to top_module*/ - grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = - add_top_module_grid_instance(module_manager, top_module, phy_tile_type, - io_side, io_coordinate); - } - } - - /* Instanciate core grids - * IMPORTANT: sequence matters here, it impacts the I/O indexing. - * We should follow the same sequence as the build_io_location_map()! - * If you change the sequence of walking through grids here, you should change - * it in the build_io_location map()! - */ - for (size_t ix = 1; ix < grids.width() - 1; ++ix) { - for (size_t iy = 1; iy < grids.height() - 1; ++iy) { - t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(ix, iy); - /* Bypass EMPTY grid */ - if (true == is_empty_type(phy_tile_type)) { - continue; - } - /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(ix, iy)) || - (0 < grids.get_height_offset(ix, iy))) { - /* Find the root of this grid, the instance id should be valid. - * We just copy it here - */ - vtr::Point root_grid_coord( - ix - grids.get_width_offset(ix, iy), - iy - grids.get_height_offset(ix, iy)); - VTR_ASSERT(size_t(-1) != - grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); - grid_instance_ids[ix][iy] = - grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]; - continue; - } - /* Add a grid module to top_module*/ - vtr::Point grid_coord(ix, iy); - grid_instance_ids[ix][iy] = add_top_module_grid_instance( - module_manager, top_module, phy_tile_type, NUM_SIDES, grid_coord); - } - } - - return grid_instance_ids; -} - -/******************************************************************** - * Add switch blocks across the FPGA fabric to the top-level module - * Return an 2-D array of instance ids of the switch blocks that - * have been added - *******************************************************************/ -static vtr::Matrix add_top_module_switch_block_instances( - ModuleManager& module_manager, const ModuleId& top_module, - const DeviceRRGSB& device_rr_gsb, const bool& compact_routing_hierarchy) { - vtr::ScopedStartFinishTimer timer("Add switch block instances to top module"); - - vtr::Point sb_range = device_rr_gsb.get_gsb_range(); - - /* Reserve an array for the instance ids */ - vtr::Matrix sb_instance_ids({sb_range.x(), sb_range.y()}); - sb_instance_ids.fill(size_t(-1)); - - for (size_t ix = 0; ix < sb_range.x(); ++ix) { - for (size_t iy = 0; iy < sb_range.y(); ++iy) { - /* If we use compact routing hierarchy, we should instanciate the unique - * module of SB */ - const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); - - if (false == rr_gsb.is_sb_exist()) { - continue; - } - - vtr::Point sb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); - if (true == compact_routing_hierarchy) { - vtr::Point sb_coord(ix, iy); - const RRGSB& unique_mirror = - device_rr_gsb.get_sb_unique_module(sb_coord); - sb_coordinate.set_x(unique_mirror.get_sb_x()); - sb_coordinate.set_y(unique_mirror.get_sb_y()); - } - std::string sb_module_name = - generate_switch_block_module_name(sb_coordinate); - ModuleId sb_module = module_manager.find_module(sb_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(sb_module)); - /* Record the instance id */ - sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()] = - module_manager.num_instance(top_module, sb_module); - /* Add the module to top_module */ - module_manager.add_child_module(top_module, sb_module, false); - /* Set an unique name to the instance - * Note: it is your risk to gurantee the name is unique! - */ - module_manager.set_child_instance_name( - top_module, sb_module, - sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()], - generate_switch_block_module_name( - vtr::Point(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()))); - } - } - - return sb_instance_ids; -} - -/******************************************************************** - * Add switch blocks across the FPGA fabric to the top-level module - *******************************************************************/ -static vtr::Matrix add_top_module_connection_block_instances( - ModuleManager& module_manager, const ModuleId& top_module, - const DeviceRRGSB& device_rr_gsb, const t_rr_type& cb_type, - const bool& compact_routing_hierarchy) { - vtr::ScopedStartFinishTimer timer( - "Add connection block instances to top module"); - - vtr::Point cb_range = device_rr_gsb.get_gsb_range(); - - /* Reserve an array for the instance ids */ - vtr::Matrix cb_instance_ids({cb_range.x(), cb_range.y()}); - cb_instance_ids.fill(size_t(-1)); - - for (size_t ix = 0; ix < cb_range.x(); ++ix) { - for (size_t iy = 0; iy < cb_range.y(); ++iy) { - /* Check if the connection block exists in the device! - * Some of them do NOT exist due to heterogeneous blocks (height > 1) - * We will skip those modules - */ - const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); - vtr::Point cb_coordinate(rr_gsb.get_cb_x(cb_type), - rr_gsb.get_cb_y(cb_type)); - if (false == rr_gsb.is_cb_exist(cb_type)) { - continue; - } - /* If we use compact routing hierarchy, we should instanciate the unique - * module of SB */ - if (true == compact_routing_hierarchy) { - vtr::Point cb_coord(ix, iy); - /* Note: use GSB coordinate when inquire for unique modules!!! */ - const RRGSB& unique_mirror = - device_rr_gsb.get_cb_unique_module(cb_type, cb_coord); - cb_coordinate.set_x(unique_mirror.get_cb_x(cb_type)); - cb_coordinate.set_y(unique_mirror.get_cb_y(cb_type)); - } - std::string cb_module_name = - generate_connection_block_module_name(cb_type, cb_coordinate); - ModuleId cb_module = module_manager.find_module(cb_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(cb_module)); - /* Record the instance id */ - cb_instance_ids[rr_gsb.get_cb_x(cb_type)][rr_gsb.get_cb_y(cb_type)] = - module_manager.num_instance(top_module, cb_module); - /* Add the module to top_module */ - module_manager.add_child_module(top_module, cb_module, false); - /* Set an unique name to the instance - * Note: it is your risk to gurantee the name is unique! - */ - std::string cb_instance_name = generate_connection_block_module_name( - cb_type, - vtr::Point(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type))); - module_manager.set_child_instance_name( - top_module, cb_module, - cb_instance_ids[rr_gsb.get_cb_x(cb_type)][rr_gsb.get_cb_y(cb_type)], - cb_instance_name); - } - } - - return cb_instance_ids; -} - -/******************************************************************** - * Add the I/O children to the top-level module, which impacts the I/O indexing - * This is the default function to build the I/O sequence/indexing - * The I/O children is added in a maze shape - * The function supports I/Os in the center of grids, starting from the - *bottom-left corner and ending at the center - * - * +----------------------+ - * |+--------------------+| - * ||+------------------+|| - * |||+----------------+||| - * ||||+-------------->|||| - * ||||+---------------+||| - * |||+-----------------+|| - * ||+-------------------+| - * |+---------------------+ - * ^ - * io[0] - *******************************************************************/ -static void add_top_module_io_children( - ModuleManager& module_manager, const ModuleId& top_module, - const DeviceGrid& grids, const vtr::Matrix& grid_instance_ids) { - /* Create the coordinate range for the perimeter I/Os of FPGA fabric */ - std::map>> io_coordinates = - generate_perimeter_grid_coordinates(grids); - - for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { - for (const vtr::Point& io_coord : io_coordinates[io_side]) { - t_physical_tile_type_ptr grid_type = - grids.get_physical_type(io_coord.x(), io_coord.y()); - /* Bypass EMPTY grid */ - if (true == is_empty_type(grid_type)) { - continue; - } - /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(io_coord.x(), io_coord.y())) || - (0 < grids.get_height_offset(io_coord.x(), io_coord.y()))) { - continue; - } - /* Find the module name for this type of grid */ - std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); - std::string grid_module_name = generate_grid_block_module_name( - grid_module_name_prefix, std::string(grid_type->name), - is_io_type(grid_type), io_side); - ModuleId grid_module = module_manager.find_module(grid_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); - /* Add a I/O children to top_module*/ - module_manager.add_io_child(top_module, grid_module, - grid_instance_ids[io_coord.x()][io_coord.y()], - vtr::Point(io_coord.x(), io_coord.y())); - } - } - - /* Walk through the center grids */ - size_t xmin = 1; - size_t xmax = grids.width() - 2; - size_t ymin = 1; - size_t ymax = grids.height() - 2; - std::vector> coords; - while (xmin < xmax && ymin < ymax) { - for (size_t iy = ymin; iy < ymax + 1; iy++) { - coords.push_back(vtr::Point(xmin, iy)); - } - for (size_t ix = xmin + 1; ix < xmax + 1; ix++) { - coords.push_back(vtr::Point(ix, ymax)); - } - for (size_t iy = ymax - 1; iy > ymin; iy--) { - coords.push_back(vtr::Point(xmax, iy)); - } - for (size_t ix = xmax; ix > xmin; ix--) { - coords.push_back(vtr::Point(ix, ymin)); - } - xmin++; - ymin++; - xmax--; - ymax--; - } - - /* If height is odd, add the missing horizental line */ - if ((grids.height() - 2) % 2 == 1) { - if (ymin == ymax) { - for (size_t ix = xmin; ix < xmax + 1; ix++) { - coords.push_back(vtr::Point(ix, ymin)); - } - } - } - /* If width is odd, add the missing vertical line */ - if ((grids.width() - 2) % 2 == 1) { - if (xmin == xmax) { - for (size_t iy = ymin; iy < ymax + 1; iy++) { - coords.push_back(vtr::Point(xmin, iy)); - } - } - } - - /* Now walk through the coordinates */ - for (vtr::Point coord : coords) { - t_physical_tile_type_ptr grid_type = - grids.get_physical_type(coord.x(), coord.y()); - /* Bypass EMPTY grid */ - if (true == is_empty_type(grid_type)) { - continue; - } - /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(coord.x(), coord.y())) || - (0 < grids.get_height_offset(coord.x(), coord.y()))) { - continue; - } - /* Find the module name for this type of grid */ - std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); - std::string grid_module_name = generate_grid_block_module_name( - grid_module_name_prefix, std::string(grid_type->name), - is_io_type(grid_type), NUM_SIDES); - ModuleId grid_module = module_manager.find_module(grid_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); - /* Add a I/O children to top_module*/ - module_manager.add_io_child(top_module, grid_module, - grid_instance_ids[coord.x()][coord.y()], - vtr::Point(coord.x(), coord.y())); - } -} - -/******************************************************************** - * Add the fine-grained instances to the top module of FPGA fabric - * The fine-grained instances include programmable blocks, connection blocks and - *switch blocks, each of which is an instance under the top module - *******************************************************************/ -static int build_top_module_fine_grained_child_instances( - ModuleManager& module_manager, const ModuleId& top_module, - MemoryBankShiftRegisterBanks& blwl_sr_banks, - const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, - const RRClockSpatialLookup& rr_clock_lookup, - const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, - const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, - 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) { - int status = CMD_EXEC_SUCCESS; - std::map> cb_instance_ids; - - /* Add sub modules, which are grid, SB and CBX/CBY modules as instances */ - /* Add all the grids across the fabric */ - vtr::Matrix grid_instance_ids = - add_top_module_grid_instances(module_manager, top_module, grids); - /* Add all the SBs across the fabric */ - vtr::Matrix sb_instance_ids = add_top_module_switch_block_instances( - module_manager, top_module, device_rr_gsb, compact_routing_hierarchy); - /* Add all the CBX and CBYs across the fabric */ - cb_instance_ids[CHANX] = add_top_module_connection_block_instances( - module_manager, top_module, device_rr_gsb, CHANX, - compact_routing_hierarchy); - cb_instance_ids[CHANY] = add_top_module_connection_block_instances( - module_manager, top_module, device_rr_gsb, CHANY, - compact_routing_hierarchy); - - /* Update I/O children list */ - add_top_module_io_children(module_manager, top_module, grids, - grid_instance_ids); - - /* Add nets when we need a complete fabric modeling, - * which is required by downstream functions - */ - if (false == frame_view) { - /* Reserve nets to be memory efficient */ - reserve_module_manager_module_nets(module_manager, top_module); - - /* Add module nets to connect the sub modules */ - add_top_module_nets_connect_grids_and_gsbs( - module_manager, top_module, vpr_device_annotation, grids, - grid_instance_ids, rr_graph, device_rr_gsb, sb_instance_ids, - cb_instance_ids, compact_routing_hierarchy, duplicate_grid_pin); - /* Add inter-CLB direct connections */ - add_top_module_nets_tile_direct_connections( - module_manager, top_module, circuit_lib, vpr_device_annotation, grids, - grid_instance_ids, tile_direct, arch_direct); - } - - /* Add global ports from grid ports that are defined as global in tile - * annotation */ - status = add_top_module_global_ports_from_grid_modules( - module_manager, top_module, tile_annotation, vpr_device_annotation, grids, - rr_graph, device_rr_gsb, cb_instance_ids, grid_instance_ids, clk_ntwk, - rr_clock_lookup); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - - /* Add GPIO ports from the sub-modules under this Verilog module - * For top-level module, we follow a special sequencing for I/O modules. So we - * rebuild the I/O children list here - */ - add_module_gpio_ports_from_child_modules(module_manager, top_module); - - /* Organize the list of memory modules and instances - * If we have an empty fabric key, we organize the memory modules as routine - * Otherwise, we will load the fabric key directly - */ - if (true == fabric_key.empty()) { - organize_top_module_memory_modules( - module_manager, top_module, circuit_lib, config_protocol, sram_model, - grids, grid_instance_ids, device_rr_gsb, sb_instance_ids, cb_instance_ids, - compact_routing_hierarchy); - } else { - VTR_ASSERT_SAFE(false == fabric_key.empty()); - /* Throw a fatal error when the fabric key has a mismatch in region - * organization. between architecture file and fabric key - */ - if (size_t(config_protocol.num_regions()) != fabric_key.regions().size()) { - VTR_LOG_ERROR( - "Fabric key has a different number of configurable regions (='%ld') " - "than architecture definition (=%d)!\n", - fabric_key.regions().size(), config_protocol.num_regions()); - return CMD_EXEC_FATAL_ERROR; - } - - status = load_top_module_memory_modules_from_fabric_key( - module_manager, top_module, circuit_lib, config_protocol, fabric_key); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - - status = load_top_module_shift_register_banks_from_fabric_key( - fabric_key, blwl_sr_banks); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - - /* 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); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Add a instance of a tile module to the top module - *******************************************************************/ -static size_t add_top_module_tile_instance(ModuleManager& module_manager, - const ModuleId& top_module, - const FabricTile& fabric_tile, - const FabricTileId& fabric_tile_id) { - /* Find the module name for this type of grid */ - vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); - FabricTileId unique_fabric_tile_id = fabric_tile.unique_tile(tile_coord); - vtr::Point unique_tile_coord = - fabric_tile.tile_coordinate(unique_fabric_tile_id); - std::string tile_module_name = generate_tile_module_name(unique_tile_coord); - ModuleId tile_module = module_manager.find_module(tile_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(tile_module)); - /* Record the instance id */ - size_t tile_instance = module_manager.num_instance(top_module, tile_module); - /* Add the module to top_module */ - module_manager.add_child_module(top_module, tile_module, false); - /* Set an unique name to the instance - * Note: it is your risk to gurantee the name is unique! - */ - std::string instance_name = generate_tile_module_name(tile_coord); - module_manager.set_child_instance_name(top_module, tile_module, tile_instance, - instance_name); - return tile_instance; -} - -/******************************************************************** - * Add all the tiles as sub-modules across the fabric - * Here, we will iterate over the full fabric (coordinates) - * and instanciate the tile modules - * - * Return an 2-D array of instance ids of the grid modules that - * have been added - * - * This function assumes an island-style floorplanning for FPGA fabric - * - * - * +-----------------------------------+ - * | I/O tiles | - * | TOP side | - * +-----------------------------------+ - * - * +-----------+ +-----------------------------------+ +------------+ - * | | | | | | - * | I/O tiles | | Core tiles | | I/O tiles | - * | LEFT side | | (CLB, Heterogeneous blocks, etc.) | | RIGHT side | - * | | | | | | - * +-----------+ +-----------------------------------+ +------------+ - * - * +-----------------------------------+ - * | I/O tiles | - * | BOTTOM side | - * +-----------------------------------+ - * - *******************************************************************/ -static int add_top_module_tile_instances(ModuleManager& module_manager, - const ModuleId& top_module, - vtr::Matrix& tile_instance_ids, - const DeviceGrid& grids, - const FabricTile& fabric_tile) { - vtr::ScopedStartFinishTimer timer("Add tile instances to top module"); - int status = CMD_EXEC_SUCCESS; - - /* Reserve an array for the instance ids */ - tile_instance_ids.resize({grids.width(), grids.height()}); - tile_instance_ids.fill(size_t(-1)); - - /* Instanciate I/O grids */ - /* Create the coordinate range for each side of FPGA fabric */ - std::map>> io_coordinates = - generate_perimeter_tile_coordinates(grids); - - for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { - for (const vtr::Point& io_coord : io_coordinates[io_side]) { - FabricTileId fabric_tile_id = fabric_tile.find_tile(io_coord); - if (!fabric_tile.valid_tile_id(fabric_tile_id)) { - continue; - } - /* Add a tile module to top_module*/ - tile_instance_ids[io_coord.x()][io_coord.y()] = - add_top_module_tile_instance(module_manager, top_module, fabric_tile, - fabric_tile_id); - } - } - - /* Instanciate core grids - * IMPORTANT: sequence matters here, it impacts the I/O indexing. - * We should follow the same sequence as the build_io_location_map()! - * If you change the sequence of walking through grids here, you should change - * it in the build_io_location map()! - */ - for (size_t ix = 1; ix < grids.width() - 1; ++ix) { - for (size_t iy = 1; iy < grids.height() - 1; ++iy) { - vtr::Point curr_coord(ix, iy); - FabricTileId fabric_tile_id = fabric_tile.find_tile(curr_coord); - if (!fabric_tile.valid_tile_id(fabric_tile_id)) { - continue; - } - /* Add a tile module to top_module*/ - tile_instance_ids[curr_coord.x()][curr_coord.y()] = - add_top_module_tile_instance(module_manager, top_module, fabric_tile, - fabric_tile_id); - } - } - return status; -} - -/******************************************************************** - * Add the tile-level instances to the top module of FPGA fabric - * and build connects between them - *******************************************************************/ -static int build_top_module_tile_child_instances( - ModuleManager& module_manager, const ModuleId& top_module, - const DeviceGrid& grids, const FabricTile& fabric_tile) { - int status = CMD_EXEC_SUCCESS; - vtr::Matrix tile_instance_ids; - status = add_top_module_tile_instances(module_manager, top_module, - tile_instance_ids, grids, fabric_tile); - if (status != CMD_EXEC_SUCCESS) { - return CMD_EXEC_FATAL_ERROR; - } - return CMD_EXEC_SUCCESS; -} - /******************************************************************** * Print the top-level module for the FPGA fabric in Verilog format * This function will diff --git a/openfpga/src/fabric/build_top_module_child_instance.cpp b/openfpga/src/fabric/build_top_module_child_instance.cpp new file mode 100644 index 000000000..1b1517e9b --- /dev/null +++ b/openfpga/src/fabric/build_top_module_child_instance.cpp @@ -0,0 +1,666 @@ +/******************************************************************** + * This file includes functions that are used to print the top-level + * module for the FPGA fabric in Verilog format + *******************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from vpr library */ +#include "vpr_utils.h" + +/* Headers from openfpgashell library */ +#include "build_module_graph_utils.h" +#include "build_top_module_child_instance.h" +#include "build_top_module_connection.h" +#include "build_top_module_directs.h" +#include "build_top_module_memory.h" +#include "build_top_module_memory_bank.h" +#include "build_top_module_utils.h" +#include "command_exit_codes.h" +#include "module_manager_memory_utils.h" +#include "module_manager_utils.h" +#include "openfpga_device_grid_utils.h" +#include "openfpga_naming.h" +#include "openfpga_reserved_words.h" +#include "rr_gsb_utils.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Add a instance of a grid module to the top module + *******************************************************************/ +static size_t add_top_module_grid_instance( + ModuleManager& module_manager, const ModuleId& top_module, + t_physical_tile_type_ptr grid_type, const e_side& border_side, + const vtr::Point& grid_coord) { + /* Find the module name for this type of grid */ + std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); + std::string grid_module_name = generate_grid_block_module_name( + grid_module_name_prefix, std::string(grid_type->name), + is_io_type(grid_type), border_side); + ModuleId grid_module = module_manager.find_module(grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); + /* Record the instance id */ + size_t grid_instance = module_manager.num_instance(top_module, grid_module); + /* Add the module to top_module */ + module_manager.add_child_module(top_module, grid_module, false); + /* Set an unique name to the instance + * Note: it is your risk to gurantee the name is unique! + */ + std::string instance_name = generate_grid_block_instance_name( + grid_module_name_prefix, std::string(grid_type->name), + is_io_type(grid_type), border_side, grid_coord); + module_manager.set_child_instance_name(top_module, grid_module, grid_instance, + instance_name); + + return grid_instance; +} + +/******************************************************************** + * Add all the grids as sub-modules across the fabric + * The grid modules are created for each unique type of grid (based + * on the type in data structure data_structure + * Here, we will iterate over the full fabric (coordinates) + * and instanciate the grid modules + * + * Return an 2-D array of instance ids of the grid modules that + * have been added + * + * This function assumes an island-style floorplanning for FPGA fabric + * + * + * +-----------------------------------+ + * | I/O grids | + * | TOP side | + * +-----------------------------------+ + * + * +-----------+ +-----------------------------------+ +------------+ + * | | | | | | + * | I/O grids | | Core grids | | I/O grids | + * | LEFT side | | (CLB, Heterogeneous blocks, etc.) | | RIGHT side | + * | | | | | | + * +-----------+ +-----------------------------------+ +------------+ + * + * +-----------------------------------+ + * | I/O grids | + * | BOTTOM side | + * +-----------------------------------+ + * + *******************************************************************/ +static vtr::Matrix add_top_module_grid_instances( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceGrid& grids) { + vtr::ScopedStartFinishTimer timer("Add grid instances to top module"); + + /* Reserve an array for the instance ids */ + vtr::Matrix grid_instance_ids({grids.width(), grids.height()}); + grid_instance_ids.fill(size_t(-1)); + + /* Instanciate I/O grids */ + /* Create the coordinate range for each side of FPGA fabric */ + std::map>> io_coordinates = + generate_perimeter_grid_coordinates(grids); + + for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { + for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { + t_physical_tile_type_ptr phy_tile_type = + grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); + /* Bypass EMPTY grid */ + if (true == is_empty_type(phy_tile_type)) { + continue; + } + /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || + (0 < grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { + /* Find the root of this grid, the instance id should be valid. + * We just copy it here + */ + vtr::Point root_grid_coord( + io_coordinate.x() - + grids.get_width_offset(io_coordinate.x(), io_coordinate.y()), + io_coordinate.y() - + grids.get_height_offset(io_coordinate.x(), io_coordinate.y())); + VTR_ASSERT(size_t(-1) != + grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); + grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = + grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]; + continue; + } + + /* Add a grid module to top_module*/ + grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = + add_top_module_grid_instance(module_manager, top_module, phy_tile_type, + io_side, io_coordinate); + } + } + + /* Instanciate core grids + * IMPORTANT: sequence matters here, it impacts the I/O indexing. + * We should follow the same sequence as the build_io_location_map()! + * If you change the sequence of walking through grids here, you should change + * it in the build_io_location map()! + */ + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(ix, iy); + /* Bypass EMPTY grid */ + if (true == is_empty_type(phy_tile_type)) { + continue; + } + /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(ix, iy)) || + (0 < grids.get_height_offset(ix, iy))) { + /* Find the root of this grid, the instance id should be valid. + * We just copy it here + */ + vtr::Point root_grid_coord( + ix - grids.get_width_offset(ix, iy), + iy - grids.get_height_offset(ix, iy)); + VTR_ASSERT(size_t(-1) != + grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); + grid_instance_ids[ix][iy] = + grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]; + continue; + } + /* Add a grid module to top_module*/ + vtr::Point grid_coord(ix, iy); + grid_instance_ids[ix][iy] = add_top_module_grid_instance( + module_manager, top_module, phy_tile_type, NUM_SIDES, grid_coord); + } + } + + return grid_instance_ids; +} + +/******************************************************************** + * Add switch blocks across the FPGA fabric to the top-level module + * Return an 2-D array of instance ids of the switch blocks that + * have been added + *******************************************************************/ +static vtr::Matrix add_top_module_switch_block_instances( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceRRGSB& device_rr_gsb, const bool& compact_routing_hierarchy) { + vtr::ScopedStartFinishTimer timer("Add switch block instances to top module"); + + vtr::Point sb_range = device_rr_gsb.get_gsb_range(); + + /* Reserve an array for the instance ids */ + vtr::Matrix sb_instance_ids({sb_range.x(), sb_range.y()}); + sb_instance_ids.fill(size_t(-1)); + + for (size_t ix = 0; ix < sb_range.x(); ++ix) { + for (size_t iy = 0; iy < sb_range.y(); ++iy) { + /* If we use compact routing hierarchy, we should instanciate the unique + * module of SB */ + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); + + if (false == rr_gsb.is_sb_exist()) { + continue; + } + + vtr::Point sb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + if (true == compact_routing_hierarchy) { + vtr::Point sb_coord(ix, iy); + const RRGSB& unique_mirror = + device_rr_gsb.get_sb_unique_module(sb_coord); + sb_coordinate.set_x(unique_mirror.get_sb_x()); + sb_coordinate.set_y(unique_mirror.get_sb_y()); + } + std::string sb_module_name = + generate_switch_block_module_name(sb_coordinate); + ModuleId sb_module = module_manager.find_module(sb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sb_module)); + /* Record the instance id */ + sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()] = + module_manager.num_instance(top_module, sb_module); + /* Add the module to top_module */ + module_manager.add_child_module(top_module, sb_module, false); + /* Set an unique name to the instance + * Note: it is your risk to gurantee the name is unique! + */ + module_manager.set_child_instance_name( + top_module, sb_module, + sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()], + generate_switch_block_module_name( + vtr::Point(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()))); + } + } + + return sb_instance_ids; +} + +/******************************************************************** + * Add switch blocks across the FPGA fabric to the top-level module + *******************************************************************/ +static vtr::Matrix add_top_module_connection_block_instances( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceRRGSB& device_rr_gsb, const t_rr_type& cb_type, + const bool& compact_routing_hierarchy) { + vtr::ScopedStartFinishTimer timer( + "Add connection block instances to top module"); + + vtr::Point cb_range = device_rr_gsb.get_gsb_range(); + + /* Reserve an array for the instance ids */ + vtr::Matrix cb_instance_ids({cb_range.x(), cb_range.y()}); + cb_instance_ids.fill(size_t(-1)); + + for (size_t ix = 0; ix < cb_range.x(); ++ix) { + for (size_t iy = 0; iy < cb_range.y(); ++iy) { + /* Check if the connection block exists in the device! + * Some of them do NOT exist due to heterogeneous blocks (height > 1) + * We will skip those modules + */ + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); + vtr::Point cb_coordinate(rr_gsb.get_cb_x(cb_type), + rr_gsb.get_cb_y(cb_type)); + if (false == rr_gsb.is_cb_exist(cb_type)) { + continue; + } + /* If we use compact routing hierarchy, we should instanciate the unique + * module of SB */ + if (true == compact_routing_hierarchy) { + vtr::Point cb_coord(ix, iy); + /* Note: use GSB coordinate when inquire for unique modules!!! */ + const RRGSB& unique_mirror = + device_rr_gsb.get_cb_unique_module(cb_type, cb_coord); + cb_coordinate.set_x(unique_mirror.get_cb_x(cb_type)); + cb_coordinate.set_y(unique_mirror.get_cb_y(cb_type)); + } + std::string cb_module_name = + generate_connection_block_module_name(cb_type, cb_coordinate); + ModuleId cb_module = module_manager.find_module(cb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(cb_module)); + /* Record the instance id */ + cb_instance_ids[rr_gsb.get_cb_x(cb_type)][rr_gsb.get_cb_y(cb_type)] = + module_manager.num_instance(top_module, cb_module); + /* Add the module to top_module */ + module_manager.add_child_module(top_module, cb_module, false); + /* Set an unique name to the instance + * Note: it is your risk to gurantee the name is unique! + */ + std::string cb_instance_name = generate_connection_block_module_name( + cb_type, + vtr::Point(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type))); + module_manager.set_child_instance_name( + top_module, cb_module, + cb_instance_ids[rr_gsb.get_cb_x(cb_type)][rr_gsb.get_cb_y(cb_type)], + cb_instance_name); + } + } + + return cb_instance_ids; +} + +/******************************************************************** + * Add the I/O children to the top-level module, which impacts the I/O indexing + * This is the default function to build the I/O sequence/indexing + * The I/O children is added in a maze shape + * The function supports I/Os in the center of grids, starting from the + *bottom-left corner and ending at the center + * + * +----------------------+ + * |+--------------------+| + * ||+------------------+|| + * |||+----------------+||| + * ||||+-------------->|||| + * ||||+---------------+||| + * |||+-----------------+|| + * ||+-------------------+| + * |+---------------------+ + * ^ + * io[0] + *******************************************************************/ +static void add_top_module_io_children( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceGrid& grids, const vtr::Matrix& grid_instance_ids) { + /* Create the coordinate range for the perimeter I/Os of FPGA fabric */ + std::map>> io_coordinates = + generate_perimeter_grid_coordinates(grids); + + for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { + for (const vtr::Point& io_coord : io_coordinates[io_side]) { + t_physical_tile_type_ptr grid_type = + grids.get_physical_type(io_coord.x(), io_coord.y()); + /* Bypass EMPTY grid */ + if (true == is_empty_type(grid_type)) { + continue; + } + /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(io_coord.x(), io_coord.y())) || + (0 < grids.get_height_offset(io_coord.x(), io_coord.y()))) { + continue; + } + /* Find the module name for this type of grid */ + std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); + std::string grid_module_name = generate_grid_block_module_name( + grid_module_name_prefix, std::string(grid_type->name), + is_io_type(grid_type), io_side); + ModuleId grid_module = module_manager.find_module(grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); + /* Add a I/O children to top_module*/ + module_manager.add_io_child(top_module, grid_module, + grid_instance_ids[io_coord.x()][io_coord.y()], + vtr::Point(io_coord.x(), io_coord.y())); + } + } + + /* Walk through the center grids */ + size_t xmin = 1; + size_t xmax = grids.width() - 2; + size_t ymin = 1; + size_t ymax = grids.height() - 2; + std::vector> coords; + while (xmin < xmax && ymin < ymax) { + for (size_t iy = ymin; iy < ymax + 1; iy++) { + coords.push_back(vtr::Point(xmin, iy)); + } + for (size_t ix = xmin + 1; ix < xmax + 1; ix++) { + coords.push_back(vtr::Point(ix, ymax)); + } + for (size_t iy = ymax - 1; iy > ymin; iy--) { + coords.push_back(vtr::Point(xmax, iy)); + } + for (size_t ix = xmax; ix > xmin; ix--) { + coords.push_back(vtr::Point(ix, ymin)); + } + xmin++; + ymin++; + xmax--; + ymax--; + } + + /* If height is odd, add the missing horizental line */ + if ((grids.height() - 2) % 2 == 1) { + if (ymin == ymax) { + for (size_t ix = xmin; ix < xmax + 1; ix++) { + coords.push_back(vtr::Point(ix, ymin)); + } + } + } + /* If width is odd, add the missing vertical line */ + if ((grids.width() - 2) % 2 == 1) { + if (xmin == xmax) { + for (size_t iy = ymin; iy < ymax + 1; iy++) { + coords.push_back(vtr::Point(xmin, iy)); + } + } + } + + /* Now walk through the coordinates */ + for (vtr::Point coord : coords) { + t_physical_tile_type_ptr grid_type = + grids.get_physical_type(coord.x(), coord.y()); + /* Bypass EMPTY grid */ + if (true == is_empty_type(grid_type)) { + continue; + } + /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(coord.x(), coord.y())) || + (0 < grids.get_height_offset(coord.x(), coord.y()))) { + continue; + } + /* Find the module name for this type of grid */ + std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); + std::string grid_module_name = generate_grid_block_module_name( + grid_module_name_prefix, std::string(grid_type->name), + is_io_type(grid_type), NUM_SIDES); + ModuleId grid_module = module_manager.find_module(grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); + /* Add a I/O children to top_module*/ + module_manager.add_io_child(top_module, grid_module, + grid_instance_ids[coord.x()][coord.y()], + vtr::Point(coord.x(), coord.y())); + } +} + +/******************************************************************** + * Add the fine-grained instances to the top module of FPGA fabric + * The fine-grained instances include programmable blocks, connection blocks and + *switch blocks, each of which is an instance under the top module + *******************************************************************/ +static int build_top_module_fine_grained_child_instances( + ModuleManager& module_manager, const ModuleId& top_module, + MemoryBankShiftRegisterBanks& blwl_sr_banks, + const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, + const RRClockSpatialLookup& rr_clock_lookup, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, + const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, + 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) { + int status = CMD_EXEC_SUCCESS; + std::map> cb_instance_ids; + + /* Add sub modules, which are grid, SB and CBX/CBY modules as instances */ + /* Add all the grids across the fabric */ + vtr::Matrix grid_instance_ids = + add_top_module_grid_instances(module_manager, top_module, grids); + /* Add all the SBs across the fabric */ + vtr::Matrix sb_instance_ids = add_top_module_switch_block_instances( + module_manager, top_module, device_rr_gsb, compact_routing_hierarchy); + /* Add all the CBX and CBYs across the fabric */ + cb_instance_ids[CHANX] = add_top_module_connection_block_instances( + module_manager, top_module, device_rr_gsb, CHANX, + compact_routing_hierarchy); + cb_instance_ids[CHANY] = add_top_module_connection_block_instances( + module_manager, top_module, device_rr_gsb, CHANY, + compact_routing_hierarchy); + + /* Update I/O children list */ + add_top_module_io_children(module_manager, top_module, grids, + grid_instance_ids); + + /* Add nets when we need a complete fabric modeling, + * which is required by downstream functions + */ + if (false == frame_view) { + /* Reserve nets to be memory efficient */ + reserve_module_manager_module_nets(module_manager, top_module); + + /* Add module nets to connect the sub modules */ + add_top_module_nets_connect_grids_and_gsbs( + module_manager, top_module, vpr_device_annotation, grids, + grid_instance_ids, rr_graph, device_rr_gsb, sb_instance_ids, + cb_instance_ids, compact_routing_hierarchy, duplicate_grid_pin); + /* Add inter-CLB direct connections */ + add_top_module_nets_tile_direct_connections( + module_manager, top_module, circuit_lib, vpr_device_annotation, grids, + grid_instance_ids, tile_direct, arch_direct); + } + + /* Add global ports from grid ports that are defined as global in tile + * annotation */ + status = add_top_module_global_ports_from_grid_modules( + module_manager, top_module, tile_annotation, vpr_device_annotation, grids, + rr_graph, device_rr_gsb, cb_instance_ids, grid_instance_ids, clk_ntwk, + rr_clock_lookup); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + + /* Add GPIO ports from the sub-modules under this Verilog module + * For top-level module, we follow a special sequencing for I/O modules. So we + * rebuild the I/O children list here + */ + add_module_gpio_ports_from_child_modules(module_manager, top_module); + + /* Organize the list of memory modules and instances + * If we have an empty fabric key, we organize the memory modules as routine + * Otherwise, we will load the fabric key directly + */ + if (true == fabric_key.empty()) { + organize_top_module_memory_modules( + module_manager, top_module, circuit_lib, config_protocol, sram_model, + grids, grid_instance_ids, device_rr_gsb, sb_instance_ids, cb_instance_ids, + compact_routing_hierarchy); + } else { + VTR_ASSERT_SAFE(false == fabric_key.empty()); + /* Throw a fatal error when the fabric key has a mismatch in region + * organization. between architecture file and fabric key + */ + if (size_t(config_protocol.num_regions()) != fabric_key.regions().size()) { + VTR_LOG_ERROR( + "Fabric key has a different number of configurable regions (='%ld') " + "than architecture definition (=%d)!\n", + fabric_key.regions().size(), config_protocol.num_regions()); + return CMD_EXEC_FATAL_ERROR; + } + + status = load_top_module_memory_modules_from_fabric_key( + module_manager, top_module, circuit_lib, config_protocol, fabric_key); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + + status = load_top_module_shift_register_banks_from_fabric_key( + fabric_key, blwl_sr_banks); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + + /* 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); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Add a instance of a tile module to the top module + *******************************************************************/ +static size_t add_top_module_tile_instance(ModuleManager& module_manager, + const ModuleId& top_module, + const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id) { + /* Find the module name for this type of grid */ + vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); + FabricTileId unique_fabric_tile_id = fabric_tile.unique_tile(tile_coord); + vtr::Point unique_tile_coord = + fabric_tile.tile_coordinate(unique_fabric_tile_id); + std::string tile_module_name = generate_tile_module_name(unique_tile_coord); + ModuleId tile_module = module_manager.find_module(tile_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(tile_module)); + /* Record the instance id */ + size_t tile_instance = module_manager.num_instance(top_module, tile_module); + /* Add the module to top_module */ + module_manager.add_child_module(top_module, tile_module, false); + /* Set an unique name to the instance + * Note: it is your risk to gurantee the name is unique! + */ + std::string instance_name = generate_tile_module_name(tile_coord); + module_manager.set_child_instance_name(top_module, tile_module, tile_instance, + instance_name); + return tile_instance; +} + +/******************************************************************** + * Add all the tiles as sub-modules across the fabric + * Here, we will iterate over the full fabric (coordinates) + * and instanciate the tile modules + * + * Return an 2-D array of instance ids of the grid modules that + * have been added + * + * This function assumes an island-style floorplanning for FPGA fabric + * + * + * +-----------------------------------+ + * | I/O tiles | + * | TOP side | + * +-----------------------------------+ + * + * +-----------+ +-----------------------------------+ +------------+ + * | | | | | | + * | I/O tiles | | Core tiles | | I/O tiles | + * | LEFT side | | (CLB, Heterogeneous blocks, etc.) | | RIGHT side | + * | | | | | | + * +-----------+ +-----------------------------------+ +------------+ + * + * +-----------------------------------+ + * | I/O tiles | + * | BOTTOM side | + * +-----------------------------------+ + * + *******************************************************************/ +static int add_top_module_tile_instances(ModuleManager& module_manager, + const ModuleId& top_module, + vtr::Matrix& tile_instance_ids, + const DeviceGrid& grids, + const FabricTile& fabric_tile) { + vtr::ScopedStartFinishTimer timer("Add tile instances to top module"); + int status = CMD_EXEC_SUCCESS; + + /* Reserve an array for the instance ids */ + tile_instance_ids.resize({grids.width(), grids.height()}); + tile_instance_ids.fill(size_t(-1)); + + /* Instanciate I/O grids */ + /* Create the coordinate range for each side of FPGA fabric */ + std::map>> io_coordinates = + generate_perimeter_tile_coordinates(grids); + + for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { + for (const vtr::Point& io_coord : io_coordinates[io_side]) { + FabricTileId fabric_tile_id = fabric_tile.find_tile(io_coord); + if (!fabric_tile.valid_tile_id(fabric_tile_id)) { + continue; + } + /* Add a tile module to top_module*/ + tile_instance_ids[io_coord.x()][io_coord.y()] = + add_top_module_tile_instance(module_manager, top_module, fabric_tile, + fabric_tile_id); + } + } + + /* Instanciate core grids + * IMPORTANT: sequence matters here, it impacts the I/O indexing. + * We should follow the same sequence as the build_io_location_map()! + * If you change the sequence of walking through grids here, you should change + * it in the build_io_location map()! + */ + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + vtr::Point curr_coord(ix, iy); + FabricTileId fabric_tile_id = fabric_tile.find_tile(curr_coord); + if (!fabric_tile.valid_tile_id(fabric_tile_id)) { + continue; + } + /* Add a tile module to top_module*/ + tile_instance_ids[curr_coord.x()][curr_coord.y()] = + add_top_module_tile_instance(module_manager, top_module, fabric_tile, + fabric_tile_id); + } + } + return status; +} + +/******************************************************************** + * Add the tile-level instances to the top module of FPGA fabric + * and build connects between them + *******************************************************************/ +int build_top_module_tile_child_instances( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceGrid& grids, const FabricTile& fabric_tile) { + int status = CMD_EXEC_SUCCESS; + vtr::Matrix tile_instance_ids; + status = add_top_module_tile_instances(module_manager, top_module, + tile_instance_ids, grids, fabric_tile); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + return CMD_EXEC_SUCCESS; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_top_module_child_instance.h b/openfpga/src/fabric/build_top_module_child_instance.h new file mode 100644 index 000000000..bd728d19b --- /dev/null +++ b/openfpga/src/fabric/build_top_module_child_instance.h @@ -0,0 +1,54 @@ +#ifndef BUILD_TOP_MODULE_CHILD_INSTANCE_H +#define BUILD_TOP_MODULE_CHILD_INSTANCE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ + +#include + +#include "arch_direct.h" +#include "circuit_library.h" +#include "clock_network.h" +#include "config_protocol.h" +#include "decoder_library.h" +#include "device_grid.h" +#include "device_rr_gsb.h" +#include "fabric_key.h" +#include "fabric_tile.h" +#include "memory_bank_shift_register_banks.h" +#include "module_manager.h" +#include "rr_clock_spatial_lookup.h" +#include "rr_graph_view.h" +#include "tile_annotation.h" +#include "tile_direct.h" +#include "vpr_device_annotation.h" +#include "vtr_geometry.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int build_top_module_fine_grained_child_instances( + ModuleManager& module_manager, const ModuleId& top_module, + MemoryBankShiftRegisterBanks& blwl_sr_banks, + const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, + const RRClockSpatialLookup& rr_clock_lookup, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, + const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, + 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); + +static int build_top_module_tile_child_instances( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceGrid& grids, const FabricTile& fabric_tile); + +} /* end namespace openfpga */ + +#endif From ef214f459062d849348ebe7f637c7ee61979f107 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 20 Jul 2023 17:00:29 -0700 Subject: [PATCH 192/391] [core] code format --- openfpga/src/fabric/build_top_module_child_instance.cpp | 7 ++++--- openfpga/src/fabric/build_top_module_child_instance.h | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/openfpga/src/fabric/build_top_module_child_instance.cpp b/openfpga/src/fabric/build_top_module_child_instance.cpp index 1b1517e9b..75426ef5e 100644 --- a/openfpga/src/fabric/build_top_module_child_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_instance.cpp @@ -650,9 +650,10 @@ static int add_top_module_tile_instances(ModuleManager& module_manager, * Add the tile-level instances to the top module of FPGA fabric * and build connects between them *******************************************************************/ -int build_top_module_tile_child_instances( - ModuleManager& module_manager, const ModuleId& top_module, - const DeviceGrid& grids, const FabricTile& fabric_tile) { +int build_top_module_tile_child_instances(ModuleManager& module_manager, + const ModuleId& top_module, + const DeviceGrid& grids, + const FabricTile& fabric_tile) { int status = CMD_EXEC_SUCCESS; vtr::Matrix tile_instance_ids; status = add_top_module_tile_instances(module_manager, top_module, diff --git a/openfpga/src/fabric/build_top_module_child_instance.h b/openfpga/src/fabric/build_top_module_child_instance.h index bd728d19b..f1ecd21d5 100644 --- a/openfpga/src/fabric/build_top_module_child_instance.h +++ b/openfpga/src/fabric/build_top_module_child_instance.h @@ -45,9 +45,10 @@ int build_top_module_fine_grained_child_instances( const bool& compact_routing_hierarchy, const bool& duplicate_grid_pin, const FabricKey& fabric_key); -static int build_top_module_tile_child_instances( - ModuleManager& module_manager, const ModuleId& top_module, - const DeviceGrid& grids, const FabricTile& fabric_tile); +static int build_top_module_tile_child_instances(ModuleManager& module_manager, + const ModuleId& top_module, + const DeviceGrid& grids, + const FabricTile& fabric_tile); } /* end namespace openfpga */ From ae9190f0518383a3d58e103a76cfeef067580f12 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Jul 2023 00:03:31 +0000 Subject: [PATCH 193/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index e198acba6..1c9ca43f7 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1200 +1.2.1214 From db179ec4bbec82685e4d292cbff67d3c32dd7404 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 20 Jul 2023 17:07:07 -0700 Subject: [PATCH 194/391] [core] split tile instance builder and the classic fine-grained builder --- openfpga/src/fabric/build_top_module.cpp | 3 +- ...op_module_child_fine_grained_instance.cpp} | 127 -------------- ..._top_module_child_fine_grained_instance.h} | 9 +- .../build_top_module_child_tile_instance.cpp | 165 ++++++++++++++++++ .../build_top_module_child_tile_instance.h | 42 +++++ 5 files changed, 211 insertions(+), 135 deletions(-) rename openfpga/src/fabric/{build_top_module_child_instance.cpp => build_top_module_child_fine_grained_instance.cpp} (79%) rename openfpga/src/fabric/{build_top_module_child_instance.h => build_top_module_child_fine_grained_instance.h} (81%) create mode 100644 openfpga/src/fabric/build_top_module_child_tile_instance.cpp create mode 100644 openfpga/src/fabric/build_top_module_child_tile_instance.h diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index 6b2a36d72..d6dc9b892 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -16,7 +16,8 @@ /* Headers from openfpgashell library */ #include "build_module_graph_utils.h" #include "build_top_module.h" -#include "build_top_module_child_instance.h" +#include "build_top_module_child_tile_instance.h" +#include "build_top_module_child_fine_grained_instance.h" #include "build_top_module_connection.h" #include "build_top_module_directs.h" #include "build_top_module_memory.h" diff --git a/openfpga/src/fabric/build_top_module_child_instance.cpp b/openfpga/src/fabric/build_top_module_child_fine_grained_instance.cpp similarity index 79% rename from openfpga/src/fabric/build_top_module_child_instance.cpp rename to openfpga/src/fabric/build_top_module_child_fine_grained_instance.cpp index 75426ef5e..75dc8404f 100644 --- a/openfpga/src/fabric/build_top_module_child_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_fine_grained_instance.cpp @@ -537,131 +537,4 @@ static int build_top_module_fine_grained_child_instances( return CMD_EXEC_SUCCESS; } -/******************************************************************** - * Add a instance of a tile module to the top module - *******************************************************************/ -static size_t add_top_module_tile_instance(ModuleManager& module_manager, - const ModuleId& top_module, - const FabricTile& fabric_tile, - const FabricTileId& fabric_tile_id) { - /* Find the module name for this type of grid */ - vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); - FabricTileId unique_fabric_tile_id = fabric_tile.unique_tile(tile_coord); - vtr::Point unique_tile_coord = - fabric_tile.tile_coordinate(unique_fabric_tile_id); - std::string tile_module_name = generate_tile_module_name(unique_tile_coord); - ModuleId tile_module = module_manager.find_module(tile_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(tile_module)); - /* Record the instance id */ - size_t tile_instance = module_manager.num_instance(top_module, tile_module); - /* Add the module to top_module */ - module_manager.add_child_module(top_module, tile_module, false); - /* Set an unique name to the instance - * Note: it is your risk to gurantee the name is unique! - */ - std::string instance_name = generate_tile_module_name(tile_coord); - module_manager.set_child_instance_name(top_module, tile_module, tile_instance, - instance_name); - return tile_instance; -} - -/******************************************************************** - * Add all the tiles as sub-modules across the fabric - * Here, we will iterate over the full fabric (coordinates) - * and instanciate the tile modules - * - * Return an 2-D array of instance ids of the grid modules that - * have been added - * - * This function assumes an island-style floorplanning for FPGA fabric - * - * - * +-----------------------------------+ - * | I/O tiles | - * | TOP side | - * +-----------------------------------+ - * - * +-----------+ +-----------------------------------+ +------------+ - * | | | | | | - * | I/O tiles | | Core tiles | | I/O tiles | - * | LEFT side | | (CLB, Heterogeneous blocks, etc.) | | RIGHT side | - * | | | | | | - * +-----------+ +-----------------------------------+ +------------+ - * - * +-----------------------------------+ - * | I/O tiles | - * | BOTTOM side | - * +-----------------------------------+ - * - *******************************************************************/ -static int add_top_module_tile_instances(ModuleManager& module_manager, - const ModuleId& top_module, - vtr::Matrix& tile_instance_ids, - const DeviceGrid& grids, - const FabricTile& fabric_tile) { - vtr::ScopedStartFinishTimer timer("Add tile instances to top module"); - int status = CMD_EXEC_SUCCESS; - - /* Reserve an array for the instance ids */ - tile_instance_ids.resize({grids.width(), grids.height()}); - tile_instance_ids.fill(size_t(-1)); - - /* Instanciate I/O grids */ - /* Create the coordinate range for each side of FPGA fabric */ - std::map>> io_coordinates = - generate_perimeter_tile_coordinates(grids); - - for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { - for (const vtr::Point& io_coord : io_coordinates[io_side]) { - FabricTileId fabric_tile_id = fabric_tile.find_tile(io_coord); - if (!fabric_tile.valid_tile_id(fabric_tile_id)) { - continue; - } - /* Add a tile module to top_module*/ - tile_instance_ids[io_coord.x()][io_coord.y()] = - add_top_module_tile_instance(module_manager, top_module, fabric_tile, - fabric_tile_id); - } - } - - /* Instanciate core grids - * IMPORTANT: sequence matters here, it impacts the I/O indexing. - * We should follow the same sequence as the build_io_location_map()! - * If you change the sequence of walking through grids here, you should change - * it in the build_io_location map()! - */ - for (size_t ix = 1; ix < grids.width() - 1; ++ix) { - for (size_t iy = 1; iy < grids.height() - 1; ++iy) { - vtr::Point curr_coord(ix, iy); - FabricTileId fabric_tile_id = fabric_tile.find_tile(curr_coord); - if (!fabric_tile.valid_tile_id(fabric_tile_id)) { - continue; - } - /* Add a tile module to top_module*/ - tile_instance_ids[curr_coord.x()][curr_coord.y()] = - add_top_module_tile_instance(module_manager, top_module, fabric_tile, - fabric_tile_id); - } - } - return status; -} - -/******************************************************************** - * Add the tile-level instances to the top module of FPGA fabric - * and build connects between them - *******************************************************************/ -int build_top_module_tile_child_instances(ModuleManager& module_manager, - const ModuleId& top_module, - const DeviceGrid& grids, - const FabricTile& fabric_tile) { - int status = CMD_EXEC_SUCCESS; - vtr::Matrix tile_instance_ids; - status = add_top_module_tile_instances(module_manager, top_module, - tile_instance_ids, grids, fabric_tile); - if (status != CMD_EXEC_SUCCESS) { - return CMD_EXEC_FATAL_ERROR; - } - return CMD_EXEC_SUCCESS; -} - } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_top_module_child_instance.h b/openfpga/src/fabric/build_top_module_child_fine_grained_instance.h similarity index 81% rename from openfpga/src/fabric/build_top_module_child_instance.h rename to openfpga/src/fabric/build_top_module_child_fine_grained_instance.h index f1ecd21d5..7db61cb93 100644 --- a/openfpga/src/fabric/build_top_module_child_instance.h +++ b/openfpga/src/fabric/build_top_module_child_fine_grained_instance.h @@ -1,5 +1,5 @@ -#ifndef BUILD_TOP_MODULE_CHILD_INSTANCE_H -#define BUILD_TOP_MODULE_CHILD_INSTANCE_H +#ifndef BUILD_TOP_MODULE_CHILD_FINE_GRAINED_INSTANCE_H +#define BUILD_TOP_MODULE_CHILD_FINE_GRAINED_INSTANCE_H /******************************************************************** * Include header files that are required by function declaration @@ -45,11 +45,6 @@ int build_top_module_fine_grained_child_instances( const bool& compact_routing_hierarchy, const bool& duplicate_grid_pin, const FabricKey& fabric_key); -static int build_top_module_tile_child_instances(ModuleManager& module_manager, - const ModuleId& top_module, - const DeviceGrid& grids, - const FabricTile& fabric_tile); - } /* end namespace openfpga */ #endif diff --git a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp new file mode 100644 index 000000000..3b7005905 --- /dev/null +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -0,0 +1,165 @@ +/******************************************************************** + * This file includes functions that are used to print the top-level + * module for the FPGA fabric in Verilog format + *******************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from vpr library */ +#include "vpr_utils.h" + +/* Headers from openfpgashell library */ +#include "build_module_graph_utils.h" +#include "build_top_module_child_tile_instance.h" +#include "build_top_module_connection.h" +#include "build_top_module_directs.h" +#include "build_top_module_memory.h" +#include "build_top_module_memory_bank.h" +#include "build_top_module_utils.h" +#include "command_exit_codes.h" +#include "module_manager_memory_utils.h" +#include "module_manager_utils.h" +#include "openfpga_device_grid_utils.h" +#include "openfpga_naming.h" +#include "openfpga_reserved_words.h" +#include "rr_gsb_utils.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Add a instance of a tile module to the top module + *******************************************************************/ +static size_t add_top_module_tile_instance(ModuleManager& module_manager, + const ModuleId& top_module, + const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id) { + /* Find the module name for this type of grid */ + vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); + FabricTileId unique_fabric_tile_id = fabric_tile.unique_tile(tile_coord); + vtr::Point unique_tile_coord = + fabric_tile.tile_coordinate(unique_fabric_tile_id); + std::string tile_module_name = generate_tile_module_name(unique_tile_coord); + ModuleId tile_module = module_manager.find_module(tile_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(tile_module)); + /* Record the instance id */ + size_t tile_instance = module_manager.num_instance(top_module, tile_module); + /* Add the module to top_module */ + module_manager.add_child_module(top_module, tile_module, false); + /* Set an unique name to the instance + * Note: it is your risk to gurantee the name is unique! + */ + std::string instance_name = generate_tile_module_name(tile_coord); + module_manager.set_child_instance_name(top_module, tile_module, tile_instance, + instance_name); + return tile_instance; +} + +/******************************************************************** + * Add all the tiles as sub-modules across the fabric + * Here, we will iterate over the full fabric (coordinates) + * and instanciate the tile modules + * + * Return an 2-D array of instance ids of the grid modules that + * have been added + * + * This function assumes an island-style floorplanning for FPGA fabric + * + * + * +-----------------------------------+ + * | I/O tiles | + * | TOP side | + * +-----------------------------------+ + * + * +-----------+ +-----------------------------------+ +------------+ + * | | | | | | + * | I/O tiles | | Core tiles | | I/O tiles | + * | LEFT side | | (CLB, Heterogeneous blocks, etc.) | | RIGHT side | + * | | | | | | + * +-----------+ +-----------------------------------+ +------------+ + * + * +-----------------------------------+ + * | I/O tiles | + * | BOTTOM side | + * +-----------------------------------+ + * + *******************************************************************/ +static int add_top_module_tile_instances(ModuleManager& module_manager, + const ModuleId& top_module, + vtr::Matrix& tile_instance_ids, + const DeviceGrid& grids, + const FabricTile& fabric_tile) { + vtr::ScopedStartFinishTimer timer("Add tile instances to top module"); + int status = CMD_EXEC_SUCCESS; + + /* Reserve an array for the instance ids */ + tile_instance_ids.resize({grids.width(), grids.height()}); + tile_instance_ids.fill(size_t(-1)); + + /* Instanciate I/O grids */ + /* Create the coordinate range for each side of FPGA fabric */ + std::map>> io_coordinates = + generate_perimeter_tile_coordinates(grids); + + for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { + for (const vtr::Point& io_coord : io_coordinates[io_side]) { + FabricTileId fabric_tile_id = fabric_tile.find_tile(io_coord); + if (!fabric_tile.valid_tile_id(fabric_tile_id)) { + continue; + } + /* Add a tile module to top_module*/ + tile_instance_ids[io_coord.x()][io_coord.y()] = + add_top_module_tile_instance(module_manager, top_module, fabric_tile, + fabric_tile_id); + } + } + + /* Instanciate core grids + * IMPORTANT: sequence matters here, it impacts the I/O indexing. + * We should follow the same sequence as the build_io_location_map()! + * If you change the sequence of walking through grids here, you should change + * it in the build_io_location map()! + */ + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + vtr::Point curr_coord(ix, iy); + FabricTileId fabric_tile_id = fabric_tile.find_tile(curr_coord); + if (!fabric_tile.valid_tile_id(fabric_tile_id)) { + continue; + } + /* Add a tile module to top_module*/ + tile_instance_ids[curr_coord.x()][curr_coord.y()] = + add_top_module_tile_instance(module_manager, top_module, fabric_tile, + fabric_tile_id); + } + } + return status; +} + +/******************************************************************** + * Add the tile-level instances to the top module of FPGA fabric + * and build connects between them + *******************************************************************/ +int build_top_module_tile_child_instances(ModuleManager& module_manager, + const ModuleId& top_module, + const DeviceGrid& grids, + const FabricTile& fabric_tile) { + int status = CMD_EXEC_SUCCESS; + vtr::Matrix tile_instance_ids; + status = add_top_module_tile_instances(module_manager, top_module, + tile_instance_ids, grids, fabric_tile); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + + /* TODO: Build the nets between tiles */ + + return CMD_EXEC_SUCCESS; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_top_module_child_tile_instance.h b/openfpga/src/fabric/build_top_module_child_tile_instance.h new file mode 100644 index 000000000..b8098b573 --- /dev/null +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.h @@ -0,0 +1,42 @@ +#ifndef BUILD_TOP_MODULE_CHILD_TILE_INSTANCE_H +#define BUILD_TOP_MODULE_CHILD_TILE_INSTANCE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ + +#include + +#include "arch_direct.h" +#include "circuit_library.h" +#include "clock_network.h" +#include "config_protocol.h" +#include "decoder_library.h" +#include "device_grid.h" +#include "device_rr_gsb.h" +#include "fabric_key.h" +#include "fabric_tile.h" +#include "memory_bank_shift_register_banks.h" +#include "module_manager.h" +#include "rr_clock_spatial_lookup.h" +#include "rr_graph_view.h" +#include "tile_annotation.h" +#include "tile_direct.h" +#include "vpr_device_annotation.h" +#include "vtr_geometry.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +static int build_top_module_tile_child_instances(ModuleManager& module_manager, + const ModuleId& top_module, + const DeviceGrid& grids, + const FabricTile& fabric_tile); + +} /* end namespace openfpga */ + +#endif From 88c5d122cacf49c7dfd6e8257c10062f1c539a7a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 20 Jul 2023 17:12:10 -0700 Subject: [PATCH 195/391] [core] syntax --- openfpga/src/fabric/build_top_module.cpp | 2 +- .../build_top_module_child_fine_grained_instance.cpp | 4 ++-- .../src/fabric/build_top_module_child_tile_instance.h | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index d6dc9b892..2502ec379 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -16,8 +16,8 @@ /* Headers from openfpgashell library */ #include "build_module_graph_utils.h" #include "build_top_module.h" -#include "build_top_module_child_tile_instance.h" #include "build_top_module_child_fine_grained_instance.h" +#include "build_top_module_child_tile_instance.h" #include "build_top_module_connection.h" #include "build_top_module_directs.h" #include "build_top_module_memory.h" 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 75dc8404f..109f29c08 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 @@ -15,7 +15,7 @@ /* Headers from openfpgashell library */ #include "build_module_graph_utils.h" -#include "build_top_module_child_instance.h" +#include "build_top_module_child_fine_grained_instance.h" #include "build_top_module_connection.h" #include "build_top_module_directs.h" #include "build_top_module_memory.h" @@ -425,7 +425,7 @@ static void add_top_module_io_children( * The fine-grained instances include programmable blocks, connection blocks and *switch blocks, each of which is an instance under the top module *******************************************************************/ -static int build_top_module_fine_grained_child_instances( +int build_top_module_fine_grained_child_instances( ModuleManager& module_manager, const ModuleId& top_module, MemoryBankShiftRegisterBanks& blwl_sr_banks, const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, 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 b8098b573..5d2b40928 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.h +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.h @@ -32,10 +32,10 @@ /* begin namespace openfpga */ namespace openfpga { -static int build_top_module_tile_child_instances(ModuleManager& module_manager, - const ModuleId& top_module, - const DeviceGrid& grids, - const FabricTile& fabric_tile); +int build_top_module_tile_child_instances(ModuleManager& module_manager, + const ModuleId& top_module, + const DeviceGrid& grids, + const FabricTile& fabric_tile); } /* end namespace openfpga */ From 6b92299e392a147d1df2e2b5328c195682531130 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 20 Jul 2023 17:38:13 -0700 Subject: [PATCH 196/391] [core] start working on the net build-up for tile instances under the top-level module --- openfpga/src/annotation/fabric_tile.cpp | 7 + openfpga/src/annotation/fabric_tile.h | 3 + openfpga/src/fabric/build_top_module.cpp | 5 +- .../build_top_module_child_tile_instance.cpp | 192 +++++++++++++++++- .../build_top_module_child_tile_instance.h | 10 +- 5 files changed, 204 insertions(+), 13 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index 24072a5ba..14880b8e0 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -16,6 +16,13 @@ vtr::Point FabricTile::tile_coordinate( return coords_[tile_id]; } +vtr::Point FabricTile::unique_tile_coordinate( + const FabricTileId& tile_id) const { + vtr::Point tile_coord = tile_coordinate(tile_id); + FabricTileId unique_fabric_tile_id = unique_tile(tile_coord); + return tile_coordinate(unique_fabric_tile_id); +} + std::vector> FabricTile::pb_coordinates( const FabricTileId& tile_id) const { VTR_ASSERT(valid_tile_id(tile_id)); diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h index 323d5d7ec..903ec42f2 100644 --- a/openfpga/src/annotation/fabric_tile.h +++ b/openfpga/src/annotation/fabric_tile.h @@ -36,6 +36,9 @@ class FabricTile { FabricTileId unique_tile(const vtr::Point& coord) const; /** @brief Find the tile info with a given coordinate */ FabricTileId find_tile(const vtr::Point& coord) const; + /** @brief Find the coordinate of the unique tile w.r.t the tile with a tile + * id */ + vtr::Point unique_tile_coordinate(const FabricTileId& tile_id) const; /** @brief Return a list of unique tiles */ std::vector unique_tiles() const; /** @brief Find the index of a programmable block in the internal list by a diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index 2502ec379..9614407f4 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -78,8 +78,9 @@ int build_top_module( frame_view, compact_routing_hierarchy, duplicate_grid_pin, fabric_key); } else { /* TODO: Build the tile instances under the top module */ - status = build_top_module_tile_child_instances(module_manager, top_module, - grids, fabric_tile); + status = build_top_module_tile_child_instances( + module_manager, top_module, blwl_sr_banks, circuit_lib, grids, + fabric_tile, config_protocol, fabric_key, frame_view); } if (status != CMD_EXEC_SUCCESS) { 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 3b7005905..5aeb78fe2 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -40,10 +40,8 @@ static size_t add_top_module_tile_instance(ModuleManager& module_manager, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id) { /* Find the module name for this type of grid */ - vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); - FabricTileId unique_fabric_tile_id = fabric_tile.unique_tile(tile_coord); vtr::Point unique_tile_coord = - fabric_tile.tile_coordinate(unique_fabric_tile_id); + fabric_tile.unique_tile_coordinate(fabric_tile_id); std::string tile_module_name = generate_tile_module_name(unique_tile_coord); ModuleId tile_module = module_manager.find_module(tile_module_name); VTR_ASSERT(true == module_manager.valid_module_id(tile_module)); @@ -54,6 +52,7 @@ static size_t add_top_module_tile_instance(ModuleManager& module_manager, /* Set an unique name to the instance * Note: it is your risk to gurantee the name is unique! */ + vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); std::string instance_name = generate_tile_module_name(tile_coord); module_manager.set_child_instance_name(top_module, tile_module, tile_instance, instance_name); @@ -141,14 +140,124 @@ static int add_top_module_tile_instances(ModuleManager& module_manager, return status; } +/******************************************************************** + * Add the I/O children to the top-level module, which impacts the I/O indexing + * This is the default function to build the I/O sequence/indexing + * The I/O children is added in a maze shape + * The function supports I/Os in the center of grids, starting from the + *bottom-left corner and ending at the center + * + * +----------------------+ + * |+--------------------+| + * ||+------------------+|| + * |||+----------------+||| + * ||||+-------------->|||| + * ||||+---------------+||| + * |||+-----------------+|| + * ||+-------------------+| + * |+---------------------+ + * ^ + * io[0] + *******************************************************************/ +static void add_top_module_tile_io_children( + ModuleManager& module_manager, const ModuleId& top_module, + const DeviceGrid& grids, const FabricTile& fabric_tile, + const vtr::Matrix& tile_instance_ids) { + /* Create the coordinate range for the perimeter I/Os of FPGA fabric */ + std::map>> io_coordinates = + generate_perimeter_tile_coordinates(grids); + + for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { + for (const vtr::Point& io_coord : io_coordinates[io_side]) { + FabricTileId fabric_tile_id = fabric_tile.find_tile(io_coord); + if (!fabric_tile.valid_tile_id(fabric_tile_id)) { + continue; + } + /* Find the module name for this type of tile */ + vtr::Point unique_tile_coord = + fabric_tile.unique_tile_coordinate(fabric_tile_id); + std::string tile_module_name = + generate_tile_module_name(unique_tile_coord); + ModuleId tile_module = module_manager.find_module(tile_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(tile_module)); + /* Add a I/O children to top_module*/ + module_manager.add_io_child(top_module, tile_module, + tile_instance_ids[io_coord.x()][io_coord.y()], + vtr::Point(io_coord.x(), io_coord.y())); + } + } + + /* Walk through the center grids */ + size_t xmin = 1; + size_t xmax = grids.width() - 2; + size_t ymin = 1; + size_t ymax = grids.height() - 2; + std::vector> coords; + while (xmin < xmax && ymin < ymax) { + for (size_t iy = ymin; iy < ymax + 1; iy++) { + coords.push_back(vtr::Point(xmin, iy)); + } + for (size_t ix = xmin + 1; ix < xmax + 1; ix++) { + coords.push_back(vtr::Point(ix, ymax)); + } + for (size_t iy = ymax - 1; iy > ymin; iy--) { + coords.push_back(vtr::Point(xmax, iy)); + } + for (size_t ix = xmax; ix > xmin; ix--) { + coords.push_back(vtr::Point(ix, ymin)); + } + xmin++; + ymin++; + xmax--; + ymax--; + } + + /* If height is odd, add the missing horizental line */ + if ((grids.height() - 2) % 2 == 1) { + if (ymin == ymax) { + for (size_t ix = xmin; ix < xmax + 1; ix++) { + coords.push_back(vtr::Point(ix, ymin)); + } + } + } + /* If width is odd, add the missing vertical line */ + if ((grids.width() - 2) % 2 == 1) { + if (xmin == xmax) { + for (size_t iy = ymin; iy < ymax + 1; iy++) { + coords.push_back(vtr::Point(xmin, iy)); + } + } + } + + /* Now walk through the coordinates */ + for (vtr::Point coord : coords) { + FabricTileId fabric_tile_id = fabric_tile.find_tile(coord); + if (!fabric_tile.valid_tile_id(fabric_tile_id)) { + continue; + } + /* Find the module name for this type of tile */ + vtr::Point unique_tile_coord = + fabric_tile.unique_tile_coordinate(fabric_tile_id); + std::string tile_module_name = generate_tile_module_name(unique_tile_coord); + ModuleId tile_module = module_manager.find_module(tile_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(tile_module)); + /* Add a I/O children to top_module*/ + module_manager.add_io_child(top_module, tile_module, + tile_instance_ids[coord.x()][coord.y()], + vtr::Point(coord.x(), coord.y())); + } +} + /******************************************************************** * Add the tile-level instances to the top module of FPGA fabric * and build connects between them *******************************************************************/ -int build_top_module_tile_child_instances(ModuleManager& module_manager, - const ModuleId& top_module, - const DeviceGrid& grids, - const FabricTile& fabric_tile) { +int build_top_module_tile_child_instances( + ModuleManager& module_manager, const ModuleId& top_module, + MemoryBankShiftRegisterBanks& blwl_sr_banks, + const CircuitLibrary& circuit_lib, const DeviceGrid& grids, + const FabricTile& fabric_tile, const ConfigProtocol& config_protocol, + const FabricKey& fabric_key, const bool& frame_view) { int status = CMD_EXEC_SUCCESS; vtr::Matrix tile_instance_ids; status = add_top_module_tile_instances(module_manager, top_module, @@ -157,7 +266,76 @@ int build_top_module_tile_child_instances(ModuleManager& module_manager, return CMD_EXEC_FATAL_ERROR; } + /* Update the I/O children list */ + add_top_module_tile_io_children(module_manager, top_module, grids, + fabric_tile, tile_instance_ids); + /* TODO: Build the nets between tiles */ + if (false == frame_view) { + /* Reserve nets to be memory efficient */ + reserve_module_manager_module_nets(module_manager, top_module); + /* TODO: Regular nets between tiles */ + /* TODO: Inter-tile direct connections */ + } + + /* TODO: Add global ports from tile modules: how to connect to clock + architecture and the global port from tile annotation status = + add_top_module_global_ports_from_grid_modules( module_manager, top_module, + tile_annotation, vpr_device_annotation, grids, rr_graph, device_rr_gsb, + cb_instance_ids, grid_instance_ids, clk_ntwk, rr_clock_lookup); if + (CMD_EXEC_FATAL_ERROR == status) { return status; + } + */ + + /* Add GPIO ports from the sub-modules under this Verilog module + * For top-level module, we follow a special sequencing for I/O modules. So we + * rebuild the I/O children list here + */ + add_module_gpio_ports_from_child_modules(module_manager, top_module); + + /* Organize the list of memory modules and instances + * If we have an empty fabric key, we organize the memory modules as routine + * Otherwise, we will load the fabric key directly + */ + if (true == fabric_key.empty()) { + /* TODO: need a special one for tiles + organize_top_module_memory_modules( + module_manager, top_module, circuit_lib, config_protocol, sram_model, + grids, grid_instance_ids, device_rr_gsb, sb_instance_ids, cb_instance_ids, + compact_routing_hierarchy); + */ + } else { + VTR_ASSERT_SAFE(false == fabric_key.empty()); + /* Throw a fatal error when the fabric key has a mismatch in region + * organization. between architecture file and fabric key + */ + if (size_t(config_protocol.num_regions()) != fabric_key.regions().size()) { + VTR_LOG_ERROR( + "Fabric key has a different number of configurable regions (='%ld') " + "than architecture definition (=%d)!\n", + fabric_key.regions().size(), config_protocol.num_regions()); + return CMD_EXEC_FATAL_ERROR; + } + + status = load_top_module_memory_modules_from_fabric_key( + module_manager, top_module, circuit_lib, config_protocol, fabric_key); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + + status = load_top_module_shift_register_banks_from_fabric_key( + fabric_key, blwl_sr_banks); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + + /* 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); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } return CMD_EXEC_SUCCESS; } 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 5d2b40928..f779d2ccd 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.h +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.h @@ -32,10 +32,12 @@ /* begin namespace openfpga */ namespace openfpga { -int build_top_module_tile_child_instances(ModuleManager& module_manager, - const ModuleId& top_module, - const DeviceGrid& grids, - const FabricTile& fabric_tile); +int build_top_module_tile_child_instances( + ModuleManager& module_manager, const ModuleId& top_module, + MemoryBankShiftRegisterBanks& blwl_sr_banks, + const CircuitLibrary& circuit_lib, const DeviceGrid& grids, + const FabricTile& fabric_tile, const ConfigProtocol& config_protocol, + const FabricKey& fabric_key, const bool& frame_view); } /* end namespace openfpga */ From b70f7fb1b6dc7c436091a9415fc32890e39786ea Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 20 Jul 2023 21:22:07 -0700 Subject: [PATCH 197/391] [core] now option conflicts in command 'build_fabric' can error out --- .../src/base/openfpga_build_fabric_template.h | 21 +++++++++++++++++++ .../build_top_module_child_tile_instance.cpp | 8 ++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index a017e6743..25d4d3685 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -102,6 +102,27 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd, CommandOptionId opt_group_tile = cmd.option("group_tile"); CommandOptionId opt_verbose = cmd.option("verbose"); + /* Report conflicts with options: + * - group tile does not support duplicate_grid_pin + * - group tile requires compress_routing to be enabled + */ + if (cmd_context.option_enable(cmd, opt_group_tile)) { + if (cmd_context.option_enable(cmd, opt_duplicate_grid_pin)) { + VTR_LOG_ERROR( + "Option '%s' requires options '%s' to be disabled due to a conflict!\n", + cmd.option_name(opt_group_tile).c_str(), + cmd.option_name(opt_duplicate_grid_pin).c_str()); + return CMD_EXEC_FATAL_ERROR; + } + if (!cmd_context.option_enable(cmd, opt_compress_routing)) { + VTR_LOG_ERROR( + "Option '%s' requires options '%s' to be enabled due to a conflict!\n", + cmd.option_name(opt_group_tile).c_str(), + cmd.option_name(opt_compress_routing).c_str()); + return CMD_EXEC_FATAL_ERROR; + } + } + if (true == cmd_context.option_enable(cmd, opt_compress_routing)) { compress_routing_hierarchy_template( openfpga_ctx, cmd_context.option_enable(cmd, opt_verbose)); 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 5aeb78fe2..c7be3ab58 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -274,7 +274,13 @@ int build_top_module_tile_child_instances( if (false == frame_view) { /* Reserve nets to be memory efficient */ reserve_module_manager_module_nets(module_manager, top_module); - /* TODO: Regular nets between tiles */ + /* TODO: Regular nets between tiles + add_top_module_nets_connect_tiles( + module_manager, top_module, vpr_device_annotation, grids, + tile_instance_ids, rr_graph, device_rr_gsb, compact_routing_hierarchy, + duplicate_grid_pin); + */ + /* TODO: Inter-tile direct connections */ } From 39934f9d16742a101216467c2d01e5b46d09f50f Mon Sep 17 00:00:00 2001 From: Chung Shien Chai Date: Thu, 20 Jul 2023 22:34:18 -0700 Subject: [PATCH 198/391] Address issue 1256 --- .../openfpga_bitstream_command_template.h | 5 + .../src/base/openfpga_bitstream_template.h | 2 + .../build_fabric_bitstream_memory_bank.cpp | 68 +++++--- .../src/fpga_bitstream/fabric_bitstream.cpp | 154 ++++++++++++++++++ .../src/fpga_bitstream/fabric_bitstream.h | 96 +++++++++++ .../write_text_fabric_bitstream.cpp | 147 ++++++++++++++++- .../write_text_fabric_bitstream.h | 3 +- .../write_xml_fabric_bitstream.cpp | 63 ++++++- .../fpga_verilog/verilog_top_testbench.cpp | 13 ++ .../verilog_top_testbench_memory_bank.cpp | 14 +- 10 files changed, 531 insertions(+), 34 deletions(-) diff --git a/openfpga/src/base/openfpga_bitstream_command_template.h b/openfpga/src/base/openfpga_bitstream_command_template.h index b11c058cd..bc781dcdc 100644 --- a/openfpga/src/base/openfpga_bitstream_command_template.h +++ b/openfpga/src/base/openfpga_bitstream_command_template.h @@ -200,6 +200,11 @@ ShellCommandId add_write_fabric_bitstream_command_template( "Keep don't care bits in bitstream file; If not enabled, don't care bits " "are converted to logic '0' or '1'"); + /* Add an option '--wl_incremental_order' */ + shell_cmd.add_option( + "wl_decremental_order", false, + "Generate bitstream in WL decremental addressing order if supported"); + /* Add an option '--no_time_stamp' */ shell_cmd.add_option("no_time_stamp", false, "Do not print time stamp in output files"); diff --git a/openfpga/src/base/openfpga_bitstream_template.h b/openfpga/src/base/openfpga_bitstream_template.h index de01d1df9..196d9ef30 100644 --- a/openfpga/src/base/openfpga_bitstream_template.h +++ b/openfpga/src/base/openfpga_bitstream_template.h @@ -91,6 +91,7 @@ int write_fabric_bitstream_template(const T& openfpga_ctx, const Command& cmd, CommandOptionId opt_file_format = cmd.option("format"); CommandOptionId opt_fast_config = cmd.option("fast_configuration"); CommandOptionId opt_keep_dont_care_bits = cmd.option("keep_dont_care_bits"); + CommandOptionId opt_wl_decremental_order = cmd.option("wl_decremental_order"); CommandOptionId opt_no_time_stamp = cmd.option("no_time_stamp"); /* Write fabric bitstream if required */ @@ -127,6 +128,7 @@ int write_fabric_bitstream_template(const T& openfpga_ctx, const Command& cmd, cmd_context.option_value(cmd, opt_file), cmd_context.option_enable(cmd, opt_fast_config), cmd_context.option_enable(cmd, opt_keep_dont_care_bits), + !cmd_context.option_enable(cmd, opt_wl_decremental_order), !cmd_context.option_enable(cmd, opt_no_time_stamp), cmd_context.option_enable(cmd, opt_verbose)); } 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..1659e3b65 100644 --- a/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp @@ -174,6 +174,11 @@ static void rec_build_module_fabric_dependent_ql_memory_bank_regional_bitstream( bitstream_manager.block_bits(parent_block)) { FabricBitId fabric_bit = fabric_bitstream.add_bit(config_bit); + /* + If both BL and WL protocols are Flatten, we will have new way of + storing information in fabric_bitstream. This will save high + memory usage, as well as fast processing + */ /* The BL address to be decoded depends on the protocol * - flatten BLs: use 1-hot decoding * - BL decoders: fully encoded @@ -181,38 +186,53 @@ static void rec_build_module_fabric_dependent_ql_memory_bank_regional_bitstream( */ size_t cur_bl_index = bl_start_index_per_tile.at(tile_coord.x()) + cur_mem_index[tile_coord] % num_bls_cur_tile; - std::vector bl_addr_bits_vec; - if (BLWL_PROTOCOL_DECODER == config_protocol.bl_protocol_type()) { - bl_addr_bits_vec = itobin_charvec(cur_bl_index, bl_addr_size); - } else if (BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type() || - BLWL_PROTOCOL_SHIFT_REGISTER == - config_protocol.bl_protocol_type()) { - bl_addr_bits_vec = - ito1hot_charvec(cur_bl_index, bl_addr_size, DONT_CARE_CHAR); + if (BLWL_PROTOCOL_FLATTEN != config_protocol.bl_protocol_type() || + BLWL_PROTOCOL_FLATTEN != config_protocol.wl_protocol_type()) { + // This is using old way + // We only do this kind of resource wasting storing if + // either protocol is not flatten + std::vector bl_addr_bits_vec; + if (BLWL_PROTOCOL_DECODER == config_protocol.bl_protocol_type()) { + bl_addr_bits_vec = itobin_charvec(cur_bl_index, bl_addr_size); + } else if (BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type() || + BLWL_PROTOCOL_SHIFT_REGISTER == + config_protocol.bl_protocol_type()) { + bl_addr_bits_vec = + ito1hot_charvec(cur_bl_index, bl_addr_size, DONT_CARE_CHAR); + } + /* Set BL address */ + fabric_bitstream.set_bit_bl_address( + fabric_bit, bl_addr_bits_vec, + BLWL_PROTOCOL_DECODER != config_protocol.bl_protocol_type()); } /* Find WL address */ size_t cur_wl_index = wl_start_index_per_tile.at(tile_coord.y()) + std::floor(cur_mem_index[tile_coord] / num_bls_cur_tile); - std::vector wl_addr_bits_vec; - if (BLWL_PROTOCOL_DECODER == config_protocol.wl_protocol_type()) { - wl_addr_bits_vec = itobin_charvec(cur_wl_index, wl_addr_size); - } else if (BLWL_PROTOCOL_FLATTEN == config_protocol.wl_protocol_type() || - BLWL_PROTOCOL_SHIFT_REGISTER == - config_protocol.wl_protocol_type()) { - wl_addr_bits_vec = ito1hot_charvec(cur_wl_index, wl_addr_size); + if (BLWL_PROTOCOL_FLATTEN != config_protocol.bl_protocol_type() || + BLWL_PROTOCOL_FLATTEN != config_protocol.wl_protocol_type()) { + // This is using old way + // We only do this kind of resource wasting storing if + // either protocol is not flatten + std::vector wl_addr_bits_vec; + if (BLWL_PROTOCOL_DECODER == config_protocol.wl_protocol_type()) { + wl_addr_bits_vec = itobin_charvec(cur_wl_index, wl_addr_size); + } else if (BLWL_PROTOCOL_FLATTEN == config_protocol.wl_protocol_type() || + BLWL_PROTOCOL_SHIFT_REGISTER == + config_protocol.wl_protocol_type()) { + wl_addr_bits_vec = ito1hot_charvec(cur_wl_index, wl_addr_size); + } + /* Set WL address */ + fabric_bitstream.set_bit_wl_address( + fabric_bit, wl_addr_bits_vec, + BLWL_PROTOCOL_DECODER != config_protocol.wl_protocol_type()); } - /* Set BL address */ - fabric_bitstream.set_bit_bl_address( - fabric_bit, bl_addr_bits_vec, - BLWL_PROTOCOL_DECODER != config_protocol.bl_protocol_type()); - - /* Set WL address */ - fabric_bitstream.set_bit_wl_address( - fabric_bit, wl_addr_bits_vec, - BLWL_PROTOCOL_DECODER != config_protocol.wl_protocol_type()); + /* New way of storing information in compact way*/ + fabric_bitstream.set_memory_bank_info( + fabric_bit, fabric_bitstream_region, cur_bl_index, cur_wl_index, + bl_addr_size, wl_addr_size, bitstream_manager.bit_value(config_bit)); /* Set data input */ fabric_bitstream.set_bit_din(fabric_bit, diff --git a/openfpga/src/fpga_bitstream/fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/fabric_bitstream.cpp index 5649388b8..9778b76e6 100644 --- a/openfpga/src/fpga_bitstream/fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/fabric_bitstream.cpp @@ -11,6 +11,133 @@ /* begin namespace openfpga */ namespace openfpga { +/************************************************** + * FabricBitstreamMemoryBank + *************************************************/ +void FabricBitstreamMemoryBank::add_bit(const fabric_size_t& bit_id, + const fabric_size_t& region_id, + const fabric_size_t& bl, + const fabric_size_t& wl, + const fabric_size_t& bl_addr_size, + const fabric_size_t& wl_addr_size, + bool bit) { + // Fabric Bit is added in sequential manner and each bit is unique + VTR_ASSERT((size_t)(bit_id) == fabric_bit_datas.size()); + // Region is added in sequntial manner but it is not unique from fabric bit + // perspective + VTR_ASSERT((size_t)(region_id) <= blwl_lengths.size()); + if ((size_t)(region_id) == blwl_lengths.size()) { + // Add if this is first time + blwl_lengths.push_back(fabric_blwl_length(bl_addr_size, wl_addr_size)); + } else { + // Otherwise if the region had been added, it must always be consistent + VTR_ASSERT(blwl_lengths[region_id].bl == bl_addr_size); + VTR_ASSERT(blwl_lengths[region_id].wl == wl_addr_size); + } + // The BL/WL index must be within respective length + VTR_ASSERT(bl < blwl_lengths[region_id].bl); + VTR_ASSERT(wl < blwl_lengths[region_id].wl); + // We might not need this at all to track the raw data + // But since it does not use a lot of memory, tracking for good + fabric_bit_datas.push_back(fabric_bit_data((fabric_size_t)(size_t)(region_id), + (fabric_size_t)(bl), + (fabric_size_t)(wl), bit)); + // This is real compact data + VTR_ASSERT(datas.size() == masks.size()); + while ((size_t)(region_id) >= datas.size()) { + datas.emplace_back(); + masks.emplace_back(); + } + VTR_ASSERT(datas[region_id].size() == masks[region_id].size()); + while ((size_t)(wl) >= datas[region_id].size()) { + datas[region_id].push_back(std::vector((bl_addr_size + 7) / 8, 0)); + masks[region_id].push_back(std::vector((bl_addr_size + 7) / 8, 0)); + } + // Same uniqie config bit cannot be set twice + VTR_ASSERT((masks[region_id][wl][bl >> 3] & (1 << (bl & 7))) == 0); + if (bit) { + // Mark the data value if bit (or din) is true + datas[region_id][wl][bl >> 3] |= (1 << (bl & 7)); + } + // Mark the mask to indicate we had used this bit + masks[region_id][wl][bl >> 3] |= (1 << (bl & 7)); +} + +void FabricBitstreamMemoryBank::fast_configuration( + const bool& fast, const bool& bit_value_to_skip) { + for (auto& wls : wls_to_skip) { + wls.clear(); + } + wls_to_skip.clear(); + // If we had processed it before, we do not need to process again + if (wls_to_skip.size() == 0) { + for (size_t region = 0; region < datas.size(); region++) { + wls_to_skip.emplace_back(); + if (fast) { + for (fabric_size_t wl = 0; wl < blwl_lengths[region].wl; wl++) { + VTR_ASSERT((size_t)(wl) < datas[region].size()); + bool skip_wl = true; + for (fabric_size_t bl = 0; bl < blwl_lengths[region].bl && skip_wl; + bl++) { + // Only check the bit that being used (marked in the mask), + // otherwise it is just a don't care, we can skip + if (masks[region][wl][bl >> 3] & (1 << (bl & 7))) { + if (datas[region][wl][bl >> 3] & (1 << (bl & 7))) { + // If bit_value_to_skip=true, and yet the din (recorded in + // datas) also 1, then we can skip + skip_wl = bit_value_to_skip; + } else { + skip_wl = !bit_value_to_skip; + } + } + } + if (skip_wl) { + // Record down that for this region, we will skip this WL + wls_to_skip[region].push_back(wl); + } + } + } + } + } else { + VTR_ASSERT(wls_to_skip.size() == datas.size()); + } +} + +fabric_size_t FabricBitstreamMemoryBank::get_lontest_effective_wl_addr_size() + const { + // This function check effective WL addr size + // Where effective WL is the WL that we wantt to program after considering + // fast configuration From all the region, it return the longest + fabric_size_t longest_wl = 0; + for (size_t region = 0; region < datas.size(); region++) { + VTR_ASSERT((size_t)(region) < wls_to_skip.size()); + fabric_size_t current_wl = + (fabric_size_t)(datas[region].size() - wls_to_skip[region].size()); + if (current_wl > longest_wl) { + longest_wl = current_wl; + } + } + return longest_wl; +} + +fabric_size_t FabricBitstreamMemoryBank::get_total_bl_addr_size() const { + // Simply total up all the BL addr size + fabric_size_t bl = 0; + for (size_t region = 0; region < datas.size(); region++) { + bl += blwl_lengths[region].bl; + } + return bl; +} + +fabric_size_t FabricBitstreamMemoryBank::get_total_wl_addr_size() const { + // Simply total up all the WL addr size + fabric_size_t wl = 0; + for (size_t region = 0; region < datas.size(); region++) { + wl += blwl_lengths[region].wl; + } + return wl; +} + /************************************************** * Public Constructor *************************************************/ @@ -129,6 +256,12 @@ bool FabricBitstream::use_address() const { return use_address_; } bool FabricBitstream::use_wl_address() const { return use_wl_address_; } +const FabricBitstreamMemoryBank* FabricBitstream::memory_bank_info() const { + VTR_ASSERT(true == use_address_); + VTR_ASSERT(true == use_wl_address_); + return &memory_bank_data_; +} + /****************************************************************************** * Public Mutators ******************************************************************************/ @@ -243,6 +376,27 @@ void FabricBitstream::set_bl_address_length(const size_t& length) { set_address_length(length); } +void FabricBitstream::set_memory_bank_info(const FabricBitId& bit_id, + const FabricBitRegionId& region_id, + const size_t& bl, const size_t& wl, + const size_t& bl_addr_size, + const size_t& wl_addr_size, + bool bit) { + // Bit must be valid one + // We only support this in protocol that use BL and WL address + VTR_ASSERT(true == valid_bit_id(bit_id)); + VTR_ASSERT(true == use_address_); + VTR_ASSERT(true == use_wl_address_); + VTR_ASSERT(bl_addr_size); + VTR_ASSERT(wl_addr_size); + // All the basic checking had passed, we can add the data into + // memory_bank_data_ + memory_bank_data_.add_bit( + (fabric_size_t)(size_t)(bit_id), (fabric_size_t)(size_t)(region_id), + (fabric_size_t)(bl), (fabric_size_t)(wl), (fabric_size_t)(bl_addr_size), + (fabric_size_t)(wl_addr_size), bit); +} + void FabricBitstream::set_use_wl_address(const bool& enable) { /* Add a lock, only can be modified when num bits are zero*/ if (0 == num_bits_) { diff --git a/openfpga/src/fpga_bitstream/fabric_bitstream.h b/openfpga/src/fpga_bitstream/fabric_bitstream.h index 68a972636..dfe8840a2 100644 --- a/openfpga/src/fpga_bitstream/fabric_bitstream.h +++ b/openfpga/src/fpga_bitstream/fabric_bitstream.h @@ -41,6 +41,85 @@ /* begin namespace openfpga */ namespace openfpga { +// Use uint32_t (maximum of 4Gigs) is good enough, we have BL and WL, +// combination of both hold up to 18 quintillion of configuration bits (+ dont +// care) +typedef uint32_t fabric_size_t; +struct fabric_bit_data { + fabric_bit_data(fabric_size_t r, fabric_size_t b, fabric_size_t w, bool bi) + : region(r), bl(b), wl(w), bit(bi) {} + const fabric_size_t region = 0; + const fabric_size_t bl = 0; + const fabric_size_t wl = 0; + const bool bit = false; +}; +struct fabric_blwl_length { + fabric_blwl_length(fabric_size_t b, fabric_size_t w) : bl(b), wl(w) {} + const fabric_size_t bl = 0; + const fabric_size_t wl = 0; +}; + +/* + This class arrange Memory Bank databae in a compact way +*/ +struct FabricBitstreamMemoryBank { + void add_bit(const fabric_size_t& bit_id, const fabric_size_t& region_id, + const fabric_size_t& bl, const fabric_size_t& wl, + const fabric_size_t& bl_addr_size, + const fabric_size_t& wl_addr_size, bool bit); + void fast_configuration(const bool& fast, const bool& bit_value_to_skip); + fabric_size_t get_lontest_effective_wl_addr_size() const; + fabric_size_t get_total_bl_addr_size() const; + fabric_size_t get_total_wl_addr_size() const; + + /************************* + * All the database (except fabric_bit_datas) is sorted by region + * 1. The very first layer of vector is region + * For the datas and masks + * 1. They are sorted by WL, hence second layer is WL + * 2. Layer is BL data stored in vector of uint8_t + * 3. Each uint8_t will store up-to 8 configuration bit info + **************************/ + // Store the BL WL of each region + std::vector blwl_lengths; + // Store config ID raw data. Not used by bitstream generation + // Used by XML generation + /* + fabric_bit_datas[Bit #0] = (region, bl, wl) + fabric_bit_datas[Bit #1] = (region, bl, wl) + fabric_bit_datas[Bit #2] = (region, bl, wl) + */ + std::vector fabric_bit_datas; + // 100K LE FPGA only need few mega bytes + /* + datas represent the Din value of a given WL and BL (1bit) + datas[region #0][wl #0] = std::vector to represent BLs + where uint8_t #0 = MSB{ BL#7, BL#6, .... BL #1, BL #0 } LSB + where uint8_t #1 = MSB{ BL#15, BL#14, .... BL #9, BL #8 } LSB + datas[region #0][wl #1] = std::vector to represent BLs + datas[region #0][wl #2] = std::vector to represent BLs + ...... + datas[region #0][wl #n-1] = std::vector to represent BLs + ...... + datas[region #1][wl #0] = std::vector to represent BLs + datas[region #1][wl #1] = std::vector to represent BLs + ...... + */ + std::vector>> datas; + /* + masks has same structure as datas + but masks presents data that being used + for exampe: + if mask's uint8_t #0 value = 0x41 it means for this WL + a. BL #0 is being used, and its Din is recoreded in datas + b. BL #6 is being used, and its Din is recoreded in datas + c. Other BLs #1, 2, 3, 4, 5, 7 are don't care bit (not being used) + */ + std::vector>> masks; + // This track which WL to skip because of fast configuration + std::vector> wls_to_skip; +}; + class FabricBitstream { public: /* Type implementations */ /* @@ -144,6 +223,8 @@ class FabricBitstream { bool use_address() const; bool use_wl_address() const; + const FabricBitstreamMemoryBank* memory_bank_info() const; + public: /* Public Mutators */ /* Reserve config bits */ void reserve_bits(const size_t& num_bits); @@ -193,6 +274,18 @@ class FabricBitstream { void set_address_length(const size_t& length); void set_bl_address_length(const size_t& length); + /* + This is setting memory bank protocol in a more efficient way + Instead of building lengthy BL/WL bits of database (BL or Wl could be in + thousand bits of size), a small device like 100K LE (compared to other + vendors offer) might end up using tens of gig bytes. + */ + void set_memory_bank_info(const FabricBitId& bit_id, + const FabricBitRegionId& region_id, + const size_t& bl, const size_t& wl, + const size_t& bl_addr_size, + const size_t& wl_addr_size, bool bit); + /* Enable the use of WL-address related data * Same priniciple as the set_use_address() */ @@ -250,6 +343,9 @@ class FabricBitstream { /* Data input (Din) bits: this is designed for memory decoders */ vtr::vector bit_dins_; + + /* New way of dealing with memory bank protocol - fast and compact */ + FabricBitstreamMemoryBank memory_bank_data_; }; } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp index 9cb2a3f9c..2ade6174b 100644 --- a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp @@ -245,6 +245,142 @@ static int write_memory_bank_flatten_fabric_bitstream_to_text_file( return status; } +/******************************************************************** + * Write the fabric bitstream fitting a memory bank protocol + * to a plain text file in efficient method + * + * Old function is write_memory_bank_flatten_fabric_bitstream_to_text_file() + * + * As compared to original function, based on 100K LE FPGA: + * 1. Original function used 600 seconds and need 80G Bytes of memory + * 2. This new function only needs 1 second and 4M Bytes + * + * Old function only print WL in decremental order. It is not by intentional + * It is because of the map-key ordering + * In QL Memory Bank with Flatten BL/WL, data is stored by WL address, + * where we use WL string as map key + * WL #0 --- "1000000000000 .... 0000" + * WL #1 --- "0100000000000 .... 0000" + * WL #n-1 --- "0000000000000 .... 0001 + * From string comparison wise, WL #n-1 will be first, and WL #0 will be last + * The sequence of WL does not really matter, but preferrable in some ordering + * manner. Using map key as ordering cannot guarantee the determinstic + * + * This new way of writting fabric guarantee the WL order in 100% deterministc + * way: either incremental (default) or decremental + * + * Return: + * - 0 if succeed + * - 1 if critical errors occured + *******************************************************************/ +static int fast_write_memory_bank_flatten_fabric_bitstream_to_text_file( + std::fstream& fp, const bool& fast_configuration, + const bool& bit_value_to_skip, const FabricBitstream& fabric_bitstream, + const bool& keep_dont_care_bits, const bool& wl_incremental_order) { + int status = 0; + + std::string dont_care_bit = "0"; + if (keep_dont_care_bits) { + dont_care_bit = "x"; + } + const FabricBitstreamMemoryBank* memory_bank = + fabric_bitstream.memory_bank_info(); + + // Must call this to prepare wls_to_skip + (const_cast(memory_bank)) + ->fast_configuration(fast_configuration, bit_value_to_skip); + + fabric_size_t lontest_effective_wl_addr_size = + memory_bank->get_lontest_effective_wl_addr_size(); + /* Output information about how to intepret the bitstream */ + fp << "// Bitstream length: " << lontest_effective_wl_addr_size << std::endl; + fp << "// Bitstream width (LSB -> MSB): "; + fp << "get_total_bl_addr_size() << " bits>"; + fp << "get_total_wl_addr_size() << " bits>"; + fp << std::endl; + + std::vector wl_indexes; + for (size_t region = 0; region < memory_bank->datas.size(); region++) { + if (wl_incremental_order) { + wl_indexes.push_back(0); + } else { + wl_indexes.push_back( + (fabric_size_t)(memory_bank->datas[region].size() - 1)); + } + } + for (size_t wl_index = 0; wl_index < lontest_effective_wl_addr_size; + wl_index++) { + /* Write BL address code */ + /* cascade region 0, 1, 2, 3 ... */ + for (size_t region = 0; region < memory_bank->datas.size(); region++) { + const fabric_blwl_length& lengths = memory_bank->blwl_lengths[region]; + fabric_size_t current_wl = wl_indexes[region]; + while (std::find(memory_bank->wls_to_skip[region].begin(), + memory_bank->wls_to_skip[region].end(), + current_wl) != memory_bank->wls_to_skip[region].end()) { + // We would like to skip this + if (wl_incremental_order) { + wl_indexes[region]++; + } else { + wl_indexes[region]--; + } + current_wl = wl_indexes[region]; + } + if (current_wl < memory_bank->datas[region].size()) { + const std::vector& data = + memory_bank->datas[region][current_wl]; + const std::vector& mask = + memory_bank->masks[region][current_wl]; + for (size_t bl = 0; bl < lengths.bl; bl++) { + if (mask[bl >> 3] & (1 << (bl & 7))) { + if (data[bl >> 3] & (1 << (bl & 7))) { + fp << "1"; + } else { + fp << "0"; + } + } else { + fp << dont_care_bit.c_str(); + } + } + } else { + /* However not all region has equal WL, for those that is shortest, + * print 'x' for all BL*/ + for (size_t bl = 0; bl < lengths.bl; bl++) { + fp << dont_care_bit.c_str(); + } + } + } + /* Write WL address code */ + /* cascade region 0, 1, 2, 3 ... */ + for (size_t region = 0; region < memory_bank->datas.size(); region++) { + const fabric_blwl_length& lengths = memory_bank->blwl_lengths[region]; + fabric_size_t current_wl = wl_indexes[region]; + if (current_wl < memory_bank->datas[region].size()) { + for (size_t wl_temp = 0; wl_temp < lengths.wl; wl_temp++) { + if (wl_temp == current_wl) { + fp << "1"; + } else { + fp << "0"; + } + } + if (wl_incremental_order) { + wl_indexes[region]++; + } else { + wl_indexes[region]--; + } + } else { + /* However not all region has equal WL, for those that is shortest, + * print 'x' for all WL */ + for (size_t wl_temp = 0; wl_temp < lengths.wl; wl_temp++) { + fp << dont_care_bit.c_str(); + } + } + } + fp << std::endl; + } + return status; +} + /******************************************************************** * Write the fabric bitstream fitting a memory bank protocol * to a plain text file @@ -393,7 +529,8 @@ int write_fabric_bitstream_to_text_file( const ConfigProtocol& config_protocol, const FabricGlobalPortInfo& global_ports, const std::string& fname, const bool& fast_configuration, const bool& keep_dont_care_bits, - const bool& include_time_stamp, const bool& verbose) { + const bool& wl_incremental_order, const bool& include_time_stamp, + const bool& verbose) { /* Ensure that we have a valid file name */ if (true == fname.empty()) { VTR_LOG_ERROR( @@ -454,6 +591,14 @@ int write_fabric_bitstream_to_text_file( if (BLWL_PROTOCOL_DECODER == config_protocol.bl_protocol_type()) { status = write_memory_bank_fabric_bitstream_to_text_file( fp, apply_fast_configuration, bit_value_to_skip, fabric_bitstream); + } else if (BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type() && + BLWL_PROTOCOL_FLATTEN == config_protocol.wl_protocol_type()) { + // If both BL and WL protocols are flatten, use new way to write the + // bitstream + status = fast_write_memory_bank_flatten_fabric_bitstream_to_text_file( + fp, apply_fast_configuration, bit_value_to_skip, fabric_bitstream, + keep_dont_care_bits, wl_incremental_order); + } else if (BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type()) { status = write_memory_bank_flatten_fabric_bitstream_to_text_file( fp, apply_fast_configuration, bit_value_to_skip, fabric_bitstream, diff --git a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.h b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.h index 0d8682739..59f4774de 100644 --- a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.h +++ b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.h @@ -27,7 +27,8 @@ int write_fabric_bitstream_to_text_file( const ConfigProtocol& config_protocol, const FabricGlobalPortInfo& global_ports, const std::string& fname, const bool& fast_configuration, const bool& keep_dont_care_bits, - const bool& include_time_stamp, const bool& verbose); + const bool& wl_incremental_order, const bool& include_time_stamp, + const bool& verbose); } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp index ec8a036fd..786a3768e 100644 --- a/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp @@ -71,7 +71,8 @@ static void write_fabric_bitstream_xml_file_head( static int write_fabric_config_bit_to_xml_file( std::fstream& fp, const BitstreamManager& bitstream_manager, const FabricBitstream& fabric_bitstream, const FabricBitId& fabric_bit, - const e_config_protocol_type& config_type, const int& xml_hierarchy_depth) { + const e_config_protocol_type& config_type, const int& xml_hierarchy_depth, + std::string& bl_addr, std::string& wl_addr) { if (false == valid_file_stream(fp)) { return 1; } @@ -106,7 +107,6 @@ static int write_fabric_config_bit_to_xml_file( case CONFIG_MEM_STANDALONE: case CONFIG_MEM_SCAN_CHAIN: break; - case CONFIG_MEM_QL_MEMORY_BANK: case CONFIG_MEM_MEMORY_BANK: { /* Bit line address */ write_tab_to_file(fp, xml_hierarchy_depth + 1); @@ -124,6 +124,41 @@ static int write_fabric_config_bit_to_xml_file( fp << "\"/>\n"; break; } + case CONFIG_MEM_QL_MEMORY_BANK: { + // New way of printing XML + // This is fast (less than 100s) as compared to original 1300s seen in + // 100K LE FPFA + const FabricBitstreamMemoryBank* memory_bank = + fabric_bitstream.memory_bank_info(); + /* Bit line address */ + write_tab_to_file(fp, xml_hierarchy_depth + 1); + const fabric_bit_data& bit = + memory_bank->fabric_bit_datas[(size_t)(fabric_bit)]; + const fabric_blwl_length& lengths = memory_bank->blwl_lengths[bit.region]; + if (bl_addr.size() == 0) { + VTR_ASSERT(wl_addr.size() == 0); + bl_addr.resize(lengths.bl); + wl_addr.resize(lengths.wl); + memset(&bl_addr[0], 'x', lengths.bl); + memset(&wl_addr[0], '0', lengths.wl); + } else { + VTR_ASSERT((fabric_size_t)(bl_addr.size()) == lengths.bl); + VTR_ASSERT((fabric_size_t)(wl_addr.size()) == lengths.wl); + } + fp << "\n"; + /* Word line address */ + write_tab_to_file(fp, xml_hierarchy_depth + 1); + fp << "\n"; + break; + } case CONFIG_MEM_FRAME_BASED: { write_tab_to_file(fp, xml_hierarchy_depth + 1); fp << "\n"; + size_t bit_index = 0; + size_t total_bits = fabric_bitstream.region_bits(fabric_region).size(); + size_t percentage = 0; for (const FabricBitId& fabric_bit : fabric_bitstream.region_bits(fabric_region)) { status = write_fabric_config_bit_to_xml_file( fp, bitstream_manager, fabric_bitstream, fabric_bit, config_type, - xml_hierarchy_depth + 1); + xml_hierarchy_depth + 1, bl_addr, wl_addr); if (1 == status) { return status; } + // Misc to print percentage of the process + bit_index++; + size_t temp = (bit_index * 100) / total_bits; + if (temp != percentage) { + VTR_LOG(" Progress: %lu%\r", percentage); + percentage = temp; + } } write_tab_to_file(fp, xml_hierarchy_depth); diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 4146fa870..1ac006cca 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -1061,6 +1061,19 @@ static size_t calculate_num_config_clock_cycles( (float)full_num_config_clock_cycles - 1.)); } + } else if (BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type() && + BLWL_PROTOCOL_FLATTEN == config_protocol.wl_protocol_type()) { + // Only support new fast way if both BL/WL protocols are flatten + // Based on 100K LE FPGA, we are wasting a lot of time to build + // MemoryBankFlattenFabricBitstream + // just to get the effective WL addr size. So wasteful of the resource + const FabricBitstreamMemoryBank* memory_bank = + fabric_bitstream.memory_bank_info(); + // Must call this to prepare wls_to_skip + (const_cast(memory_bank)) + ->fast_configuration(fast_configuration, bit_value_to_skip); + num_config_clock_cycles = + 1 + memory_bank->get_lontest_effective_wl_addr_size(); } else if (BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type()) { num_config_clock_cycles = 1 + build_memory_bank_flatten_fabric_bitstream( diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench_memory_bank.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench_memory_bank.cpp index 8b70ef3fb..7b964be62 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench_memory_bank.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench_memory_bank.cpp @@ -565,9 +565,15 @@ static void print_verilog_full_testbench_ql_memory_bank_flatten_bitstream( valid_file_stream(fp); /* Reorganize the fabric bitstream by the same address across regions */ - MemoryBankFlattenFabricBitstream fabric_bits_by_addr = - build_memory_bank_flatten_fabric_bitstream( - fabric_bitstream, fast_configuration, bit_value_to_skip); + // New way to get the effective WL addr size. + // Based on 100K LE FPGA, we are wasting a lot of time to build + // MemoryBankFlattenFabricBitstream just to get size(). So wasteful of the + // resource + const FabricBitstreamMemoryBank* memory_bank = + fabric_bitstream.memory_bank_info(); + // Must call this to prepare wls_to_skip + (const_cast(memory_bank)) + ->fast_configuration(fast_configuration, bit_value_to_skip); /* Feed address and data input pair one by one * Note: the first cycle is reserved for programming reset @@ -604,7 +610,7 @@ static void print_verilog_full_testbench_ql_memory_bank_flatten_bitstream( /* Define a constant for the bitstream length */ print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_LENGTH_VARIABLE), - fabric_bits_by_addr.size()); + memory_bank->get_lontest_effective_wl_addr_size()); print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_WIDTH_VARIABLE), bl_port_width + wl_port_width); From fcf308fcd6b7c7591932c7044c543f9a29a05d93 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 20 Jul 2023 23:00:35 -0700 Subject: [PATCH 199/391] [core] developing inter-tile connections for top module --- openfpga/src/fabric/build_device_module.cpp | 2 +- openfpga/src/fabric/build_top_module.cpp | 4 +- openfpga/src/fabric/build_top_module.h | 3 +- .../build_top_module_child_tile_instance.cpp | 732 +++++++++++++++++- .../build_top_module_child_tile_instance.h | 3 +- 5 files changed, 732 insertions(+), 12 deletions(-) diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index 0ba0dd167..b4d9cb222 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -128,7 +128,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); + fabric_key, generate_random_fabric_key, 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 9614407f4..389b6091b 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -57,7 +57,7 @@ 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& generate_random_fabric_key, const bool& verbose) { vtr::ScopedStartFinishTimer timer("Build FPGA fabric module"); int status = CMD_EXEC_SUCCESS; @@ -80,7 +80,7 @@ int build_top_module( /* 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, grids, - fabric_tile, config_protocol, fabric_key, frame_view); + fabric_tile, config_protocol, fabric_key, 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 dbee3d6cd..7ee641072 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& generate_random_fabric_key, + const bool& verbose); } /* 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 c7be3ab58..ef5a42d18 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -248,6 +248,723 @@ static void add_top_module_tile_io_children( } } +/******************************************************************** + * Add module nets to connect a switch block in a given tile to the programmable block in adjacent tiles + * + * TileA | TileB + * +------------+ | +------------+ + * | | | | | + * | Grid | | | Grid | + * | [x][y+1] | | | [x+1][y+1] | + * | |--|-+ +----| | + * +------------+ | | | +------------+ + * | | | | | + * ------------------+ v v | + * | +------------+ | + * +------>| |<-----+ + * | Switch | + * | Block | + * +------>| [x][y] |<-----+ + * | +------------+ | + * | ^ ^ | + * | | | | + * +------------+ | | +------------+ + * | |----+ +-----| | + * | Grid | | Grid | + * | [x][y] | | [x+1][y] | + * | | | | + * +------------+ +------------+ + * + *******************************************************************/ +static int build_top_module_tile_nets_between_sb_and_pb( + ModuleManager& module_manager, const ModuleId& top_module, + const ModuleId& tile_module, const size_t& tile_instance_id, + const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, + const RRGSB& rr_gsb, const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id, + const bool& frame_view, const bool& verbose) { + /* Skip those Switch blocks that do not exist */ + if (false == rr_gsb.is_sb_exist()) { + return CMD_EXEC_SUCCESS; + } + + /* We could have two different coordinators, one is the instance, the other is + * the module */ + vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), + rr_gsb.get_sb_y()); + vtr::Point module_gsb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); + + /* If we use compact routing hierarchy, we should find the unique module of + * CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + vtr::Point gsb_coord(rr_gsb.get_x(), rr_gsb.get_y()); + const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(gsb_coord); + module_gsb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_coordinate.set_y(unique_mirror.get_y()); + } + + /* This is the source cb that is added to the top module */ + const RRGSB& module_sb = device_rr_gsb.get_gsb(module_gsb_coordinate); + vtr::Point module_sb_coordinate(module_sb.get_sb_x(), + module_sb.get_sb_y()); + + /* Collect sink-related information */ + std::string sink_sb_module_name = + generate_switch_block_module_name(module_sb_coordinate); + ModuleId sink_sb_module = module_manager.find_module(sink_sb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sink_sb_module)); + size_t sink_sb_instance = sb_instance; + + /* Connect grid output pins (OPIN) to switch block grid pins */ + for (size_t side = 0; side < module_sb.get_num_sides(); ++side) { + SideManager side_manager(side); + for (size_t inode = 0; + inode < module_sb.get_num_opin_nodes(side_manager.get_side()); + ++inode) { + /* Collect source-related information */ + /* Generate the grid module name by considering if it locates on the + * border */ + vtr::Point grid_coordinate( + rr_graph.node_xlow( + rr_gsb.get_opin_node(side_manager.get_side(), inode)), + rr_graph.node_ylow( + rr_gsb.get_opin_node(side_manager.get_side(), inode))); + std::string src_grid_module_name = + generate_grid_block_module_name_in_top_module( + std::string(GRID_MODULE_NAME_PREFIX), grids, grid_coordinate); + ModuleId src_grid_module = + module_manager.find_module(src_grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(src_grid_module)); + size_t src_grid_pin_index = rr_graph.node_pin_num( + rr_gsb.get_opin_node(side_manager.get_side(), inode)); + + t_physical_tile_type_ptr grid_type_descriptor = + grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + size_t src_grid_pin_width = + grid_type_descriptor->pin_width_offset[src_grid_pin_index]; + size_t src_grid_pin_height = + grid_type_descriptor->pin_height_offset[src_grid_pin_index]; + BasicPort src_grid_pin_info = + vpr_device_annotation.physical_tile_pin_port_info(grid_type_descriptor, + src_grid_pin_index); + VTR_ASSERT(true == src_grid_pin_info.is_valid()); + int subtile_index = vpr_device_annotation.physical_tile_pin_subtile_index( + grid_type_descriptor, src_grid_pin_index); + VTR_ASSERT(OPEN != subtile_index && + subtile_index < grid_type_descriptor->capacity); + std::string src_grid_port_name = generate_grid_port_name( + src_grid_pin_width, src_grid_pin_height, subtile_index, + get_rr_graph_single_node_side( + rr_graph, rr_gsb.get_opin_node(side_manager.get_side(), inode)), + src_grid_pin_info); + ModulePortId src_grid_port_id = + module_manager.find_module_port(src_grid_module, src_grid_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(src_grid_module, + src_grid_port_id)); + BasicPort src_grid_port = + module_manager.module_port(src_grid_module, src_grid_port_id); + + /* Collect sink-related information */ + vtr::Point sink_sb_port_coord( + rr_graph.node_xlow( + module_sb.get_opin_node(side_manager.get_side(), inode)), + rr_graph.node_ylow( + module_sb.get_opin_node(side_manager.get_side(), inode))); + std::string sink_sb_port_name = generate_sb_module_grid_port_name( + side_manager.get_side(), + get_rr_graph_single_node_side( + rr_graph, module_sb.get_opin_node(side_manager.get_side(), inode)), + grids, vpr_device_annotation, rr_graph, + module_sb.get_opin_node(side_manager.get_side(), inode)); + ModulePortId sink_sb_port_id = + module_manager.find_module_port(sink_sb_module, sink_sb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(sink_sb_module, + sink_sb_port_id)); + BasicPort sink_sb_port = + module_manager.module_port(sink_sb_module, sink_sb_port_id); + + /* Check if the grid is inside the tile, if not, create ports */ + if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { + continue; + } + /* TODO: Create nets */ + if (!frame_view) { + } + } + } + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * This function will create nets for the connections + * between grid input pins and connection blocks + * In this case, the net source is the connection block pin, + * while the net sink is the grid input + * + * TileA | TileB + * +------------+ | +------------------+ +------------+ + * | | | | | | | + * | Grid |<-----| Connection Block |----->| Grid | + * | [x][y+1] | | | Y-direction | | [x+1][y+1] | + * | | | | [x][y+1] | | | + * +------------+ | +------------------+ +------------+ + * ^ | + * ----------------+ + * | + * +------------+ +------------------+ + * | Connection | | | + * | Block | | Switch Block | + * | X-direction| | [x][y] | + * | [x][y] | | | + * +------------+ +------------------+ + * | + * v + * +------------+ + * | | + * | Grid | + * | [x][y] | + * | | + * +------------+ + * + * + * Relationship between source connection block and its unique module + * Take an example of a CBY + * + * grid_pin name should follow unique module of Grid[x][y+1] + * cb_pin name should follow unique module of CBY[x][y+1] + * + * However, instace id should follow the origin Grid and Connection block + * + * + * +------------+ +------------------+ + * | | | | + * | Grid |<------------| Connection Block | + * | [x][y+1] | | Y-direction | + * | | | [x][y+1] | + * +------------+ +------------------+ + * ^ + * || unique mirror + * +------------+ +------------------+ + * | | | | + * | Grid |<------------| Connection Block | + * | [i][j+1] | | Y-direction | + * | | | [i][j+1] | + * +------------+ +------------------+ + * + *******************************************************************/ +static int build_top_module_tile_nets_between_cb_and_pb( + ModuleManager& module_manager, const ModuleId& top_module, + const ModuleId& tile_module, const size_t& tile_instance_id, + const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, + const RRGSB& rr_gsb, const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id, const t_rr_type& cb_type, + const bool& frame_view, + const bool& verbose) { + /* We could have two different coordinators, one is the instance, the other is + * the module */ + vtr::Point instance_cb_coordinate(rr_gsb.get_cb_x(cb_type), + rr_gsb.get_cb_y(cb_type)); + vtr::Point module_gsb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); + + /* Skip those Connection blocks that do not exist */ + if (false == rr_gsb.is_cb_exist(cb_type)) { + return CMD_EXEC_SUCCESS; + } + + /* Skip if the cb does not contain any configuration bits! */ + if (true == connection_block_contain_only_routing_tracks(rr_gsb, cb_type)) { + return CMD_EXEC_SUCCESS; + } + + /* If we use compact routing hierarchy, we should find the unique module of + * CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + vtr::Point gsb_coord(rr_gsb.get_x(), rr_gsb.get_y()); + const RRGSB& unique_mirror = + device_rr_gsb.get_cb_unique_module(cb_type, gsb_coord); + module_gsb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_coordinate.set_y(unique_mirror.get_y()); + } + + /* This is the source cb that is added to the top module */ + const RRGSB& module_cb = device_rr_gsb.get_gsb(module_gsb_coordinate); + vtr::Point module_cb_coordinate(module_cb.get_cb_x(cb_type), + module_cb.get_cb_y(cb_type)); + + /* Collect source-related information */ + std::string src_cb_module_name = + generate_connection_block_module_name(cb_type, module_cb_coordinate); + ModuleId src_cb_module = module_manager.find_module(src_cb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(src_cb_module)); + /* Instance id should follow the instance cb coordinate */ + size_t src_cb_instance = cb_instance; + + /* Iterate over the output pins of the Connection Block */ + std::vector cb_ipin_sides = module_cb.get_cb_ipin_sides(cb_type); + for (size_t iside = 0; iside < cb_ipin_sides.size(); ++iside) { + enum e_side cb_ipin_side = cb_ipin_sides[iside]; + for (size_t inode = 0; inode < module_cb.get_num_ipin_nodes(cb_ipin_side); + ++inode) { + /* Collect source-related information */ + RRNodeId module_ipin_node = module_cb.get_ipin_node(cb_ipin_side, inode); + vtr::Point cb_src_port_coord( + rr_graph.node_xlow(module_ipin_node), + rr_graph.node_ylow(module_ipin_node)); + std::string src_cb_port_name = generate_cb_module_grid_port_name( + cb_ipin_side, grids, vpr_device_annotation, rr_graph, module_ipin_node); + ModulePortId src_cb_port_id = + module_manager.find_module_port(src_cb_module, src_cb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(src_cb_module, + src_cb_port_id)); + BasicPort src_cb_port = + module_manager.module_port(src_cb_module, src_cb_port_id); + + /* Collect sink-related information */ + /* Note that we use the instance cb pin here!!! + * because it has the correct coordinator for the grid!!! + */ + RRNodeId instance_ipin_node = rr_gsb.get_ipin_node(cb_ipin_side, inode); + vtr::Point grid_coordinate( + rr_graph.node_xlow(instance_ipin_node), + rr_graph.node_ylow(instance_ipin_node)); + std::string sink_grid_module_name = + generate_grid_block_module_name_in_top_module( + std::string(GRID_MODULE_NAME_PREFIX), grids, grid_coordinate); + ModuleId sink_grid_module = + module_manager.find_module(sink_grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sink_grid_module)); + size_t sink_grid_pin_index = rr_graph.node_pin_num(instance_ipin_node); + + t_physical_tile_type_ptr grid_type_descriptor = + grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + size_t sink_grid_pin_width = + grid_type_descriptor->pin_width_offset[sink_grid_pin_index]; + size_t sink_grid_pin_height = + grid_type_descriptor->pin_height_offset[sink_grid_pin_index]; + BasicPort sink_grid_pin_info = + vpr_device_annotation.physical_tile_pin_port_info(grid_type_descriptor, + sink_grid_pin_index); + VTR_ASSERT(true == sink_grid_pin_info.is_valid()); + int subtile_index = vpr_device_annotation.physical_tile_pin_subtile_index( + grid_type_descriptor, sink_grid_pin_index); + VTR_ASSERT(OPEN != subtile_index && + subtile_index < grid_type_descriptor->capacity); + std::string sink_grid_port_name = generate_grid_port_name( + sink_grid_pin_width, sink_grid_pin_height, subtile_index, + get_rr_graph_single_node_side( + rr_graph, rr_gsb.get_ipin_node(cb_ipin_side, inode)), + sink_grid_pin_info); + ModulePortId sink_grid_port_id = + module_manager.find_module_port(sink_grid_module, sink_grid_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id( + sink_grid_module, sink_grid_port_id)); + BasicPort sink_grid_port = + module_manager.module_port(sink_grid_module, sink_grid_port_id); + + /* Check if the grid is inside the tile, if not, create ports */ + if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { + continue; + } + /* TODO: Create nets */ + if (!frame_view) { + } + } + } + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * This function will create nets for the connections + * between connection block and switch block pins + * Two cases should be considered: + * a. The switch block pin denotes an input of a routing track + * The net source is an output of a routing track of connection block + * while the net sink is an input of a routing track of switch block + * b. The switch block pin denotes an output of a routing track + * The net source is an output of routing track of switch block + * while the net sink is an input of a routing track of connection block + * + * TileA | TileB + * +------------+ +------------------+ | +------------+ + * | | | | | | | + * | Grid | | Connection Block | | | Grid | + * | [x][y+1] | | Y-direction | | | [x+1][y+1] | + * | | | [x][y+1] | | | | + * +------------+ +------------------+ | +------------+ + * | ^ | + * v | | + * +------------+ +------------------+ | +------------+ + * | Connection |----->| |----->| Connection | + * | Block | | Switch Block | | | Block | + * | X-direction|<-----| [x][y] |<-----| X-direction| + * | [x][y] | | | | | [x+1][y] | + * +------------+ +------------------+ | +------------+ + * | ^ | + * ------------------------------------------+ + * v | + * +------------+ +------------------+ +------------+ + * | | | | | | + * | Grid | | Connection Block | | Grid | + * | [x][y] | | Y-direction | | [x][y+1] | + * | | | [x][y] | | | + * +------------+ +------------------+ +------------+ + * + * Here, to achieve the purpose, we can simply iterate over the + * four sides of switch block and make connections to adjancent + * connection blocks + * + *******************************************************************/ +static int build_top_module_tile_nets_between_sb_and_cb( + ModuleManager& module_manager, const ModuleId& top_module, + const ModuleId& tile_module, const size_t& tile_instance_id, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, + const RRGSB& rr_gsb, const FabricTile& fabric_tile, + const FabricTileId& fabric_tile_id, + const bool& frame_view, const bool& verbose) { + /* We could have two different coordinators, one is the instance, the other is + * the module */ + vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), + rr_gsb.get_sb_y()); + vtr::Point module_gsb_sb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); + + /* Skip those Switch blocks that do not exist */ + if (false == rr_gsb.is_sb_exist()) { + return CMD_EXEC_SUCCESS; + } + + /* If we use compact routing hierarchy, we should find the unique module of + * CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + vtr::Point gsb_coord(rr_gsb.get_x(), rr_gsb.get_y()); + const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(gsb_coord); + module_gsb_sb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_sb_coordinate.set_y(unique_mirror.get_y()); + } + + /* This is the source cb that is added to the top module */ + const RRGSB& module_sb = device_rr_gsb.get_gsb(module_gsb_sb_coordinate); + vtr::Point module_sb_coordinate(module_sb.get_sb_x(), + module_sb.get_sb_y()); + std::string sb_module_name = + generate_switch_block_module_name(module_sb_coordinate); + ModuleId sb_module_id = module_manager.find_module(sb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sb_module_id)); + + /* Connect grid output pins (OPIN) to switch block grid pins */ + for (size_t side = 0; side < module_sb.get_num_sides(); ++side) { + SideManager side_manager(side); + /* Iterate over the routing tracks on this side */ + /* Early skip: if there is no routing tracks at this side */ + if (0 == module_sb.get_chan_width(side_manager.get_side())) { + continue; + } + /* Find the Connection Block module */ + /* We find the original connection block and then spot its unique mirror! + * Do NOT use module_sb here!!! + */ + t_rr_type cb_type = + find_top_module_cb_type_by_sb_side(side_manager.get_side()); + vtr::Point instance_gsb_cb_coordinate = + find_top_module_gsb_coordinate_by_sb_side(rr_gsb, + side_manager.get_side()); + vtr::Point module_gsb_cb_coordinate = + find_top_module_gsb_coordinate_by_sb_side(rr_gsb, + side_manager.get_side()); + + /* Skip those Connection blocks that do not exist: + * 1. The CB does not exist in the device level! We should skip! + * 2. The CB does exist but we need to make sure if the GSB includes such + * CBs For TOP and LEFT side, check the existence using RRGSB method + * is_cb_exist() FOr RIGHT and BOTTOM side, find the adjacent RRGSB and then + * use is_cb_exist() + */ + if (TOP == side_manager.get_side() || LEFT == side_manager.get_side()) { + if (false == rr_gsb.is_cb_exist(cb_type)) { + continue; + } + } + + if (RIGHT == side_manager.get_side() || BOTTOM == side_manager.get_side()) { + const RRGSB& adjacent_gsb = + device_rr_gsb.get_gsb(module_gsb_cb_coordinate); + if (false == adjacent_gsb.is_cb_exist(cb_type)) { + continue; + } + } + + /* If we use compact routing hierarchy, we should find the unique module of + * CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + const RRGSB& unique_mirror = + device_rr_gsb.get_cb_unique_module(cb_type, module_gsb_cb_coordinate); + module_gsb_cb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_cb_coordinate.set_y(unique_mirror.get_y()); + } + + const RRGSB& module_cb = device_rr_gsb.get_gsb(module_gsb_cb_coordinate); + vtr::Point module_cb_coordinate(module_cb.get_cb_x(cb_type), + module_cb.get_cb_y(cb_type)); + std::string cb_module_name = + generate_connection_block_module_name(cb_type, module_cb_coordinate); + ModuleId cb_module_id = module_manager.find_module(cb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(cb_module_id)); + const RRGSB& instance_cb = + device_rr_gsb.get_gsb(instance_gsb_cb_coordinate); + vtr::Point instance_cb_coordinate(instance_cb.get_cb_x(cb_type), + instance_cb.get_cb_y(cb_type)); + + /* Check if the grid is inside the tile, if not, create ports */ + if (fabric_tile.cb_in_tile(fabric_tile_id, cb_type, + instance_cb_coordinate)) { + continue; + } + /* TODO: Create nets */ + if (!frame_view) { + } + } + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * This function will create nets between two programmable blocks, which are direct connections + ********************************************************************/ +static int build_top_module_tile_nets_between_pbs( + ModuleManager& module_manager, const ModuleId& top_module, + const ModuleId& tile_module, const size_t& tile_instance_id, + const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const vtr::Point& pb_coord, const size_t& pb_instance, + const bool& frame_view, const bool& verbose) { + t_physical_tile_type_ptr phy_tile = + grids.get_physical_type(pb_coord.x(), pb_coord.y()); + /* Empty type does not require a module */ + if (is_empty_type(phy_tile)) { + return CMD_EXEC_SUCCESS; + } + e_side grid_side = find_grid_border_side( + vtr::Point(grids.width(), grids.height()), pb_coord); + std::string pb_module_name = generate_grid_block_module_name( + std::string(GRID_MODULE_NAME_PREFIX), std::string(phy_tile->name), + is_io_type(phy_tile), grid_side); + ModuleId pb_module = module_manager.find_module(pb_module_name); + if (!pb_module) { + VTR_LOG_ERROR("Failed to find pb module '%s' required by tile[%lu][%lu]!\n", + pb_module_name.c_str(), pb_coord.x(), pb_coord.y()); + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the pin side for I/O grids*/ + std::vector grid_pin_sides; + /* For I/O grids, we care only one side + * Otherwise, we will iterate all the 4 sides + */ + if (true == is_io_type(phy_tile)) { + grid_pin_sides = find_grid_module_pin_sides(phy_tile, grid_side); + } else { + grid_pin_sides = {TOP, RIGHT, BOTTOM, LEFT}; + } + + /* Create a map between pin class type and grid pin direction */ + std::map pin_type2type_map; + pin_type2type_map[RECEIVER] = ModuleManager::MODULE_INPUT_PORT; + pin_type2type_map[DRIVER] = ModuleManager::MODULE_OUTPUT_PORT; + + /* Iterate over sides, height and pins */ + for (const e_side& side : grid_pin_sides) { + for (int iwidth = 0; iwidth < phy_tile->width; ++iwidth) { + for (int iheight = 0; iheight < phy_tile->height; ++iheight) { + for (int ipin = 0; ipin < phy_tile->num_pins; ++ipin) { + if (!phy_tile->pinloc[iwidth][iheight][side][ipin]) { + continue; + } + /* Reach here, it means this pin is on this side */ + /* Generate the pin name, + * we give a empty coordinate but it will not be used (see details in + * the function + */ + BasicPort pin_info = + vpr_device_annotation.physical_tile_pin_port_info(phy_tile, ipin); + VTR_ASSERT(true == pin_info.is_valid()); + int subtile_index = + vpr_device_annotation.physical_tile_pin_subtile_index(phy_tile, + ipin); + VTR_ASSERT(OPEN != subtile_index && + subtile_index < phy_tile->capacity); + std::string port_name = generate_grid_port_name( + iwidth, iheight, subtile_index, side, pin_info); + BasicPort pb_port(port_name, 0, 0); + ModulePortId pb_module_port_id = + module_manager.find_module_port(pb_module, port_name); + if (!module_manager.valid_module_port_id(pb_module, + pb_module_port_id)) { + VTR_LOG_ERROR( + "Failed to find port '%s' for pb module '%s' required by " + "tile[%lu][%lu]!\n", + pb_port.to_verilog_string().c_str(), pb_module_name.c_str(), + pb_coord.x(), pb_coord.y()); + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the port from the pb module and see if it is already been + * driven or driving a net. if not, create a new port at the tile + * module */ + if (module_manager.port_type(pb_module, pb_module_port_id) == + ModuleManager::e_module_port_type::MODULE_INPUT_PORT) { + /* TODO: Create pb-to-pb nets */ + } else if (module_manager.port_type(pb_module, pb_module_port_id) == + ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT) { + /* TODO: Create pb-to-pb nets */ + } else { + VTR_LOG_ERROR( + "Expect either input or output port '%s' for pb module '%s' " + "required by tile[%lu][%lu]!\n", + pb_port.to_verilog_string().c_str(), pb_module_name.c_str(), + pb_coord.x(), pb_coord.y()); + return CMD_EXEC_FATAL_ERROR; + } + } + } + } + } + return CMD_EXEC_SUCCESS; +} + + +/******************************************************************** + * Add module nets to connect the pins between tiles + * To make it easy, this function will iterate over all the tiles, through which we can obtain the coordinates + * of each programmable block (PB), connection block (CB) and switch block (SB). With the coordinates, we can then trace the connections between these blocks using the RRGSB data structure. + * + * +--------+ +----------+ + * | Tile |--->| Tile | + * | [x][y] |<---| [x+1][y] | + * +--------+ +----------+ + * + * The inter-tile connections can be categorized into four types: + * - PB-to-SB connections: We use the GSB to find the connections with a given SB coordinate. Note that we only care the connections where the driver PB is not in this tile. + * - CB-to-PB connections: We use the GSB to find the connections with a given CB coordinate. Note that we only care the connections where the sink PB is not in this tile. + * - SB-to-CB connections: We use the GSB to find the connections with a given SB coordinate. Note that we only care the connections where the sink CB is not in this tile. + * - PB-to-PB connections: We use the tile direct data structure to find all the connections. Note that we only care the connections where the driver PB is not in this tile. + * + *******************************************************************/ +static int add_top_module_nets_around_one_tile( + ModuleManager& module_manager, const ModuleId& top_module, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const vtr::Matrix& tile_instance_ids, const RRGraphView& rr_graph, + const DeviceRRGSB& device_rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, + const bool& verbose) { + int status = CMD_EXEC_SUCCESS; + + /* Find the module name for this type of tile */ + vtr::Point unique_tile_coord = + fabric_tile.unique_tile_coordinate(fabric_tile_id); + std::string tile_module_name = generate_tile_module_name(unique_tile_coord); + ModuleId tile_module = module_manager.find_module(tile_module_name); + if (!module_manager.valid_module_id(tile_module)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the instance id for this tile */ + vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); + size_t tile_instance_id = tile_instance_ids[tile_coord.x()][tile_coord.y()]; + + /* Get the submodule of Switch blocks one by one, build connections between sb + * and pb */ + for (size_t isb = 0; isb < fabric_tile.sb_coordinates(fabric_tile_id).size(); + ++isb) { + vtr::Point sb_coord = + fabric_tile.sb_coordinates(fabric_tile_id)[isb]; + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); + status_code = build_top_module_tile_nets_between_sb_and_pb( + module_manager, top_module, tile_module, tile_instance_id, grids, vpr_device_annotation, device_rr_gsb, + rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, verbose); + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } + /* Get the submodule of connection blocks one by one, build connections + * between cb and pb */ + for (t_rr_type cb_type : {CHANX, CHANY}) { + for (size_t icb = 0; + icb < fabric_tile.cb_coordinates(fabric_tile_id, cb_type).size(); + ++icb) { + vtr::Point cb_coord = + fabric_tile.cb_coordinates(fabric_tile_id, cb_type)[icb]; + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(cb_coord); + status_code = build_top_module_tile_nets_between_cb_and_pb( + module_manager, tile_module, grids, vpr_device_annotation, + device_rr_gsb, rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, + cb_type, pb_instances, cb_instances.at(cb_type)[icb], true, frame_view, + verbose); + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } + } + /* Get the submodule of connection blocks one by one, build connections + * between sb and cb */ + for (size_t isb = 0; isb < fabric_tile.sb_coordinates(fabric_tile_id).size(); + ++isb) { + vtr::Point sb_coord = + fabric_tile.sb_coordinates(fabric_tile_id)[isb]; + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); + status_code = build_top_module_tile_nets_between_sb_and_cb( + module_manager, tile_module, device_rr_gsb, rr_graph_view, rr_gsb, + fabric_tile, fabric_tile_id, cb_instances, sb_instances[isb], true, + frame_view, verbose); + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } + /* Create the ports from pb which only connects to adjacent sb and cbs, as + * well as pb */ + for (size_t ipb = 0; ipb < fabric_tile.pb_coordinates(fabric_tile_id).size(); + ++ipb) { + vtr::Point pb_coord = + fabric_tile.pb_coordinates(fabric_tile_id)[ipb]; + status_code = build_top_module_tile_nets_between_pbs( + module_manager, tile_module, grids, vpr_device_annotation, pb_coord, + pb_instances[ipb], frame_view, verbose); + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Walk through each tile instance and add module nets to connect the pins between tiles + *******************************************************************/ +static int add_top_module_nets_connect_tiles( + ModuleManager& module_manager, const ModuleId& top_module, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const vtr::Matrix& tile_instance_ids, const RRGraphView& rr_graph, + const DeviceRRGSB& device_rr_gsb, const FabricTile& fabric_tile, + const bool& verbose) { + vtr::ScopedStartFinishTimer timer("Add module nets between tiles"); + int status = CMD_EXEC_SUCCESS; + + for (size_t ix = 0; ix < grids.width(); ++ix) { + for (size_t iy = 0; iy < grids.height(); ++iy) { + vtr::Point curr_coord(ix, iy); + FabricTileId fabric_tile_id = fabric_tile.find_tile(curr_coord); + if (!fabric_tile.valid_tile_id(fabric_tile_id)) { + continue; + } + status = add_top_module_nets_around_one_tile( + module_manager, top_module, vpr_device_annotation, grids, + tile_instance_ids, rr_graph, device_rr_gsb, fabric_tile, fabric_tile_id, verbose); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } + } + + return CMD_EXEC_SUCCESS; +} + /******************************************************************** * Add the tile-level instances to the top module of FPGA fabric * and build connects between them @@ -257,7 +974,8 @@ int build_top_module_tile_child_instances( MemoryBankShiftRegisterBanks& blwl_sr_banks, const CircuitLibrary& circuit_lib, const DeviceGrid& grids, const FabricTile& fabric_tile, const ConfigProtocol& config_protocol, - const FabricKey& fabric_key, const bool& frame_view) { + const FabricKey& fabric_key, 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, @@ -274,13 +992,13 @@ int build_top_module_tile_child_instances( if (false == frame_view) { /* Reserve nets to be memory efficient */ reserve_module_manager_module_nets(module_manager, top_module); - /* TODO: Regular nets between tiles - add_top_module_nets_connect_tiles( + /* TODO: Regular nets between tiles */ + status = add_top_module_nets_connect_tiles( module_manager, top_module, vpr_device_annotation, grids, - tile_instance_ids, rr_graph, device_rr_gsb, compact_routing_hierarchy, - duplicate_grid_pin); - */ - + tile_instance_ids, rr_graph, device_rr_gsb, fabric_tile, verbose); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } /* TODO: Inter-tile direct connections */ } 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 f779d2ccd..6fc5461e3 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.h +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.h @@ -37,7 +37,8 @@ int build_top_module_tile_child_instances( MemoryBankShiftRegisterBanks& blwl_sr_banks, const CircuitLibrary& circuit_lib, const DeviceGrid& grids, const FabricTile& fabric_tile, const ConfigProtocol& config_protocol, - const FabricKey& fabric_key, const bool& frame_view); + const FabricKey& fabric_key, const bool& frame_view, + const bool& verbose); } /* end namespace openfpga */ From 9afab9841b8701bcdd336cbefb88bcc92db12e75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Jul 2023 06:16:37 +0000 Subject: [PATCH 200/391] Bump yosys from `83c9261` to `d5d2bf8` Bumps [yosys](https://github.com/YosysHQ/yosys) from `83c9261` to `d5d2bf8`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/83c9261d6ca47e7974258ae4e7f1c86f0dfa8961...d5d2bf815ae73cd9e003196e0b880e98b42caa1a) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 83c9261d6..d5d2bf815 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 83c9261d6ca47e7974258ae4e7f1c86f0dfa8961 +Subproject commit d5d2bf815ae73cd9e003196e0b880e98b42caa1a From 6c03819c5fa0f3362f7a6b53a6b6ca422a04d5fb Mon Sep 17 00:00:00 2001 From: Chung Shien Chai Date: Fri, 21 Jul 2023 03:14:26 -0700 Subject: [PATCH 201/391] 100% limited new flow for flatten bl/wl protocol --- .../build_fabric_bitstream_memory_bank.cpp | 14 ++- .../write_xml_fabric_bitstream.cpp | 112 ++++++++++-------- 2 files changed, 69 insertions(+), 57 deletions(-) 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 1659e3b65..9aea7c3d0 100644 --- a/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream_memory_bank.cpp @@ -228,11 +228,15 @@ static void rec_build_module_fabric_dependent_ql_memory_bank_regional_bitstream( fabric_bit, wl_addr_bits_vec, BLWL_PROTOCOL_DECODER != config_protocol.wl_protocol_type()); } - - /* New way of storing information in compact way*/ - fabric_bitstream.set_memory_bank_info( - fabric_bit, fabric_bitstream_region, cur_bl_index, cur_wl_index, - bl_addr_size, wl_addr_size, bitstream_manager.bit_value(config_bit)); + if (BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type() && + BLWL_PROTOCOL_FLATTEN == config_protocol.wl_protocol_type()) { + // New way of storing information in compact way + // Only for Flatten protocol (can easily support shift register as well) + // Need to understand decoder to better assessment + fabric_bitstream.set_memory_bank_info( + fabric_bit, fabric_bitstream_region, cur_bl_index, cur_wl_index, + bl_addr_size, wl_addr_size, bitstream_manager.bit_value(config_bit)); + } /* Set data input */ fabric_bitstream.set_bit_din(fabric_bit, diff --git a/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp index 786a3768e..5f7d668c6 100644 --- a/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp @@ -71,8 +71,8 @@ static void write_fabric_bitstream_xml_file_head( static int write_fabric_config_bit_to_xml_file( std::fstream& fp, const BitstreamManager& bitstream_manager, const FabricBitstream& fabric_bitstream, const FabricBitId& fabric_bit, - const e_config_protocol_type& config_type, const int& xml_hierarchy_depth, - std::string& bl_addr, std::string& wl_addr) { + const e_config_protocol_type& config_type, bool fast_xml, + const int& xml_hierarchy_depth, std::string& bl_addr, std::string& wl_addr) { if (false == valid_file_stream(fp)) { return 1; } @@ -107,56 +107,60 @@ static int write_fabric_config_bit_to_xml_file( case CONFIG_MEM_STANDALONE: case CONFIG_MEM_SCAN_CHAIN: break; - case CONFIG_MEM_MEMORY_BANK: { - /* Bit line address */ - write_tab_to_file(fp, xml_hierarchy_depth + 1); - fp << "\n"; - - write_tab_to_file(fp, xml_hierarchy_depth + 1); - fp << "\n"; - break; - } + case CONFIG_MEM_MEMORY_BANK: case CONFIG_MEM_QL_MEMORY_BANK: { - // New way of printing XML - // This is fast (less than 100s) as compared to original 1300s seen in - // 100K LE FPFA - const FabricBitstreamMemoryBank* memory_bank = - fabric_bitstream.memory_bank_info(); - /* Bit line address */ - write_tab_to_file(fp, xml_hierarchy_depth + 1); - const fabric_bit_data& bit = - memory_bank->fabric_bit_datas[(size_t)(fabric_bit)]; - const fabric_blwl_length& lengths = memory_bank->blwl_lengths[bit.region]; - if (bl_addr.size() == 0) { - VTR_ASSERT(wl_addr.size() == 0); - bl_addr.resize(lengths.bl); - wl_addr.resize(lengths.wl); - memset(&bl_addr[0], 'x', lengths.bl); - memset(&wl_addr[0], '0', lengths.wl); + if (fast_xml) { + // New way of printing XML + // This is fast (less than 100s) as compared to original 1300s seen in + // 100K LE FPFA + const FabricBitstreamMemoryBank* memory_bank = + fabric_bitstream.memory_bank_info(); + /* Bit line address */ + write_tab_to_file(fp, xml_hierarchy_depth + 1); + const fabric_bit_data& bit = + memory_bank->fabric_bit_datas[(size_t)(fabric_bit)]; + const fabric_blwl_length& lengths = + memory_bank->blwl_lengths[bit.region]; + if (bl_addr.size() == 0) { + VTR_ASSERT(wl_addr.size() == 0); + bl_addr.resize(lengths.bl); + wl_addr.resize(lengths.wl); + memset(&bl_addr[0], 'x', lengths.bl); + memset(&wl_addr[0], '0', lengths.wl); + } else { + VTR_ASSERT((fabric_size_t)(bl_addr.size()) == lengths.bl); + VTR_ASSERT((fabric_size_t)(wl_addr.size()) == lengths.wl); + } + fp << "\n"; + /* Word line address */ + write_tab_to_file(fp, xml_hierarchy_depth + 1); + fp << "\n"; } else { - VTR_ASSERT((fabric_size_t)(bl_addr.size()) == lengths.bl); - VTR_ASSERT((fabric_size_t)(wl_addr.size()) == lengths.wl); + /* Bit line address */ + write_tab_to_file(fp, xml_hierarchy_depth + 1); + fp << "\n"; + + write_tab_to_file(fp, xml_hierarchy_depth + 1); + fp << "\n"; } - fp << "\n"; - /* Word line address */ - write_tab_to_file(fp, xml_hierarchy_depth + 1); - fp << "\n"; break; } case CONFIG_MEM_FRAME_BASED: { @@ -191,13 +195,15 @@ static int write_fabric_regional_config_bit_to_xml_file( std::fstream& fp, const BitstreamManager& bitstream_manager, const FabricBitstream& fabric_bitstream, const FabricBitRegionId& fabric_region, - const e_config_protocol_type& config_type, const int& xml_hierarchy_depth) { + const e_config_protocol_type& config_type, bool fast_xml, + const int& xml_hierarchy_depth) { if (false == valid_file_stream(fp)) { return 1; } int status = 0; // Use string to print, instead of char by char + // This is for Flatten BL/WL protocol // You will find this much more faster than char by char // We do not need to build the string for every BL/WL // It is one-hot and sequal addr @@ -205,7 +211,7 @@ static int write_fabric_regional_config_bit_to_xml_file( // By setting "1' and resettting ('0' or 'x') at approriate bit position // We could create one-hot string much faster // Use FPGA 100K as example: old way needs 1300seconds to write 85Gig XML - /// New way only needs 80seconds to write identical XML + // New way only needs 80seconds to write identical XML std::string bl_addr = ""; std::string wl_addr = ""; write_tab_to_file(fp, xml_hierarchy_depth); @@ -222,7 +228,7 @@ static int write_fabric_regional_config_bit_to_xml_file( fabric_bitstream.region_bits(fabric_region)) { status = write_fabric_config_bit_to_xml_file( fp, bitstream_manager, fabric_bitstream, fabric_bit, config_type, - xml_hierarchy_depth + 1, bl_addr, wl_addr); + fast_xml, xml_hierarchy_depth + 1, bl_addr, wl_addr); if (1 == status) { return status; } @@ -286,6 +292,8 @@ int write_fabric_bitstream_to_xml_file( for (const FabricBitRegionId& region : fabric_bitstream.regions()) { status = write_fabric_regional_config_bit_to_xml_file( fp, bitstream_manager, fabric_bitstream, region, config_protocol.type(), + BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type() && + BLWL_PROTOCOL_FLATTEN == config_protocol.wl_protocol_type(), xml_hierarchy_depth + 1); if (1 == status) { break; From 93c5a6859237fcfff13f62cf5ab2997d941e80fd Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 21 Jul 2023 23:21:53 -0700 Subject: [PATCH 202/391] [core] developing top-level nets for tiles --- openfpga/src/annotation/fabric_tile.cpp | 226 +++++++++++++++++- openfpga/src/annotation/fabric_tile.h | 29 ++- openfpga/src/base/openfpga_naming.cpp | 9 +- openfpga/src/base/openfpga_naming.h | 2 + .../build_top_module_child_tile_instance.cpp | 115 +++++---- 5 files changed, 329 insertions(+), 52 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index 14880b8e0..cfe3985c4 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -78,6 +78,84 @@ FabricTileId FabricTile::find_tile(const vtr::Point& coord) const { return tile_coord2id_lookup_[coord.x()][coord.y()]; } +FabricTileId FabricTile::find_tile_by_pb_coordinate(const vtr::Point& coord) const { + if (coord.x() >= pb_coord2id_lookup_.size()) { + VTR_LOG_ERROR( + "Programmable block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + coord.x(), coord.y(), pb_coord2id_lookup_.size(), + pb_coord2id_lookup_[0].size()); + return FabricTileId::INVALID(); + } + if (coord.y() >= pb_coord2id_lookup_[coord.x()].size()) { + VTR_LOG_ERROR( + "Programmable block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + coord.x(), coord.y(), pb_coord2id_lookup_.size(), + pb_coord2id_lookup_[0].size()); + return FabricTileId::INVALID(); + } + return pb_coord2id_lookup_[coord.x()][coord.y()]; +} + +FabricTileId FabricTile::find_tile_by_cb_coordinate(const t_rr_type& cb_type, const vtr::Point& coord) const { + switch (cb_type) { + case CHANX: { + if (coord.x() >= cbx_coord2id_lookup_.size()) { + VTR_LOG_ERROR( + "X-direction connection block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + coord.x(), coord.y(), cbx_coord2id_lookup_.size(), + cbx_coord2id_lookup_[0].size()); + return FabricTileId::INVALID(); + } + if (coord.y() >= cbx_coord2id_lookup_[coord.x()].size()) { + VTR_LOG_ERROR( + "X-direction connection block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + coord.x(), coord.y(), cbx_coord2id_lookup_.size(), + cbx_coord2id_lookup_[0].size()); + return FabricTileId::INVALID(); + } + return cbx_coord2id_lookup_[coord.x()][coord.y()]; + } + case CHANY: { + if (coord.x() >= cby_coord2id_lookup_.size()) { + VTR_LOG_ERROR( + "Y-direction connection block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + coord.x(), coord.y(), cby_coord2id_lookup_.size(), + cby_coord2id_lookup_[0].size()); + return FabricTileId::INVALID(); + } + if (coord.y() >= cby_coord2id_lookup_[coord.x()].size()) { + VTR_LOG_ERROR( + "Y-direction connection block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + coord.x(), coord.y(), cby_coord2id_lookup_.size(), + cby_coord2id_lookup_[0].size()); + return FabricTileId::INVALID(); + } + return cby_coord2id_lookup_[coord.x()][coord.y()]; + } + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } +} + +FabricTileId FabricTile::find_tile_by_sb_coordinate(const vtr::Point& coord) const { + if (coord.x() >= sb_coord2id_lookup_.size()) { + VTR_LOG_ERROR( + "Switch block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + coord.x(), coord.y(), sb_coord2id_lookup_.size(), + sb_coord2id_lookup_[0].size()); + return FabricTileId::INVALID(); + } + if (coord.y() >= sb_coord2id_lookup_[coord.x()].size()) { + VTR_LOG_ERROR( + "Switch block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + coord.x(), coord.y(), sb_coord2id_lookup_.size(), + sb_coord2id_lookup_[0].size()); + return FabricTileId::INVALID(); + } + return sb_coord2id_lookup_[coord.x()][coord.y()]; +} + bool FabricTile::pb_in_tile(const FabricTileId& tile_id, const vtr::Point& coord, const bool& use_gsb_coord) const { @@ -204,8 +282,16 @@ FabricTileId FabricTile::create_tile(const vtr::Point& coord) { void FabricTile::init(const vtr::Point& max_coord) { tile_coord2id_lookup_.resize(max_coord.x()); + pb_coord2id_lookup_.resize(max_coord.x()); + cbx_coord2id_lookup_.resize(max_coord.x()); + cby_coord2id_lookup_.resize(max_coord.x()); + sb_coord2id_lookup_.resize(max_coord.x()); for (size_t ix = 0; ix < max_coord.x(); ++ix) { tile_coord2id_lookup_[ix].resize(max_coord.y(), FabricTileId::INVALID()); + pb_coord2id_lookup_[ix].resize(max_coord.y(), FabricTileId::INVALID()); + cbx_coord2id_lookup_[ix].resize(max_coord.y(), FabricTileId::INVALID()); + cby_coord2id_lookup_[ix].resize(max_coord.y(), FabricTileId::INVALID()); + sb_coord2id_lookup_[ix].resize(max_coord.y(), FabricTileId::INVALID()); } tile_coord2unique_tile_ids_.resize(max_coord.x()); for (size_t ix = 0; ix < max_coord.x(); ++ix) { @@ -240,10 +326,130 @@ bool FabricTile::register_tile_in_lookup(const FabricTileId& tile_id, return true; } +bool FabricTile::register_pb_in_lookup(const FabricTileId& tile_id, + const vtr::Point& coord) { + if (coord.x() >= pb_coord2id_lookup_.size()) { + VTR_LOG_ERROR( + "Fast look-up has not been re-allocated properly! Given x='%lu' exceeds " + "the upper-bound '%lu'!\n", + coord.x(), pb_coord2id_lookup_.size()); + return false; + } + if (coord.y() >= pb_coord2id_lookup_[coord.x()].size()) { + VTR_LOG_ERROR( + "Fast look-up has not been re-allocated properly! Given y='%lu' exceeds " + "the upper-bound '%lu'!\n", + coord.y(), pb_coord2id_lookup_[coord.x()].size()); + return false; + } + /* Throw error if this coord is already registered! */ + if (pb_coord2id_lookup_[coord.x()][coord.y()]) { + VTR_LOG_ERROR("Programmable block at [%lu][%lu] has already been registered!\n"); + return false; + } + pb_coord2id_lookup_[coord.x()][coord.y()] = tile_id; + + return true; +} + +bool FabricTile::register_cbx_in_lookup(const FabricTileId& tile_id, + const vtr::Point& coord) { + if (coord.x() >= cbx_coord2id_lookup_.size()) { + VTR_LOG_ERROR( + "Fast look-up has not been re-allocated properly! Given x='%lu' exceeds " + "the upper-bound '%lu'!\n", + coord.x(), cbx_coord2id_lookup_.size()); + return false; + } + if (coord.y() >= cbx_coord2id_lookup_[coord.x()].size()) { + VTR_LOG_ERROR( + "Fast look-up has not been re-allocated properly! Given y='%lu' exceeds " + "the upper-bound '%lu'!\n", + coord.y(), cbx_coord2id_lookup_[coord.x()].size()); + return false; + } + /* Throw error if this coord is already registered! */ + if (cbx_coord2id_lookup_[coord.x()][coord.y()]) { + VTR_LOG_ERROR("X-direction connection block at [%lu][%lu] has already been registered!\n"); + return false; + } + cbx_coord2id_lookup_[coord.x()][coord.y()] = tile_id; + + return true; +} + +bool FabricTile::register_cby_in_lookup(const FabricTileId& tile_id, + const vtr::Point& coord) { + if (coord.x() >= cby_coord2id_lookup_.size()) { + VTR_LOG_ERROR( + "Fast look-up has not been re-allocated properly! Given x='%lu' exceeds " + "the upper-bound '%lu'!\n", + coord.x(), cby_coord2id_lookup_.size()); + return false; + } + if (coord.y() >= cby_coord2id_lookup_[coord.x()].size()) { + VTR_LOG_ERROR( + "Fast look-up has not been re-allocated properly! Given y='%lu' exceeds " + "the upper-bound '%lu'!\n", + coord.y(), cby_coord2id_lookup_[coord.x()].size()); + return false; + } + /* Throw error if this coord is already registered! */ + if (cby_coord2id_lookup_[coord.x()][coord.y()]) { + VTR_LOG_ERROR("Y-direction connection block at [%lu][%lu] has already been registered!\n"); + return false; + } + cby_coord2id_lookup_[coord.x()][coord.y()] = tile_id; + + return true; +} + +bool FabricTile::register_sb_in_lookup(const FabricTileId& tile_id, + const vtr::Point& coord) { + if (coord.x() >= sb_coord2id_lookup_.size()) { + VTR_LOG_ERROR( + "Fast look-up has not been re-allocated properly! Given x='%lu' exceeds " + "the upper-bound '%lu'!\n", + coord.x(), sb_coord2id_lookup_.size()); + return false; + } + if (coord.y() >= sb_coord2id_lookup_[coord.x()].size()) { + VTR_LOG_ERROR( + "Fast look-up has not been re-allocated properly! Given y='%lu' exceeds " + "the upper-bound '%lu'!\n", + coord.y(), sb_coord2id_lookup_[coord.x()].size()); + return false; + } + /* Throw error if this coord is already registered! */ + if (sb_coord2id_lookup_[coord.x()][coord.y()]) { + VTR_LOG_ERROR("Switch block at [%lu][%lu] has already been registered!\n"); + return false; + } + sb_coord2id_lookup_[coord.x()][coord.y()] = tile_id; + + return true; +} + void FabricTile::invalidate_tile_in_lookup(const vtr::Point& coord) { tile_coord2id_lookup_[coord.x()][coord.y()] = FabricTileId::INVALID(); } +void FabricTile::invalidate_pb_in_lookup(const vtr::Point& coord) { + pb_coord2id_lookup_[coord.x()][coord.y()] = FabricTileId::INVALID(); +} + +void FabricTile::invalidate_cbx_in_lookup(const vtr::Point& coord) { + cbx_coord2id_lookup_[coord.x()][coord.y()] = FabricTileId::INVALID(); +} + +void FabricTile::invalidate_cby_in_lookup(const vtr::Point& coord) { + cby_coord2id_lookup_[coord.x()][coord.y()] = FabricTileId::INVALID(); +} + +void FabricTile::invalidate_sb_in_lookup(const vtr::Point& coord) { + sb_coord2id_lookup_[coord.x()][coord.y()] = FabricTileId::INVALID(); +} + bool FabricTile::set_tile_coordinate(const FabricTileId& tile_id, const vtr::Point& coord) { VTR_ASSERT(valid_tile_id(tile_id)); @@ -255,35 +461,41 @@ bool FabricTile::set_tile_coordinate(const FabricTileId& tile_id, return register_tile_in_lookup(tile_id, coord); } -void FabricTile::add_pb_coordinate(const FabricTileId& tile_id, +int FabricTile::add_pb_coordinate(const FabricTileId& tile_id, const vtr::Point& coord, const vtr::Point& gsb_coord) { VTR_ASSERT(valid_tile_id(tile_id)); pb_coords_[tile_id].push_back(coord); pb_gsb_coords_[tile_id].push_back(gsb_coord); + /* Register in fast look-up */ + return register_pb_in_lookup(tile_id, coord); } -void FabricTile::add_cb_coordinate(const FabricTileId& tile_id, +int FabricTile::add_cb_coordinate(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord) { VTR_ASSERT(valid_tile_id(tile_id)); switch (cb_type) { case CHANX: cbx_coords_[tile_id].push_back(coord); - break; + /* Register in fast look-up */ + return register_cbx_in_lookup(tile_id, coord); case CHANY: cby_coords_[tile_id].push_back(coord); - break; + /* Register in fast look-up */ + return register_cby_in_lookup(tile_id, coord); default: VTR_LOG("Invalid type of connection block!\n"); exit(1); } } -void FabricTile::add_sb_coordinate(const FabricTileId& tile_id, +int FabricTile::add_sb_coordinate(const FabricTileId& tile_id, const vtr::Point& coord) { VTR_ASSERT(valid_tile_id(tile_id)); sb_coords_[tile_id].push_back(coord); + /* Register in fast look-up */ + return register_sb_in_lookup(tile_id, coord); } void FabricTile::clear() { @@ -295,6 +507,10 @@ void FabricTile::clear() { cby_coords_.clear(); sb_coords_.clear(); tile_coord2id_lookup_.clear(); + pb_coord2id_lookup_.clear(); + cbx_coord2id_lookup_.clear(); + cby_coord2id_lookup_.clear(); + sb_coord2id_lookup_.clear(); tile_coord2unique_tile_ids_.clear(); unique_tile_ids_.clear(); } diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h index 903ec42f2..dee4a783a 100644 --- a/openfpga/src/annotation/fabric_tile.h +++ b/openfpga/src/annotation/fabric_tile.h @@ -36,6 +36,12 @@ class FabricTile { FabricTileId unique_tile(const vtr::Point& coord) const; /** @brief Find the tile info with a given coordinate */ FabricTileId find_tile(const vtr::Point& coord) const; + /** @brief Find the id of a tile, with a given coordinate of the programmable block under the tile */ + FabricTileId find_tile_by_pb_coordinate(const vtr::Point& coord) const; + /** @brief Find the id of a tile, with a given coordinate of the connection block under the tile */ + FabricTileId find_tile_by_cb_coordinate(const t_rr_type& cb_type, const vtr::Point& coord) const; + /** @brief Find the id of a tile, with a given coordinate of the switch block under the tile */ + FabricTileId find_tile_by_sb_coordinate(const vtr::Point& coord) const; /** @brief Find the coordinate of the unique tile w.r.t the tile with a tile * id */ vtr::Point unique_tile_coordinate(const FabricTileId& tile_id) const; @@ -75,12 +81,12 @@ class FabricTile { FabricTileId create_tile(const vtr::Point& coord); bool set_tile_coordinate(const FabricTileId& tile_id, const vtr::Point& coord); - void add_pb_coordinate(const FabricTileId& tile_id, + int add_pb_coordinate(const FabricTileId& tile_id, const vtr::Point& coord, const vtr::Point& gsb_coord); - void add_cb_coordinate(const FabricTileId& tile_id, const t_rr_type& cb_type, + int add_cb_coordinate(const FabricTileId& tile_id, const t_rr_type& cb_type, const vtr::Point& coord); - void add_sb_coordinate(const FabricTileId& tile_id, + int add_sb_coordinate(const FabricTileId& tile_id, const vtr::Point& coord); /** @brief Build a list of unique tiles by comparing the coordinates in * DeviceRRGSB */ @@ -105,8 +111,20 @@ class FabricTile { private: /* Internal builders */ void invalidate_tile_in_lookup(const vtr::Point& coord); + void invalidate_pb_in_lookup(const vtr::Point& coord); + void invalidate_cbx_in_lookup(const vtr::Point& coord); + void invalidate_cby_in_lookup(const vtr::Point& coord); + void invalidate_sb_in_lookup(const vtr::Point& coord); bool register_tile_in_lookup(const FabricTileId& tile_id, const vtr::Point& coord); + bool register_pb_in_lookup(const FabricTileId& tile_id, + const vtr::Point& coord); + bool register_cbx_in_lookup(const FabricTileId& tile_id, + const vtr::Point& coord); + bool register_cby_in_lookup(const FabricTileId& tile_id, + const vtr::Point& coord); + bool register_sb_in_lookup(const FabricTileId& tile_id, + const vtr::Point& coord); private: /* Internal Data */ vtr::vector ids_; @@ -124,6 +142,11 @@ class FabricTile { vtr::vector>> cbx_coords_; vtr::vector>> cby_coords_; vtr::vector>> sb_coords_; + /* A few fast lookup to spot tile by coordinate of programmable blocks, connection blocks and switch blocks */ + std::vector> pb_coord2id_lookup_; + std::vector> cbx_coord2id_lookup_; + std::vector> cby_coord2id_lookup_; + std::vector> sb_coord2id_lookup_; /* A fast lookup to spot tile by coordinate */ std::vector> tile_coord2id_lookup_; std::vector> diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index e14a608e7..2551585d2 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -498,13 +498,20 @@ std::string generate_switch_block_module_name( } /********************************************************************* - * Generate the module name for a switch block with a given index + * Generate the module name for a tile module with a given coordinate *********************************************************************/ std::string generate_tile_module_name(const vtr::Point& tile_coord) { return std::string("tile_" + std::to_string(tile_coord.x()) + "__" + std::to_string(tile_coord.y()) + "_"); } +/********************************************************************* + * Generate the port name for a tile. Note that use the index to make the tile port name unique! + *********************************************************************/ +std::string generate_tile_module_port_name(const std::string& prefix, const std::string& port_name) { + return prefix + port_name; +} + /********************************************************************* * Generate the netlist name of a grid block **********************************************************************/ diff --git a/openfpga/src/base/openfpga_naming.h b/openfpga/src/base/openfpga_naming.h index f828de5cf..8efe39a68 100644 --- a/openfpga/src/base/openfpga_naming.h +++ b/openfpga/src/base/openfpga_naming.h @@ -112,6 +112,8 @@ std::string generate_connection_block_module_name( std::string generate_tile_module_name(const vtr::Point& tile_coord); +std::string generate_tile_module_port_name(const vtr::Point& tile_coord, const std::string& port_name); + std::string generate_tile_module_netlist_name(const std::string& block_name, const std::string& postfix); 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 ef5a42d18..ccd05a3d9 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -278,17 +278,22 @@ static void add_top_module_tile_io_children( *******************************************************************/ static int build_top_module_tile_nets_between_sb_and_pb( ModuleManager& module_manager, const ModuleId& top_module, - const ModuleId& tile_module, const size_t& tile_instance_id, + const ModuleId& curr_tile_module, + const vtr::Matrix& tile_instance_ids, + const size_t& curr_tile_instance_id, const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, - const bool& frame_view, const bool& verbose) { + const bool& compact_routing_hierarchy, + const bool& verbose) { /* Skip those Switch blocks that do not exist */ if (false == rr_gsb.is_sb_exist()) { return CMD_EXEC_SUCCESS; } + vtr::Point sink_tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); + /* We could have two different coordinators, one is the instance, the other is * the module */ vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), @@ -312,9 +317,6 @@ static int build_top_module_tile_nets_between_sb_and_pb( /* Collect sink-related information */ std::string sink_sb_module_name = generate_switch_block_module_name(module_sb_coordinate); - ModuleId sink_sb_module = module_manager.find_module(sink_sb_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(sink_sb_module)); - size_t sink_sb_instance = sb_instance; /* Connect grid output pins (OPIN) to switch block grid pins */ for (size_t side = 0; side < module_sb.get_num_sides(); ++side) { @@ -323,19 +325,28 @@ static int build_top_module_tile_nets_between_sb_and_pb( inode < module_sb.get_num_opin_nodes(side_manager.get_side()); ++inode) { /* Collect source-related information */ - /* Generate the grid module name by considering if it locates on the - * border */ vtr::Point grid_coordinate( rr_graph.node_xlow( rr_gsb.get_opin_node(side_manager.get_side(), inode)), rr_graph.node_ylow( rr_gsb.get_opin_node(side_manager.get_side(), inode))); - std::string src_grid_module_name = - generate_grid_block_module_name_in_top_module( - std::string(GRID_MODULE_NAME_PREFIX), grids, grid_coordinate); - ModuleId src_grid_module = - module_manager.find_module(src_grid_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(src_grid_module)); + /* Check if the grid is inside the tile, if not, create ports */ + if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { + continue; + } + + /* Find the source tile id, coordinate etc., which is required to find source tile module and port */ + FabricTileId src_fabric_tile_id = fabric_tile.find_tile_by_pb_coordinate(grid_coordinate); + size_t pb_idx_in_src_fabric_tile = fabric_tile.find_pb_index_in_tile(src_fabric_tile_id, grid_coordinate); + vtr::Point src_tile_coord = fabric_tile.tile_coordinate(src_fabric_tile_id); + vtr::Point src_unique_tile_coord = fabric_tile.unique_tile_coordinate(src_fabric_tile_id); + FabricTileId src_unique_tile = fabric_tile.unique_tile(src_tile_coord); + vtr::Point src_pb_coord_in_unique_tile = fabric_tile.pb_coordinates(src_unique_tile)[pb_idx_in_src_fabric_tile]; + std::string src_tile_module_name = + generate_tile_module_name(src_unique_tile_coord); + ModuleId src_tile_module = + module_manager.find_module(src_tile_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(src_tile_module)); size_t src_grid_pin_index = rr_graph.node_pin_num( rr_gsb.get_opin_node(side_manager.get_side(), inode)); @@ -358,12 +369,14 @@ static int build_top_module_tile_nets_between_sb_and_pb( get_rr_graph_single_node_side( rr_graph, rr_gsb.get_opin_node(side_manager.get_side(), inode)), src_grid_pin_info); - ModulePortId src_grid_port_id = - module_manager.find_module_port(src_grid_module, src_grid_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id(src_grid_module, - src_grid_port_id)); - BasicPort src_grid_port = - module_manager.module_port(src_grid_module, src_grid_port_id); + std::string src_grid_module_name = generate_grid_block_module_name_in_top_module(std::string(GRID_MODULE_NAME_PREFIX), grids, src_pb_coord_in_unique_tile); + std::string src_tile_grid_port_name = generate_tile_module_port_name(src_grid_module_name, src_grid_port_name); + ModulePortId src_tile_grid_port_id = + module_manager.find_module_port(src_tile_module, src_tile_grid_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(src_tile_module, + src_tile_grid_port_id)); + BasicPort src_tile_grid_port = + module_manager.module_port(src_tile_module, src_tile_grid_port_id); /* Collect sink-related information */ vtr::Point sink_sb_port_coord( @@ -377,19 +390,37 @@ static int build_top_module_tile_nets_between_sb_and_pb( rr_graph, module_sb.get_opin_node(side_manager.get_side(), inode)), grids, vpr_device_annotation, rr_graph, module_sb.get_opin_node(side_manager.get_side(), inode)); - ModulePortId sink_sb_port_id = - module_manager.find_module_port(sink_sb_module, sink_sb_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id(sink_sb_module, - sink_sb_port_id)); - BasicPort sink_sb_port = - module_manager.module_port(sink_sb_module, sink_sb_port_id); + std::string sink_tile_sb_port_name = generate_tile_module_port_name(sink_sb_module_name, sink_sb_port_name); + ModulePortId sink_tile_sb_port_id = + module_manager.find_module_port(tile_module, sink_tile_sb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(tile_module, + sink_tile_sb_port_id)); + BasicPort sink_tile_sb_port = + module_manager.module_port(tile_module, sink_tile_sb_port_id); - /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { - continue; - } - /* TODO: Create nets */ - if (!frame_view) { + /* Create nets */ + VTR_LOGV(verbose, + "Build inter-tile nets between switch block '%s' in tile[%lu][%lu] and " + "programmable block in tile[%lu][%lu]\n", + sink_sb_module_name.c_str(), + sink_tile_coord.x(), sink_tile_coord.y(), + src_tile_coord.x(), src_tile_coord.y()); + size_t src_tile_instance = + tile_instance_ids[src_tile_coord.x(), src_tile_coord.y()]; + + /* Source and sink port should match in size */ + VTR_ASSERT(src_tile_grid_port.get_width() == sink_tile_sb_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_tile_grid_port.pins().size(); + ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, top_module, src_tile_module, src_tile_instance, + src_tile_grid_port_id, src_tile_grid_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink( + top_module, net, curr_tile_module, curr_tile_instance_id, + sink_tile_sb_port_id, sink_tile_sb_port.pins()[pin_id]); } } } @@ -460,7 +491,7 @@ static int build_top_module_tile_nets_between_cb_and_pb( const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, const t_rr_type& cb_type, - const bool& frame_view, + const bool& compact_routing_hierarchy, const bool& verbose) { /* We could have two different coordinators, one is the instance, the other is * the module */ @@ -568,8 +599,6 @@ static int build_top_module_tile_nets_between_cb_and_pb( continue; } /* TODO: Create nets */ - if (!frame_view) { - } } } return CMD_EXEC_SUCCESS; @@ -622,7 +651,8 @@ static int build_top_module_tile_nets_between_sb_and_cb( const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, - const bool& frame_view, const bool& verbose) { + const bool& compact_routing_hierarchy, + const bool& verbose) { /* We could have two different coordinators, one is the instance, the other is * the module */ vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), @@ -721,8 +751,6 @@ static int build_top_module_tile_nets_between_sb_and_cb( continue; } /* TODO: Create nets */ - if (!frame_view) { - } } return CMD_EXEC_SUCCESS; } @@ -735,7 +763,8 @@ static int build_top_module_tile_nets_between_pbs( const ModuleId& tile_module, const size_t& tile_instance_id, const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, const vtr::Point& pb_coord, const size_t& pb_instance, - const bool& frame_view, const bool& verbose) { + const bool& compact_routing_hierarchy, + const bool& verbose) { t_physical_tile_type_ptr phy_tile = grids.get_physical_type(pb_coord.x(), pb_coord.y()); /* Empty type does not require a module */ @@ -877,8 +906,8 @@ static int add_top_module_nets_around_one_tile( fabric_tile.sb_coordinates(fabric_tile_id)[isb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); status_code = build_top_module_tile_nets_between_sb_and_pb( - module_manager, top_module, tile_module, tile_instance_id, grids, vpr_device_annotation, device_rr_gsb, - rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, verbose); + module_manager, top_module, tile_module, tile_instance_ids, tile_instance_id, grids, vpr_device_annotation, device_rr_gsb, + rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, true, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -895,7 +924,7 @@ static int add_top_module_nets_around_one_tile( status_code = build_top_module_tile_nets_between_cb_and_pb( module_manager, tile_module, grids, vpr_device_annotation, device_rr_gsb, rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, - cb_type, pb_instances, cb_instances.at(cb_type)[icb], true, frame_view, + cb_type, pb_instances, cb_instances.at(cb_type)[icb], true, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; @@ -912,7 +941,7 @@ static int add_top_module_nets_around_one_tile( status_code = build_top_module_tile_nets_between_sb_and_cb( module_manager, tile_module, device_rr_gsb, rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, cb_instances, sb_instances[isb], true, - frame_view, verbose); + verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -925,7 +954,7 @@ static int add_top_module_nets_around_one_tile( fabric_tile.pb_coordinates(fabric_tile_id)[ipb]; status_code = build_top_module_tile_nets_between_pbs( module_manager, tile_module, grids, vpr_device_annotation, pb_coord, - pb_instances[ipb], frame_view, verbose); + pb_instances[ipb], verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } From 003d9515ffa461551b433fc38f1911398b7d03d2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 22 Jul 2023 17:13:30 -0700 Subject: [PATCH 203/391] [core] developing tile-based top module builder --- .../build_top_module_child_tile_instance.cpp | 496 ++++++++++-------- .../src/fabric/build_top_module_utils.cpp | 37 ++ openfpga/src/fabric/build_top_module_utils.h | 6 + 3 files changed, 320 insertions(+), 219 deletions(-) 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 ccd05a3d9..24da0fced 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -284,7 +284,8 @@ static int build_top_module_tile_nets_between_sb_and_pb( const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const FabricTile& fabric_tile, - const FabricTileId& fabric_tile_id, + const FabricTileId& curr_fabric_tile_id, + const size_t& sb_idx_in_curr_fabric_tile, const bool& compact_routing_hierarchy, const bool& verbose) { /* Skip those Switch blocks that do not exist */ @@ -293,6 +294,10 @@ static int build_top_module_tile_nets_between_sb_and_pb( } vtr::Point sink_tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); + FabricTileId sink_unique_tile = fabric_tile.unique_tile(curr_fabric_tile_id); + vtr::Point sink_sb_coord_in_unique_tile = fabric_tile.sb_coordinates(src_unique_tile)[sb_idx_in_curr_fabric_tile]; + std::string sink_sb_instance_name_in_unique_tile = + generate_switch_block_module_name(sink_sb_coord_in_unique_tile); /* We could have two different coordinators, one is the instance, the other is * the module */ @@ -314,10 +319,6 @@ static int build_top_module_tile_nets_between_sb_and_pb( vtr::Point module_sb_coordinate(module_sb.get_sb_x(), module_sb.get_sb_y()); - /* Collect sink-related information */ - std::string sink_sb_module_name = - generate_switch_block_module_name(module_sb_coordinate); - /* Connect grid output pins (OPIN) to switch block grid pins */ for (size_t side = 0; side < module_sb.get_num_sides(); ++side) { SideManager side_manager(side); @@ -331,11 +332,36 @@ static int build_top_module_tile_nets_between_sb_and_pb( rr_graph.node_ylow( rr_gsb.get_opin_node(side_manager.get_side(), inode))); /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { + if (fabric_tile.pb_in_tile(curr_fabric_tile_id, grid_coordinate)) { continue; } - /* Find the source tile id, coordinate etc., which is required to find source tile module and port */ + /* Find the source tile id, coordinate etc., which is required to find source tile module and port + * Relationship between source tile and its unique module + * Take an example: + * + * grid_pin name should follow unique module [i0][j0] of src_tile[x0][y0] + * sb_pin name should follow unique module [i1][j1] of des_tile[x1][y1] + * + * However, instance id should follow the origin tiles + * + * Src tile Des tile + * +------------+ +--------------+ + * | | | | + * | Grid |------------>| Switch Block | + * | [x0][y0] | | [x1][y1] | + * | | | | + * +------------+ +--------------+ + * ^ ^ + * || unique_mirror || unique mirror + * +------------+ +--------------+ + * | | | | + * | Grid |<------------| Switch Block | + * | [i0][j0] | | [i1][j1] | + * | | | | + * +------------+ +--------------+ + * + */ FabricTileId src_fabric_tile_id = fabric_tile.find_tile_by_pb_coordinate(grid_coordinate); size_t pb_idx_in_src_fabric_tile = fabric_tile.find_pb_index_in_tile(src_fabric_tile_id, grid_coordinate); vtr::Point src_tile_coord = fabric_tile.tile_coordinate(src_fabric_tile_id); @@ -347,28 +373,15 @@ static int build_top_module_tile_nets_between_sb_and_pb( ModuleId src_tile_module = module_manager.find_module(src_tile_module_name); VTR_ASSERT(true == module_manager.valid_module_id(src_tile_module)); + size_t src_tile_instance = + tile_instance_ids[src_tile_coord.x()][src_tile_coord.y()]; + size_t src_grid_pin_index = rr_graph.node_pin_num( rr_gsb.get_opin_node(side_manager.get_side(), inode)); + std::string src_grid_port_name = generate_grid_module_port_name_in_top_module( + grid_coordinate, src_grid_pin_index, vpr_device_annotation, + rr_graph, rr_gsb.get_opin_node(side_manager.get_side(), inode)); - t_physical_tile_type_ptr grid_type_descriptor = - grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); - size_t src_grid_pin_width = - grid_type_descriptor->pin_width_offset[src_grid_pin_index]; - size_t src_grid_pin_height = - grid_type_descriptor->pin_height_offset[src_grid_pin_index]; - BasicPort src_grid_pin_info = - vpr_device_annotation.physical_tile_pin_port_info(grid_type_descriptor, - src_grid_pin_index); - VTR_ASSERT(true == src_grid_pin_info.is_valid()); - int subtile_index = vpr_device_annotation.physical_tile_pin_subtile_index( - grid_type_descriptor, src_grid_pin_index); - VTR_ASSERT(OPEN != subtile_index && - subtile_index < grid_type_descriptor->capacity); - std::string src_grid_port_name = generate_grid_port_name( - src_grid_pin_width, src_grid_pin_height, subtile_index, - get_rr_graph_single_node_side( - rr_graph, rr_gsb.get_opin_node(side_manager.get_side(), inode)), - src_grid_pin_info); std::string src_grid_module_name = generate_grid_block_module_name_in_top_module(std::string(GRID_MODULE_NAME_PREFIX), grids, src_pb_coord_in_unique_tile); std::string src_tile_grid_port_name = generate_tile_module_port_name(src_grid_module_name, src_grid_port_name); ModulePortId src_tile_grid_port_id = @@ -390,7 +403,8 @@ static int build_top_module_tile_nets_between_sb_and_pb( rr_graph, module_sb.get_opin_node(side_manager.get_side(), inode)), grids, vpr_device_annotation, rr_graph, module_sb.get_opin_node(side_manager.get_side(), inode)); - std::string sink_tile_sb_port_name = generate_tile_module_port_name(sink_sb_module_name, sink_sb_port_name); + + std::string sink_tile_sb_port_name = generate_tile_module_port_name(sink_sb_instance_name_in_unique_tile, sink_sb_port_name); ModulePortId sink_tile_sb_port_id = module_manager.find_module_port(tile_module, sink_tile_sb_port_name); VTR_ASSERT(true == module_manager.valid_module_port_id(tile_module, @@ -402,11 +416,9 @@ static int build_top_module_tile_nets_between_sb_and_pb( VTR_LOGV(verbose, "Build inter-tile nets between switch block '%s' in tile[%lu][%lu] and " "programmable block in tile[%lu][%lu]\n", - sink_sb_module_name.c_str(), + sink_sb_instance_name_in_unique_tile.c_str(), sink_tile_coord.x(), sink_tile_coord.y(), src_tile_coord.x(), src_tile_coord.y()); - size_t src_tile_instance = - tile_instance_ids[src_tile_coord.x(), src_tile_coord.y()]; /* Source and sink port should match in size */ VTR_ASSERT(src_tile_grid_port.get_width() == sink_tile_sb_port.get_width()); @@ -465,7 +477,7 @@ static int build_top_module_tile_nets_between_sb_and_pb( * grid_pin name should follow unique module of Grid[x][y+1] * cb_pin name should follow unique module of CBY[x][y+1] * - * However, instace id should follow the origin Grid and Connection block + * However, instance id should follow the origin Grid and Connection block * * * +------------+ +------------------+ @@ -486,13 +498,22 @@ static int build_top_module_tile_nets_between_sb_and_pb( *******************************************************************/ static int build_top_module_tile_nets_between_cb_and_pb( ModuleManager& module_manager, const ModuleId& top_module, - const ModuleId& tile_module, const size_t& tile_instance_id, + const ModuleId& tile_module, + const vtr::Matrix& tile_instance_ids, + const size_t& tile_instance_id, const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const FabricTile& fabric_tile, - const FabricTileId& fabric_tile_id, const t_rr_type& cb_type, + const FabricTileId& curr_fabric_tile_id, const t_rr_type& cb_type, + const size_t& cb_idx_in_curr_fabric_tile, const bool& compact_routing_hierarchy, const bool& verbose) { + vtr::Point src_tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); + FabricTileId src_unique_tile = fabric_tile.unique_tile(curr_fabric_tile_id); + vtr::Point src_cb_coord_in_unique_tile = fabric_tile.cb_coordinates(src_unique_tile, cb_type)[cb_idx_in_curr_fabric_tile]; + std::string src_cb_instance_name_in_unique_tile = + generate_connection_block_module_name(cb_type, sink_sb_coord_in_unique_tile); + /* We could have two different coordinators, one is the instance, the other is * the module */ vtr::Point instance_cb_coordinate(rr_gsb.get_cb_x(cb_type), @@ -524,14 +545,6 @@ static int build_top_module_tile_nets_between_cb_and_pb( vtr::Point module_cb_coordinate(module_cb.get_cb_x(cb_type), module_cb.get_cb_y(cb_type)); - /* Collect source-related information */ - std::string src_cb_module_name = - generate_connection_block_module_name(cb_type, module_cb_coordinate); - ModuleId src_cb_module = module_manager.find_module(src_cb_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(src_cb_module)); - /* Instance id should follow the instance cb coordinate */ - size_t src_cb_instance = cb_instance; - /* Iterate over the output pins of the Connection Block */ std::vector cb_ipin_sides = module_cb.get_cb_ipin_sides(cb_type); for (size_t iside = 0; iside < cb_ipin_sides.size(); ++iside) { @@ -545,12 +558,13 @@ static int build_top_module_tile_nets_between_cb_and_pb( rr_graph.node_ylow(module_ipin_node)); std::string src_cb_port_name = generate_cb_module_grid_port_name( cb_ipin_side, grids, vpr_device_annotation, rr_graph, module_ipin_node); + std::string src_tile_cb_port_name = generate_tile_module_port_name(src_cb_instance_name_in_unique_tile, src_cb_port_name); ModulePortId src_cb_port_id = - module_manager.find_module_port(src_cb_module, src_cb_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id(src_cb_module, + module_manager.find_module_port(tile_module, src_cb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(tile_module, src_cb_port_id)); BasicPort src_cb_port = - module_manager.module_port(src_cb_module, src_cb_port_id); + module_manager.module_port(tile_module, src_cb_port_id); /* Collect sink-related information */ /* Note that we use the instance cb pin here!!! @@ -560,45 +574,57 @@ static int build_top_module_tile_nets_between_cb_and_pb( vtr::Point grid_coordinate( rr_graph.node_xlow(instance_ipin_node), rr_graph.node_ylow(instance_ipin_node)); - std::string sink_grid_module_name = - generate_grid_block_module_name_in_top_module( - std::string(GRID_MODULE_NAME_PREFIX), grids, grid_coordinate); - ModuleId sink_grid_module = - module_manager.find_module(sink_grid_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(sink_grid_module)); - size_t sink_grid_pin_index = rr_graph.node_pin_num(instance_ipin_node); - - t_physical_tile_type_ptr grid_type_descriptor = - grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); - size_t sink_grid_pin_width = - grid_type_descriptor->pin_width_offset[sink_grid_pin_index]; - size_t sink_grid_pin_height = - grid_type_descriptor->pin_height_offset[sink_grid_pin_index]; - BasicPort sink_grid_pin_info = - vpr_device_annotation.physical_tile_pin_port_info(grid_type_descriptor, - sink_grid_pin_index); - VTR_ASSERT(true == sink_grid_pin_info.is_valid()); - int subtile_index = vpr_device_annotation.physical_tile_pin_subtile_index( - grid_type_descriptor, sink_grid_pin_index); - VTR_ASSERT(OPEN != subtile_index && - subtile_index < grid_type_descriptor->capacity); - std::string sink_grid_port_name = generate_grid_port_name( - sink_grid_pin_width, sink_grid_pin_height, subtile_index, - get_rr_graph_single_node_side( - rr_graph, rr_gsb.get_ipin_node(cb_ipin_side, inode)), - sink_grid_pin_info); - ModulePortId sink_grid_port_id = - module_manager.find_module_port(sink_grid_module, sink_grid_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id( - sink_grid_module, sink_grid_port_id)); - BasicPort sink_grid_port = - module_manager.module_port(sink_grid_module, sink_grid_port_id); - /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.pb_in_tile(fabric_tile_id, grid_coordinate)) { + if (fabric_tile.pb_in_tile(curr_fabric_tile_id, grid_coordinate)) { continue; } - /* TODO: Create nets */ + + FabricTileId sink_fabric_tile_id = fabric_tile.find_tile_by_pb_coordinate(grid_coordinate); + size_t pb_idx_in_sink_fabric_tile = fabric_tile.find_pb_index_in_tile(sink_fabric_tile_id, grid_coordinate); + vtr::Point sink_tile_coord = fabric_tile.tile_coordinate(sink_fabric_tile_id); + vtr::Point sink_unique_tile_coord = fabric_tile.unique_tile_coordinate(sink_fabric_tile_id); + FabricTileId sink_unique_tile = fabric_tile.unique_tile(sink_tile_coord); + vtr::Point sink_pb_coord_in_unique_tile = fabric_tile.pb_coordinates(sink_unique_tile)[pb_idx_in_sink_fabric_tile]; + std::string sink_tile_module_name = + generate_tile_module_name(sink_unique_tile_coord); + ModuleId sink_tile_module = + module_manager.find_module(sink_tile_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sink_tile_module)); + size_t sink_tile_instance_id = tile_instance_ids[sink_tile_coord.x()][sink_tile_coord.y()]; + + std::string sink_grid_module_name = + generate_grid_block_module_name_in_top_module( + std::string(GRID_MODULE_NAME_PREFIX), grids, sink_pb_coord_in_unique_tile); + + size_t sink_grid_pin_index = rr_graph.node_pin_num(instance_ipin_node); + + std::string sink_grid_port_name = generate_grid_module_port_name_in_top_module( + grid_coordinate, sink_grid_pin_index, vpr_device_annotation, + rr_graph, rr_gsb.get_ipin_node(cb_ipin_side, inode)); + + std::string sink_tile_grid_port_name = generate_tile_module_port_name(sink_grid_module_name, sink_grid_port_name); + ModulePortId sink_grid_port_id = + module_manager.find_module_port(sink_tile_module, sink_grid_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id( + sink_tile_module, sink_grid_port_id)); + BasicPort sink_grid_port = + module_manager.module_port(sink_tile_module, sink_grid_port_id); + + /* Create nets */ + /* Source and sink port should match in size */ + VTR_ASSERT(src_cb_port.get_width() == sink_grid_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); + ++pin_id) { + ModuleNetId net = create_module_source_pin_net( + module_manager, top_module, tile_module, tile_instance_id, + src_cb_port_id, src_cb_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink( + top_module, net, sink_tile_module, sink_tile_instance_id, + sink_grid_port_id, sink_grid_port.pins()[pin_id]); + } } } return CMD_EXEC_SUCCESS; @@ -647,10 +673,13 @@ static int build_top_module_tile_nets_between_cb_and_pb( *******************************************************************/ static int build_top_module_tile_nets_between_sb_and_cb( ModuleManager& module_manager, const ModuleId& top_module, - const ModuleId& tile_module, const size_t& tile_instance_id, + const ModuleId& tile_module, + const vtr::Matrix& tile_instance_ids, + const size_t& tile_instance_id, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const FabricTile& fabric_tile, - const FabricTileId& fabric_tile_id, + const FabricTileId& curr_fabric_tile_id, + const size_t& sb_idx_in_curr_fabric_tile, const bool& compact_routing_hierarchy, const bool& verbose) { /* We could have two different coordinators, one is the instance, the other is @@ -659,6 +688,12 @@ static int build_top_module_tile_nets_between_sb_and_cb( rr_gsb.get_sb_y()); vtr::Point module_gsb_sb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); + vtr::Point sb_tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); + FabricTileId sb_unique_tile = fabric_tile.unique_tile(curr_fabric_tile_id); + vtr::Point sb_coord_in_unique_tile = fabric_tile.sb_coordinates(sb_unique_tile)[sb_idx_in_curr_fabric_tile]; + std::string sb_instance_name_in_unique_tile = + generate_switch_block_module_name(sb_coord_in_unique_tile); + /* Skip those Switch blocks that do not exist */ if (false == rr_gsb.is_sb_exist()) { return CMD_EXEC_SUCCESS; @@ -733,13 +768,6 @@ static int build_top_module_tile_nets_between_sb_and_cb( module_gsb_cb_coordinate.set_y(unique_mirror.get_y()); } - const RRGSB& module_cb = device_rr_gsb.get_gsb(module_gsb_cb_coordinate); - vtr::Point module_cb_coordinate(module_cb.get_cb_x(cb_type), - module_cb.get_cb_y(cb_type)); - std::string cb_module_name = - generate_connection_block_module_name(cb_type, module_cb_coordinate); - ModuleId cb_module_id = module_manager.find_module(cb_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(cb_module_id)); const RRGSB& instance_cb = device_rr_gsb.get_gsb(instance_gsb_cb_coordinate); vtr::Point instance_cb_coordinate(instance_cb.get_cb_x(cb_type), @@ -750,116 +778,95 @@ static int build_top_module_tile_nets_between_sb_and_cb( instance_cb_coordinate)) { continue; } - /* TODO: Create nets */ - } - return CMD_EXEC_SUCCESS; -} + /* Collect cb tile information */ + FabricTileId cb_tile = fabric_tile.find_tile_by_cb_coordinate(cb_type, instance_gsb_cb_coordinate); + vtr::Point cb_tile_coord = fabric_tile.tile_coordindate(cb_tile); + size_t cb_idx_in_cb_tile = fabric_tile.find_cb_index_in_tile(cb_tile, cb_type, instance_gsb_cb_coordinate); + FabricTileId cb_unique_tile = fabric_tile.unique_tile(cb_tile_coord); + vtr::Point cb_unique_tile_coord = fabric_tile.tile_coordindate(cb_unique_tile); + vtr::Point cb_coord_in_cb_unique_tile = fabric_tile.cb_coordinates(cb_unique_tile, cb_type)[cb_idx_in_cb_tile]; + std::string cb_instance_name_in_unique_tile = + generate_connection_block_module_name(cb_type, cb_coord_in_cb_unique_tile); + std::string cb_tile_module_name = + generate_tile_module_name(cb_unique_tile_coord); + ModuleId cb_tile_module = + module_manager.find_module(cb_tile_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(cb_tile_module)); + size_t cb_tile_instance = + tile_instance_ids[cb_tile_coord.x()][cb_tile_coord.y()]; -/******************************************************************** - * This function will create nets between two programmable blocks, which are direct connections - ********************************************************************/ -static int build_top_module_tile_nets_between_pbs( - ModuleManager& module_manager, const ModuleId& top_module, - const ModuleId& tile_module, const size_t& tile_instance_id, - const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, - const vtr::Point& pb_coord, const size_t& pb_instance, - const bool& compact_routing_hierarchy, - const bool& verbose) { - t_physical_tile_type_ptr phy_tile = - grids.get_physical_type(pb_coord.x(), pb_coord.y()); - /* Empty type does not require a module */ - if (is_empty_type(phy_tile)) { - return CMD_EXEC_SUCCESS; - } - e_side grid_side = find_grid_border_side( - vtr::Point(grids.width(), grids.height()), pb_coord); - std::string pb_module_name = generate_grid_block_module_name( - std::string(GRID_MODULE_NAME_PREFIX), std::string(phy_tile->name), - is_io_type(phy_tile), grid_side); - ModuleId pb_module = module_manager.find_module(pb_module_name); - if (!pb_module) { - VTR_LOG_ERROR("Failed to find pb module '%s' required by tile[%lu][%lu]!\n", - pb_module_name.c_str(), pb_coord.x(), pb_coord.y()); - return CMD_EXEC_FATAL_ERROR; - } + /* Create nets */ + for (size_t itrack = 0; + itrack < module_sb.get_chan_width(side_manager.get_side()); + ++itrack) { + std::string sb_port_name = generate_sb_module_track_port_name( + rr_graph.node_type( + module_sb.get_chan_node(side_manager.get_side(), itrack)), + side_manager.get_side(), + module_sb.get_chan_node_direction(side_manager.get_side(), itrack)); + /* Prepare SB-related port information */ + std::string sb_tile_sb_port_name = generate_tile_module_port_name(sb_instance_name_in_unique_tile, sb_port_name); + ModulePortId sb_port_id = + module_manager.find_module_port(tile_module, sb_tile_sb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(tile_module, + sb_port_id)); + BasicPort sb_port = + module_manager.module_port(tile_module, sb_port_id); - /* Find the pin side for I/O grids*/ - std::vector grid_pin_sides; - /* For I/O grids, we care only one side - * Otherwise, we will iterate all the 4 sides - */ - if (true == is_io_type(phy_tile)) { - grid_pin_sides = find_grid_module_pin_sides(phy_tile, grid_side); - } else { - grid_pin_sides = {TOP, RIGHT, BOTTOM, LEFT}; - } + /* Prepare CB-related port information */ + PORTS cb_port_direction = OUT_PORT; + /* The cb port direction should be opposite to the sb port !!! */ + if (OUT_PORT == module_sb.get_chan_node_direction( + side_manager.get_side(), itrack)) { + cb_port_direction = IN_PORT; + } else { + VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( + side_manager.get_side(), itrack)); + } - /* Create a map between pin class type and grid pin direction */ - std::map pin_type2type_map; - pin_type2type_map[RECEIVER] = ModuleManager::MODULE_INPUT_PORT; - pin_type2type_map[DRIVER] = ModuleManager::MODULE_OUTPUT_PORT; + /* Upper CB port is required if the routing tracks are on the top or + * right sides of the switch block, which indicated bottom and left + * sides of the connection blocks + */ + bool use_cb_upper_port = (TOP == side_manager.get_side()) || + (RIGHT == side_manager.get_side()); + std::string cb_port_name = generate_cb_module_track_port_name( + cb_type, cb_port_direction, use_cb_upper_port); + std::string cb_tile_cb_port_name = generate_tile_module_port_name(cb_instance_name_in_unique_tile, cb_port_name); + ModulePortId cb_port_id = + module_manager.find_module_port(cb_tile_module, cb_tile_cb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(cb_tile_module, + cb_port_id)); + BasicPort cb_port = + module_manager.module_port(cb_tile_module, cb_port_id); - /* Iterate over sides, height and pins */ - for (const e_side& side : grid_pin_sides) { - for (int iwidth = 0; iwidth < phy_tile->width; ++iwidth) { - for (int iheight = 0; iheight < phy_tile->height; ++iheight) { - for (int ipin = 0; ipin < phy_tile->num_pins; ++ipin) { - if (!phy_tile->pinloc[iwidth][iheight][side][ipin]) { - continue; - } - /* Reach here, it means this pin is on this side */ - /* Generate the pin name, - * we give a empty coordinate but it will not be used (see details in - * the function - */ - BasicPort pin_info = - vpr_device_annotation.physical_tile_pin_port_info(phy_tile, ipin); - VTR_ASSERT(true == pin_info.is_valid()); - int subtile_index = - vpr_device_annotation.physical_tile_pin_subtile_index(phy_tile, - ipin); - VTR_ASSERT(OPEN != subtile_index && - subtile_index < phy_tile->capacity); - std::string port_name = generate_grid_port_name( - iwidth, iheight, subtile_index, side, pin_info); - BasicPort pb_port(port_name, 0, 0); - ModulePortId pb_module_port_id = - module_manager.find_module_port(pb_module, port_name); - if (!module_manager.valid_module_port_id(pb_module, - pb_module_port_id)) { - VTR_LOG_ERROR( - "Failed to find port '%s' for pb module '%s' required by " - "tile[%lu][%lu]!\n", - pb_port.to_verilog_string().c_str(), pb_module_name.c_str(), - pb_coord.x(), pb_coord.y()); - return CMD_EXEC_FATAL_ERROR; - } - - /* Find the port from the pb module and see if it is already been - * driven or driving a net. if not, create a new port at the tile - * module */ - if (module_manager.port_type(pb_module, pb_module_port_id) == - ModuleManager::e_module_port_type::MODULE_INPUT_PORT) { - /* TODO: Create pb-to-pb nets */ - } else if (module_manager.port_type(pb_module, pb_module_port_id) == - ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT) { - /* TODO: Create pb-to-pb nets */ - } else { - VTR_LOG_ERROR( - "Expect either input or output port '%s' for pb module '%s' " - "required by tile[%lu][%lu]!\n", - pb_port.to_verilog_string().c_str(), pb_module_name.c_str(), - pb_coord.x(), pb_coord.y()); - return CMD_EXEC_FATAL_ERROR; - } - } + /* Configure the net source and sink: + * If sb port is an output (source), cb port is an input (sink) + * If sb port is an input (sink), cb port is an output (source) + */ + if (OUT_PORT == module_sb.get_chan_node_direction( + side_manager.get_side(), itrack)) { + ModuleNetId net = create_module_source_pin_net( + module_manager, top_module, tile_module, tile_instance_id, + sb_port_id, itrack / 2); + module_manager.add_module_net_sink(top_module, net, cb_tile_module, + cb_tile_instance, cb_port_id, + itrack / 2); + } else { + VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( + side_manager.get_side(), itrack)); + ModuleNetId net = create_module_source_pin_net( + module_manager, top_module, cb_tile_module, cb_tile_instance, + cb_port_id, itrack / 2); + module_manager.add_module_net_sink(top_module, net, tile_module, + tile_instance_id, sb_port_id, + itrack / 2); } } } return CMD_EXEC_SUCCESS; } - /******************************************************************** * Add module nets to connect the pins between tiles * To make it easy, this function will iterate over all the tiles, through which we can obtain the coordinates @@ -881,13 +888,13 @@ static int add_top_module_nets_around_one_tile( ModuleManager& module_manager, const ModuleId& top_module, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, const vtr::Matrix& tile_instance_ids, const RRGraphView& rr_graph, - const DeviceRRGSB& device_rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, + const DeviceRRGSB& device_rr_gsb, const FabricTile& fabric_tile, const FabricTileId& curr_fabric_tile_id, const bool& verbose) { int status = CMD_EXEC_SUCCESS; /* Find the module name for this type of tile */ vtr::Point unique_tile_coord = - fabric_tile.unique_tile_coordinate(fabric_tile_id); + fabric_tile.unique_tile_coordinate(curr_fabric_tile_id); std::string tile_module_name = generate_tile_module_name(unique_tile_coord); ModuleId tile_module = module_manager.find_module(tile_module_name); if (!module_manager.valid_module_id(tile_module)) { @@ -895,7 +902,7 @@ static int add_top_module_nets_around_one_tile( } /* Find the instance id for this tile */ - vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); + vtr::Point tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); size_t tile_instance_id = tile_instance_ids[tile_coord.x()][tile_coord.y()]; /* Get the submodule of Switch blocks one by one, build connections between sb @@ -907,7 +914,7 @@ static int add_top_module_nets_around_one_tile( const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); status_code = build_top_module_tile_nets_between_sb_and_pb( module_manager, top_module, tile_module, tile_instance_ids, tile_instance_id, grids, vpr_device_annotation, device_rr_gsb, - rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, true, verbose); + rr_graph_view, rr_gsb, fabric_tile, curr_fabric_tile_id, isb, true, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -922,9 +929,11 @@ static int add_top_module_nets_around_one_tile( fabric_tile.cb_coordinates(fabric_tile_id, cb_type)[icb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(cb_coord); status_code = build_top_module_tile_nets_between_cb_and_pb( - module_manager, tile_module, grids, vpr_device_annotation, - device_rr_gsb, rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, - cb_type, pb_instances, cb_instances.at(cb_type)[icb], true, + module_manager, top_module, tile_module, tile_instance_ids, tile_instance_id, + grids, vpr_device_annotation, + device_rr_gsb, rr_graph_view, rr_gsb, + fabric_tile, curr_fabric_tile_id, + cb_type, icb, true, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; @@ -939,26 +948,13 @@ static int add_top_module_nets_around_one_tile( fabric_tile.sb_coordinates(fabric_tile_id)[isb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); status_code = build_top_module_tile_nets_between_sb_and_cb( - module_manager, tile_module, device_rr_gsb, rr_graph_view, rr_gsb, - fabric_tile, fabric_tile_id, cb_instances, sb_instances[isb], true, + module_manager, top_module, tile_module, tile_instance_ids, tile_instance_id, device_rr_gsb, rr_graph_view, rr_gsb, + fabric_tile, curr_fabric_tile_id, isb, true, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } } - /* Create the ports from pb which only connects to adjacent sb and cbs, as - * well as pb */ - for (size_t ipb = 0; ipb < fabric_tile.pb_coordinates(fabric_tile_id).size(); - ++ipb) { - vtr::Point pb_coord = - fabric_tile.pb_coordinates(fabric_tile_id)[ipb]; - status_code = build_top_module_tile_nets_between_pbs( - module_manager, tile_module, grids, vpr_device_annotation, pb_coord, - pb_instances[ipb], verbose); - if (status_code != CMD_EXEC_SUCCESS) { - return CMD_EXEC_FATAL_ERROR; - } - } return CMD_EXEC_SUCCESS; } @@ -978,13 +974,13 @@ static int add_top_module_nets_connect_tiles( for (size_t ix = 0; ix < grids.width(); ++ix) { for (size_t iy = 0; iy < grids.height(); ++iy) { vtr::Point curr_coord(ix, iy); - FabricTileId fabric_tile_id = fabric_tile.find_tile(curr_coord); - if (!fabric_tile.valid_tile_id(fabric_tile_id)) { + FabricTileId curr_fabric_tile_id = fabric_tile.find_tile(curr_coord); + if (!fabric_tile.valid_tile_id(curr_fabric_tile_id)) { continue; } status = add_top_module_nets_around_one_tile( module_manager, top_module, vpr_device_annotation, grids, - tile_instance_ids, rr_graph, device_rr_gsb, fabric_tile, fabric_tile_id, verbose); + tile_instance_ids, rr_graph, device_rr_gsb, fabric_tile, curr_fabric_tile_id, verbose); if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -994,6 +990,71 @@ static int add_top_module_nets_connect_tiles( return CMD_EXEC_SUCCESS; } +/******************************************************************** + * Organize the memory modules for tiles under the top-level module + * Follow a snake-like sequence + * +-----------------------> Tail + * +<----------------------+ + * | + * ... + * +---------------------->+ + * +<----------------------+ + * head ----------------------->+ + *******************************************************************/ +static void organize_top_module_tile_based_memory_modules( + ModuleManager& module_manager, const ModuleId& top_module, + const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, + 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()); + + std::vector> tile_coords; + bool positive_direction = true; + for (size_t iy = 0; iy < grids.height(); ++iy) { + /* For positive direction: -----> */ + if (true == positive_direction) { + for (size_t ix = 0; ix < grids.width(); ++ix) { + tile_coords.push_back(vtr::Point(ix, iy)); + } + } else { + VTR_ASSERT(false == positive_direction); + /* For negative direction: -----> */ + for (size_t ix = grids.width() - 1; ix >= 0; --ix) { + tile_coords.push_back(vtr::Point(ix, iy)); + } + } + /* Flip the positive direction to be negative */ + positive_direction = !positive_direction; + } + + for (const vtr::Point& tile_coord : tile_coords) { + FabricTileId curr_fabric_tile_id = fabric_tile.find_tile(tile_coord); + if (!fabric_tile.valid_tile_id(curr_fabric_tile_id)) { + continue; + } + vtr::Point curr_tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); + vtr::Point unique_tile_coord = fabric_tile.unique_tile_coordinate(curr_fabric_tile_id); + std::string tile_module_name = generate_tile_module_name(unique_tile_coord); + ModuleId tile_module = mnodule_manager.find_module(tile_module_name); + VTR_ASSERT(module_manager.valid_module_id(tile_module)); + + if (0 < find_module_num_config_bits(module_manager, tile_module, + circuit_lib, sram_model, + sram_orgz_type)) { + module_manager.add_configurable_child( + top_module, tile_module, + tile_instance_ids[curr_tile_coord.x()][curr_tile_coord.y()], curr_tile_coord); + } + } + + /* Split memory modules into different regions */ + build_top_module_configurable_regions(module_manager, top_module, + config_protocol); +} + + /******************************************************************** * Add the tile-level instances to the top module of FPGA fabric * and build connects between them @@ -1051,12 +1112,9 @@ int build_top_module_tile_child_instances( * Otherwise, we will load the fabric key directly */ if (true == fabric_key.empty()) { - /* TODO: need a special one for tiles - organize_top_module_memory_modules( + organize_top_module_tile_based_memory_modules( module_manager, top_module, circuit_lib, config_protocol, sram_model, - grids, grid_instance_ids, device_rr_gsb, sb_instance_ids, cb_instance_ids, - compact_routing_hierarchy); - */ + grids, tile_instance_ids, fabric_tile); } else { VTR_ASSERT_SAFE(false == fabric_key.empty()); /* Throw a fatal error when the fabric key has a mismatch in region diff --git a/openfpga/src/fabric/build_top_module_utils.cpp b/openfpga/src/fabric/build_top_module_utils.cpp index b75c2bafc..e52ad6519 100644 --- a/openfpga/src/fabric/build_top_module_utils.cpp +++ b/openfpga/src/fabric/build_top_module_utils.cpp @@ -36,6 +36,43 @@ std::string generate_grid_block_module_name_in_top_module( border_side); } +/******************************************************************** + * Generate the port name for a grid block, by considering + * 1. its pin index + * 2. side on the grid + * 3. relative position in the physical tile + * + * This function is mainly used in the top-level module generation + * Note that it may be useful for other modules but not tried yet! + *******************************************************************/ +std::string generate_grid_module_port_name_in_top_module( + const vtr::Point& grid_coordinate, + const size_t& sink_grid_pin_index, + const VprDeviceAnnotation& vpr_device_annotation, + const RRGraph& rr_graph, const RRNodeId& inode) { + t_physical_tile_type_ptr grid_type_descriptor = + grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + size_t sink_grid_pin_width = + grid_type_descriptor->pin_width_offset[sink_grid_pin_index]; + size_t sink_grid_pin_height = + grid_type_descriptor->pin_height_offset[sink_grid_pin_index]; + BasicPort sink_grid_pin_info = + vpr_device_annotation.physical_tile_pin_port_info(grid_type_descriptor, + sink_grid_pin_index); + VTR_ASSERT(true == sink_grid_pin_info.is_valid()); + int subtile_index = vpr_device_annotation.physical_tile_pin_subtile_index( + grid_type_descriptor, sink_grid_pin_index); + VTR_ASSERT(OPEN != subtile_index && + subtile_index < grid_type_descriptor->capacity); + std::string sink_grid_port_name = generate_grid_port_name( + sink_grid_pin_width, sink_grid_pin_height, subtile_index, + get_rr_graph_single_node_side( + rr_graph, rr_gsb.get_ipin_node(cb_ipin_side, inode)), + sink_grid_pin_info); + + return sink_grid_port_name; +} + /******************************************************************** * Find the cb_type of a GSB in the top-level module * depending on the side of SB diff --git a/openfpga/src/fabric/build_top_module_utils.h b/openfpga/src/fabric/build_top_module_utils.h index 394a3408a..392e9e09d 100644 --- a/openfpga/src/fabric/build_top_module_utils.h +++ b/openfpga/src/fabric/build_top_module_utils.h @@ -22,6 +22,12 @@ std::string generate_grid_block_module_name_in_top_module( const std::string& prefix, const DeviceGrid& grids, const vtr::Point& grid_coord); +std::string generate_grid_module_port_name_in_top_module( + const vtr::Point& grid_coordinate, + const size_t& sink_grid_pin_index, + const VprDeviceAnnotation& vpr_device_annotation, + const RRGraph& rr_graph, const RRNodeId& inode); + t_rr_type find_top_module_cb_type_by_sb_side(const e_side& sb_side); vtr::Point find_top_module_gsb_coordinate_by_sb_side( From 0f3f4b0d8145382fbc34c561695ca32a184bf876 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 22 Jul 2023 23:55:54 -0700 Subject: [PATCH 204/391] [core] now tile module use unique port name (for heterogeneous blocks) --- openfpga/src/fabric/build_tile_modules.cpp | 40 ++++++++++++++++------ 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 6aafad219..d4bf74fc1 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -64,13 +64,16 @@ static int build_tile_module_port_and_nets_between_sb_and_pb( const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, const std::vector& pb_instances, - const size_t& sb_instance, const bool& compact_routing_hierarchy, + const std::vector& sb_instances, + const size_t& isb, const bool& compact_routing_hierarchy, const bool& frame_view, const bool& verbose) { /* Skip those Switch blocks that do not exist */ if (false == rr_gsb.is_sb_exist()) { return CMD_EXEC_SUCCESS; } + size_t sb_instance = sb_instances[isb]; + /* We could have two different coordinators, one is the instance, the other is * the module */ vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), @@ -195,7 +198,8 @@ static int build_tile_module_port_and_nets_between_sb_and_pb( } } else { /* Create a port on the tile module and create the net if required. - * FIXME: Create a proper name to avoid naming conflicts */ + * Create a proper name to avoid naming conflicts */ + src_grid_port.set_name(generate_tile_module_port_name(fabric_tile.sb_coordinates(fabric_tile_id, isb), src_grid_port.get_name())); ModulePortId src_tile_port_id = module_manager.add_port( tile_module, src_grid_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); @@ -284,9 +288,12 @@ static int build_tile_module_port_and_nets_between_cb_and_pb( const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, const t_rr_type& cb_type, - const std::vector& pb_instances, const size_t& cb_instance, + const std::vector& pb_instances, + const std::vector& cb_instances, + const size_t& icb, const bool& compact_routing_hierarchy, const bool& frame_view, const bool& verbose) { + size_t cb_instance = cb_instances.at(cb_type)[icb]; /* We could have two different coordinators, one is the instance, the other is * the module */ vtr::Point instance_cb_coordinate(rr_gsb.get_cb_x(cb_type), @@ -413,6 +420,7 @@ static int build_tile_module_port_and_nets_between_cb_and_pb( } else { /* Create a port on the tile module and create the net if required. * FIXME: Create a proper name to avoid naming conflicts */ + src_cb_port.set_name(generate_tile_module_port_name(fabric_tile.cb_coordinates(fabric_tile_id, cb_type, icb), src_cb_port.get_name())); ModulePortId sink_tile_port_id = module_manager.add_port( tile_module, src_cb_port, ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); @@ -485,8 +493,11 @@ static int build_tile_module_port_and_nets_between_sb_and_cb( const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, const std::map>& cb_instances, - const size_t& sb_instance, const bool& compact_routing_hierarchy, + const std::vector& sb_instances, + const size_t& isb, + const bool& compact_routing_hierarchy, const bool& frame_view, const bool& verbose) { + size_t sb_instance = sb_instances[isb]; /* We could have two different coordinators, one is the instance, the other is * the module */ vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), @@ -663,6 +674,7 @@ static int build_tile_module_port_and_nets_between_sb_and_cb( module_manager.find_module_port(sb_module_id, chan_input_port_name); BasicPort chan_input_port = module_manager.module_port(sb_module_id, sb_chan_input_port_id); + chan_input_port.set_name(generate_tile_module_port_name(fabric_tile.sb_coordinates(fabric_tile_id, isb), chan_input_port.get_name())); ModulePortId tile_chan_input_port_id = module_manager.add_port( tile_module, chan_input_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); @@ -692,6 +704,7 @@ static int build_tile_module_port_and_nets_between_sb_and_cb( module_manager.find_module_port(sb_module_id, chan_output_port_name); BasicPort chan_output_port = module_manager.module_port(sb_module_id, sb_chan_output_port_id); + chan_output_port.set_name(generate_tile_module_port_name(fabric_tile.sb_coordinates(fabric_tile_id, isb), chan_output_port.get_name())); ModulePortId tile_chan_output_port_id = module_manager.add_port( tile_module, chan_output_port, ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); @@ -739,8 +752,12 @@ static int build_tile_module_port_and_nets_between_sb_and_cb( static int build_tile_port_and_nets_from_pb( ModuleManager& module_manager, const ModuleId& tile_module, const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, - const vtr::Point& pb_coord, const size_t& pb_instance, + const vtr::Point& pb_coord, + const std::vector& pb_instances, + const size_t& ipb, const bool& frame_view, const bool& verbose) { + /* FIXME: Should consider tile direct connections here! */ + size_t pb_instance = pb_instances[ipb]; t_physical_tile_type_ptr phy_tile = grids.get_physical_type(pb_coord.x(), pb_coord.y()); /* Empty type does not require a module */ @@ -810,6 +827,8 @@ static int build_tile_port_and_nets_from_pb( pb_coord.x(), pb_coord.y()); return CMD_EXEC_FATAL_ERROR; } + /* Create a proper name to avoid naming conflicts */ + pb_port.set_name(generate_tile_module_port_name(fabric_tile.pb_coordinates(fabric_tile_id, ipb), pb_port.get_name())); /* Find the port from the pb module and see if it is already been * driven or driving a net. if not, create a new port at the tile @@ -829,8 +848,7 @@ static int build_tile_port_and_nets_from_pb( "programmable block '%s'...\n", pb_port.to_verilog_string().c_str(), pb_module_name.c_str()); - /* Create a new port and a new net. FIXME: Create a proper name to - * avoid naming conflicts */ + /* Create a new port and a new net. */ ModulePortId tile_module_port_id = module_manager.add_port( tile_module, pb_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); @@ -921,7 +939,7 @@ static int build_tile_module_ports_and_nets( status_code = build_tile_module_port_and_nets_between_sb_and_pb( module_manager, tile_module, grids, vpr_device_annotation, device_rr_gsb, rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, pb_instances, - sb_instances[isb], true, frame_view, verbose); + sb_instances, isb, true, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -938,7 +956,7 @@ static int build_tile_module_ports_and_nets( status_code = build_tile_module_port_and_nets_between_cb_and_pb( module_manager, tile_module, grids, vpr_device_annotation, device_rr_gsb, rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, - cb_type, pb_instances, cb_instances.at(cb_type)[icb], true, frame_view, + cb_type, pb_instances, cb_instances, icb, true, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; @@ -954,7 +972,7 @@ static int build_tile_module_ports_and_nets( const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); status_code = build_tile_module_port_and_nets_between_sb_and_cb( module_manager, tile_module, device_rr_gsb, rr_graph_view, rr_gsb, - fabric_tile, fabric_tile_id, cb_instances, sb_instances[isb], true, + fabric_tile, fabric_tile_id, cb_instances, sb_instances, isb, true, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; @@ -968,7 +986,7 @@ static int build_tile_module_ports_and_nets( fabric_tile.pb_coordinates(fabric_tile_id)[ipb]; status_code = build_tile_port_and_nets_from_pb( module_manager, tile_module, grids, vpr_device_annotation, pb_coord, - pb_instances[ipb], frame_view, verbose); + pb_instances, ipb, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } From 399259ea1d367d0c860b44953f5826e01c14b36c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 23 Jul 2023 13:11:13 -0700 Subject: [PATCH 205/391] [core] adding prog clock arch support for tile modules --- .../build_top_module_child_tile_instance.cpp | 484 +++++++++++++++++- 1 file changed, 477 insertions(+), 7 deletions(-) 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 24da0fced..6f8f63730 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1054,6 +1054,476 @@ static void organize_top_module_tile_based_memory_modules( config_protocol); } +/********************************************************************* + * Generate an input port for routing multiplexer inside the tile + * which is the middle output of a routing track + ********************************************************************/ +ModulePinInfo find_tile_module_chan_port( + const ModuleManager& module_manager, const ModuleId& tile_module, + const vtr::Point& cb_coord_in_tile, + const RRGraphView& rr_graph, const RRGSB& rr_gsb, const t_rr_type& cb_type, + const RRNodeId& chan_rr_node) { + ModulePinInfo input_port_info; + /* Generate the input port object */ + switch (rr_graph.node_type(chan_rr_node)) { + case CHANX: + case CHANY: { + /* Create port description for the routing track middle output */ + int chan_node_track_id = + rr_gsb.get_cb_chan_node_index(cb_type, chan_rr_node); + /* Create a port description for the middle output */ + std::string input_port_name = generate_cb_module_track_port_name( + cb_type, IN_PORT, 0 == chan_node_track_id % 2); + std::string tile_input_port_name = generate_tile_module_port_name(generate_connection_block_module_name(cb_type, cb_coord_in_tile), input_port_name); + /* Must find a valid port id in the Switch Block module */ + input_port_info.first = + module_manager.find_module_port(tile_module, tile_input_port_name); + input_port_info.second = chan_node_track_id / 2; + VTR_ASSERT(true == module_manager.valid_module_port_id( + tile_module, input_port_info.first)); + break; + } + default: /* OPIN, SOURCE, IPIN, SINK are invalid*/ + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n"); + exit(1); + } + + return input_port_info; +} + +/******************************************************************** + * Add nets between a global port and its sinks at an entry point of clock tree + *******************************************************************/ +static int build_top_module_global_net_from_tile_clock_arch_tree( + ModuleManager& module_manager, const ModuleId& top_module, + const ModulePortId& top_module_port, const RRGraphView& rr_graph, + const DeviceRRGSB& device_rr_gsb, + const vtr::Matrix& tile_instance_ids, + const FabricTile& fabric_tile, + const ClockNetwork& clk_ntwk, const std::string& clk_tree_name, + const RRClockSpatialLookup& rr_clock_lookup) { + int status = CMD_EXEC_SUCCESS; + + /* Ensure the clock arch tree name is valid */ + ClockTreeId clk_tree = clk_ntwk.find_tree(clk_tree_name); + if (!clk_ntwk.valid_tree_id(clk_tree)) { + VTR_LOG( + "Fail to find a matched clock tree '%s' in the clock architecture " + "definition", + clk_tree_name.c_str()); + return CMD_EXEC_FATAL_ERROR; + } + + /* Ensure the clock tree width matches the global port size */ + if (clk_ntwk.tree_width(clk_tree) != + module_manager.module_port(top_module, top_module_port).get_width()) { + VTR_LOG( + "Clock tree '%s' does not have the same width '%lu' as the port '%'s of " + "FPGA top module", + clk_tree_name.c_str(), clk_ntwk.tree_width(clk_tree), + module_manager.module_port(top_module, top_module_port) + .get_name() + .c_str()); + return CMD_EXEC_FATAL_ERROR; + } + + for (ClockTreePinId pin : clk_ntwk.pins(clk_tree)) { + BasicPort src_port = + module_manager.module_port(top_module, top_module_port); + /* Add the module net */ + ModuleNetId net = create_module_source_pin_net( + module_manager, top_module, top_module, 0, top_module_port, + src_port.pins()[size_t(pin)]); + VTR_ASSERT(ModuleNetId::INVALID() != net); + + for (ClockSpineId spine : clk_ntwk.tree_top_spines(clk_tree)) { + vtr::Point entry_point = clk_ntwk.spine_start_point(spine); + Direction entry_dir = clk_ntwk.spine_direction(spine); + t_rr_type entry_track_type = clk_ntwk.spine_track_type(spine); + /* Find the routing resource node of the entry point */ + RRNodeId entry_rr_node = + rr_clock_lookup.find_node(entry_point.x(), entry_point.y(), clk_tree, + clk_ntwk.spine_level(spine), pin, entry_dir); + + /* Get the tile module and instance at the entry point */ + const RRGSB& rr_gsb = device_rr_gsb.get_gsb_by_cb_coordinate( + entry_track_type, vtr::Point(entry_point.x(), entry_point.y())); + vtr::Point cb_coord_in_tile = rr_gsb.get_sb_coordinate(); + FabricTileId curr_fabric_tile_id = fabric_tile.find_tile_by_cb_coordinate(entry_track_type, cb_coord_in_tile); + vtr::Point curr_fabric_tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); + FabricTileId unique_fabric_tile_id = fabric_tile.unique_tile(curr_fabric_tile_coord); + vtr::Point unique_fabric_tile_coord = fabric_tile.tile_coordinate(unique_fabric_tile_id); + + ModuleId tile_module = module_manager.find_module(generate_tile_module_name(unique_fabric_tile_coord)); + size_t tile_instance = tile_instance_ids[curr_fabric_tile_coord.x()][curr_fabric_tile_coord.y()]; + + /* Find the port name */ + size_t cb_idx_in_curr_fabric_tile = fabric_tile.find_cb_index_in_tile(curr_fabric_tile_id, entry_track_type, cb_coord_in_tile); + vtr::Point cb_coord_in_unique_fabric_tile = fabric_tile.cb_coordinates(unique_fabric_tile_id, entry_track_type)[cb_idx_in_curr_fabric_tile]; + ModulePinInfo des_pin_info = find_tile_module_chan_port( + module_manager, tile_module, cb_coord_in_unique_fabric_tile, rr_graph, rr_gsb, entry_track_type, + entry_rr_node); + + /* Configure the net sink */ + BasicPort sink_port = + module_manager.module_port(tile_module, des_pin_info.first); + module_manager.add_module_net_sink(top_module, net, tile_module, + tile_instance, des_pin_info.first, + sink_port.pins()[des_pin_info.second]); + } + } + + return status; +} + +/******************************************************************** + * Add global port connection for a given port of a physical tile + * that are defined as global in tile annotation + *******************************************************************/ +static int build_top_module_global_net_for_given_tile_module( + ModuleManager& module_manager, const ModuleId& top_module, + const ModulePortId& top_module_port, const TileAnnotation& tile_annotation, + const TileGlobalPortId& tile_global_port, + const BasicPort& tile_port_to_connect, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const vtr::Point& grid_coordinate, const e_side& border_side, + const vtr::Matrix& grid_instance_ids) { + t_physical_tile_type_ptr physical_tile = + grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + /* Find the module name for this type of grid */ + std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); + std::string grid_module_name = generate_grid_block_module_name( + grid_module_name_prefix, std::string(physical_tile->name), + is_io_type(physical_tile), border_side); + ModuleId grid_module = module_manager.find_module(grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); + size_t grid_instance = + grid_instance_ids[grid_coordinate.x()][grid_coordinate.y()]; + /* Find the source port at the top-level module */ + BasicPort src_port = module_manager.module_port(top_module, top_module_port); + + /* Walk through each instance considering the unique sub tile and capacity + * range, each instance may have an independent pin to be driven by a global + * net! */ + for (const t_sub_tile& sub_tile : physical_tile->sub_tiles) { + VTR_ASSERT(1 == sub_tile.equivalent_sites.size()); + int grid_pin_start_index = physical_tile->num_pins; + t_physical_tile_port physical_tile_port; + physical_tile_port.num_pins = 0; + + /* Count the total number of pins for this type of sub tile */ + int sub_tile_num_pins = sub_tile.num_phy_pins / sub_tile.capacity.total(); + + /* For each instance of the same sub tile type, find the port of the grid + * module according to the tile annotation A tile may consist of multiple + * subtile, connect to all the pins from sub tiles */ + for (int subtile_index = sub_tile.capacity.low; + subtile_index <= sub_tile.capacity.high; subtile_index++) { + for (const t_physical_tile_port& tile_port : sub_tile.ports) { + if (std::string(tile_port.name) == tile_port_to_connect.get_name()) { + BasicPort ref_tile_port(tile_port.name, tile_port.num_pins); + /* Port size must match!!! */ + if (false == ref_tile_port.contained(tile_port_to_connect)) { + VTR_LOG_ERROR( + "Tile annotation '%s' port '%s[%lu:%lu]' is out of the range of " + "physical tile port '%s[%lu:%lu]'!", + tile_annotation.global_port_name(tile_global_port).c_str(), + tile_port_to_connect.get_name().c_str(), + tile_port_to_connect.get_lsb(), tile_port_to_connect.get_msb(), + ref_tile_port.get_name().c_str(), ref_tile_port.get_lsb(), + ref_tile_port.get_msb()); + return CMD_EXEC_FATAL_ERROR; + } + grid_pin_start_index = + (subtile_index - sub_tile.capacity.low) * sub_tile_num_pins + + tile_port.absolute_first_pin_index; + physical_tile_port = tile_port; + break; + } + } + /* Ensure the pin index is valid */ + VTR_ASSERT(grid_pin_start_index < physical_tile->num_pins); + /* Ensure port width is in range */ + VTR_ASSERT(src_port.get_width() == tile_port_to_connect.get_width()); + + /* Create a pin id mapping between the source port (top module) and the + * sink port (grid module) */ + std::map sink2src_pin_map; + for (size_t ipin = 0; ipin < tile_port_to_connect.get_width(); ++ipin) { + size_t sink_pin = tile_port_to_connect.pins()[ipin]; + size_t src_pin = src_port.pins()[ipin]; + sink2src_pin_map[sink_pin] = src_pin; + } + + /* Create the connections */ + for (size_t pin_id = tile_port_to_connect.get_lsb(); + pin_id < tile_port_to_connect.get_msb() + 1; ++pin_id) { + int grid_pin_index = grid_pin_start_index + pin_id; + /* Find the module pin */ + size_t grid_pin_width = physical_tile->pin_width_offset[grid_pin_index]; + size_t grid_pin_height = + physical_tile->pin_height_offset[grid_pin_index]; + std::vector pin_sides = find_physical_tile_pin_side( + physical_tile, grid_pin_index, border_side); + + BasicPort grid_pin_info = + vpr_device_annotation.physical_tile_pin_port_info(physical_tile, + grid_pin_index); + VTR_ASSERT(true == grid_pin_info.is_valid()); + + /* Build nets */ + for (const e_side& pin_side : pin_sides) { + std::string grid_port_name = + generate_grid_port_name(grid_pin_width, grid_pin_height, + subtile_index, pin_side, grid_pin_info); + ModulePortId grid_port_id = + module_manager.find_module_port(grid_module, grid_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(grid_module, + grid_port_id)); + + VTR_ASSERT( + 1 == + module_manager.module_port(grid_module, grid_port_id).get_width()); + + ModuleNetId net = create_module_source_pin_net( + module_manager, top_module, top_module, 0, top_module_port, + src_port.pins()[sink2src_pin_map[pin_id]]); + VTR_ASSERT(ModuleNetId::INVALID() != net); + + /* Configure the net sink */ + BasicPort sink_port = + module_manager.module_port(grid_module, grid_port_id); + module_manager.add_module_net_sink(top_module, net, grid_module, + grid_instance, grid_port_id, + sink_port.pins()[0]); + } + } + } + } + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Add nets between a global port and its sinks at each tile modules + *******************************************************************/ +static int build_top_module_global_net_from_tile_modules( + ModuleManager& module_manager, const ModuleId& top_module, + const ModulePortId& top_module_port, const TileAnnotation& tile_annotation, + const TileGlobalPortId& tile_global_port, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const vtr::Matrix& tile_instance_ids, + const FabricTile& fabric_tile) { + int status = CMD_EXEC_SUCCESS; + + std::map>> io_coordinates = + generate_perimeter_grid_coordinates(grids); + + for (size_t tile_info_id = 0; + tile_info_id < + tile_annotation.global_port_tile_names(tile_global_port).size(); + ++tile_info_id) { + std::string tile_name = + tile_annotation.global_port_tile_names(tile_global_port)[tile_info_id]; + BasicPort tile_port = + tile_annotation.global_port_tile_ports(tile_global_port)[tile_info_id]; + /* Find the coordinates for the wanted tiles */ + vtr::Point start_coord(1, 1); + vtr::Point end_coord(grids.width() - 1, grids.height() - 1); + vtr::Point range = tile_annotation.global_port_tile_coordinates( + tile_global_port)[tile_info_id]; + bool out_of_range = false; + + /* -1 means all the x should be considered */ + if (size_t(-1) != range.x()) { + if ((range.x() < start_coord.x()) || (range.x() > end_coord.x())) { + out_of_range = true; + } else { + /* Set the range */ + start_coord.set_x(range.x()); + end_coord.set_x(range.x()); + } + } + + /* -1 means all the y should be considered */ + if (size_t(-1) != range.y()) { + if ((range.y() < start_coord.y()) || (range.y() > end_coord.y())) { + out_of_range = true; + } else { + /* Set the range */ + start_coord.set_y(range.y()); + end_coord.set_y(range.y()); + } + } + + /* Error out immediately if the coordinate is not valid! */ + if (true == out_of_range) { + VTR_LOG_ERROR( + "Coordinate (%lu, %lu) in tile annotation for tile '%s' is out of " + "range (%lu:%lu, %lu:%lu)!", + range.x(), range.y(), tile_name.c_str(), start_coord.x(), end_coord.x(), + start_coord.y(), end_coord.y()); + return CMD_EXEC_FATAL_ERROR; + } + + /* Spot the port from child modules from core grids */ + for (size_t ix = start_coord.x(); ix < end_coord.x(); ++ix) { + for (size_t iy = start_coord.y(); iy < end_coord.y(); ++iy) { + t_physical_tile_type_ptr phy_tile_type = + grids.get_physical_type(ix, iy); + /* Bypass EMPTY tiles */ + if (true == is_empty_type(phy_tile_type)) { + continue; + } + /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(ix, iy)) || + (0 < grids.get_height_offset(ix, iy))) { + continue; + } + + /* Bypass the tiles whose names do not match */ + if (std::string(phy_tile_type->name) != tile_name) { + continue; + } + + /* Create nets and finish connection build-up */ + status = build_top_module_global_net_for_given_tile_module( + module_manager, top_module, top_module_port, tile_annotation, + tile_global_port, tile_port, vpr_device_annotation, grids, + vtr::Point(ix, iy), NUM_SIDES, grid_instance_ids); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + + /* Walk through all the grids on the perimeter, which are I/O grids */ + for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { + for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { + t_physical_tile_type_ptr phy_tile_type = + grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); + /* Bypass EMPTY grid */ + if (true == is_empty_type(phy_tile_type)) { + continue; + } + + /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < + grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || + (0 < + grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { + continue; + } + + /* Bypass the tiles whose names do not match */ + if (std::string(phy_tile_type->name) != tile_name) { + continue; + } + + /* Check if the coordinate satisfy the tile coordinate defintion + * - Bypass if the x is a specific number (!= -1), and io_coordinate + * is different + * - Bypass if the y is a specific number (!= -1), and io_coordinate + * is different + */ + if ((size_t(-1) != range.x()) && (range.x() != io_coordinate.x())) { + continue; + } + if ((size_t(-1) != range.y()) && (range.y() != io_coordinate.y())) { + continue; + } + + /* Create nets and finish connection build-up */ + status = build_top_module_global_net_for_given_tile_module( + module_manager, top_module, top_module_port, tile_annotation, + tile_global_port, tile_port, vpr_device_annotation, grids, + io_coordinate, io_side, grid_instance_ids); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + } + + return status; +} + +/******************************************************************** + * Add global ports from tile ports that are defined as global in tile + *annotation + *******************************************************************/ +static int add_top_module_global_ports_from_tile_modules( + ModuleManager& module_manager, const ModuleId& top_module, + const TileAnnotation& tile_annotation, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, + const vtr::Matrix& tile_instance_ids, + const FabricTile& fabric_tile, const ClockNetwork& clk_ntwk, + const RRClockSpatialLookup& rr_clock_lookup) { + int status = CMD_EXEC_SUCCESS; + + /* Add the global ports which are NOT yet added to the top-level module + * (in different names than the global ports defined in circuit library + */ + std::vector global_ports_to_add; + for (const TileGlobalPortId& tile_global_port : + tile_annotation.global_ports()) { + ModulePortId module_port = module_manager.find_module_port( + top_module, tile_annotation.global_port_name(tile_global_port)); + /* The global port size is derived from the maximum port size among all the + * tile port defintion */ + if (ModulePortId::INVALID() == module_port) { + BasicPort global_port_to_add; + global_port_to_add.set_name( + tile_annotation.global_port_name(tile_global_port)); + size_t max_port_size = 0; + for (const BasicPort& tile_port : + tile_annotation.global_port_tile_ports(tile_global_port)) { + max_port_size = std::max(tile_port.get_width(), max_port_size); + } + global_port_to_add.set_width(max_port_size); + global_ports_to_add.push_back(global_port_to_add); + } + } + + for (const BasicPort& global_port_to_add : global_ports_to_add) { + module_manager.add_port(top_module, global_port_to_add, + ModuleManager::MODULE_GLOBAL_PORT); + } + + /* Add module nets */ + for (const TileGlobalPortId& tile_global_port : + tile_annotation.global_ports()) { + /* Must found one valid port! */ + ModulePortId top_module_port = module_manager.find_module_port( + top_module, tile_annotation.global_port_name(tile_global_port)); + VTR_ASSERT(ModulePortId::INVALID() != top_module_port); + + /* There are two cases when building the nets: + * - If the net will go through a dedicated clock tree network, the net will + * drive an input of a routing block + * - If the net will be directly wired to tiles, the net will drive an input + * of a tile + */ + if (!tile_annotation.global_port_clock_arch_tree_name(tile_global_port) + .empty()) { + status = build_top_module_global_net_from_tile_clock_arch_tree( + module_manager, top_module, top_module_port, rr_graph, device_rr_gsb, + tile_instance_ids, fabric_tile, clk_ntwk, + tile_annotation.global_port_clock_arch_tree_name(tile_global_port), + rr_clock_lookup); + } else { + status = build_top_module_global_net_from_tile_modules( + module_manager, top_module, top_module_port, tile_annotation, + tile_global_port, vpr_device_annotation, grids, tile_instance_ids, fabric_tile); + } + if (status == CMD_EXEC_FATAL_ERROR) { + return status; + } + } + return status; +} /******************************************************************** * Add the tile-level instances to the top module of FPGA fabric @@ -1092,14 +1562,14 @@ int build_top_module_tile_child_instances( /* TODO: Inter-tile direct connections */ } - /* TODO: Add global ports from tile modules: how to connect to clock - architecture and the global port from tile annotation status = - add_top_module_global_ports_from_grid_modules( module_manager, top_module, - tile_annotation, vpr_device_annotation, grids, rr_graph, device_rr_gsb, - cb_instance_ids, grid_instance_ids, clk_ntwk, rr_clock_lookup); if - (CMD_EXEC_FATAL_ERROR == status) { return status; + /* TODO: Add global ports from tile modules: how to connect to clock architecture and the global port from tile annotation + */ + status = add_top_module_global_ports_from_tile_modules( module_manager, top_module, + tile_annotation, vpr_device_annotation, grids, rr_graph, device_rr_gsb, + tile_instance_ids, fabric_tile, clk_ntwk, rr_clock_lookup); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; } - */ /* Add GPIO ports from the sub-modules under this Verilog module * For top-level module, we follow a special sequencing for I/O modules. So we From 1ee7448070562dcd725cc4e865bbf6a6b69e6695 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 23 Jul 2023 13:38:16 -0700 Subject: [PATCH 206/391] [core] supporting tile annotation (for global port) in tile modules --- .../build_top_module_child_tile_instance.cpp | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) 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 6f8f63730..fc4c6acff 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1187,19 +1187,29 @@ static int build_top_module_global_net_for_given_tile_module( const TileGlobalPortId& tile_global_port, const BasicPort& tile_port_to_connect, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const vtr::Point& grid_coordinate, const e_side& border_side, - const vtr::Matrix& grid_instance_ids) { + const vtr::Point& grid_coordinate, + const vtr::Matrix& tile_instance_ids, + const FabricTile& fabric_tile) { + /* Get the tile module and instance */ + FabricTileId curr_fabric_tile_id = fabric_tile.find_tile_by_pb_coordinate(grid_coordinate); + vtr::Point curr_fabric_tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); + FabricTileId unique_fabric_tile_id = fabric_tile.unique_tile(curr_fabric_tile_coord); + vtr::Point unique_fabric_tile_coord = fabric_tile.tile_coordinate(unique_fabric_tile_id); + std::string tile_module_name = generate_tile_module_name(unique_fabric_tile_coord); + ModuleId tile_module = module_manager.find_module(tile_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(tile_module)); + size_t tile_instance = tile_instance_ids[curr_fabric_tile_coord.x()][curr_fabric_tile_coord.y()]; + + /* Get the grid coordinate in the context of the tile */ + size_t pb_idx_in_curr_fabric_tile = fabric_tile.find_pb_index_in_tile(curr_fabric_tile_id, grid_coordinate); + vtr::Point pb_coord_in_unique_fabric_tile = fabric_tile.pb_coordinates(unique_fabric_tile_id)[pb_idx_in_curr_fabric_tile]; + t_physical_tile_type_ptr physical_tile = grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); /* Find the module name for this type of grid */ std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); - std::string grid_module_name = generate_grid_block_module_name( - grid_module_name_prefix, std::string(physical_tile->name), - is_io_type(physical_tile), border_side); - ModuleId grid_module = module_manager.find_module(grid_module_name); - VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); - size_t grid_instance = - grid_instance_ids[grid_coordinate.x()][grid_coordinate.y()]; + std::string grid_instance_name = generate_grid_block_module_name_in_top_module( + grid_module_name_prefix, grids, pb_coord_in_unique_fabric_tile); /* Find the source port at the top-level module */ BasicPort src_port = module_manager.module_port(top_module, top_module_port); @@ -1277,14 +1287,15 @@ static int build_top_module_global_net_for_given_tile_module( std::string grid_port_name = generate_grid_port_name(grid_pin_width, grid_pin_height, subtile_index, pin_side, grid_pin_info); - ModulePortId grid_port_id = - module_manager.find_module_port(grid_module, grid_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id(grid_module, - grid_port_id)); + std::string tile_grid_port_name = generate_tile_module_port_name(grid_instance_name, grid_port_name); + ModulePortId tile_grid_port_id = + module_manager.find_module_port(grid_module, tile_grid_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(tile_module, + tile_port_id)); VTR_ASSERT( 1 == - module_manager.module_port(grid_module, grid_port_id).get_width()); + module_manager.module_port(tile_module, tile_grid_port_id).get_width()); ModuleNetId net = create_module_source_pin_net( module_manager, top_module, top_module, 0, top_module_port, @@ -1293,9 +1304,9 @@ static int build_top_module_global_net_for_given_tile_module( /* Configure the net sink */ BasicPort sink_port = - module_manager.module_port(grid_module, grid_port_id); - module_manager.add_module_net_sink(top_module, net, grid_module, - grid_instance, grid_port_id, + module_manager.module_port(tile_module, tile_port_id); + module_manager.add_module_net_sink(top_module, net, tile_module, + tile_instance, tile_grid_port_id, sink_port.pins()[0]); } } From 0b3b7b5472043cec2f4650c9e04ffe3c25616e6f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 23 Jul 2023 13:39:06 -0700 Subject: [PATCH 207/391] [core] hotfix --- openfpga/src/fabric/build_top_module_child_tile_instance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 fc4c6acff..e6cac8c52 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1449,7 +1449,7 @@ static int build_top_module_global_net_from_tile_modules( status = build_top_module_global_net_for_given_tile_module( module_manager, top_module, top_module_port, tile_annotation, tile_global_port, tile_port, vpr_device_annotation, grids, - io_coordinate, io_side, grid_instance_ids); + io_coordinate, io_side, tile_instance_ids, fabric_tile); if (CMD_EXEC_FATAL_ERROR == status) { return status; } From 14666f3ae5305150f9942a590eb0ef146c1d6447 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 23 Jul 2023 20:45:59 -0700 Subject: [PATCH 208/391] [core] sync --- openfpga/src/fabric/build_top_module_child_tile_instance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e6cac8c52..eed5c4c9e 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1402,7 +1402,7 @@ static int build_top_module_global_net_from_tile_modules( status = build_top_module_global_net_for_given_tile_module( module_manager, top_module, top_module_port, tile_annotation, tile_global_port, tile_port, vpr_device_annotation, grids, - vtr::Point(ix, iy), NUM_SIDES, grid_instance_ids); + vtr::Point(ix, iy), NUM_SIDES, tile_instance_ids, fabric_tile); if (CMD_EXEC_FATAL_ERROR == status) { return status; } From f551188d0f8807b4811493dc062cf413940cb372 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 23 Jul 2023 21:45:45 -0700 Subject: [PATCH 209/391] [core] developed tile directs to support tile modules --- openfpga/src/fabric/build_tile_modules.cpp | 2 +- openfpga/src/fabric/build_top_module.cpp | 2 +- .../build_top_module_child_tile_instance.cpp | 224 +++++++++++++++++- .../build_top_module_child_tile_instance.h | 2 + 4 files changed, 225 insertions(+), 5 deletions(-) diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index d4bf74fc1..78b2fff75 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -741,6 +741,7 @@ static int build_tile_module_port_and_nets_between_sb_and_cb( *block is the only submodule under the tile. Also, it could happen that the *programmable block drives or is driven by other blocks from another tile. As a *result, we should build the nets for these unconnected pins + * Note that direct connections, e.g., carry chain is not handled at tile-level. It will be handled in the top-level module, which should not case a negative impact on the physical design. * * +------------+ * | |-->grid_xxx @@ -756,7 +757,6 @@ static int build_tile_port_and_nets_from_pb( const std::vector& pb_instances, const size_t& ipb, const bool& frame_view, const bool& verbose) { - /* FIXME: Should consider tile direct connections here! */ size_t pb_instance = pb_instances[ipb]; t_physical_tile_type_ptr phy_tile = grids.get_physical_type(pb_coord.x(), pb_coord.y()); diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index 389b6091b..e3c01a083 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -80,7 +80,7 @@ int build_top_module( /* 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, grids, - fabric_tile, config_protocol, fabric_key, frame_view, verbose); + fabric_tile, config_protocol, fabric_key, frame_view, tile_direct, arch_direct, verbose); } if (status != CMD_EXEC_SUCCESS) { 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 eed5c4c9e..48590e054 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1536,6 +1536,219 @@ static int add_top_module_global_ports_from_tile_modules( return status; } +/******************************************************************** + * Add module net for one direction connection between two CLBs or + * two grids + * This function will + * 1. find the pin id and port id of the source clb port in module manager + * 2. find the pin id and port id of the destination clb port in module manager + * 3. add a direct connection module to the top module + * 4. add a first module net and configure its source and sink, + * in order to connect the source pin to the input of the top module + * 4. add a second module net and configure its source and sink, + * in order to connect the sink pin to the output of the top module + *******************************************************************/ +static void add_module_nets_connect_tile_direct_connection( + ModuleManager& module_manager, const ModuleId& top_module, + const CircuitLibrary& circuit_lib, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const vtr::Matrix& tile_instance_ids, + const FabricTile& fabric_tile, + const TileDirect& tile_direct, + const TileDirectId& tile_direct_id, const ArchDirect& arch_direct) { + vtr::Point device_size(grids.width(), grids.height()); + std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); + + /* Find the module name of source clb */ + vtr::Point src_clb_coord = + tile_direct.from_tile_coordinate(tile_direct_id); + t_physical_tile_type_ptr src_grid_type = + grids.get_physical_type(src_clb_coord.x(), src_clb_coord.y()); + FabricTileId src_tile_id = fabric_tile.find_tile_by_pb_coordinate(src_clb_coord); + vtr::Point src_tile_coord = fabric_tile.tile_coordinate(src_tile_id); + FabricTileId src_unique_tile_id = fabric_tile.unique_tile(src_tile_coord); + vtr::Point src_unique_tile_coord = fabric_tile.tile_coordinate(src_unique_tile_id); + std::string src_module_name = generate_tile_module_name(src_unique_tile_coord); + ModuleId src_tile_module = module_manager.find_module(src_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(src_tile_module)); + /* Record the instance id */ + size_t src_tile_instance = + tile_instance_ids[src_tile_coord.x()][src_tile_coord.y()]; + /* Grid instance name in the unique tile */ + size_t pb_idx_in_src_tile = fabric_tile.find_pb_index_in_tile(src_tile_id, src_clb_coord); + vtr::Point pb_coord_in_unique_src_tile = fabric_tile.pb_coordinates(src_unique_tile_id)[pb_idx_in_src_tile]; + std::string src_grid_instance_name = generate_grid_block_module_name_in_top_module( + grid_module_name_prefix, grids, pb_coord_in_unique_src_tile); + + /* Find the module name of sink clb */ + vtr::Point des_clb_coord = + tile_direct.to_tile_coordinate(tile_direct_id); + t_physical_tile_type_ptr sink_grid_type = + grids.get_physical_type(des_clb_coord.x(), des_clb_coord.y()); + FabricTileId des_tile_id = fabric_tile.find_tile_by_pb_coordinate(des_clb_coord); + vtr::Point des_tile_coord = fabric_tile.tile_coordinate(des_tile_id); + FabricTileId des_unique_tile_id = fabric_tile.unique_tile(des_tile_coord); + vtr::Point des_unique_tile_coord = fabric_tile.tile_coordinate(des_unique_tile_id); + std::string des_module_name = generate_tile_module_name(des_unique_tile_coord); + ModuleId des_tile_module = module_manager.find_module(des_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(des_tile_module)); + /* Record the instance id */ + size_t des_tile_instance = + tile_instance_ids[des_tile_coord.x()][des_tile_coord.y()]; + /* Grid instance name in the unique tile */ + size_t pb_idx_in_des_tile = fabric_tile.find_pb_index_in_tile(des_tile_id, des_clb_coord); + vtr::Point pb_coord_in_unique_des_tile = fabric_tile.pb_coordinates(des_unique_tile_id)[pb_idx_in_des_tile]; + std::string des_grid_instance_name = generate_grid_block_module_name_in_top_module( + grid_module_name_prefix, grids, pb_coord_in_unique_des_tile); + + /* Find the module id of a direct connection module */ + CircuitModelId direct_circuit_model = + arch_direct.circuit_model(tile_direct.arch_direct(tile_direct_id)); + std::string direct_module_name = circuit_lib.model_name(direct_circuit_model); + ModuleId direct_module = module_manager.find_module(direct_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(direct_module)); + + /* Find inputs and outputs of the direct circuit module */ + std::vector direct_input_ports = + circuit_lib.model_ports_by_type(direct_circuit_model, + CIRCUIT_MODEL_PORT_INPUT, true); + VTR_ASSERT(1 == direct_input_ports.size()); + ModulePortId direct_input_port_id = module_manager.find_module_port( + direct_module, circuit_lib.port_prefix(direct_input_ports[0])); + VTR_ASSERT(true == module_manager.valid_module_port_id(direct_module, + direct_input_port_id)); + VTR_ASSERT(1 == + module_manager.module_port(direct_module, direct_input_port_id) + .get_width()); + + std::vector direct_output_ports = + circuit_lib.model_ports_by_type(direct_circuit_model, + CIRCUIT_MODEL_PORT_OUTPUT, true); + VTR_ASSERT(1 == direct_output_ports.size()); + ModulePortId direct_output_port_id = module_manager.find_module_port( + direct_module, circuit_lib.port_prefix(direct_output_ports[0])); + VTR_ASSERT(true == module_manager.valid_module_port_id( + direct_module, direct_output_port_id)); + VTR_ASSERT(1 == + module_manager.module_port(direct_module, direct_output_port_id) + .get_width()); + + /* Generate the pin name of source port/pin in the grid */ + e_side src_pin_grid_side = tile_direct.from_tile_side(tile_direct_id); + size_t src_tile_pin = tile_direct.from_tile_pin(tile_direct_id); + + t_physical_tile_type_ptr src_grid_type_descriptor = + grids.get_physical_type(src_clb_coord.x(), src_clb_coord.y()); + size_t src_pin_width = + src_grid_type_descriptor->pin_width_offset[src_tile_pin]; + size_t src_pin_height = + src_grid_type_descriptor->pin_height_offset[src_tile_pin]; + + BasicPort src_pin_info = vpr_device_annotation.physical_tile_pin_port_info( + src_grid_type_descriptor, src_tile_pin); + VTR_ASSERT(true == src_pin_info.is_valid()); + int src_subtile_index = vpr_device_annotation.physical_tile_pin_subtile_index( + src_grid_type_descriptor, src_tile_pin); + VTR_ASSERT(OPEN != src_subtile_index && + src_subtile_index < src_grid_type_descriptor->capacity); + std::string src_port_name = + generate_grid_port_name(src_pin_width, src_pin_height, src_subtile_index, + src_pin_grid_side, src_pin_info); + src_port_name = generate_tile_module_port_name(src_grid_instance_name, src_port_name); + ModulePortId src_port_id = + module_manager.find_module_port(src_tile_module, src_port_name); + if (true != + module_manager.valid_module_port_id(src_tile_module, src_port_id)) { + VTR_LOG_ERROR("Fail to find port '%s[%lu][%lu].%s'\n", + src_module_name.c_str(), src_tile_coord.x(), src_tile_coord.y(), + src_port_name.c_str()); + } + VTR_ASSERT(true == + module_manager.valid_module_port_id(src_tile_module, src_port_id)); + VTR_ASSERT( + 1 == module_manager.module_port(src_tile_module, src_port_id).get_width()); + + /* Generate the pin name of sink port/pin in the grid */ + e_side sink_pin_grid_side = tile_direct.to_tile_side(tile_direct_id); + size_t sink_tile_pin = tile_direct.to_tile_pin(tile_direct_id); + + t_physical_tile_type_ptr sink_grid_type_descriptor = + grids.get_physical_type(des_clb_coord.x(), des_clb_coord.y()); + size_t sink_pin_width = + sink_grid_type_descriptor->pin_width_offset[src_tile_pin]; + size_t sink_pin_height = + sink_grid_type_descriptor->pin_height_offset[src_tile_pin]; + + BasicPort sink_pin_info = vpr_device_annotation.physical_tile_pin_port_info( + sink_grid_type_descriptor, sink_tile_pin); + VTR_ASSERT(true == sink_pin_info.is_valid()); + int sink_subtile_index = + vpr_device_annotation.physical_tile_pin_subtile_index( + sink_grid_type_descriptor, sink_tile_pin); + VTR_ASSERT(OPEN != src_subtile_index && + src_subtile_index < sink_grid_type_descriptor->capacity); + std::string sink_port_name = + generate_grid_port_name(sink_pin_width, sink_pin_height, sink_subtile_index, + sink_pin_grid_side, sink_pin_info); + sink_port_name = generate_tile_module_port_name(des_grid_instance_name, sink_port_name); + ModulePortId sink_port_id = + module_manager.find_module_port(sink_tile_module, sink_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(sink_tile_module, + sink_port_id)); + VTR_ASSERT( + 1 == + module_manager.module_port(sink_tile_module, sink_port_id).get_width()); + + /* Add a submodule of direct connection module to the top-level module */ + size_t direct_instance_id = + module_manager.num_instance(top_module, direct_module); + module_manager.add_child_module(top_module, direct_module, false); + + /* Create the 1st module net */ + ModuleNetId net_direct_src = module_manager.create_module_net(top_module); + /* Connect the wire between src_pin of clb and direct_instance input*/ + module_manager.add_module_net_source(top_module, net_direct_src, + src_tile_module, src_tile_instance, + src_port_id, 0); + module_manager.add_module_net_sink(top_module, net_direct_src, direct_module, + direct_instance_id, direct_input_port_id, + 0); + + /* Create the 2nd module net + * Connect the wire between direct_instance output and sink_pin of clb + */ + ModuleNetId net_direct_sink = + create_module_source_pin_net(module_manager, top_module, direct_module, + direct_instance_id, direct_output_port_id, 0); + module_manager.add_module_net_sink(top_module, net_direct_sink, + des_tile_module, des_tile_instance, + sink_port_id, 0); +} + +/******************************************************************** + * Add module net of clb-to-clb direct connections to module manager + * Note that the direct connections are not limited to CLBs only. + * It can be more generic and thus cover all the grid types, + * such as heterogeneous blocks + *******************************************************************/ +static void add_top_module_nets_connect_tile_direct_connections( + ModuleManager& module_manager, const ModuleId& top_module, + const CircuitLibrary& circuit_lib, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const vtr::Matrix& tile_instance_ids, + const FabricTile& fabric_tile, + const TileDirect& tile_direct, + const ArchDirect& arch_direct) { + vtr::ScopedStartFinishTimer timer( + "Add module nets for inter-tile connections"); + + for (const TileDirectId& tile_direct_id : tile_direct.directs()) { + add_module_nets_connect_tile_direct_connection( + module_manager, top_module, circuit_lib, vpr_device_annotation, grids, + tile_instance_ids, fabric_tile, tile_direct, tile_direct_id, arch_direct); + } +} + /******************************************************************** * Add the tile-level instances to the top module of FPGA fabric * and build connects between them @@ -1546,6 +1759,8 @@ int build_top_module_tile_child_instances( const CircuitLibrary& circuit_lib, const DeviceGrid& grids, const FabricTile& fabric_tile, const ConfigProtocol& config_protocol, const FabricKey& fabric_key, const bool& frame_view, + const TileDirect& tile_direct, + const ArchDirect& arch_direct, const bool& verbose) { int status = CMD_EXEC_SUCCESS; vtr::Matrix tile_instance_ids; @@ -1559,11 +1774,11 @@ int build_top_module_tile_child_instances( add_top_module_tile_io_children(module_manager, top_module, grids, fabric_tile, tile_instance_ids); - /* TODO: Build the nets between tiles */ + /* Build the nets between tiles */ if (false == frame_view) { /* Reserve nets to be memory efficient */ reserve_module_manager_module_nets(module_manager, top_module); - /* TODO: Regular nets between tiles */ + /* Regular nets between tiles */ status = add_top_module_nets_connect_tiles( module_manager, top_module, vpr_device_annotation, grids, tile_instance_ids, rr_graph, device_rr_gsb, fabric_tile, verbose); @@ -1571,9 +1786,12 @@ int build_top_module_tile_child_instances( return CMD_EXEC_FATAL_ERROR; } /* TODO: Inter-tile direct connections */ + add_top_module_nets_connect_tile_direct_connections( + module_manager, top_module, circuit_lib, vpr_device_annotation, grids, + tile_instance_ids, fabric_tile, tile_direct, arch_direct); } - /* TODO: Add global ports from tile modules: how to connect to clock architecture and the global port from tile annotation + /* Add global ports from tile modules: how to connect to clock architecture and the global port from tile annotation */ status = add_top_module_global_ports_from_tile_modules( module_manager, top_module, tile_annotation, vpr_device_annotation, grids, rr_graph, device_rr_gsb, 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 6fc5461e3..f5cc7ca8f 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.h +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.h @@ -38,6 +38,8 @@ int build_top_module_tile_child_instances( const CircuitLibrary& circuit_lib, const DeviceGrid& grids, const FabricTile& fabric_tile, const ConfigProtocol& config_protocol, const FabricKey& fabric_key, const bool& frame_view, + const TileDirect& tile_direct, + const ArchDirect& arch_direct, const bool& verbose); } /* end namespace openfpga */ From f03114895926c90d333146a2356331ae45b2ca9f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 23 Jul 2023 22:39:36 -0700 Subject: [PATCH 210/391] [core] syntax --- openfpga/src/fabric/build_top_module.cpp | 4 ++-- .../build_top_module_child_tile_instance.cpp | 16 ++++++++++------ .../build_top_module_child_tile_instance.h | 10 +++++++--- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index e3c01a083..898159431 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -79,8 +79,8 @@ int build_top_module( } 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, grids, - fabric_tile, config_protocol, fabric_key, frame_view, tile_direct, arch_direct, verbose); + 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); } if (status != CMD_EXEC_SUCCESS) { 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 48590e054..119ed3e34 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1692,12 +1692,12 @@ static void add_module_nets_connect_tile_direct_connection( sink_pin_grid_side, sink_pin_info); sink_port_name = generate_tile_module_port_name(des_grid_instance_name, sink_port_name); ModulePortId sink_port_id = - module_manager.find_module_port(sink_tile_module, sink_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id(sink_tile_module, + module_manager.find_module_port(des_tile_module, sink_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(des_tile_module, sink_port_id)); VTR_ASSERT( 1 == - module_manager.module_port(sink_tile_module, sink_port_id).get_width()); + module_manager.module_port(des_tile_module, sink_port_id).get_width()); /* Add a submodule of direct connection module to the top-level module */ size_t direct_instance_id = @@ -1756,11 +1756,15 @@ static void add_top_module_nets_connect_tile_direct_connections( int build_top_module_tile_child_instances( ModuleManager& module_manager, const ModuleId& top_module, MemoryBankShiftRegisterBanks& blwl_sr_banks, - const CircuitLibrary& circuit_lib, const DeviceGrid& grids, + const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, + const RRClockSpatialLookup& rr_clock_lookup, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, + 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 TileDirect& tile_direct, - const ArchDirect& arch_direct, const bool& verbose) { int status = CMD_EXEC_SUCCESS; vtr::Matrix tile_instance_ids; 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 f5cc7ca8f..d0b03bb05 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.h +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.h @@ -35,11 +35,15 @@ namespace openfpga { int build_top_module_tile_child_instances( ModuleManager& module_manager, const ModuleId& top_module, MemoryBankShiftRegisterBanks& blwl_sr_banks, - const CircuitLibrary& circuit_lib, const DeviceGrid& grids, + const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, + const RRClockSpatialLookup& rr_clock_lookup, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, + 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 TileDirect& tile_direct, - const ArchDirect& arch_direct, const bool& verbose); } /* end namespace openfpga */ From da36b735c6dc9457430f3baf56b2db309d53a837 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 24 Jul 2023 12:13:45 -0700 Subject: [PATCH 211/391] [core] syntax --- openfpga/src/annotation/fabric_tile.cpp | 62 +- openfpga/src/annotation/fabric_tile.h | 37 +- openfpga/src/base/openfpga_naming.cpp | 6 +- openfpga/src/base/openfpga_naming.h | 3 +- openfpga/src/fabric/build_tile_modules.cpp | 66 ++- openfpga/src/fabric/build_top_module.cpp | 6 +- openfpga/src/fabric/build_top_module.h | 3 +- .../build_top_module_child_tile_instance.cpp | 537 ++++++++++-------- .../build_top_module_child_tile_instance.h | 8 +- .../src/fabric/build_top_module_memory.cpp | 2 +- openfpga/src/fabric/build_top_module_memory.h | 4 + .../src/fabric/build_top_module_utils.cpp | 11 +- openfpga/src/fabric/build_top_module_utils.h | 7 +- 13 files changed, 439 insertions(+), 313 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index cfe3985c4..39bac50fc 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -78,17 +78,20 @@ FabricTileId FabricTile::find_tile(const vtr::Point& coord) const { return tile_coord2id_lookup_[coord.x()][coord.y()]; } -FabricTileId FabricTile::find_tile_by_pb_coordinate(const vtr::Point& coord) const { +FabricTileId FabricTile::find_tile_by_pb_coordinate( + const vtr::Point& coord) const { if (coord.x() >= pb_coord2id_lookup_.size()) { VTR_LOG_ERROR( - "Programmable block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + "Programmable block coordinate [%lu][%lu] exceeds the maximum range " + "[%lu][%lu]!\n", coord.x(), coord.y(), pb_coord2id_lookup_.size(), pb_coord2id_lookup_[0].size()); return FabricTileId::INVALID(); } if (coord.y() >= pb_coord2id_lookup_[coord.x()].size()) { VTR_LOG_ERROR( - "Programmable block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + "Programmable block coordinate [%lu][%lu] exceeds the maximum range " + "[%lu][%lu]!\n", coord.x(), coord.y(), pb_coord2id_lookup_.size(), pb_coord2id_lookup_[0].size()); return FabricTileId::INVALID(); @@ -96,19 +99,22 @@ FabricTileId FabricTile::find_tile_by_pb_coordinate(const vtr::Point& co return pb_coord2id_lookup_[coord.x()][coord.y()]; } -FabricTileId FabricTile::find_tile_by_cb_coordinate(const t_rr_type& cb_type, const vtr::Point& coord) const { +FabricTileId FabricTile::find_tile_by_cb_coordinate( + const t_rr_type& cb_type, const vtr::Point& coord) const { switch (cb_type) { case CHANX: { if (coord.x() >= cbx_coord2id_lookup_.size()) { VTR_LOG_ERROR( - "X-direction connection block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + "X-direction connection block coordinate [%lu][%lu] exceeds the " + "maximum range [%lu][%lu]!\n", coord.x(), coord.y(), cbx_coord2id_lookup_.size(), cbx_coord2id_lookup_[0].size()); return FabricTileId::INVALID(); } if (coord.y() >= cbx_coord2id_lookup_[coord.x()].size()) { VTR_LOG_ERROR( - "X-direction connection block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + "X-direction connection block coordinate [%lu][%lu] exceeds the " + "maximum range [%lu][%lu]!\n", coord.x(), coord.y(), cbx_coord2id_lookup_.size(), cbx_coord2id_lookup_[0].size()); return FabricTileId::INVALID(); @@ -118,14 +124,16 @@ FabricTileId FabricTile::find_tile_by_cb_coordinate(const t_rr_type& cb_type, co case CHANY: { if (coord.x() >= cby_coord2id_lookup_.size()) { VTR_LOG_ERROR( - "Y-direction connection block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + "Y-direction connection block coordinate [%lu][%lu] exceeds the " + "maximum range [%lu][%lu]!\n", coord.x(), coord.y(), cby_coord2id_lookup_.size(), cby_coord2id_lookup_[0].size()); return FabricTileId::INVALID(); } if (coord.y() >= cby_coord2id_lookup_[coord.x()].size()) { VTR_LOG_ERROR( - "Y-direction connection block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + "Y-direction connection block coordinate [%lu][%lu] exceeds the " + "maximum range [%lu][%lu]!\n", coord.x(), coord.y(), cby_coord2id_lookup_.size(), cby_coord2id_lookup_[0].size()); return FabricTileId::INVALID(); @@ -138,17 +146,20 @@ FabricTileId FabricTile::find_tile_by_cb_coordinate(const t_rr_type& cb_type, co } } -FabricTileId FabricTile::find_tile_by_sb_coordinate(const vtr::Point& coord) const { +FabricTileId FabricTile::find_tile_by_sb_coordinate( + const vtr::Point& coord) const { if (coord.x() >= sb_coord2id_lookup_.size()) { VTR_LOG_ERROR( - "Switch block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + "Switch block coordinate [%lu][%lu] exceeds the maximum range " + "[%lu][%lu]!\n", coord.x(), coord.y(), sb_coord2id_lookup_.size(), sb_coord2id_lookup_[0].size()); return FabricTileId::INVALID(); } if (coord.y() >= sb_coord2id_lookup_[coord.x()].size()) { VTR_LOG_ERROR( - "Switch block coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n", + "Switch block coordinate [%lu][%lu] exceeds the maximum range " + "[%lu][%lu]!\n", coord.x(), coord.y(), sb_coord2id_lookup_.size(), sb_coord2id_lookup_[0].size()); return FabricTileId::INVALID(); @@ -327,7 +338,7 @@ bool FabricTile::register_tile_in_lookup(const FabricTileId& tile_id, } bool FabricTile::register_pb_in_lookup(const FabricTileId& tile_id, - const vtr::Point& coord) { + const vtr::Point& coord) { if (coord.x() >= pb_coord2id_lookup_.size()) { VTR_LOG_ERROR( "Fast look-up has not been re-allocated properly! Given x='%lu' exceeds " @@ -344,7 +355,8 @@ bool FabricTile::register_pb_in_lookup(const FabricTileId& tile_id, } /* Throw error if this coord is already registered! */ if (pb_coord2id_lookup_[coord.x()][coord.y()]) { - VTR_LOG_ERROR("Programmable block at [%lu][%lu] has already been registered!\n"); + VTR_LOG_ERROR( + "Programmable block at [%lu][%lu] has already been registered!\n"); return false; } pb_coord2id_lookup_[coord.x()][coord.y()] = tile_id; @@ -353,7 +365,7 @@ bool FabricTile::register_pb_in_lookup(const FabricTileId& tile_id, } bool FabricTile::register_cbx_in_lookup(const FabricTileId& tile_id, - const vtr::Point& coord) { + const vtr::Point& coord) { if (coord.x() >= cbx_coord2id_lookup_.size()) { VTR_LOG_ERROR( "Fast look-up has not been re-allocated properly! Given x='%lu' exceeds " @@ -370,7 +382,9 @@ bool FabricTile::register_cbx_in_lookup(const FabricTileId& tile_id, } /* Throw error if this coord is already registered! */ if (cbx_coord2id_lookup_[coord.x()][coord.y()]) { - VTR_LOG_ERROR("X-direction connection block at [%lu][%lu] has already been registered!\n"); + VTR_LOG_ERROR( + "X-direction connection block at [%lu][%lu] has already been " + "registered!\n"); return false; } cbx_coord2id_lookup_[coord.x()][coord.y()] = tile_id; @@ -379,7 +393,7 @@ bool FabricTile::register_cbx_in_lookup(const FabricTileId& tile_id, } bool FabricTile::register_cby_in_lookup(const FabricTileId& tile_id, - const vtr::Point& coord) { + const vtr::Point& coord) { if (coord.x() >= cby_coord2id_lookup_.size()) { VTR_LOG_ERROR( "Fast look-up has not been re-allocated properly! Given x='%lu' exceeds " @@ -396,7 +410,9 @@ bool FabricTile::register_cby_in_lookup(const FabricTileId& tile_id, } /* Throw error if this coord is already registered! */ if (cby_coord2id_lookup_[coord.x()][coord.y()]) { - VTR_LOG_ERROR("Y-direction connection block at [%lu][%lu] has already been registered!\n"); + VTR_LOG_ERROR( + "Y-direction connection block at [%lu][%lu] has already been " + "registered!\n"); return false; } cby_coord2id_lookup_[coord.x()][coord.y()] = tile_id; @@ -405,7 +421,7 @@ bool FabricTile::register_cby_in_lookup(const FabricTileId& tile_id, } bool FabricTile::register_sb_in_lookup(const FabricTileId& tile_id, - const vtr::Point& coord) { + const vtr::Point& coord) { if (coord.x() >= sb_coord2id_lookup_.size()) { VTR_LOG_ERROR( "Fast look-up has not been re-allocated properly! Given x='%lu' exceeds " @@ -462,8 +478,8 @@ bool FabricTile::set_tile_coordinate(const FabricTileId& tile_id, } int FabricTile::add_pb_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord, - const vtr::Point& gsb_coord) { + const vtr::Point& coord, + const vtr::Point& gsb_coord) { VTR_ASSERT(valid_tile_id(tile_id)); pb_coords_[tile_id].push_back(coord); pb_gsb_coords_[tile_id].push_back(gsb_coord); @@ -472,8 +488,8 @@ int FabricTile::add_pb_coordinate(const FabricTileId& tile_id, } int FabricTile::add_cb_coordinate(const FabricTileId& tile_id, - const t_rr_type& cb_type, - const vtr::Point& coord) { + const t_rr_type& cb_type, + const vtr::Point& coord) { VTR_ASSERT(valid_tile_id(tile_id)); switch (cb_type) { case CHANX: @@ -491,7 +507,7 @@ int FabricTile::add_cb_coordinate(const FabricTileId& tile_id, } int FabricTile::add_sb_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord) { + const vtr::Point& coord) { VTR_ASSERT(valid_tile_id(tile_id)); sb_coords_[tile_id].push_back(coord); /* Register in fast look-up */ diff --git a/openfpga/src/annotation/fabric_tile.h b/openfpga/src/annotation/fabric_tile.h index dee4a783a..7f2091637 100644 --- a/openfpga/src/annotation/fabric_tile.h +++ b/openfpga/src/annotation/fabric_tile.h @@ -36,12 +36,18 @@ class FabricTile { FabricTileId unique_tile(const vtr::Point& coord) const; /** @brief Find the tile info with a given coordinate */ FabricTileId find_tile(const vtr::Point& coord) const; - /** @brief Find the id of a tile, with a given coordinate of the programmable block under the tile */ - FabricTileId find_tile_by_pb_coordinate(const vtr::Point& coord) const; - /** @brief Find the id of a tile, with a given coordinate of the connection block under the tile */ - FabricTileId find_tile_by_cb_coordinate(const t_rr_type& cb_type, const vtr::Point& coord) const; - /** @brief Find the id of a tile, with a given coordinate of the switch block under the tile */ - FabricTileId find_tile_by_sb_coordinate(const vtr::Point& coord) const; + /** @brief Find the id of a tile, with a given coordinate of the programmable + * block under the tile */ + FabricTileId find_tile_by_pb_coordinate( + const vtr::Point& coord) const; + /** @brief Find the id of a tile, with a given coordinate of the connection + * block under the tile */ + FabricTileId find_tile_by_cb_coordinate( + const t_rr_type& cb_type, const vtr::Point& coord) const; + /** @brief Find the id of a tile, with a given coordinate of the switch block + * under the tile */ + FabricTileId find_tile_by_sb_coordinate( + const vtr::Point& coord) const; /** @brief Find the coordinate of the unique tile w.r.t the tile with a tile * id */ vtr::Point unique_tile_coordinate(const FabricTileId& tile_id) const; @@ -82,12 +88,12 @@ class FabricTile { bool set_tile_coordinate(const FabricTileId& tile_id, const vtr::Point& coord); int add_pb_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord, - const vtr::Point& gsb_coord); + const vtr::Point& coord, + const vtr::Point& gsb_coord); int add_cb_coordinate(const FabricTileId& tile_id, const t_rr_type& cb_type, - const vtr::Point& coord); + const vtr::Point& coord); int add_sb_coordinate(const FabricTileId& tile_id, - const vtr::Point& coord); + const vtr::Point& coord); /** @brief Build a list of unique tiles by comparing the coordinates in * DeviceRRGSB */ void build_unique_tiles(); @@ -118,13 +124,13 @@ class FabricTile { bool register_tile_in_lookup(const FabricTileId& tile_id, const vtr::Point& coord); bool register_pb_in_lookup(const FabricTileId& tile_id, - const vtr::Point& coord); + const vtr::Point& coord); bool register_cbx_in_lookup(const FabricTileId& tile_id, - const vtr::Point& coord); + const vtr::Point& coord); bool register_cby_in_lookup(const FabricTileId& tile_id, - const vtr::Point& coord); + const vtr::Point& coord); bool register_sb_in_lookup(const FabricTileId& tile_id, - const vtr::Point& coord); + const vtr::Point& coord); private: /* Internal Data */ vtr::vector ids_; @@ -142,7 +148,8 @@ class FabricTile { vtr::vector>> cbx_coords_; vtr::vector>> cby_coords_; vtr::vector>> sb_coords_; - /* A few fast lookup to spot tile by coordinate of programmable blocks, connection blocks and switch blocks */ + /* A few fast lookup to spot tile by coordinate of programmable blocks, + * connection blocks and switch blocks */ std::vector> pb_coord2id_lookup_; std::vector> cbx_coord2id_lookup_; std::vector> cby_coord2id_lookup_; diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index 2551585d2..7153b5b65 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -506,9 +506,11 @@ std::string generate_tile_module_name(const vtr::Point& tile_coord) { } /********************************************************************* - * Generate the port name for a tile. Note that use the index to make the tile port name unique! + * Generate the port name for a tile. Note that use the index to make the tile + *port name unique! *********************************************************************/ -std::string generate_tile_module_port_name(const std::string& prefix, const std::string& port_name) { +std::string generate_tile_module_port_name(const std::string& prefix, + const std::string& port_name) { return prefix + port_name; } diff --git a/openfpga/src/base/openfpga_naming.h b/openfpga/src/base/openfpga_naming.h index 8efe39a68..389e4c951 100644 --- a/openfpga/src/base/openfpga_naming.h +++ b/openfpga/src/base/openfpga_naming.h @@ -112,7 +112,8 @@ std::string generate_connection_block_module_name( std::string generate_tile_module_name(const vtr::Point& tile_coord); -std::string generate_tile_module_port_name(const vtr::Point& tile_coord, const std::string& port_name); +std::string generate_tile_module_port_name(const std::string& prefix, + const std::string& port_name); std::string generate_tile_module_netlist_name(const std::string& block_name, const std::string& postfix); diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 78b2fff75..d979e29c9 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -64,9 +64,9 @@ static int build_tile_module_port_and_nets_between_sb_and_pb( const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, const std::vector& pb_instances, - const std::vector& sb_instances, - const size_t& isb, const bool& compact_routing_hierarchy, - const bool& frame_view, const bool& verbose) { + const std::vector& sb_instances, const size_t& isb, + const bool& compact_routing_hierarchy, const bool& frame_view, + const bool& verbose) { /* Skip those Switch blocks that do not exist */ if (false == rr_gsb.is_sb_exist()) { return CMD_EXEC_SUCCESS; @@ -199,7 +199,10 @@ static int build_tile_module_port_and_nets_between_sb_and_pb( } else { /* Create a port on the tile module and create the net if required. * Create a proper name to avoid naming conflicts */ - src_grid_port.set_name(generate_tile_module_port_name(fabric_tile.sb_coordinates(fabric_tile_id, isb), src_grid_port.get_name())); + src_grid_port.set_name(generate_tile_module_port_name( + generate_switch_block_module_name( + fabric_tile.sb_coordinates(fabric_tile_id)[isb]), + src_grid_port.get_name())); ModulePortId src_tile_port_id = module_manager.add_port( tile_module, src_grid_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); @@ -289,10 +292,9 @@ static int build_tile_module_port_and_nets_between_cb_and_pb( const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, const t_rr_type& cb_type, const std::vector& pb_instances, - const std::vector& cb_instances, - const size_t& icb, - const bool& compact_routing_hierarchy, const bool& frame_view, - const bool& verbose) { + const std::map>& cb_instances, + const size_t& icb, const bool& compact_routing_hierarchy, + const bool& frame_view, const bool& verbose) { size_t cb_instance = cb_instances.at(cb_type)[icb]; /* We could have two different coordinators, one is the instance, the other is * the module */ @@ -420,7 +422,13 @@ static int build_tile_module_port_and_nets_between_cb_and_pb( } else { /* Create a port on the tile module and create the net if required. * FIXME: Create a proper name to avoid naming conflicts */ - src_cb_port.set_name(generate_tile_module_port_name(fabric_tile.cb_coordinates(fabric_tile_id, cb_type, icb), src_cb_port.get_name())); + const RRGSB& cb_inst_rr_gsb = device_rr_gsb.get_gsb( + fabric_tile.cb_coordinates(fabric_tile_id, cb_type)[icb]); + std::string cb_instance_name_in_tile = + generate_connection_block_module_name( + cb_type, cb_inst_rr_gsb.get_cb_coordinate(cb_type)); + src_cb_port.set_name(generate_tile_module_port_name( + cb_instance_name_in_tile, src_cb_port.get_name())); ModulePortId sink_tile_port_id = module_manager.add_port( tile_module, src_cb_port, ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); @@ -493,10 +501,9 @@ static int build_tile_module_port_and_nets_between_sb_and_cb( const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, const std::map>& cb_instances, - const std::vector& sb_instances, - const size_t& isb, - const bool& compact_routing_hierarchy, - const bool& frame_view, const bool& verbose) { + const std::vector& sb_instances, const size_t& isb, + const bool& compact_routing_hierarchy, const bool& frame_view, + const bool& verbose) { size_t sb_instance = sb_instances[isb]; /* We could have two different coordinators, one is the instance, the other is * the module */ @@ -674,7 +681,10 @@ static int build_tile_module_port_and_nets_between_sb_and_cb( module_manager.find_module_port(sb_module_id, chan_input_port_name); BasicPort chan_input_port = module_manager.module_port(sb_module_id, sb_chan_input_port_id); - chan_input_port.set_name(generate_tile_module_port_name(fabric_tile.sb_coordinates(fabric_tile_id, isb), chan_input_port.get_name())); + chan_input_port.set_name(generate_tile_module_port_name( + generate_switch_block_module_name( + fabric_tile.sb_coordinates(fabric_tile_id)[isb]), + chan_input_port.get_name())); ModulePortId tile_chan_input_port_id = module_manager.add_port( tile_module, chan_input_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); @@ -704,7 +714,10 @@ static int build_tile_module_port_and_nets_between_sb_and_cb( module_manager.find_module_port(sb_module_id, chan_output_port_name); BasicPort chan_output_port = module_manager.module_port(sb_module_id, sb_chan_output_port_id); - chan_output_port.set_name(generate_tile_module_port_name(fabric_tile.sb_coordinates(fabric_tile_id, isb), chan_output_port.get_name())); + chan_output_port.set_name(generate_tile_module_port_name( + generate_switch_block_module_name( + fabric_tile.sb_coordinates(fabric_tile_id)[isb]), + chan_output_port.get_name())); ModulePortId tile_chan_output_port_id = module_manager.add_port( tile_module, chan_output_port, ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT); @@ -741,7 +754,9 @@ static int build_tile_module_port_and_nets_between_sb_and_cb( *block is the only submodule under the tile. Also, it could happen that the *programmable block drives or is driven by other blocks from another tile. As a *result, we should build the nets for these unconnected pins - * Note that direct connections, e.g., carry chain is not handled at tile-level. It will be handled in the top-level module, which should not case a negative impact on the physical design. + * Note that direct connections, e.g., carry chain is not handled at tile-level. + *It will be handled in the top-level module, which should not case a negative + *impact on the physical design. * * +------------+ * | |-->grid_xxx @@ -753,10 +768,9 @@ static int build_tile_module_port_and_nets_between_sb_and_cb( static int build_tile_port_and_nets_from_pb( ModuleManager& module_manager, const ModuleId& tile_module, const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, - const vtr::Point& pb_coord, - const std::vector& pb_instances, - const size_t& ipb, - const bool& frame_view, const bool& verbose) { + const vtr::Point& pb_coord, const std::vector& pb_instances, + const FabricTile& fabric_tile, const FabricTileId& curr_fabric_tile_id, + const size_t& ipb, const bool& frame_view, const bool& verbose) { size_t pb_instance = pb_instances[ipb]; t_physical_tile_type_ptr phy_tile = grids.get_physical_type(pb_coord.x(), pb_coord.y()); @@ -828,7 +842,12 @@ static int build_tile_port_and_nets_from_pb( return CMD_EXEC_FATAL_ERROR; } /* Create a proper name to avoid naming conflicts */ - pb_port.set_name(generate_tile_module_port_name(fabric_tile.pb_coordinates(fabric_tile_id, ipb), pb_port.get_name())); + std::string pb_instance_name_in_tile = + generate_grid_block_module_name_in_top_module( + std::string(GRID_MODULE_NAME_PREFIX), grids, + fabric_tile.pb_coordinates(curr_fabric_tile_id)[ipb]); + pb_port.set_name(generate_tile_module_port_name( + pb_instance_name_in_tile, pb_port.get_name())); /* Find the port from the pb module and see if it is already been * driven or driving a net. if not, create a new port at the tile @@ -956,8 +975,7 @@ static int build_tile_module_ports_and_nets( status_code = build_tile_module_port_and_nets_between_cb_and_pb( module_manager, tile_module, grids, vpr_device_annotation, device_rr_gsb, rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, - cb_type, pb_instances, cb_instances, icb, true, frame_view, - verbose); + cb_type, pb_instances, cb_instances, icb, true, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -986,7 +1004,7 @@ static int build_tile_module_ports_and_nets( fabric_tile.pb_coordinates(fabric_tile_id)[ipb]; status_code = build_tile_port_and_nets_from_pb( module_manager, tile_module, grids, vpr_device_annotation, pb_coord, - pb_instances, ipb, frame_view, verbose); + pb_instances, fabric_tile, fabric_tile_id, ipb, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index 898159431..cb98ed8dd 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -79,8 +79,10 @@ int build_top_module( } 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); + 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); } if (status != CMD_EXEC_SUCCESS) { diff --git a/openfpga/src/fabric/build_top_module.h b/openfpga/src/fabric/build_top_module.h index 7ee641072..46fe36219 100644 --- a/openfpga/src/fabric/build_top_module.h +++ b/openfpga/src/fabric/build_top_module.h @@ -44,8 +44,7 @@ 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& verbose); } /* 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 119ed3e34..103cf0148 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -15,6 +15,7 @@ /* Headers from openfpgashell library */ #include "build_module_graph_utils.h" +#include "build_routing_module_utils.h" #include "build_top_module_child_tile_instance.h" #include "build_top_module_connection.h" #include "build_top_module_directs.h" @@ -26,7 +27,9 @@ #include "module_manager_utils.h" #include "openfpga_device_grid_utils.h" #include "openfpga_naming.h" +#include "openfpga_physical_tile_utils.h" #include "openfpga_reserved_words.h" +#include "openfpga_rr_graph_utils.h" #include "rr_gsb_utils.h" /* begin namespace openfpga */ @@ -249,7 +252,8 @@ static void add_top_module_tile_io_children( } /******************************************************************** - * Add module nets to connect a switch block in a given tile to the programmable block in adjacent tiles + * Add module nets to connect a switch block in a given tile to the programmable + *block in adjacent tiles * * TileA | TileB * +------------+ | +------------+ @@ -279,23 +283,24 @@ static void add_top_module_tile_io_children( static int build_top_module_tile_nets_between_sb_and_pb( ModuleManager& module_manager, const ModuleId& top_module, const ModuleId& curr_tile_module, - const vtr::Matrix& tile_instance_ids, - const size_t& curr_tile_instance_id, - const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const vtr::Matrix& tile_instance_ids, + const size_t& curr_tile_instance_id, const DeviceGrid& grids, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& curr_fabric_tile_id, const size_t& sb_idx_in_curr_fabric_tile, - const bool& compact_routing_hierarchy, - const bool& verbose) { + const bool& compact_routing_hierarchy, const bool& verbose) { /* Skip those Switch blocks that do not exist */ if (false == rr_gsb.is_sb_exist()) { return CMD_EXEC_SUCCESS; } - vtr::Point sink_tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); - FabricTileId sink_unique_tile = fabric_tile.unique_tile(curr_fabric_tile_id); - vtr::Point sink_sb_coord_in_unique_tile = fabric_tile.sb_coordinates(src_unique_tile)[sb_idx_in_curr_fabric_tile]; + vtr::Point sink_tile_coord = + fabric_tile.tile_coordinate(curr_fabric_tile_id); + FabricTileId sink_unique_tile = fabric_tile.unique_tile(sink_tile_coord); + vtr::Point sink_sb_coord_in_unique_tile = + fabric_tile.sb_coordinates(sink_unique_tile)[sb_idx_in_curr_fabric_tile]; std::string sink_sb_instance_name_in_unique_tile = generate_switch_block_module_name(sink_sb_coord_in_unique_tile); @@ -336,12 +341,13 @@ static int build_top_module_tile_nets_between_sb_and_pb( continue; } - /* Find the source tile id, coordinate etc., which is required to find source tile module and port - * Relationship between source tile and its unique module - * Take an example: + /* Find the source tile id, coordinate etc., which is required to find + * source tile module and port Relationship between source tile and its + * unique module Take an example: * - * grid_pin name should follow unique module [i0][j0] of src_tile[x0][y0] - * sb_pin name should follow unique module [i1][j1] of des_tile[x1][y1] + * grid_pin name should follow unique module [i0][j0] of + * src_tile[x0][y0] sb_pin name should follow unique module [i1][j1] of + * des_tile[x1][y1] * * However, instance id should follow the origin tiles * @@ -362,12 +368,17 @@ static int build_top_module_tile_nets_between_sb_and_pb( * +------------+ +--------------+ * */ - FabricTileId src_fabric_tile_id = fabric_tile.find_tile_by_pb_coordinate(grid_coordinate); - size_t pb_idx_in_src_fabric_tile = fabric_tile.find_pb_index_in_tile(src_fabric_tile_id, grid_coordinate); - vtr::Point src_tile_coord = fabric_tile.tile_coordinate(src_fabric_tile_id); - vtr::Point src_unique_tile_coord = fabric_tile.unique_tile_coordinate(src_fabric_tile_id); + FabricTileId src_fabric_tile_id = + fabric_tile.find_tile_by_pb_coordinate(grid_coordinate); + size_t pb_idx_in_src_fabric_tile = + fabric_tile.find_pb_index_in_tile(src_fabric_tile_id, grid_coordinate); + vtr::Point src_tile_coord = + fabric_tile.tile_coordinate(src_fabric_tile_id); + vtr::Point src_unique_tile_coord = + fabric_tile.unique_tile_coordinate(src_fabric_tile_id); FabricTileId src_unique_tile = fabric_tile.unique_tile(src_tile_coord); - vtr::Point src_pb_coord_in_unique_tile = fabric_tile.pb_coordinates(src_unique_tile)[pb_idx_in_src_fabric_tile]; + vtr::Point src_pb_coord_in_unique_tile = + fabric_tile.pb_coordinates(src_unique_tile)[pb_idx_in_src_fabric_tile]; std::string src_tile_module_name = generate_tile_module_name(src_unique_tile_coord); ModuleId src_tile_module = @@ -378,16 +389,21 @@ static int build_top_module_tile_nets_between_sb_and_pb( size_t src_grid_pin_index = rr_graph.node_pin_num( rr_gsb.get_opin_node(side_manager.get_side(), inode)); - std::string src_grid_port_name = generate_grid_module_port_name_in_top_module( - grid_coordinate, src_grid_pin_index, vpr_device_annotation, + std::string src_grid_port_name = + generate_grid_module_port_name_in_top_module( + grids, grid_coordinate, src_grid_pin_index, vpr_device_annotation, rr_graph, rr_gsb.get_opin_node(side_manager.get_side(), inode)); - std::string src_grid_module_name = generate_grid_block_module_name_in_top_module(std::string(GRID_MODULE_NAME_PREFIX), grids, src_pb_coord_in_unique_tile); - std::string src_tile_grid_port_name = generate_tile_module_port_name(src_grid_module_name, src_grid_port_name); - ModulePortId src_tile_grid_port_id = - module_manager.find_module_port(src_tile_module, src_tile_grid_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id(src_tile_module, - src_tile_grid_port_id)); + std::string src_grid_module_name = + generate_grid_block_module_name_in_top_module( + std::string(GRID_MODULE_NAME_PREFIX), grids, + src_pb_coord_in_unique_tile); + std::string src_tile_grid_port_name = generate_tile_module_port_name( + src_grid_module_name, src_grid_port_name); + ModulePortId src_tile_grid_port_id = module_manager.find_module_port( + src_tile_module, src_tile_grid_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id( + src_tile_module, src_tile_grid_port_id)); BasicPort src_tile_grid_port = module_manager.module_port(src_tile_module, src_tile_grid_port_id); @@ -404,24 +420,26 @@ static int build_top_module_tile_nets_between_sb_and_pb( grids, vpr_device_annotation, rr_graph, module_sb.get_opin_node(side_manager.get_side(), inode)); - std::string sink_tile_sb_port_name = generate_tile_module_port_name(sink_sb_instance_name_in_unique_tile, sink_sb_port_name); - ModulePortId sink_tile_sb_port_id = - module_manager.find_module_port(tile_module, sink_tile_sb_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id(tile_module, - sink_tile_sb_port_id)); + std::string sink_tile_sb_port_name = generate_tile_module_port_name( + sink_sb_instance_name_in_unique_tile, sink_sb_port_name); + ModulePortId sink_tile_sb_port_id = module_manager.find_module_port( + curr_tile_module, sink_tile_sb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id( + curr_tile_module, sink_tile_sb_port_id)); BasicPort sink_tile_sb_port = - module_manager.module_port(tile_module, sink_tile_sb_port_id); + module_manager.module_port(curr_tile_module, sink_tile_sb_port_id); /* Create nets */ - VTR_LOGV(verbose, - "Build inter-tile nets between switch block '%s' in tile[%lu][%lu] and " - "programmable block in tile[%lu][%lu]\n", - sink_sb_instance_name_in_unique_tile.c_str(), - sink_tile_coord.x(), sink_tile_coord.y(), - src_tile_coord.x(), src_tile_coord.y()); + VTR_LOGV( + verbose, + "Build inter-tile nets between switch block '%s' in tile[%lu][%lu] and " + "programmable block in tile[%lu][%lu]\n", + sink_sb_instance_name_in_unique_tile.c_str(), sink_tile_coord.x(), + sink_tile_coord.y(), src_tile_coord.x(), src_tile_coord.y()); /* Source and sink port should match in size */ - VTR_ASSERT(src_tile_grid_port.get_width() == sink_tile_sb_port.get_width()); + VTR_ASSERT(src_tile_grid_port.get_width() == + sink_tile_sb_port.get_width()); /* Create a net for each pin */ for (size_t pin_id = 0; pin_id < src_tile_grid_port.pins().size(); @@ -498,21 +516,24 @@ static int build_top_module_tile_nets_between_sb_and_pb( *******************************************************************/ static int build_top_module_tile_nets_between_cb_and_pb( ModuleManager& module_manager, const ModuleId& top_module, - const ModuleId& tile_module, - const vtr::Matrix& tile_instance_ids, - const size_t& tile_instance_id, - const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const ModuleId& tile_module, const vtr::Matrix& tile_instance_ids, + const size_t& tile_instance_id, const DeviceGrid& grids, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& curr_fabric_tile_id, const t_rr_type& cb_type, const size_t& cb_idx_in_curr_fabric_tile, - const bool& compact_routing_hierarchy, - const bool& verbose) { - vtr::Point src_tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); - FabricTileId src_unique_tile = fabric_tile.unique_tile(curr_fabric_tile_id); - vtr::Point src_cb_coord_in_unique_tile = fabric_tile.cb_coordinates(src_unique_tile, cb_type)[cb_idx_in_curr_fabric_tile]; + const bool& compact_routing_hierarchy, const bool& verbose) { + vtr::Point src_tile_coord = + fabric_tile.tile_coordinate(curr_fabric_tile_id); + FabricTileId src_unique_tile = fabric_tile.unique_tile(src_tile_coord); + vtr::Point src_cb_coord_in_unique_tile = fabric_tile.cb_coordinates( + src_unique_tile, cb_type)[cb_idx_in_curr_fabric_tile]; + const RRGSB& src_cb_inst_rr_gsb = + device_rr_gsb.get_gsb(src_cb_coord_in_unique_tile); std::string src_cb_instance_name_in_unique_tile = - generate_connection_block_module_name(cb_type, sink_sb_coord_in_unique_tile); + generate_connection_block_module_name( + cb_type, src_cb_inst_rr_gsb.get_cb_coordinate(cb_type)); /* We could have two different coordinators, one is the instance, the other is * the module */ @@ -558,7 +579,8 @@ static int build_top_module_tile_nets_between_cb_and_pb( rr_graph.node_ylow(module_ipin_node)); std::string src_cb_port_name = generate_cb_module_grid_port_name( cb_ipin_side, grids, vpr_device_annotation, rr_graph, module_ipin_node); - std::string src_tile_cb_port_name = generate_tile_module_port_name(src_cb_instance_name_in_unique_tile, src_cb_port_name); + std::string src_tile_cb_port_name = generate_tile_module_port_name( + src_cb_instance_name_in_unique_tile, src_cb_port_name); ModulePortId src_cb_port_id = module_manager.find_module_port(tile_module, src_cb_port_name); VTR_ASSERT(true == module_manager.valid_module_port_id(tile_module, @@ -579,30 +601,40 @@ static int build_top_module_tile_nets_between_cb_and_pb( continue; } - FabricTileId sink_fabric_tile_id = fabric_tile.find_tile_by_pb_coordinate(grid_coordinate); - size_t pb_idx_in_sink_fabric_tile = fabric_tile.find_pb_index_in_tile(sink_fabric_tile_id, grid_coordinate); - vtr::Point sink_tile_coord = fabric_tile.tile_coordinate(sink_fabric_tile_id); - vtr::Point sink_unique_tile_coord = fabric_tile.unique_tile_coordinate(sink_fabric_tile_id); + FabricTileId sink_fabric_tile_id = + fabric_tile.find_tile_by_pb_coordinate(grid_coordinate); + size_t pb_idx_in_sink_fabric_tile = + fabric_tile.find_pb_index_in_tile(sink_fabric_tile_id, grid_coordinate); + vtr::Point sink_tile_coord = + fabric_tile.tile_coordinate(sink_fabric_tile_id); + vtr::Point sink_unique_tile_coord = + fabric_tile.unique_tile_coordinate(sink_fabric_tile_id); FabricTileId sink_unique_tile = fabric_tile.unique_tile(sink_tile_coord); - vtr::Point sink_pb_coord_in_unique_tile = fabric_tile.pb_coordinates(sink_unique_tile)[pb_idx_in_sink_fabric_tile]; + vtr::Point sink_pb_coord_in_unique_tile = + fabric_tile.pb_coordinates( + sink_unique_tile)[pb_idx_in_sink_fabric_tile]; std::string sink_tile_module_name = generate_tile_module_name(sink_unique_tile_coord); ModuleId sink_tile_module = module_manager.find_module(sink_tile_module_name); VTR_ASSERT(true == module_manager.valid_module_id(sink_tile_module)); - size_t sink_tile_instance_id = tile_instance_ids[sink_tile_coord.x()][sink_tile_coord.y()]; + size_t sink_tile_instance_id = + tile_instance_ids[sink_tile_coord.x()][sink_tile_coord.y()]; std::string sink_grid_module_name = generate_grid_block_module_name_in_top_module( - std::string(GRID_MODULE_NAME_PREFIX), grids, sink_pb_coord_in_unique_tile); + std::string(GRID_MODULE_NAME_PREFIX), grids, + sink_pb_coord_in_unique_tile); size_t sink_grid_pin_index = rr_graph.node_pin_num(instance_ipin_node); - std::string sink_grid_port_name = generate_grid_module_port_name_in_top_module( - grid_coordinate, sink_grid_pin_index, vpr_device_annotation, + std::string sink_grid_port_name = + generate_grid_module_port_name_in_top_module( + grids, grid_coordinate, sink_grid_pin_index, vpr_device_annotation, rr_graph, rr_gsb.get_ipin_node(cb_ipin_side, inode)); - std::string sink_tile_grid_port_name = generate_tile_module_port_name(sink_grid_module_name, sink_grid_port_name); + std::string sink_tile_grid_port_name = generate_tile_module_port_name( + sink_grid_module_name, sink_grid_port_name); ModulePortId sink_grid_port_id = module_manager.find_module_port(sink_tile_module, sink_grid_port_name); VTR_ASSERT(true == module_manager.valid_module_port_id( @@ -615,8 +647,7 @@ static int build_top_module_tile_nets_between_cb_and_pb( VTR_ASSERT(src_cb_port.get_width() == sink_grid_port.get_width()); /* Create a net for each pin */ - for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); - ++pin_id) { + for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); ++pin_id) { ModuleNetId net = create_module_source_pin_net( module_manager, top_module, tile_module, tile_instance_id, src_cb_port_id, src_cb_port.pins()[pin_id]); @@ -673,24 +704,23 @@ static int build_top_module_tile_nets_between_cb_and_pb( *******************************************************************/ static int build_top_module_tile_nets_between_sb_and_cb( ModuleManager& module_manager, const ModuleId& top_module, - const ModuleId& tile_module, - const vtr::Matrix& tile_instance_ids, - const size_t& tile_instance_id, - const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, - const RRGSB& rr_gsb, const FabricTile& fabric_tile, - const FabricTileId& curr_fabric_tile_id, + const ModuleId& tile_module, const vtr::Matrix& tile_instance_ids, + const size_t& tile_instance_id, const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph, const RRGSB& rr_gsb, + const FabricTile& fabric_tile, const FabricTileId& curr_fabric_tile_id, const size_t& sb_idx_in_curr_fabric_tile, - const bool& compact_routing_hierarchy, - const bool& verbose) { + const bool& compact_routing_hierarchy, const bool& verbose) { /* We could have two different coordinators, one is the instance, the other is * the module */ vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); vtr::Point module_gsb_sb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); - vtr::Point sb_tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); - FabricTileId sb_unique_tile = fabric_tile.unique_tile(curr_fabric_tile_id); - vtr::Point sb_coord_in_unique_tile = fabric_tile.sb_coordinates(sb_unique_tile)[sb_idx_in_curr_fabric_tile]; + vtr::Point sb_tile_coord = + fabric_tile.tile_coordinate(curr_fabric_tile_id); + FabricTileId sb_unique_tile = fabric_tile.unique_tile(sb_tile_coord); + vtr::Point sb_coord_in_unique_tile = + fabric_tile.sb_coordinates(sb_unique_tile)[sb_idx_in_curr_fabric_tile]; std::string sb_instance_name_in_unique_tile = generate_switch_block_module_name(sb_coord_in_unique_tile); @@ -774,50 +804,53 @@ static int build_top_module_tile_nets_between_sb_and_cb( instance_cb.get_cb_y(cb_type)); /* Check if the grid is inside the tile, if not, create ports */ - if (fabric_tile.cb_in_tile(fabric_tile_id, cb_type, + if (fabric_tile.cb_in_tile(curr_fabric_tile_id, cb_type, instance_cb_coordinate)) { continue; } /* Collect cb tile information */ - FabricTileId cb_tile = fabric_tile.find_tile_by_cb_coordinate(cb_type, instance_gsb_cb_coordinate); - vtr::Point cb_tile_coord = fabric_tile.tile_coordindate(cb_tile); - size_t cb_idx_in_cb_tile = fabric_tile.find_cb_index_in_tile(cb_tile, cb_type, instance_gsb_cb_coordinate); + FabricTileId cb_tile = fabric_tile.find_tile_by_cb_coordinate( + cb_type, instance_gsb_cb_coordinate); + vtr::Point cb_tile_coord = fabric_tile.tile_coordinate(cb_tile); + size_t cb_idx_in_cb_tile = fabric_tile.find_cb_index_in_tile( + cb_tile, cb_type, instance_gsb_cb_coordinate); FabricTileId cb_unique_tile = fabric_tile.unique_tile(cb_tile_coord); - vtr::Point cb_unique_tile_coord = fabric_tile.tile_coordindate(cb_unique_tile); - vtr::Point cb_coord_in_cb_unique_tile = fabric_tile.cb_coordinates(cb_unique_tile, cb_type)[cb_idx_in_cb_tile]; + vtr::Point cb_unique_tile_coord = + fabric_tile.tile_coordinate(cb_unique_tile); + vtr::Point cb_coord_in_cb_unique_tile = + fabric_tile.cb_coordinates(cb_unique_tile, cb_type)[cb_idx_in_cb_tile]; std::string cb_instance_name_in_unique_tile = - generate_connection_block_module_name(cb_type, cb_coord_in_cb_unique_tile); + generate_connection_block_module_name(cb_type, + cb_coord_in_cb_unique_tile); std::string cb_tile_module_name = generate_tile_module_name(cb_unique_tile_coord); - ModuleId cb_tile_module = - module_manager.find_module(cb_tile_module_name); + ModuleId cb_tile_module = module_manager.find_module(cb_tile_module_name); VTR_ASSERT(true == module_manager.valid_module_id(cb_tile_module)); size_t cb_tile_instance = tile_instance_ids[cb_tile_coord.x()][cb_tile_coord.y()]; /* Create nets */ for (size_t itrack = 0; - itrack < module_sb.get_chan_width(side_manager.get_side()); - ++itrack) { + itrack < module_sb.get_chan_width(side_manager.get_side()); ++itrack) { std::string sb_port_name = generate_sb_module_track_port_name( rr_graph.node_type( module_sb.get_chan_node(side_manager.get_side(), itrack)), side_manager.get_side(), module_sb.get_chan_node_direction(side_manager.get_side(), itrack)); /* Prepare SB-related port information */ - std::string sb_tile_sb_port_name = generate_tile_module_port_name(sb_instance_name_in_unique_tile, sb_port_name); + std::string sb_tile_sb_port_name = generate_tile_module_port_name( + sb_instance_name_in_unique_tile, sb_port_name); ModulePortId sb_port_id = module_manager.find_module_port(tile_module, sb_tile_sb_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id(tile_module, - sb_port_id)); - BasicPort sb_port = - module_manager.module_port(tile_module, sb_port_id); + VTR_ASSERT(true == + module_manager.valid_module_port_id(tile_module, sb_port_id)); + BasicPort sb_port = module_manager.module_port(tile_module, sb_port_id); /* Prepare CB-related port information */ PORTS cb_port_direction = OUT_PORT; /* The cb port direction should be opposite to the sb port !!! */ - if (OUT_PORT == module_sb.get_chan_node_direction( - side_manager.get_side(), itrack)) { + if (OUT_PORT == + module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { cb_port_direction = IN_PORT; } else { VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction( @@ -828,11 +861,12 @@ static int build_top_module_tile_nets_between_sb_and_cb( * right sides of the switch block, which indicated bottom and left * sides of the connection blocks */ - bool use_cb_upper_port = (TOP == side_manager.get_side()) || - (RIGHT == side_manager.get_side()); + bool use_cb_upper_port = + (TOP == side_manager.get_side()) || (RIGHT == side_manager.get_side()); std::string cb_port_name = generate_cb_module_track_port_name( cb_type, cb_port_direction, use_cb_upper_port); - std::string cb_tile_cb_port_name = generate_tile_module_port_name(cb_instance_name_in_unique_tile, cb_port_name); + std::string cb_tile_cb_port_name = generate_tile_module_port_name( + cb_instance_name_in_unique_tile, cb_port_name); ModulePortId cb_port_id = module_manager.find_module_port(cb_tile_module, cb_tile_cb_port_name); VTR_ASSERT(true == module_manager.valid_module_port_id(cb_tile_module, @@ -844,11 +878,11 @@ static int build_top_module_tile_nets_between_sb_and_cb( * If sb port is an output (source), cb port is an input (sink) * If sb port is an input (sink), cb port is an output (source) */ - if (OUT_PORT == module_sb.get_chan_node_direction( - side_manager.get_side(), itrack)) { + if (OUT_PORT == + module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { ModuleNetId net = create_module_source_pin_net( - module_manager, top_module, tile_module, tile_instance_id, - sb_port_id, itrack / 2); + module_manager, top_module, tile_module, tile_instance_id, sb_port_id, + itrack / 2); module_manager.add_module_net_sink(top_module, net, cb_tile_module, cb_tile_instance, cb_port_id, itrack / 2); @@ -869,8 +903,10 @@ static int build_top_module_tile_nets_between_sb_and_cb( /******************************************************************** * Add module nets to connect the pins between tiles - * To make it easy, this function will iterate over all the tiles, through which we can obtain the coordinates - * of each programmable block (PB), connection block (CB) and switch block (SB). With the coordinates, we can then trace the connections between these blocks using the RRGSB data structure. + * To make it easy, this function will iterate over all the tiles, through which + *we can obtain the coordinates of each programmable block (PB), connection + *block (CB) and switch block (SB). With the coordinates, we can then trace the + *connections between these blocks using the RRGSB data structure. * * +--------+ +----------+ * | Tile |--->| Tile | @@ -878,17 +914,26 @@ static int build_top_module_tile_nets_between_sb_and_cb( * +--------+ +----------+ * * The inter-tile connections can be categorized into four types: - * - PB-to-SB connections: We use the GSB to find the connections with a given SB coordinate. Note that we only care the connections where the driver PB is not in this tile. - * - CB-to-PB connections: We use the GSB to find the connections with a given CB coordinate. Note that we only care the connections where the sink PB is not in this tile. - * - SB-to-CB connections: We use the GSB to find the connections with a given SB coordinate. Note that we only care the connections where the sink CB is not in this tile. - * - PB-to-PB connections: We use the tile direct data structure to find all the connections. Note that we only care the connections where the driver PB is not in this tile. + * - PB-to-SB connections: We use the GSB to find the connections with a given + *SB coordinate. Note that we only care the connections where the driver PB is + *not in this tile. + * - CB-to-PB connections: We use the GSB to find the connections with a given + *CB coordinate. Note that we only care the connections where the sink PB is not + *in this tile. + * - SB-to-CB connections: We use the GSB to find the connections with a given + *SB coordinate. Note that we only care the connections where the sink CB is not + *in this tile. + * - PB-to-PB connections: We use the tile direct data structure to find all the + *connections. Note that we only care the connections where the driver PB is not + *in this tile. * *******************************************************************/ static int add_top_module_nets_around_one_tile( ModuleManager& module_manager, const ModuleId& top_module, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const vtr::Matrix& tile_instance_ids, const RRGraphView& rr_graph, - const DeviceRRGSB& device_rr_gsb, const FabricTile& fabric_tile, const FabricTileId& curr_fabric_tile_id, + const vtr::Matrix& tile_instance_ids, + const RRGraphView& rr_graph_view, const DeviceRRGSB& device_rr_gsb, + const FabricTile& fabric_tile, const FabricTileId& curr_fabric_tile_id, const bool& verbose) { int status = CMD_EXEC_SUCCESS; @@ -900,22 +945,25 @@ static int add_top_module_nets_around_one_tile( if (!module_manager.valid_module_id(tile_module)) { return CMD_EXEC_FATAL_ERROR; } - + /* Find the instance id for this tile */ - vtr::Point tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); + vtr::Point tile_coord = + fabric_tile.tile_coordinate(curr_fabric_tile_id); size_t tile_instance_id = tile_instance_ids[tile_coord.x()][tile_coord.y()]; /* Get the submodule of Switch blocks one by one, build connections between sb * and pb */ - for (size_t isb = 0; isb < fabric_tile.sb_coordinates(fabric_tile_id).size(); - ++isb) { + for (size_t isb = 0; + isb < fabric_tile.sb_coordinates(curr_fabric_tile_id).size(); ++isb) { vtr::Point sb_coord = - fabric_tile.sb_coordinates(fabric_tile_id)[isb]; + fabric_tile.sb_coordinates(curr_fabric_tile_id)[isb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); - status_code = build_top_module_tile_nets_between_sb_and_pb( - module_manager, top_module, tile_module, tile_instance_ids, tile_instance_id, grids, vpr_device_annotation, device_rr_gsb, - rr_graph_view, rr_gsb, fabric_tile, curr_fabric_tile_id, isb, true, verbose); - if (status_code != CMD_EXEC_SUCCESS) { + status = build_top_module_tile_nets_between_sb_and_pb( + module_manager, top_module, tile_module, tile_instance_ids, + tile_instance_id, grids, vpr_device_annotation, device_rr_gsb, + rr_graph_view, rr_gsb, fabric_tile, curr_fabric_tile_id, isb, true, + verbose); + if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } } @@ -923,35 +971,33 @@ static int add_top_module_nets_around_one_tile( * between cb and pb */ for (t_rr_type cb_type : {CHANX, CHANY}) { for (size_t icb = 0; - icb < fabric_tile.cb_coordinates(fabric_tile_id, cb_type).size(); + icb < fabric_tile.cb_coordinates(curr_fabric_tile_id, cb_type).size(); ++icb) { vtr::Point cb_coord = - fabric_tile.cb_coordinates(fabric_tile_id, cb_type)[icb]; + fabric_tile.cb_coordinates(curr_fabric_tile_id, cb_type)[icb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(cb_coord); - status_code = build_top_module_tile_nets_between_cb_and_pb( - module_manager, top_module, tile_module, tile_instance_ids, tile_instance_id, - grids, vpr_device_annotation, - device_rr_gsb, rr_graph_view, rr_gsb, - fabric_tile, curr_fabric_tile_id, - cb_type, icb, true, - verbose); - if (status_code != CMD_EXEC_SUCCESS) { + status = build_top_module_tile_nets_between_cb_and_pb( + module_manager, top_module, tile_module, tile_instance_ids, + tile_instance_id, grids, vpr_device_annotation, device_rr_gsb, + rr_graph_view, rr_gsb, fabric_tile, curr_fabric_tile_id, cb_type, icb, + true, verbose); + if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } } } /* Get the submodule of connection blocks one by one, build connections * between sb and cb */ - for (size_t isb = 0; isb < fabric_tile.sb_coordinates(fabric_tile_id).size(); - ++isb) { + for (size_t isb = 0; + isb < fabric_tile.sb_coordinates(curr_fabric_tile_id).size(); ++isb) { vtr::Point sb_coord = - fabric_tile.sb_coordinates(fabric_tile_id)[isb]; + fabric_tile.sb_coordinates(curr_fabric_tile_id)[isb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); - status_code = build_top_module_tile_nets_between_sb_and_cb( - module_manager, top_module, tile_module, tile_instance_ids, tile_instance_id, device_rr_gsb, rr_graph_view, rr_gsb, - fabric_tile, curr_fabric_tile_id, isb, true, - verbose); - if (status_code != CMD_EXEC_SUCCESS) { + status = build_top_module_tile_nets_between_sb_and_cb( + module_manager, top_module, tile_module, tile_instance_ids, + tile_instance_id, device_rr_gsb, rr_graph_view, rr_gsb, fabric_tile, + curr_fabric_tile_id, isb, true, verbose); + if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } } @@ -960,7 +1006,8 @@ static int add_top_module_nets_around_one_tile( } /******************************************************************** - * Walk through each tile instance and add module nets to connect the pins between tiles + * Walk through each tile instance and add module nets to connect the pins + *between tiles *******************************************************************/ static int add_top_module_nets_connect_tiles( ModuleManager& module_manager, const ModuleId& top_module, @@ -980,7 +1027,8 @@ static int add_top_module_nets_connect_tiles( } status = add_top_module_nets_around_one_tile( module_manager, top_module, vpr_device_annotation, grids, - tile_instance_ids, rr_graph, device_rr_gsb, fabric_tile, curr_fabric_tile_id, verbose); + tile_instance_ids, rr_graph, device_rr_gsb, fabric_tile, + curr_fabric_tile_id, verbose); if (status != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -996,7 +1044,7 @@ static int add_top_module_nets_connect_tiles( * +-----------------------> Tail * +<----------------------+ * | - * ... + * ... * +---------------------->+ * +<----------------------+ * head ----------------------->+ @@ -1005,8 +1053,7 @@ static void organize_top_module_tile_based_memory_modules( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, const CircuitModelId& sram_model, const DeviceGrid& grids, - const vtr::Matrix& tile_instance_ids, - const FabricTile& fabric_tile) { + 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()); @@ -1034,18 +1081,21 @@ static void organize_top_module_tile_based_memory_modules( if (!fabric_tile.valid_tile_id(curr_fabric_tile_id)) { continue; } - vtr::Point curr_tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); - vtr::Point unique_tile_coord = fabric_tile.unique_tile_coordinate(curr_fabric_tile_id); + vtr::Point curr_tile_coord = + fabric_tile.tile_coordinate(curr_fabric_tile_id); + vtr::Point unique_tile_coord = + fabric_tile.unique_tile_coordinate(curr_fabric_tile_id); std::string tile_module_name = generate_tile_module_name(unique_tile_coord); - ModuleId tile_module = mnodule_manager.find_module(tile_module_name); + ModuleId tile_module = module_manager.find_module(tile_module_name); VTR_ASSERT(module_manager.valid_module_id(tile_module)); - + if (0 < find_module_num_config_bits(module_manager, tile_module, circuit_lib, sram_model, - sram_orgz_type)) { + config_protocol.type())) { module_manager.add_configurable_child( top_module, tile_module, - tile_instance_ids[curr_tile_coord.x()][curr_tile_coord.y()], curr_tile_coord); + tile_instance_ids[curr_tile_coord.x()][curr_tile_coord.y()], + vtr::Point(curr_tile_coord.x(), curr_tile_coord.y())); } } @@ -1060,9 +1110,8 @@ static void organize_top_module_tile_based_memory_modules( ********************************************************************/ ModulePinInfo find_tile_module_chan_port( const ModuleManager& module_manager, const ModuleId& tile_module, - const vtr::Point& cb_coord_in_tile, - const RRGraphView& rr_graph, const RRGSB& rr_gsb, const t_rr_type& cb_type, - const RRNodeId& chan_rr_node) { + const vtr::Point& cb_coord_in_tile, const RRGraphView& rr_graph, + const RRGSB& rr_gsb, const t_rr_type& cb_type, const RRNodeId& chan_rr_node) { ModulePinInfo input_port_info; /* Generate the input port object */ switch (rr_graph.node_type(chan_rr_node)) { @@ -1074,7 +1123,9 @@ ModulePinInfo find_tile_module_chan_port( /* Create a port description for the middle output */ std::string input_port_name = generate_cb_module_track_port_name( cb_type, IN_PORT, 0 == chan_node_track_id % 2); - std::string tile_input_port_name = generate_tile_module_port_name(generate_connection_block_module_name(cb_type, cb_coord_in_tile), input_port_name); + std::string tile_input_port_name = generate_tile_module_port_name( + generate_connection_block_module_name(cb_type, cb_coord_in_tile), + input_port_name); /* Must find a valid port id in the Switch Block module */ input_port_info.first = module_manager.find_module_port(tile_module, tile_input_port_name); @@ -1099,8 +1150,7 @@ static int build_top_module_global_net_from_tile_clock_arch_tree( ModuleManager& module_manager, const ModuleId& top_module, const ModulePortId& top_module_port, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, - const vtr::Matrix& tile_instance_ids, - const FabricTile& fabric_tile, + const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile, const ClockNetwork& clk_ntwk, const std::string& clk_tree_name, const RRClockSpatialLookup& rr_clock_lookup) { int status = CMD_EXEC_SUCCESS; @@ -1150,20 +1200,29 @@ static int build_top_module_global_net_from_tile_clock_arch_tree( const RRGSB& rr_gsb = device_rr_gsb.get_gsb_by_cb_coordinate( entry_track_type, vtr::Point(entry_point.x(), entry_point.y())); vtr::Point cb_coord_in_tile = rr_gsb.get_sb_coordinate(); - FabricTileId curr_fabric_tile_id = fabric_tile.find_tile_by_cb_coordinate(entry_track_type, cb_coord_in_tile); - vtr::Point curr_fabric_tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); - FabricTileId unique_fabric_tile_id = fabric_tile.unique_tile(curr_fabric_tile_coord); - vtr::Point unique_fabric_tile_coord = fabric_tile.tile_coordinate(unique_fabric_tile_id); - - ModuleId tile_module = module_manager.find_module(generate_tile_module_name(unique_fabric_tile_coord)); - size_t tile_instance = tile_instance_ids[curr_fabric_tile_coord.x()][curr_fabric_tile_coord.y()]; + FabricTileId curr_fabric_tile_id = fabric_tile.find_tile_by_cb_coordinate( + entry_track_type, cb_coord_in_tile); + vtr::Point curr_fabric_tile_coord = + fabric_tile.tile_coordinate(curr_fabric_tile_id); + FabricTileId unique_fabric_tile_id = + fabric_tile.unique_tile(curr_fabric_tile_coord); + vtr::Point unique_fabric_tile_coord = + fabric_tile.tile_coordinate(unique_fabric_tile_id); + + ModuleId tile_module = module_manager.find_module( + generate_tile_module_name(unique_fabric_tile_coord)); + size_t tile_instance = tile_instance_ids[curr_fabric_tile_coord.x()] + [curr_fabric_tile_coord.y()]; /* Find the port name */ - size_t cb_idx_in_curr_fabric_tile = fabric_tile.find_cb_index_in_tile(curr_fabric_tile_id, entry_track_type, cb_coord_in_tile); - vtr::Point cb_coord_in_unique_fabric_tile = fabric_tile.cb_coordinates(unique_fabric_tile_id, entry_track_type)[cb_idx_in_curr_fabric_tile]; + size_t cb_idx_in_curr_fabric_tile = fabric_tile.find_cb_index_in_tile( + curr_fabric_tile_id, entry_track_type, cb_coord_in_tile); + vtr::Point cb_coord_in_unique_fabric_tile = + fabric_tile.cb_coordinates( + unique_fabric_tile_id, entry_track_type)[cb_idx_in_curr_fabric_tile]; ModulePinInfo des_pin_info = find_tile_module_chan_port( - module_manager, tile_module, cb_coord_in_unique_fabric_tile, rr_graph, rr_gsb, entry_track_type, - entry_rr_node); + module_manager, tile_module, cb_coord_in_unique_fabric_tile, rr_graph, + rr_gsb, entry_track_type, entry_rr_node); /* Configure the net sink */ BasicPort sink_port = @@ -1187,29 +1246,38 @@ static int build_top_module_global_net_for_given_tile_module( const TileGlobalPortId& tile_global_port, const BasicPort& tile_port_to_connect, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const vtr::Point& grid_coordinate, - const vtr::Matrix& tile_instance_ids, - const FabricTile& fabric_tile) { + const vtr::Point& grid_coordinate, const e_side& border_side, + const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile) { /* Get the tile module and instance */ - FabricTileId curr_fabric_tile_id = fabric_tile.find_tile_by_pb_coordinate(grid_coordinate); - vtr::Point curr_fabric_tile_coord = fabric_tile.tile_coordinate(curr_fabric_tile_id); - FabricTileId unique_fabric_tile_id = fabric_tile.unique_tile(curr_fabric_tile_coord); - vtr::Point unique_fabric_tile_coord = fabric_tile.tile_coordinate(unique_fabric_tile_id); - std::string tile_module_name = generate_tile_module_name(unique_fabric_tile_coord); + FabricTileId curr_fabric_tile_id = + fabric_tile.find_tile_by_pb_coordinate(grid_coordinate); + vtr::Point curr_fabric_tile_coord = + fabric_tile.tile_coordinate(curr_fabric_tile_id); + FabricTileId unique_fabric_tile_id = + fabric_tile.unique_tile(curr_fabric_tile_coord); + vtr::Point unique_fabric_tile_coord = + fabric_tile.tile_coordinate(unique_fabric_tile_id); + std::string tile_module_name = + generate_tile_module_name(unique_fabric_tile_coord); ModuleId tile_module = module_manager.find_module(tile_module_name); VTR_ASSERT(true == module_manager.valid_module_id(tile_module)); - size_t tile_instance = tile_instance_ids[curr_fabric_tile_coord.x()][curr_fabric_tile_coord.y()]; + size_t tile_instance = + tile_instance_ids[curr_fabric_tile_coord.x()][curr_fabric_tile_coord.y()]; /* Get the grid coordinate in the context of the tile */ - size_t pb_idx_in_curr_fabric_tile = fabric_tile.find_pb_index_in_tile(curr_fabric_tile_id, grid_coordinate); - vtr::Point pb_coord_in_unique_fabric_tile = fabric_tile.pb_coordinates(unique_fabric_tile_id)[pb_idx_in_curr_fabric_tile]; + size_t pb_idx_in_curr_fabric_tile = + fabric_tile.find_pb_index_in_tile(curr_fabric_tile_id, grid_coordinate); + vtr::Point pb_coord_in_unique_fabric_tile = + fabric_tile.pb_coordinates( + unique_fabric_tile_id)[pb_idx_in_curr_fabric_tile]; t_physical_tile_type_ptr physical_tile = grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); /* Find the module name for this type of grid */ std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); - std::string grid_instance_name = generate_grid_block_module_name_in_top_module( - grid_module_name_prefix, grids, pb_coord_in_unique_fabric_tile); + std::string grid_instance_name = + generate_grid_block_module_name_in_top_module( + grid_module_name_prefix, grids, pb_coord_in_unique_fabric_tile); /* Find the source port at the top-level module */ BasicPort src_port = module_manager.module_port(top_module, top_module_port); @@ -1287,15 +1355,16 @@ static int build_top_module_global_net_for_given_tile_module( std::string grid_port_name = generate_grid_port_name(grid_pin_width, grid_pin_height, subtile_index, pin_side, grid_pin_info); - std::string tile_grid_port_name = generate_tile_module_port_name(grid_instance_name, grid_port_name); + std::string tile_grid_port_name = + generate_tile_module_port_name(grid_instance_name, grid_port_name); ModulePortId tile_grid_port_id = - module_manager.find_module_port(grid_module, tile_grid_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id(tile_module, - tile_port_id)); + module_manager.find_module_port(tile_module, tile_grid_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id( + tile_module, tile_grid_port_id)); - VTR_ASSERT( - 1 == - module_manager.module_port(tile_module, tile_grid_port_id).get_width()); + VTR_ASSERT(1 == + module_manager.module_port(tile_module, tile_grid_port_id) + .get_width()); ModuleNetId net = create_module_source_pin_net( module_manager, top_module, top_module, 0, top_module_port, @@ -1304,7 +1373,7 @@ static int build_top_module_global_net_for_given_tile_module( /* Configure the net sink */ BasicPort sink_port = - module_manager.module_port(tile_module, tile_port_id); + module_manager.module_port(tile_module, tile_grid_port_id); module_manager.add_module_net_sink(top_module, net, tile_module, tile_instance, tile_grid_port_id, sink_port.pins()[0]); @@ -1324,8 +1393,7 @@ static int build_top_module_global_net_from_tile_modules( const ModulePortId& top_module_port, const TileAnnotation& tile_annotation, const TileGlobalPortId& tile_global_port, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const vtr::Matrix& tile_instance_ids, - const FabricTile& fabric_tile) { + const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile) { int status = CMD_EXEC_SUCCESS; std::map>> io_coordinates = @@ -1402,7 +1470,8 @@ static int build_top_module_global_net_from_tile_modules( status = build_top_module_global_net_for_given_tile_module( module_manager, top_module, top_module_port, tile_annotation, tile_global_port, tile_port, vpr_device_annotation, grids, - vtr::Point(ix, iy), NUM_SIDES, tile_instance_ids, fabric_tile); + vtr::Point(ix, iy), NUM_SIDES, tile_instance_ids, + fabric_tile); if (CMD_EXEC_FATAL_ERROR == status) { return status; } @@ -1469,9 +1538,8 @@ static int add_top_module_global_ports_from_tile_modules( const TileAnnotation& tile_annotation, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, - const vtr::Matrix& tile_instance_ids, - const FabricTile& fabric_tile, const ClockNetwork& clk_ntwk, - const RRClockSpatialLookup& rr_clock_lookup) { + const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile, + const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup) { int status = CMD_EXEC_SUCCESS; /* Add the global ports which are NOT yet added to the top-level module @@ -1527,7 +1595,8 @@ static int add_top_module_global_ports_from_tile_modules( } else { status = build_top_module_global_net_from_tile_modules( module_manager, top_module, top_module_port, tile_annotation, - tile_global_port, vpr_device_annotation, grids, tile_instance_ids, fabric_tile); + tile_global_port, vpr_device_annotation, grids, tile_instance_ids, + fabric_tile); } if (status == CMD_EXEC_FATAL_ERROR) { return status; @@ -1552,10 +1621,9 @@ static void add_module_nets_connect_tile_direct_connection( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const vtr::Matrix& tile_instance_ids, - const FabricTile& fabric_tile, - const TileDirect& tile_direct, - const TileDirectId& tile_direct_id, const ArchDirect& arch_direct) { + const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile, + const TileDirect& tile_direct, const TileDirectId& tile_direct_id, + const ArchDirect& arch_direct) { vtr::Point device_size(grids.width(), grids.height()); std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); @@ -1564,42 +1632,54 @@ static void add_module_nets_connect_tile_direct_connection( tile_direct.from_tile_coordinate(tile_direct_id); t_physical_tile_type_ptr src_grid_type = grids.get_physical_type(src_clb_coord.x(), src_clb_coord.y()); - FabricTileId src_tile_id = fabric_tile.find_tile_by_pb_coordinate(src_clb_coord); + FabricTileId src_tile_id = + fabric_tile.find_tile_by_pb_coordinate(src_clb_coord); vtr::Point src_tile_coord = fabric_tile.tile_coordinate(src_tile_id); FabricTileId src_unique_tile_id = fabric_tile.unique_tile(src_tile_coord); - vtr::Point src_unique_tile_coord = fabric_tile.tile_coordinate(src_unique_tile_id); - std::string src_module_name = generate_tile_module_name(src_unique_tile_coord); + vtr::Point src_unique_tile_coord = + fabric_tile.tile_coordinate(src_unique_tile_id); + std::string src_module_name = + generate_tile_module_name(src_unique_tile_coord); ModuleId src_tile_module = module_manager.find_module(src_module_name); VTR_ASSERT(true == module_manager.valid_module_id(src_tile_module)); /* Record the instance id */ size_t src_tile_instance = tile_instance_ids[src_tile_coord.x()][src_tile_coord.y()]; /* Grid instance name in the unique tile */ - size_t pb_idx_in_src_tile = fabric_tile.find_pb_index_in_tile(src_tile_id, src_clb_coord); - vtr::Point pb_coord_in_unique_src_tile = fabric_tile.pb_coordinates(src_unique_tile_id)[pb_idx_in_src_tile]; - std::string src_grid_instance_name = generate_grid_block_module_name_in_top_module( - grid_module_name_prefix, grids, pb_coord_in_unique_src_tile); + size_t pb_idx_in_src_tile = + fabric_tile.find_pb_index_in_tile(src_tile_id, src_clb_coord); + vtr::Point pb_coord_in_unique_src_tile = + fabric_tile.pb_coordinates(src_unique_tile_id)[pb_idx_in_src_tile]; + std::string src_grid_instance_name = + generate_grid_block_module_name_in_top_module( + grid_module_name_prefix, grids, pb_coord_in_unique_src_tile); /* Find the module name of sink clb */ vtr::Point des_clb_coord = tile_direct.to_tile_coordinate(tile_direct_id); t_physical_tile_type_ptr sink_grid_type = grids.get_physical_type(des_clb_coord.x(), des_clb_coord.y()); - FabricTileId des_tile_id = fabric_tile.find_tile_by_pb_coordinate(des_clb_coord); + FabricTileId des_tile_id = + fabric_tile.find_tile_by_pb_coordinate(des_clb_coord); vtr::Point des_tile_coord = fabric_tile.tile_coordinate(des_tile_id); FabricTileId des_unique_tile_id = fabric_tile.unique_tile(des_tile_coord); - vtr::Point des_unique_tile_coord = fabric_tile.tile_coordinate(des_unique_tile_id); - std::string des_module_name = generate_tile_module_name(des_unique_tile_coord); + vtr::Point des_unique_tile_coord = + fabric_tile.tile_coordinate(des_unique_tile_id); + std::string des_module_name = + generate_tile_module_name(des_unique_tile_coord); ModuleId des_tile_module = module_manager.find_module(des_module_name); VTR_ASSERT(true == module_manager.valid_module_id(des_tile_module)); /* Record the instance id */ size_t des_tile_instance = tile_instance_ids[des_tile_coord.x()][des_tile_coord.y()]; /* Grid instance name in the unique tile */ - size_t pb_idx_in_des_tile = fabric_tile.find_pb_index_in_tile(des_tile_id, des_clb_coord); - vtr::Point pb_coord_in_unique_des_tile = fabric_tile.pb_coordinates(des_unique_tile_id)[pb_idx_in_des_tile]; - std::string des_grid_instance_name = generate_grid_block_module_name_in_top_module( - grid_module_name_prefix, grids, pb_coord_in_unique_des_tile); + size_t pb_idx_in_des_tile = + fabric_tile.find_pb_index_in_tile(des_tile_id, des_clb_coord); + vtr::Point pb_coord_in_unique_des_tile = + fabric_tile.pb_coordinates(des_unique_tile_id)[pb_idx_in_des_tile]; + std::string des_grid_instance_name = + generate_grid_block_module_name_in_top_module( + grid_module_name_prefix, grids, pb_coord_in_unique_des_tile); /* Find the module id of a direct connection module */ CircuitModelId direct_circuit_model = @@ -1654,14 +1734,15 @@ static void add_module_nets_connect_tile_direct_connection( std::string src_port_name = generate_grid_port_name(src_pin_width, src_pin_height, src_subtile_index, src_pin_grid_side, src_pin_info); - src_port_name = generate_tile_module_port_name(src_grid_instance_name, src_port_name); + src_port_name = + generate_tile_module_port_name(src_grid_instance_name, src_port_name); ModulePortId src_port_id = module_manager.find_module_port(src_tile_module, src_port_name); if (true != module_manager.valid_module_port_id(src_tile_module, src_port_id)) { VTR_LOG_ERROR("Fail to find port '%s[%lu][%lu].%s'\n", - src_module_name.c_str(), src_tile_coord.x(), src_tile_coord.y(), - src_port_name.c_str()); + src_module_name.c_str(), src_tile_coord.x(), + src_tile_coord.y(), src_port_name.c_str()); } VTR_ASSERT(true == module_manager.valid_module_port_id(src_tile_module, src_port_id)); @@ -1690,14 +1771,14 @@ static void add_module_nets_connect_tile_direct_connection( std::string sink_port_name = generate_grid_port_name(sink_pin_width, sink_pin_height, sink_subtile_index, sink_pin_grid_side, sink_pin_info); - sink_port_name = generate_tile_module_port_name(des_grid_instance_name, sink_port_name); + sink_port_name = + generate_tile_module_port_name(des_grid_instance_name, sink_port_name); ModulePortId sink_port_id = module_manager.find_module_port(des_tile_module, sink_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id(des_tile_module, - sink_port_id)); VTR_ASSERT( - 1 == - module_manager.module_port(des_tile_module, sink_port_id).get_width()); + true == module_manager.valid_module_port_id(des_tile_module, sink_port_id)); + VTR_ASSERT( + 1 == module_manager.module_port(des_tile_module, sink_port_id).get_width()); /* Add a submodule of direct connection module to the top-level module */ size_t direct_instance_id = @@ -1735,10 +1816,8 @@ static void add_top_module_nets_connect_tile_direct_connections( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const vtr::Matrix& tile_instance_ids, - const FabricTile& fabric_tile, - const TileDirect& tile_direct, - const ArchDirect& arch_direct) { + const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile, + const TileDirect& tile_direct, const ArchDirect& arch_direct) { vtr::ScopedStartFinishTimer timer( "Add module nets for inter-tile connections"); @@ -1761,11 +1840,9 @@ int build_top_module_tile_child_instances( const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, 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 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) { int status = CMD_EXEC_SUCCESS; vtr::Matrix tile_instance_ids; status = add_top_module_tile_instances(module_manager, top_module, @@ -1795,11 +1872,13 @@ int build_top_module_tile_child_instances( tile_instance_ids, fabric_tile, tile_direct, arch_direct); } - /* Add global ports from tile modules: how to connect to clock architecture and the global port from tile annotation + /* Add global ports from tile modules: how to connect to clock architecture + * and the global port from tile annotation */ - status = add_top_module_global_ports_from_tile_modules( module_manager, top_module, - tile_annotation, vpr_device_annotation, grids, rr_graph, device_rr_gsb, - tile_instance_ids, fabric_tile, clk_ntwk, rr_clock_lookup); + status = add_top_module_global_ports_from_tile_modules( + module_manager, top_module, tile_annotation, vpr_device_annotation, grids, + rr_graph, device_rr_gsb, tile_instance_ids, fabric_tile, clk_ntwk, + rr_clock_lookup); 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 d0b03bb05..02df43e76 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.h +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.h @@ -40,11 +40,9 @@ int build_top_module_tile_child_instances( const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, 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 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); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_top_module_memory.cpp b/openfpga/src/fabric/build_top_module_memory.cpp index 74a8713a1..92eef9967 100644 --- a/openfpga/src/fabric/build_top_module_memory.cpp +++ b/openfpga/src/fabric/build_top_module_memory.cpp @@ -262,7 +262,7 @@ static void organize_top_module_tile_memory_modules( * - This function should NOT modify configurable children * *******************************************************************/ -static void build_top_module_configurable_regions( +void build_top_module_configurable_regions( ModuleManager& module_manager, const ModuleId& top_module, const ConfigProtocol& config_protocol) { vtr::ScopedStartFinishTimer timer( diff --git a/openfpga/src/fabric/build_top_module_memory.h b/openfpga/src/fabric/build_top_module_memory.h index 0f4a17d6a..a452588ec 100644 --- a/openfpga/src/fabric/build_top_module_memory.h +++ b/openfpga/src/fabric/build_top_module_memory.h @@ -37,6 +37,10 @@ void organize_top_module_memory_modules( const std::map>& cb_instance_ids, const bool& compact_routing_hierarchy); +void build_top_module_configurable_regions( + ModuleManager& module_manager, const ModuleId& top_module, + const ConfigProtocol& config_protocol); + void shuffle_top_module_configurable_children( ModuleManager& module_manager, const ModuleId& top_module, const ConfigProtocol& config_protocol); diff --git a/openfpga/src/fabric/build_top_module_utils.cpp b/openfpga/src/fabric/build_top_module_utils.cpp index e52ad6519..1fe1bd158 100644 --- a/openfpga/src/fabric/build_top_module_utils.cpp +++ b/openfpga/src/fabric/build_top_module_utils.cpp @@ -11,6 +11,7 @@ /* Module builder headers */ #include "build_top_module_utils.h" +#include "openfpga_rr_graph_utils.h" /* begin namespace openfpga */ namespace openfpga { @@ -46,10 +47,10 @@ std::string generate_grid_block_module_name_in_top_module( * Note that it may be useful for other modules but not tried yet! *******************************************************************/ std::string generate_grid_module_port_name_in_top_module( - const vtr::Point& grid_coordinate, + const DeviceGrid& grids, const vtr::Point& grid_coordinate, const size_t& sink_grid_pin_index, - const VprDeviceAnnotation& vpr_device_annotation, - const RRGraph& rr_graph, const RRNodeId& inode) { + const VprDeviceAnnotation& vpr_device_annotation, const RRGraphView& rr_graph, + const RRNodeId& inode) { t_physical_tile_type_ptr grid_type_descriptor = grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); size_t sink_grid_pin_width = @@ -66,9 +67,7 @@ std::string generate_grid_module_port_name_in_top_module( subtile_index < grid_type_descriptor->capacity); std::string sink_grid_port_name = generate_grid_port_name( sink_grid_pin_width, sink_grid_pin_height, subtile_index, - get_rr_graph_single_node_side( - rr_graph, rr_gsb.get_ipin_node(cb_ipin_side, inode)), - sink_grid_pin_info); + get_rr_graph_single_node_side(rr_graph, inode), sink_grid_pin_info); return sink_grid_port_name; } diff --git a/openfpga/src/fabric/build_top_module_utils.h b/openfpga/src/fabric/build_top_module_utils.h index 392e9e09d..3a871b3e1 100644 --- a/openfpga/src/fabric/build_top_module_utils.h +++ b/openfpga/src/fabric/build_top_module_utils.h @@ -9,6 +9,7 @@ #include "device_grid.h" #include "rr_gsb.h" +#include "vpr_device_annotation.h" #include "vtr_geometry.h" /******************************************************************** @@ -23,10 +24,10 @@ std::string generate_grid_block_module_name_in_top_module( const vtr::Point& grid_coord); std::string generate_grid_module_port_name_in_top_module( - const vtr::Point& grid_coordinate, + const DeviceGrid& grids, const vtr::Point& grid_coordinate, const size_t& sink_grid_pin_index, - const VprDeviceAnnotation& vpr_device_annotation, - const RRGraph& rr_graph, const RRNodeId& inode); + const VprDeviceAnnotation& vpr_device_annotation, const RRGraphView& rr_graph, + const RRNodeId& inode); t_rr_type find_top_module_cb_type_by_sb_side(const e_side& sb_side); From 812473ef045df584bca10b8e6a869132201e29ee Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 24 Jul 2023 14:50:39 -0700 Subject: [PATCH 212/391] [core] fixed the bug on io location map for tiled top module --- .../src/base/openfpga_build_fabric_template.h | 2 +- .../fabric/build_fabric_io_location_map.cpp | 142 +++++++++++++++++- .../src/fabric/build_fabric_io_location_map.h | 3 +- openfpga/src/fabric/build_tile_modules.cpp | 3 + 4 files changed, 146 insertions(+), 4 deletions(-) diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index 25d4d3685..3a9460147 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -186,7 +186,7 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd, /* Build I/O location map */ openfpga_ctx.mutable_io_location_map() = build_fabric_io_location_map( - openfpga_ctx.module_graph(), g_vpr_ctx.device().grid); + openfpga_ctx.module_graph(), g_vpr_ctx.device().grid, cmd_context.option_enable(cmd, opt_group_tile)); /* Build fabric global port information */ openfpga_ctx.mutable_fabric_global_port_info() = diff --git a/openfpga/src/fabric/build_fabric_io_location_map.cpp b/openfpga/src/fabric/build_fabric_io_location_map.cpp index 65e1a5824..ed85648ee 100644 --- a/openfpga/src/fabric/build_fabric_io_location_map.cpp +++ b/openfpga/src/fabric/build_fabric_io_location_map.cpp @@ -31,7 +31,7 @@ namespace openfpga { *io_children() list of top-level module. Here we just build a fast lookup from *(x, y, z) coordinate to the actual indices *******************************************************************/ -IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, +static IoLocationMap build_fabric_fine_grained_io_location_map(const ModuleManager& module_manager, const DeviceGrid& grids) { vtr::ScopedStartFinishTimer timer( "Create I/O location mapping for top module"); @@ -114,7 +114,6 @@ IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, if (curr_io_index == io_counter.end()) { io_counter[gpio_port.get_name()] = 0; } - /* FIXME: Will cause critical bugs when tile modules are added */ io_location_map.set_io_index(coord.x(), coord.y(), subchild_coord.x(), gpio_port.get_name(), io_counter[gpio_port.get_name()]); @@ -145,4 +144,143 @@ IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, return io_location_map; } +/******************************************************************** + * Find all the GPIO ports in the tile modules + * and cache their port/pin index in the top-level module + * + * .. note:: The I/O sequence(indexing) is already determined in the + *io_children() list of top-level module. Here we just build a fast lookup from + *(x, y, z) coordinate to the actual indices + *******************************************************************/ +static IoLocationMap build_fabric_tiled_io_location_map(const ModuleManager& module_manager, + const DeviceGrid& grids) { + vtr::ScopedStartFinishTimer timer( + "Create I/O location mapping for top module"); + + IoLocationMap io_location_map; + + std::map io_counter; + + std::string top_module_name = generate_fpga_top_module_name(); + ModuleId top_module = module_manager.find_module(top_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(top_module)); + + /* Walk through the I/O child list */ + for (size_t ichild = 0; + ichild < module_manager.io_children(top_module).size(); ++ichild) { + ModuleId child = module_manager.io_children(top_module)[ichild]; + vtr::Point coord = + module_manager.io_child_coordinates(top_module)[ichild]; + t_physical_tile_type_ptr phy_tile_type = + grids.get_physical_type(coord.x(), coord.y()); + + /* Bypass EMPTY grid */ + if (true == is_empty_type(phy_tile_type)) { + continue; + } + + /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(coord.x(), coord.y())) || + (0 < grids.get_height_offset(coord.x(), coord.y()))) { + continue; + } + + VTR_ASSERT_SAFE(true == module_manager.valid_module_id(child)); + + /* Find all the GPIO ports in the grid module */ + + /* MUST DO: register in io location mapping! + * I/O location mapping is a critical look-up for testbench generators + */ + if (size_t(phy_tile_type->capacity) != + module_manager.io_children(child).size()) { + VTR_LOG("%s[%ld][%ld] capacity: %d while io_child number is %d", + phy_tile_type->name, coord.x(), coord.y(), + phy_tile_type->capacity, + module_manager.io_children(child).size()); + } + VTR_ASSERT(size_t(phy_tile_type->capacity) == + module_manager.io_children(child).size()); + for (ModuleId tile_child : module_manager.io_children(child)) { + /* Note that we should use the subchild of the subchild module when checking the GPIO + * ports. The child module is the tile module while the subchild module is actually the grid-level I/O module, while + * the subchild module is the subtile inside grid-level I/O modules. Note + * that grid-level I/O module contains all the GPIO ports while the + * subtile may have part of it. For example, a grid I/O module may have 24 + * GPINs and 12 GPOUTs, while the first subtile only have 4 GPINs, and the + * second subtile only have 3 GPOUTs. Therefore, to accurately build the + * I/O location map downto subtile level, we need to check the subchild + * module here. + */ + for (size_t isubchild = 0; + isubchild < module_manager.io_children(tile_child).size(); ++isubchild) { + ModuleId subchild = module_manager.io_children(tile_child)[isubchild]; + vtr::Point subchild_coord = + module_manager.io_child_coordinates(tile_child)[isubchild]; + + for (const ModuleManager::e_module_port_type& module_io_port_type : + MODULE_IO_PORT_TYPES) { + for (const ModulePortId& gpio_port_id : + module_manager.module_port_ids_by_type(subchild, + module_io_port_type)) { + /* Only care mappable I/O */ + if (false == + module_manager.port_is_mappable_io(subchild, gpio_port_id)) { + continue; + } + const BasicPort& gpio_port = + module_manager.module_port(subchild, gpio_port_id); + + auto curr_io_index = io_counter.find(gpio_port.get_name()); + /* Index always start from zero */ + if (curr_io_index == io_counter.end()) { + io_counter[gpio_port.get_name()] = 0; + } + /* This is a dirty hack! */ + io_location_map.set_io_index(coord.x(), coord.y(), subchild_coord.x(), + gpio_port.get_name(), + io_counter[gpio_port.get_name()]); + io_counter[gpio_port.get_name()]++; + } + } + } + } + } + + /* Check all the GPIO ports in the top-level module has been mapped */ + for (const ModuleManager::e_module_port_type& module_io_port_type : + MODULE_IO_PORT_TYPES) { + for (const ModulePortId& gpio_port_id : + module_manager.module_port_ids_by_type(top_module, + module_io_port_type)) { + /* Only care mappable I/O */ + if (false == + module_manager.port_is_mappable_io(top_module, gpio_port_id)) { + continue; + } + + const BasicPort& gpio_port = + module_manager.module_port(top_module, gpio_port_id); + VTR_ASSERT(io_counter[gpio_port.get_name()] == gpio_port.get_width()); + } + } + + return io_location_map; +} + +/******************************************************************** + * Top-level function, if tile modules are built under the top-level module + * The data to access for I/O location is different than the fine-grained grid modules + * FIXME: Think about a unified way for the two kinds of fabrics!!! + *******************************************************************/ +IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, + const DeviceGrid& grids, + const bool& tiled_fabric) { + if (tiled_fabric) { + return build_fabric_tiled_io_location_map(module_manager, grids); + } + return build_fabric_fine_grained_io_location_map(module_manager, grids); +} + + } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_fabric_io_location_map.h b/openfpga/src/fabric/build_fabric_io_location_map.h index d84060390..200637e0b 100644 --- a/openfpga/src/fabric/build_fabric_io_location_map.h +++ b/openfpga/src/fabric/build_fabric_io_location_map.h @@ -19,7 +19,8 @@ namespace openfpga { IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, - const DeviceGrid& grids); + const DeviceGrid& grids, + const bool& tiled_fabric); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index d979e29c9..a1d62f49e 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -1071,6 +1071,9 @@ static int build_tile_module( VTR_LOGV(verbose, "Added programmable module '%s' to tile[%lu][%lu]\n", pb_module_name.c_str(), tile_coord.x(), tile_coord.y()); pb_instances.push_back(pb_instance); + /* Add a custom I/O child with the grid */ + module_manager.add_io_child(tile_module, pb_module, pb_instance, + vtr::Point(grid_coord.x(), grid_coord.y())); } } From 4294914987d28ec9ffe119ca5ab0ba18820f9ef4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 24 Jul 2023 14:59:43 -0700 Subject: [PATCH 213/391] [core] fixed compiler warnings --- .../fabric/build_top_module_child_tile_instance.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 103cf0148..f298891c1 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -656,6 +656,8 @@ static int build_top_module_tile_nets_between_cb_and_pb( top_module, net, sink_tile_module, sink_tile_instance_id, sink_grid_port_id, sink_grid_port.pins()[pin_id]); } + VTR_LOGV(verbose, "Built nets between connection block of tile[%lu][%lu] and grid block of tile[%lu][%lu]\n", + src_tile_coord.x(), src_tile_coord.y(), sink_tile_coord.x(), sink_tile_coord.y()); } } return CMD_EXEC_SUCCESS; @@ -896,6 +898,8 @@ static int build_top_module_tile_nets_between_sb_and_cb( tile_instance_id, sb_port_id, itrack / 2); } + VTR_LOGV(verbose, "Built nets between switch block of tile[%lu][%lu] and connection block of tile[%lu][%lu]\n", + sb_tile_coord.x(), sb_tile_coord.y(), cb_tile_coord.x(), cb_tile_coord.y()); } } return CMD_EXEC_SUCCESS; @@ -1068,7 +1072,7 @@ static void organize_top_module_tile_based_memory_modules( } else { VTR_ASSERT(false == positive_direction); /* For negative direction: -----> */ - for (size_t ix = grids.width() - 1; ix >= 0; --ix) { + for (int ix = grids.width() - 1; ix >= 0; --ix) { tile_coords.push_back(vtr::Point(ix, iy)); } } @@ -1108,7 +1112,7 @@ static void organize_top_module_tile_based_memory_modules( * Generate an input port for routing multiplexer inside the tile * which is the middle output of a routing track ********************************************************************/ -ModulePinInfo find_tile_module_chan_port( +static ModulePinInfo find_tile_module_chan_port( const ModuleManager& module_manager, const ModuleId& tile_module, const vtr::Point& cb_coord_in_tile, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const t_rr_type& cb_type, const RRNodeId& chan_rr_node) { @@ -1630,8 +1634,6 @@ static void add_module_nets_connect_tile_direct_connection( /* Find the module name of source clb */ vtr::Point src_clb_coord = tile_direct.from_tile_coordinate(tile_direct_id); - t_physical_tile_type_ptr src_grid_type = - grids.get_physical_type(src_clb_coord.x(), src_clb_coord.y()); FabricTileId src_tile_id = fabric_tile.find_tile_by_pb_coordinate(src_clb_coord); vtr::Point src_tile_coord = fabric_tile.tile_coordinate(src_tile_id); @@ -1657,8 +1659,6 @@ static void add_module_nets_connect_tile_direct_connection( /* Find the module name of sink clb */ vtr::Point des_clb_coord = tile_direct.to_tile_coordinate(tile_direct_id); - t_physical_tile_type_ptr sink_grid_type = - grids.get_physical_type(des_clb_coord.x(), des_clb_coord.y()); FabricTileId des_tile_id = fabric_tile.find_tile_by_pb_coordinate(des_clb_coord); vtr::Point des_tile_coord = fabric_tile.tile_coordinate(des_tile_id); From 48b0ba8b785c218e0ed111937b95f452186e79d1 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 24 Jul 2023 15:00:26 -0700 Subject: [PATCH 214/391] [core] format --- .../src/base/openfpga_build_fabric_template.h | 3 +- .../fabric/build_fabric_io_location_map.cpp | 40 ++++++++++--------- openfpga/src/fabric/build_tile_modules.cpp | 5 ++- .../build_top_module_child_tile_instance.cpp | 16 +++++--- 4 files changed, 37 insertions(+), 27 deletions(-) diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index 3a9460147..31e9c565a 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -186,7 +186,8 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd, /* Build I/O location map */ openfpga_ctx.mutable_io_location_map() = build_fabric_io_location_map( - openfpga_ctx.module_graph(), g_vpr_ctx.device().grid, cmd_context.option_enable(cmd, opt_group_tile)); + openfpga_ctx.module_graph(), g_vpr_ctx.device().grid, + cmd_context.option_enable(cmd, opt_group_tile)); /* Build fabric global port information */ openfpga_ctx.mutable_fabric_global_port_info() = diff --git a/openfpga/src/fabric/build_fabric_io_location_map.cpp b/openfpga/src/fabric/build_fabric_io_location_map.cpp index ed85648ee..275e04e47 100644 --- a/openfpga/src/fabric/build_fabric_io_location_map.cpp +++ b/openfpga/src/fabric/build_fabric_io_location_map.cpp @@ -31,8 +31,8 @@ namespace openfpga { *io_children() list of top-level module. Here we just build a fast lookup from *(x, y, z) coordinate to the actual indices *******************************************************************/ -static IoLocationMap build_fabric_fine_grained_io_location_map(const ModuleManager& module_manager, - const DeviceGrid& grids) { +static IoLocationMap build_fabric_fine_grained_io_location_map( + const ModuleManager& module_manager, const DeviceGrid& grids) { vtr::ScopedStartFinishTimer timer( "Create I/O location mapping for top module"); @@ -152,8 +152,8 @@ static IoLocationMap build_fabric_fine_grained_io_location_map(const ModuleManag *io_children() list of top-level module. Here we just build a fast lookup from *(x, y, z) coordinate to the actual indices *******************************************************************/ -static IoLocationMap build_fabric_tiled_io_location_map(const ModuleManager& module_manager, - const DeviceGrid& grids) { +static IoLocationMap build_fabric_tiled_io_location_map( + const ModuleManager& module_manager, const DeviceGrid& grids) { vtr::ScopedStartFinishTimer timer( "Create I/O location mapping for top module"); @@ -202,18 +202,20 @@ static IoLocationMap build_fabric_tiled_io_location_map(const ModuleManager& mod VTR_ASSERT(size_t(phy_tile_type->capacity) == module_manager.io_children(child).size()); for (ModuleId tile_child : module_manager.io_children(child)) { - /* Note that we should use the subchild of the subchild module when checking the GPIO - * ports. The child module is the tile module while the subchild module is actually the grid-level I/O module, while - * the subchild module is the subtile inside grid-level I/O modules. Note - * that grid-level I/O module contains all the GPIO ports while the - * subtile may have part of it. For example, a grid I/O module may have 24 - * GPINs and 12 GPOUTs, while the first subtile only have 4 GPINs, and the - * second subtile only have 3 GPOUTs. Therefore, to accurately build the - * I/O location map downto subtile level, we need to check the subchild - * module here. + /* Note that we should use the subchild of the subchild module when + * checking the GPIO ports. The child module is the tile module while the + * subchild module is actually the grid-level I/O module, while the + * subchild module is the subtile inside grid-level I/O modules. Note that + * grid-level I/O module contains all the GPIO ports while the subtile may + * have part of it. For example, a grid I/O module may have 24 GPINs and + * 12 GPOUTs, while the first subtile only have 4 GPINs, and the second + * subtile only have 3 GPOUTs. Therefore, to accurately build the I/O + * location map downto subtile level, we need to check the subchild module + * here. */ for (size_t isubchild = 0; - isubchild < module_manager.io_children(tile_child).size(); ++isubchild) { + isubchild < module_manager.io_children(tile_child).size(); + ++isubchild) { ModuleId subchild = module_manager.io_children(tile_child)[isubchild]; vtr::Point subchild_coord = module_manager.io_child_coordinates(tile_child)[isubchild]; @@ -237,9 +239,9 @@ static IoLocationMap build_fabric_tiled_io_location_map(const ModuleManager& mod io_counter[gpio_port.get_name()] = 0; } /* This is a dirty hack! */ - io_location_map.set_io_index(coord.x(), coord.y(), subchild_coord.x(), - gpio_port.get_name(), - io_counter[gpio_port.get_name()]); + io_location_map.set_io_index( + coord.x(), coord.y(), subchild_coord.x(), gpio_port.get_name(), + io_counter[gpio_port.get_name()]); io_counter[gpio_port.get_name()]++; } } @@ -270,7 +272,8 @@ static IoLocationMap build_fabric_tiled_io_location_map(const ModuleManager& mod /******************************************************************** * Top-level function, if tile modules are built under the top-level module - * The data to access for I/O location is different than the fine-grained grid modules + * The data to access for I/O location is different than the fine-grained grid + *modules * FIXME: Think about a unified way for the two kinds of fabrics!!! *******************************************************************/ IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, @@ -282,5 +285,4 @@ IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, return build_fabric_fine_grained_io_location_map(module_manager, grids); } - } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index a1d62f49e..a02069810 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -1072,8 +1072,9 @@ static int build_tile_module( pb_module_name.c_str(), tile_coord.x(), tile_coord.y()); pb_instances.push_back(pb_instance); /* Add a custom I/O child with the grid */ - module_manager.add_io_child(tile_module, pb_module, pb_instance, - vtr::Point(grid_coord.x(), grid_coord.y())); + module_manager.add_io_child( + tile_module, pb_module, pb_instance, + vtr::Point(grid_coord.x(), grid_coord.y())); } } 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 f298891c1..fb653b4d8 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -656,8 +656,11 @@ static int build_top_module_tile_nets_between_cb_and_pb( top_module, net, sink_tile_module, sink_tile_instance_id, sink_grid_port_id, sink_grid_port.pins()[pin_id]); } - VTR_LOGV(verbose, "Built nets between connection block of tile[%lu][%lu] and grid block of tile[%lu][%lu]\n", - src_tile_coord.x(), src_tile_coord.y(), sink_tile_coord.x(), sink_tile_coord.y()); + VTR_LOGV(verbose, + "Built nets between connection block of tile[%lu][%lu] and grid " + "block of tile[%lu][%lu]\n", + src_tile_coord.x(), src_tile_coord.y(), sink_tile_coord.x(), + sink_tile_coord.y()); } } return CMD_EXEC_SUCCESS; @@ -898,8 +901,11 @@ static int build_top_module_tile_nets_between_sb_and_cb( tile_instance_id, sb_port_id, itrack / 2); } - VTR_LOGV(verbose, "Built nets between switch block of tile[%lu][%lu] and connection block of tile[%lu][%lu]\n", - sb_tile_coord.x(), sb_tile_coord.y(), cb_tile_coord.x(), cb_tile_coord.y()); + VTR_LOGV(verbose, + "Built nets between switch block of tile[%lu][%lu] and " + "connection block of tile[%lu][%lu]\n", + sb_tile_coord.x(), sb_tile_coord.y(), cb_tile_coord.x(), + cb_tile_coord.y()); } } return CMD_EXEC_SUCCESS; @@ -1072,7 +1078,7 @@ static void organize_top_module_tile_based_memory_modules( } else { VTR_ASSERT(false == positive_direction); /* For negative direction: -----> */ - for (int ix = grids.width() - 1; ix >= 0; --ix) { + for (size_t ix = grids.width() - 1; ix >= 0; --ix) { tile_coords.push_back(vtr::Point(ix, iy)); } } From 3745897ff6e0f9256062bedba2aabe39fa75aeff Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 24 Jul 2023 16:10:29 -0700 Subject: [PATCH 215/391] [core] fixed a few bugs --- openfpga/src/base/openfpga_naming.cpp | 2 +- openfpga/src/fabric/build_tile_modules.cpp | 2 +- .../build_top_module_child_tile_instance.cpp | 35 ++++++++++--------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index 7153b5b65..784f51685 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -511,7 +511,7 @@ std::string generate_tile_module_name(const vtr::Point& tile_coord) { *********************************************************************/ std::string generate_tile_module_port_name(const std::string& prefix, const std::string& port_name) { - return prefix + port_name; + return prefix + std::string("_") + port_name; } /********************************************************************* diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index a02069810..6fabcf37d 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -202,7 +202,7 @@ static int build_tile_module_port_and_nets_between_sb_and_pb( src_grid_port.set_name(generate_tile_module_port_name( generate_switch_block_module_name( fabric_tile.sb_coordinates(fabric_tile_id)[isb]), - src_grid_port.get_name())); + sink_sb_port.get_name())); ModulePortId src_tile_port_id = module_manager.add_port( tile_module, src_grid_port, ModuleManager::e_module_port_type::MODULE_INPUT_PORT); 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 fb653b4d8..91036a02b 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -572,22 +572,6 @@ static int build_top_module_tile_nets_between_cb_and_pb( enum e_side cb_ipin_side = cb_ipin_sides[iside]; for (size_t inode = 0; inode < module_cb.get_num_ipin_nodes(cb_ipin_side); ++inode) { - /* Collect source-related information */ - RRNodeId module_ipin_node = module_cb.get_ipin_node(cb_ipin_side, inode); - vtr::Point cb_src_port_coord( - rr_graph.node_xlow(module_ipin_node), - rr_graph.node_ylow(module_ipin_node)); - std::string src_cb_port_name = generate_cb_module_grid_port_name( - cb_ipin_side, grids, vpr_device_annotation, rr_graph, module_ipin_node); - std::string src_tile_cb_port_name = generate_tile_module_port_name( - src_cb_instance_name_in_unique_tile, src_cb_port_name); - ModulePortId src_cb_port_id = - module_manager.find_module_port(tile_module, src_cb_port_name); - VTR_ASSERT(true == module_manager.valid_module_port_id(tile_module, - src_cb_port_id)); - BasicPort src_cb_port = - module_manager.module_port(tile_module, src_cb_port_id); - /* Collect sink-related information */ /* Note that we use the instance cb pin here!!! * because it has the correct coordinator for the grid!!! @@ -601,6 +585,23 @@ static int build_top_module_tile_nets_between_cb_and_pb( continue; } + /* Collect source-related information */ + RRNodeId module_ipin_node = module_cb.get_ipin_node(cb_ipin_side, inode); + vtr::Point cb_src_port_coord( + rr_graph.node_xlow(module_ipin_node), + rr_graph.node_ylow(module_ipin_node)); + std::string src_cb_port_name = generate_cb_module_grid_port_name( + cb_ipin_side, grids, vpr_device_annotation, rr_graph, module_ipin_node); + std::string src_tile_cb_port_name = generate_tile_module_port_name( + src_cb_instance_name_in_unique_tile, src_cb_port_name); + VTR_LOGV(verbose, "Finding port '%s' from connection block in tile [%lu][%lu]\n", src_tile_cb_port_name.c_str(), src_tile_coord.x(), src_tile_coord.y()); + ModulePortId src_cb_port_id = + module_manager.find_module_port(tile_module, src_tile_cb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(tile_module, + src_cb_port_id)); + BasicPort src_cb_port = + module_manager.module_port(tile_module, src_cb_port_id); + FabricTileId sink_fabric_tile_id = fabric_tile.find_tile_by_pb_coordinate(grid_coordinate); size_t pb_idx_in_sink_fabric_tile = @@ -636,7 +637,7 @@ static int build_top_module_tile_nets_between_cb_and_pb( std::string sink_tile_grid_port_name = generate_tile_module_port_name( sink_grid_module_name, sink_grid_port_name); ModulePortId sink_grid_port_id = - module_manager.find_module_port(sink_tile_module, sink_grid_port_name); + module_manager.find_module_port(sink_tile_module, sink_tile_grid_port_name); VTR_ASSERT(true == module_manager.valid_module_port_id( sink_tile_module, sink_grid_port_id)); BasicPort sink_grid_port = From b8d080b08e07456999e32886638de43c97c43a85 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 24 Jul 2023 20:40:25 -0700 Subject: [PATCH 216/391] [core] fixed a bug where undriven cb ports are not connected to tile --- openfpga/src/fabric/build_tile_modules.cpp | 236 ++++++++++++++++++ .../build_top_module_child_tile_instance.cpp | 8 +- 2 files changed, 241 insertions(+), 3 deletions(-) diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 6fabcf37d..f6e4d1e81 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -743,6 +743,222 @@ static int build_tile_module_port_and_nets_between_sb_and_cb( return CMD_EXEC_SUCCESS; } +/******************************************************************** + * This function will a port for tile where its connection blocks + * need to drive or to be driven by a switch block which is not in the tile + * + * +------------+ +------------------+ + * | Connection | | | + * | Block |----->| Switch Block | + * | X-direction|<-----| [x][y] | + * | [x][y] | | | + * +------------+ +------------------+ + * + *******************************************************************/ +static int build_tile_module_one_port_from_cb( + ModuleManager& module_manager, const ModuleId& tile_module, + const ModuleId& cb_module, const std::string& chan_port_name, + const vtr::Point& tile_coord, + const std::string& cb_instance_name_in_tile, const size_t& cb_instance, + const bool& frame_view, const bool& verbose) { + std::string cb_module_name = module_manager.module_name(cb_module); + ModulePortId chan_port_id = + module_manager.find_module_port(cb_module, chan_port_name); + BasicPort chan_port = module_manager.module_port(cb_module, chan_port_id); + ModuleManager::e_module_port_type chan_port_type = + module_manager.port_type(cb_module, chan_port_id); + + bool require_port_addition = false; + for (size_t pin_id = 0; pin_id < chan_port.pins().size(); ++pin_id) { + if (module_manager.valid_module_net_id( + tile_module, module_manager.module_instance_port_net( + tile_module, cb_module, cb_instance, chan_port_id, + chan_port.pins()[pin_id]))) { + continue; + } + require_port_addition = true; + break; + } + + /* Early exit if this port is fully connected inside the tile */ + if (!require_port_addition) { + return CMD_EXEC_SUCCESS; + } + + BasicPort tile_chan_port(chan_port); + tile_chan_port.set_name(generate_tile_module_port_name( + cb_instance_name_in_tile, chan_port.get_name())); + + /* Add new port */ + VTR_LOGV(verbose, + "Adding ports '%s' to tile as required by the " + "connection block '%s'...\n", + tile_chan_port.to_verilog_string().c_str(), cb_module_name.c_str()); + /* Create a new port and a new net. FIXME: Create a proper name to + * avoid naming conflicts */ + ModulePortId tile_module_port_id = + module_manager.add_port(tile_module, tile_chan_port, chan_port_type); + + if (!frame_view) { + for (size_t pin_id = 0; pin_id < chan_port.pins().size(); ++pin_id) { + if (module_manager.valid_module_net_id( + tile_module, module_manager.module_instance_port_net( + tile_module, cb_module, cb_instance, chan_port_id, + chan_port.pins()[pin_id]))) { + continue; + } + if (chan_port_type == + ModuleManager::e_module_port_type::MODULE_INPUT_PORT) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, tile_module, 0, tile_module_port_id, + chan_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, cb_module, + cb_instance, chan_port_id, + chan_port.pins()[pin_id]); + } else if (chan_port_type == + ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT) { + ModuleNetId net = create_module_source_pin_net( + module_manager, tile_module, cb_module, cb_instance, chan_port_id, + chan_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(tile_module, net, tile_module, 0, + tile_module_port_id, + chan_port.pins()[pin_id]); + } else { + VTR_LOG_ERROR( + "Expect either input or output port '%s' for cb module '%s' " + "required by tile[%lu][%lu]!\n", + chan_port.to_verilog_string().c_str(), cb_module_name.c_str(), + tile_coord.x(), tile_coord.y()); + return CMD_EXEC_FATAL_ERROR; + } + } + } + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * This function will create ports for tile where its connection blocks + * need to drive or to be driven by a switch block which is not in the tile + * + * +------------+ +------------------+ + * | Connection | | | + * | Block |----->| Switch Block | + * | X-direction|<-----| [x][y] | + * | [x][y] | | | + * +------------+ +------------------+ + * + *******************************************************************/ +static int build_tile_module_ports_from_cb( + ModuleManager& module_manager, const ModuleId& tile_module, + const DeviceRRGSB& device_rr_gsb, const RRGSB& rr_gsb, + const FabricTile& fabric_tile, const FabricTileId& curr_fabric_tile_id, + const t_rr_type& cb_type, + const std::map>& cb_instances, + const size_t& icb, const bool& compact_routing_hierarchy, + const bool& frame_view, const bool& verbose) { + int status = CMD_EXEC_SUCCESS; + + size_t cb_instance = cb_instances.at(cb_type)[icb]; + /* We could have two different coordinators, one is the instance, the other is + * the module */ + vtr::Point instance_cb_coordinate(rr_gsb.get_cb_x(cb_type), + rr_gsb.get_cb_y(cb_type)); + vtr::Point module_gsb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); + + /* Skip those Connection blocks that do not exist */ + if (false == rr_gsb.is_cb_exist(cb_type)) { + return CMD_EXEC_SUCCESS; + } + + /* Skip if the cb does not contain any configuration bits! */ + if (true == connection_block_contain_only_routing_tracks(rr_gsb, cb_type)) { + return CMD_EXEC_SUCCESS; + } + + /* If we use compact routing hierarchy, we should find the unique module of + * CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + vtr::Point gsb_coord(rr_gsb.get_x(), rr_gsb.get_y()); + const RRGSB& unique_mirror = + device_rr_gsb.get_cb_unique_module(cb_type, gsb_coord); + module_gsb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_coordinate.set_y(unique_mirror.get_y()); + } + + /* This is the source cb that is added to the top module */ + const RRGSB& module_cb = device_rr_gsb.get_gsb(module_gsb_coordinate); + vtr::Point module_cb_coordinate(module_cb.get_cb_x(cb_type), + module_cb.get_cb_y(cb_type)); + + /* Collect source-related information */ + std::string cb_module_name = + generate_connection_block_module_name(cb_type, module_cb_coordinate); + ModuleId cb_module = module_manager.find_module(cb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(cb_module)); + + /* Find the instance name for the connection block in the context of the tile + */ + vtr::Point cb_coord_in_unique_tile = + fabric_tile.cb_coordinates(curr_fabric_tile_id, cb_type)[icb]; + std::string cb_instance_name_in_tile = + generate_connection_block_module_name(cb_type, cb_coord_in_unique_tile); + vtr::Point tile_coord = + fabric_tile.tile_coordinate(curr_fabric_tile_id); + + /* Check any track input and output are unconnected in the tile */ + /* Upper input port: W/2 == 0 tracks */ + std::string chan_upper_input_port_name = + generate_cb_module_track_port_name(cb_type, IN_PORT, true); + + /* Check if any of the input port is driven, if not add new port */ + status = build_tile_module_one_port_from_cb( + module_manager, tile_module, cb_module, chan_upper_input_port_name, + tile_coord, cb_instance_name_in_tile, cb_instance, frame_view, verbose); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Lower input port: W/2 == 1 tracks */ + std::string chan_lower_input_port_name = + generate_cb_module_track_port_name(cb_type, IN_PORT, false); + + /* Check if any of the input port is driven, if not add new port */ + status = build_tile_module_one_port_from_cb( + module_manager, tile_module, cb_module, chan_lower_input_port_name, + tile_coord, cb_instance_name_in_tile, cb_instance, frame_view, verbose); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Upper output port: W/2 == 0 tracks */ + std::string chan_upper_output_port_name = + generate_cb_module_track_port_name(cb_type, OUT_PORT, true); + + /* Check if any of the input port is driven, if not add new port */ + status = build_tile_module_one_port_from_cb( + module_manager, tile_module, cb_module, chan_upper_output_port_name, + tile_coord, cb_instance_name_in_tile, cb_instance, frame_view, verbose); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Lower output port: W/2 == 1 tracks */ + std::string chan_lower_output_port_name = + generate_cb_module_track_port_name(cb_type, OUT_PORT, false); + + /* Check if any of the input port is driven, if not add new port */ + status = build_tile_module_one_port_from_cb( + module_manager, tile_module, cb_module, chan_lower_output_port_name, + tile_coord, cb_instance_name_in_tile, cb_instance, frame_view, verbose); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + + return CMD_EXEC_SUCCESS; +} + /******************************************************************** * This function will create nets for the unconnected pins for a programmable *block in a tile This function should be called after the following functions: @@ -981,6 +1197,7 @@ static int build_tile_module_ports_and_nets( } } } + /* Get the submodule of connection blocks one by one, build connections * between sb and cb */ for (size_t isb = 0; isb < fabric_tile.sb_coordinates(fabric_tile_id).size(); @@ -1009,6 +1226,25 @@ static int build_tile_module_ports_and_nets( return CMD_EXEC_FATAL_ERROR; } } + /* Get the submodule of connection blocks one by one, build connections + * between cb and pb */ + for (t_rr_type cb_type : {CHANX, CHANY}) { + for (size_t icb = 0; + icb < fabric_tile.cb_coordinates(fabric_tile_id, cb_type).size(); + ++icb) { + vtr::Point cb_coord = + fabric_tile.cb_coordinates(fabric_tile_id, cb_type)[icb]; + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(cb_coord); + + /* Build any ports missing from connection blocks */ + status_code = build_tile_module_ports_from_cb( + module_manager, tile_module, device_rr_gsb, rr_gsb, fabric_tile, + fabric_tile_id, cb_type, cb_instances, icb, true, frame_view, verbose); + if (status_code != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + } + } return status_code; } 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 91036a02b..a7064284b 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -594,7 +594,9 @@ static int build_top_module_tile_nets_between_cb_and_pb( cb_ipin_side, grids, vpr_device_annotation, rr_graph, module_ipin_node); std::string src_tile_cb_port_name = generate_tile_module_port_name( src_cb_instance_name_in_unique_tile, src_cb_port_name); - VTR_LOGV(verbose, "Finding port '%s' from connection block in tile [%lu][%lu]\n", src_tile_cb_port_name.c_str(), src_tile_coord.x(), src_tile_coord.y()); + VTR_LOGV( + verbose, "Finding port '%s' from connection block in tile [%lu][%lu]\n", + src_tile_cb_port_name.c_str(), src_tile_coord.x(), src_tile_coord.y()); ModulePortId src_cb_port_id = module_manager.find_module_port(tile_module, src_tile_cb_port_name); VTR_ASSERT(true == module_manager.valid_module_port_id(tile_module, @@ -636,8 +638,8 @@ static int build_top_module_tile_nets_between_cb_and_pb( std::string sink_tile_grid_port_name = generate_tile_module_port_name( sink_grid_module_name, sink_grid_port_name); - ModulePortId sink_grid_port_id = - module_manager.find_module_port(sink_tile_module, sink_tile_grid_port_name); + ModulePortId sink_grid_port_id = module_manager.find_module_port( + sink_tile_module, sink_tile_grid_port_name); VTR_ASSERT(true == module_manager.valid_module_port_id( sink_tile_module, sink_grid_port_id)); BasicPort sink_grid_port = From e7d714b94db5254f8803052f7e2b06924a1f17a1 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 24 Jul 2023 21:21:25 -0700 Subject: [PATCH 217/391] [core] fixed a bug on the tile module port addition: some grid output was not pulled out --- openfpga/src/fabric/build_tile_modules.cpp | 36 +++++++++++++------ .../build_top_module_child_tile_instance.cpp | 3 ++ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index f6e4d1e81..6d0497246 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -984,9 +984,10 @@ static int build_tile_module_ports_from_cb( static int build_tile_port_and_nets_from_pb( ModuleManager& module_manager, const ModuleId& tile_module, const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, - const vtr::Point& pb_coord, const std::vector& pb_instances, - const FabricTile& fabric_tile, const FabricTileId& curr_fabric_tile_id, - const size_t& ipb, const bool& frame_view, const bool& verbose) { + const RRGraphView& rr_graph, const vtr::Point& pb_coord, + const std::vector& pb_instances, const FabricTile& fabric_tile, + const FabricTileId& curr_fabric_tile_id, const size_t& ipb, + const bool& frame_view, const bool& verbose) { size_t pb_instance = pb_instances[ipb]; t_physical_tile_type_ptr phy_tile = grids.get_physical_type(pb_coord.x(), pb_coord.y()); @@ -1099,12 +1100,26 @@ static int build_tile_port_and_nets_from_pb( } } else if (module_manager.port_type(pb_module, pb_module_port_id) == ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT) { + /* Note that an output may drive multiple blocks, therefore, we + * cannot just check if there is a net driven by this pin, need to + * check the fannout of the net!!! */ for (size_t pin_id = 0; pin_id < pb_port.pins().size(); ++pin_id) { - if (module_manager.valid_module_net_id( - tile_module, - module_manager.module_instance_port_net( - tile_module, pb_module, pb_instance, pb_module_port_id, - pb_port.pins()[pin_id]))) { + ModuleNetId curr_net = module_manager.module_instance_port_net( + tile_module, pb_module, pb_instance, pb_module_port_id, + pb_port.pins()[pin_id]); + bool require_port_addition = true; + if (module_manager.valid_module_net_id(tile_module, curr_net)) { + size_t num_fanout_in_tile = + module_manager.module_net_sinks(tile_module, curr_net).size(); + RRNodeId rr_node = rr_graph.node_lookup().find_node( + pb_coord.x(), pb_coord.y(), OPIN, ipin, side); + size_t num_fanout_required = + rr_graph.node_out_edges(rr_node).size(); + if (num_fanout_in_tile == num_fanout_required) { + require_port_addition = false; + } + } + if (!require_port_addition) { continue; } VTR_LOGV(verbose, @@ -1220,8 +1235,9 @@ static int build_tile_module_ports_and_nets( vtr::Point pb_coord = fabric_tile.pb_coordinates(fabric_tile_id)[ipb]; status_code = build_tile_port_and_nets_from_pb( - module_manager, tile_module, grids, vpr_device_annotation, pb_coord, - pb_instances, fabric_tile, fabric_tile_id, ipb, frame_view, verbose); + module_manager, tile_module, grids, vpr_device_annotation, rr_graph_view, + pb_coord, pb_instances, fabric_tile, fabric_tile_id, ipb, frame_view, + verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } 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 a7064284b..b9e3306f1 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -400,6 +400,9 @@ static int build_top_module_tile_nets_between_sb_and_pb( src_pb_coord_in_unique_tile); std::string src_tile_grid_port_name = generate_tile_module_port_name( src_grid_module_name, src_grid_port_name); + VTR_LOGV(verbose, "Try to find port '%s' from tile[%lu][%lu]\n", + src_tile_grid_port_name.c_str(), src_tile_coord.x(), + src_tile_coord.y()); ModulePortId src_tile_grid_port_id = module_manager.find_module_port( src_tile_module, src_tile_grid_port_name); VTR_ASSERT(true == module_manager.valid_module_port_id( From 2105abdbca3dc649b96925d6f310f4f980149f05 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 24 Jul 2023 21:26:29 -0700 Subject: [PATCH 218/391] [core] fixed a bug --- openfpga/src/fabric/build_top_module_child_tile_instance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b9e3306f1..48e1b635b 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1084,7 +1084,7 @@ static void organize_top_module_tile_based_memory_modules( } else { VTR_ASSERT(false == positive_direction); /* For negative direction: -----> */ - for (size_t ix = grids.width() - 1; ix >= 0; --ix) { + for (int ix = grids.width() - 1; ix >= 0; --ix) { tile_coords.push_back(vtr::Point(ix, iy)); } } From 64698443c9cf9ff5a5fe87b35ce92eccabbe740f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 24 Jul 2023 22:11:57 -0700 Subject: [PATCH 219/391] [core] fixed a bug on io location map for tile modules --- .../fabric/build_fabric_io_location_map.cpp | 18 +++++++++--------- openfpga/src/fabric/build_tile_modules.cpp | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/openfpga/src/fabric/build_fabric_io_location_map.cpp b/openfpga/src/fabric/build_fabric_io_location_map.cpp index 275e04e47..c1f31f999 100644 --- a/openfpga/src/fabric/build_fabric_io_location_map.cpp +++ b/openfpga/src/fabric/build_fabric_io_location_map.cpp @@ -192,15 +192,6 @@ static IoLocationMap build_fabric_tiled_io_location_map( /* MUST DO: register in io location mapping! * I/O location mapping is a critical look-up for testbench generators */ - if (size_t(phy_tile_type->capacity) != - module_manager.io_children(child).size()) { - VTR_LOG("%s[%ld][%ld] capacity: %d while io_child number is %d", - phy_tile_type->name, coord.x(), coord.y(), - phy_tile_type->capacity, - module_manager.io_children(child).size()); - } - VTR_ASSERT(size_t(phy_tile_type->capacity) == - module_manager.io_children(child).size()); for (ModuleId tile_child : module_manager.io_children(child)) { /* Note that we should use the subchild of the subchild module when * checking the GPIO ports. The child module is the tile module while the @@ -213,6 +204,15 @@ static IoLocationMap build_fabric_tiled_io_location_map( * location map downto subtile level, we need to check the subchild module * here. */ + if (size_t(phy_tile_type->capacity) != + module_manager.io_children(tile_child).size()) { + VTR_LOG("%s[%ld][%ld] capacity: %d while io_child number is %d", + phy_tile_type->name, coord.x(), coord.y(), + phy_tile_type->capacity, + module_manager.io_children(tile_child).size()); + } + VTR_ASSERT(size_t(phy_tile_type->capacity) == + module_manager.io_children(tile_child).size()); for (size_t isubchild = 0; isubchild < module_manager.io_children(tile_child).size(); ++isubchild) { diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 6d0497246..ad21fde0f 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -1352,7 +1352,7 @@ static int build_tile_module( return CMD_EXEC_FATAL_ERROR; } size_t cb_instance = module_manager.num_instance(tile_module, cb_module); - module_manager.add_child_module(tile_module, cb_module); + module_manager.add_child_module(tile_module, cb_module, false); if (0 < find_module_num_config_bits(module_manager, cb_module, circuit_lib, sram_model, sram_orgz_type)) { @@ -1385,7 +1385,7 @@ static int build_tile_module( return CMD_EXEC_FATAL_ERROR; } size_t sb_instance = module_manager.num_instance(tile_module, sb_module); - module_manager.add_child_module(tile_module, sb_module); + module_manager.add_child_module(tile_module, sb_module, false); 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, From 81147c3c3f0c05f471b61279377a4163ac1f6969 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Jul 2023 06:53:46 +0000 Subject: [PATCH 220/391] Bump yosys from `d5d2bf8` to `b04d0e0` Bumps [yosys](https://github.com/YosysHQ/yosys) from `d5d2bf8` to `b04d0e0`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/d5d2bf815ae73cd9e003196e0b880e98b42caa1a...b04d0e09e83102e14a53bb8b8dcc8c35f63b2fbe) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index d5d2bf815..b04d0e09e 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit d5d2bf815ae73cd9e003196e0b880e98b42caa1a +Subproject commit b04d0e09e83102e14a53bb8b8dcc8c35f63b2fbe From 95a32628aba3db95ab4be7c8842953212390dae2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 25 Jul 2023 14:15:15 -0700 Subject: [PATCH 221/391] [core] fixed the bug in arch bitgen due to the tile modules --- .../src/bitstream_manager.cpp | 17 +++++- libs/libfpgabitstream/src/bitstream_manager.h | 8 +++ .../fpga_bitstream/build_device_bitstream.cpp | 18 +++--- .../fpga_bitstream/build_grid_bitstream.cpp | 39 +++++++++++-- .../src/fpga_bitstream/build_grid_bitstream.h | 8 ++- .../build_routing_bitstream.cpp | 57 +++++++++++++------ .../fpga_bitstream/build_routing_bitstream.h | 7 ++- 7 files changed, 116 insertions(+), 38 deletions(-) diff --git a/libs/libfpgabitstream/src/bitstream_manager.cpp b/libs/libfpgabitstream/src/bitstream_manager.cpp index d82c5832f..cf18a6e86 100644 --- a/libs/libfpgabitstream/src/bitstream_manager.cpp +++ b/libs/libfpgabitstream/src/bitstream_manager.cpp @@ -6,6 +6,7 @@ #include #include "vtr_assert.h" +#include "vtr_log.h" /* begin namespace openfpga */ namespace openfpga { @@ -200,10 +201,20 @@ ConfigBlockId BitstreamManager::create_block() { } ConfigBlockId BitstreamManager::add_block(const std::string& block_name) { - ConfigBlockId block = create_block(); - set_block_name(block, block_name); + ConfigBlockId new_block = create_block(); + set_block_name(new_block, block_name); + return new_block; +} - return block; +ConfigBlockId BitstreamManager::find_or_create_child_block( + const ConfigBlockId& block_id, const std::string& child_block_name) { + ConfigBlockId curr_block = find_child_block(block_id, child_block_name); + if (valid_block_id(curr_block)) { + return curr_block; + } + curr_block = add_block(child_block_name); + add_child_block(block_id, curr_block); + return curr_block; } void BitstreamManager::set_block_name(const ConfigBlockId& block_id, diff --git a/libs/libfpgabitstream/src/bitstream_manager.h b/libs/libfpgabitstream/src/bitstream_manager.h index aaf25b14c..4af15084d 100644 --- a/libs/libfpgabitstream/src/bitstream_manager.h +++ b/libs/libfpgabitstream/src/bitstream_manager.h @@ -181,6 +181,11 @@ class BitstreamManager { /* Add a new block of configuration bits to the bitstream manager */ ConfigBlockId add_block(const std::string& block_name); + /* Try to find the child block in a bitstream manager with a given name. If + * not found, create a new child block */ + ConfigBlockId find_or_create_child_block(const ConfigBlockId& block_id, + const std::string& child_block_name); + /* Set a name for a block */ void set_block_name(const ConfigBlockId& block_id, const std::string& block_name); @@ -234,6 +239,9 @@ class BitstreamManager { vtr::vector parent_block_ids_; vtr::vector> child_block_ids_; + /* Fast look-up by block name to ids */ + std::map block_name2ids_; + /* The ids of the inputs of routing multiplexer blocks which is propagated to * outputs By default, it will be -2 (which is invalid) A valid id starts from * -1 -1 indicates an unused routing multiplexer. It will be converted to a diff --git a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp index 0cbbdb242..1e283358e 100644 --- a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp @@ -197,20 +197,22 @@ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx, /* Create bitstream from grids */ VTR_LOGV(verbose, "Building grid bitstream...\n"); - build_grid_bitstream( - bitstream_manager, top_block, openfpga_ctx.module_graph(), - openfpga_ctx.arch().circuit_lib, openfpga_ctx.mux_lib(), - vpr_ctx.device().grid, vpr_ctx.atom(), openfpga_ctx.vpr_device_annotation(), - openfpga_ctx.vpr_clustering_annotation(), - openfpga_ctx.vpr_placement_annotation(), - openfpga_ctx.vpr_bitstream_annotation(), verbose); + build_grid_bitstream(bitstream_manager, top_block, + openfpga_ctx.module_graph(), openfpga_ctx.fabric_tile(), + openfpga_ctx.arch().circuit_lib, openfpga_ctx.mux_lib(), + vpr_ctx.device().grid, vpr_ctx.atom(), + openfpga_ctx.vpr_device_annotation(), + openfpga_ctx.vpr_clustering_annotation(), + openfpga_ctx.vpr_placement_annotation(), + openfpga_ctx.vpr_bitstream_annotation(), verbose); VTR_LOGV(verbose, "Done\n"); /* Create bitstream from routing architectures */ VTR_LOGV(verbose, "Building routing bitstream...\n"); build_routing_bitstream( bitstream_manager, top_block, openfpga_ctx.module_graph(), - openfpga_ctx.arch().circuit_lib, openfpga_ctx.mux_lib(), vpr_ctx.atom(), + openfpga_ctx.fabric_tile(), openfpga_ctx.arch().circuit_lib, + openfpga_ctx.mux_lib(), vpr_ctx.atom(), openfpga_ctx.vpr_device_annotation(), openfpga_ctx.vpr_routing_annotation(), vpr_ctx.device().rr_graph, openfpga_ctx.device_rr_gsb(), openfpga_ctx.flow_manager().compress_routing(), verbose); diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index 35a6556dc..a5a6ea97e 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -819,9 +819,10 @@ static void build_physical_block_bitstream( *******************************************************************/ void build_grid_bitstream( BitstreamManager& bitstream_manager, const ConfigBlockId& top_block, - const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, const DeviceGrid& grids, - const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, + const ModuleManager& module_manager, const FabricTile& fabric_tile, + const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, + const DeviceGrid& grids, const AtomContext& atom_ctx, + const VprDeviceAnnotation& device_annotation, const VprClusteringAnnotation& cluster_annotation, const VprPlacementAnnotation& place_annotation, const VprBitstreamAnnotation& bitstream_annotation, const bool& verbose) { @@ -841,8 +842,22 @@ void build_grid_bitstream( } /* Add a grid module to top_module*/ vtr::Point grid_coord(ix, iy); + /* TODO: If the fabric tile is not empty, find the tile module and create + * the block accordingly. Also to support future hierarchy changes, when + * creating the blocks, trace backward until reach the current top block. + * If any block is missing during the back tracing, create it. */ + ConfigBlockId parent_block = top_block; + FabricTileId curr_tile = + fabric_tile.find_tile_by_pb_coordinate(grid_coord); + if (fabric_tile.valid_tile_id(curr_tile)) { + vtr::Point tile_coord = fabric_tile.tile_coordinate(curr_tile); + std::string tile_inst_name = generate_tile_module_name(tile_coord); + parent_block = bitstream_manager.find_or_create_child_block( + top_block, tile_inst_name); + } + build_physical_block_bitstream( - bitstream_manager, top_block, module_manager, circuit_lib, mux_lib, + bitstream_manager, parent_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, cluster_annotation, place_annotation, bitstream_annotation, grids, grid_coord, NUM_SIDES); } @@ -868,8 +883,22 @@ void build_grid_bitstream( (0 < grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { continue; } + /* TODO: If the fabric tile is not empty, find the tile module and create + * the block accordingly. Also to support future hierarchy changes, when + * creating the blocks, trace backward until reach the current top block. + * If any block is missing during the back tracing, create it. */ + ConfigBlockId parent_block = top_block; + FabricTileId curr_tile = + fabric_tile.find_tile_by_pb_coordinate(io_coordinate); + if (fabric_tile.valid_tile_id(curr_tile)) { + vtr::Point tile_coord = fabric_tile.tile_coordinate(curr_tile); + std::string tile_inst_name = generate_tile_module_name(tile_coord); + parent_block = bitstream_manager.find_or_create_child_block( + top_block, tile_inst_name); + } + build_physical_block_bitstream( - bitstream_manager, top_block, module_manager, circuit_lib, mux_lib, + bitstream_manager, parent_block, module_manager, circuit_lib, mux_lib, atom_ctx, device_annotation, cluster_annotation, place_annotation, bitstream_annotation, grids, io_coordinate, io_side); } diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.h b/openfpga/src/fpga_bitstream/build_grid_bitstream.h index 36597918a..e6b882fcd 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.h +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.h @@ -9,6 +9,7 @@ #include "bitstream_manager.h" #include "circuit_library.h" #include "device_grid.h" +#include "fabric_tile.h" #include "module_manager.h" #include "mux_library.h" #include "vpr_bitstream_annotation.h" @@ -26,9 +27,10 @@ namespace openfpga { void build_grid_bitstream( BitstreamManager& bitstream_manager, const ConfigBlockId& top_block, - const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, const DeviceGrid& grids, - const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, + const ModuleManager& module_manager, const FabricTile& fabric_tile, + const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, + const DeviceGrid& grids, const AtomContext& atom_ctx, + const VprDeviceAnnotation& device_annotation, const VprClusteringAnnotation& cluster_annotation, const VprPlacementAnnotation& place_annotation, const VprBitstreamAnnotation& bitstream_annotation, const bool& verbose); diff --git a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp index c2d2e18ae..2740fad12 100644 --- a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp @@ -433,9 +433,9 @@ static void build_connection_block_bitstream( static void build_connection_block_bitstreams( BitstreamManager& bitstream_manager, const ConfigBlockId& top_configurable_block, - const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, const AtomContext& atom_ctx, - const VprDeviceAnnotation& device_annotation, + const ModuleManager& module_manager, const FabricTile& fabric_tile, + const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, + const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, const VprRoutingAnnotation& routing_annotation, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const bool& compact_routing_hierarchy, const t_rr_type& cb_type, const bool& verbose) { @@ -491,13 +491,26 @@ static void build_connection_block_bitstreams( continue; } + /* TODO: If the fabric tile is not empty, find the tile module and create + * the block accordingly. Also to support future hierarchy changes, when + * creating the blocks, trace backward until reach the current top block. + * If any block is missing during the back tracing, create it. */ + ConfigBlockId parent_block = top_configurable_block; + FabricTileId curr_tile = fabric_tile.find_tile_by_cb_coordinate( + cb_type, vtr::Point(ix, iy)); + if (fabric_tile.valid_tile_id(curr_tile)) { + vtr::Point tile_coord = fabric_tile.tile_coordinate(curr_tile); + std::string tile_inst_name = generate_tile_module_name(tile_coord); + parent_block = bitstream_manager.find_or_create_child_block( + top_configurable_block, tile_inst_name); + } + /* Create a block for the bitstream which corresponds to the Switch block */ ConfigBlockId cb_configurable_block = bitstream_manager.add_block( generate_connection_block_module_name(cb_type, cb_coord)); /* Set switch block as a child of top block */ - bitstream_manager.add_child_block(top_configurable_block, - cb_configurable_block); + bitstream_manager.add_child_block(parent_block, cb_configurable_block); /* Reserve child blocks for new created block */ bitstream_manager.reserve_child_blocks( @@ -524,9 +537,9 @@ static void build_connection_block_bitstreams( void build_routing_bitstream( BitstreamManager& bitstream_manager, const ConfigBlockId& top_configurable_block, - const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, const AtomContext& atom_ctx, - const VprDeviceAnnotation& device_annotation, + const ModuleManager& module_manager, const FabricTile& fabric_tile, + const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, + const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, const VprRoutingAnnotation& routing_annotation, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const bool& compact_routing_hierarchy, const bool& verbose) { @@ -573,13 +586,25 @@ void build_routing_bitstream( continue; } + /* TODO: If the fabric tile is not empty, find the tile module and create + * the block accordingly. Also to support future hierarchy changes, when + * creating the blocks, trace backward until reach the current top block. + * If any block is missing during the back tracing, create it. */ + ConfigBlockId parent_block = top_configurable_block; + FabricTileId curr_tile = fabric_tile.find_tile_by_sb_coordinate(sb_coord); + if (fabric_tile.valid_tile_id(curr_tile)) { + vtr::Point tile_coord = fabric_tile.tile_coordinate(curr_tile); + std::string tile_inst_name = generate_tile_module_name(tile_coord); + parent_block = bitstream_manager.find_or_create_child_block( + top_configurable_block, tile_inst_name); + } + /* Create a block for the bitstream which corresponds to the Switch block */ ConfigBlockId sb_configurable_block = bitstream_manager.add_block( generate_switch_block_module_name(sb_coord)); /* Set switch block as a child of top block */ - bitstream_manager.add_child_block(top_configurable_block, - sb_configurable_block); + bitstream_manager.add_child_block(parent_block, sb_configurable_block); /* Reserve child blocks for new created block */ bitstream_manager.reserve_child_blocks( @@ -605,17 +630,17 @@ void build_routing_bitstream( VTR_LOG("Generating bitstream for X-direction Connection blocks ..."); build_connection_block_bitstreams( - bitstream_manager, top_configurable_block, module_manager, circuit_lib, - mux_lib, atom_ctx, device_annotation, routing_annotation, rr_graph, - device_rr_gsb, compact_routing_hierarchy, CHANX, verbose); + bitstream_manager, top_configurable_block, module_manager, fabric_tile, + circuit_lib, mux_lib, atom_ctx, device_annotation, routing_annotation, + rr_graph, device_rr_gsb, compact_routing_hierarchy, CHANX, verbose); VTR_LOG("Done\n"); VTR_LOG("Generating bitstream for Y-direction Connection blocks ..."); build_connection_block_bitstreams( - bitstream_manager, top_configurable_block, module_manager, circuit_lib, - mux_lib, atom_ctx, device_annotation, routing_annotation, rr_graph, - device_rr_gsb, compact_routing_hierarchy, CHANY, verbose); + bitstream_manager, top_configurable_block, module_manager, fabric_tile, + circuit_lib, mux_lib, atom_ctx, device_annotation, routing_annotation, + rr_graph, device_rr_gsb, compact_routing_hierarchy, CHANY, verbose); VTR_LOG("Done\n"); } diff --git a/openfpga/src/fpga_bitstream/build_routing_bitstream.h b/openfpga/src/fpga_bitstream/build_routing_bitstream.h index 433c8dbe5..b268acd79 100644 --- a/openfpga/src/fpga_bitstream/build_routing_bitstream.h +++ b/openfpga/src/fpga_bitstream/build_routing_bitstream.h @@ -12,6 +12,7 @@ #include "bitstream_manager.h" #include "circuit_library.h" #include "device_rr_gsb.h" +#include "fabric_tile.h" #include "module_manager.h" #include "mux_library.h" #include "vpr_context.h" @@ -28,9 +29,9 @@ namespace openfpga { void build_routing_bitstream( BitstreamManager& bitstream_manager, const ConfigBlockId& top_configurable_block, - const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, const AtomContext& atom_ctx, - const VprDeviceAnnotation& device_annotation, + const ModuleManager& module_manager, const FabricTile& fabric_tile, + const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, + const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, const VprRoutingAnnotation& routing_annotation, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const bool& compact_routing_hierarchy, const bool& verbose); From 6ecbbb3a94880607418be52ffdd47b6dd8989692 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 25 Jul 2023 14:49:12 -0700 Subject: [PATCH 222/391] [core] fixed a bug in fabric bitgen due to tile modules --- openfpga/src/fabric/build_tile_modules.cpp | 15 +++++++++++++++ .../fpga_bitstream/build_fabric_bitstream.cpp | 19 ++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index ad21fde0f..22f957af8 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -1314,6 +1314,11 @@ static int build_tile_module( } size_t pb_instance = module_manager.num_instance(tile_module, pb_module); module_manager.add_child_module(tile_module, pb_module); + std::string pb_instance_name = generate_grid_block_instance_name( + std::string(GRID_MODULE_NAME_PREFIX), std::string(phy_tile->name), + is_io_type(phy_tile), grid_side, grid_coord); + module_manager.set_child_instance_name(tile_module, pb_module, + pb_instance, pb_instance_name); if (0 < find_module_num_config_bits(module_manager, pb_module, circuit_lib, sram_model, sram_orgz_type)) { @@ -1353,6 +1358,11 @@ static int build_tile_module( } size_t cb_instance = module_manager.num_instance(tile_module, cb_module); module_manager.add_child_module(tile_module, cb_module, false); + const RRGSB& inst_rr_gsb = device_rr_gsb.get_gsb(cb_coord); + std::string cb_instance_name = generate_connection_block_module_name( + cb_type, inst_rr_gsb.get_cb_coordinate(cb_type)); + module_manager.set_child_instance_name(tile_module, cb_module, + cb_instance, cb_instance_name); if (0 < find_module_num_config_bits(module_manager, cb_module, circuit_lib, sram_model, sram_orgz_type)) { @@ -1386,6 +1396,11 @@ static int build_tile_module( } size_t sb_instance = module_manager.num_instance(tile_module, sb_module); module_manager.add_child_module(tile_module, sb_module, false); + const RRGSB& inst_rr_gsb = device_rr_gsb.get_gsb(sb_coord); + std::string sb_instance_name = + generate_switch_block_module_name(inst_rr_gsb.get_sb_coordinate()); + module_manager.set_child_instance_name(tile_module, sb_module, sb_instance, + 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, diff --git a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp index b4d526627..a5341217f 100644 --- a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp @@ -39,7 +39,7 @@ static void rec_build_module_fabric_dependent_chain_bitstream( const ModuleManager& module_manager, const ModuleId& top_module, const ModuleId& parent_module, const ConfigRegionId& config_region, FabricBitstream& fabric_bitstream, - const FabricBitRegionId& fabric_bitstream_region) { + const FabricBitRegionId& fabric_bitstream_region, const bool& verbose) { /* Depth-first search: if we have any children in the parent_block, * we dive to the next level first! */ @@ -69,7 +69,7 @@ static void rec_build_module_fabric_dependent_chain_bitstream( rec_build_module_fabric_dependent_chain_bitstream( bitstream_manager, child_block, module_manager, top_module, child_module, config_region, fabric_bitstream, - fabric_bitstream_region); + fabric_bitstream_region, verbose); } } else { for (size_t child_id = 0; @@ -87,6 +87,11 @@ static void rec_build_module_fabric_dependent_chain_bitstream( /* Find the child block that matches the instance name! */ ConfigBlockId child_block = bitstream_manager.find_child_block(parent_block, instance_name); + VTR_LOGV(verbose, + "Try to find a configurable block corresponding to module " + "'%s' in FPGA fabric under its parent block '%s'\n", + instance_name.c_str(), + bitstream_manager.block_name(parent_block).c_str()); /* We must have one valid block id! */ VTR_ASSERT(true == bitstream_manager.valid_block_id(child_block)); @@ -94,7 +99,7 @@ static void rec_build_module_fabric_dependent_chain_bitstream( rec_build_module_fabric_dependent_chain_bitstream( bitstream_manager, child_block, module_manager, top_module, child_module, config_region, fabric_bitstream, - fabric_bitstream_region); + fabric_bitstream_region, verbose); } } /* Ensure that there should be no configuration bits in the parent block */ @@ -534,7 +539,7 @@ static void build_module_fabric_dependent_bitstream( const ConfigProtocol& config_protocol, const CircuitLibrary& circuit_lib, const BitstreamManager& bitstream_manager, const ConfigBlockId& top_block, const ModuleManager& module_manager, const ModuleId& top_module, - FabricBitstream& fabric_bitstream) { + FabricBitstream& fabric_bitstream, const bool& verbose) { switch (config_protocol.type()) { case CONFIG_MEM_STANDALONE: { /* Reserve bits before build-up */ @@ -546,7 +551,7 @@ static void build_module_fabric_dependent_bitstream( fabric_bitstream.add_region(); rec_build_module_fabric_dependent_chain_bitstream( bitstream_manager, top_block, module_manager, top_module, top_module, - config_region, fabric_bitstream, fabric_bitstream_region); + config_region, fabric_bitstream, fabric_bitstream_region, verbose); } break; @@ -561,7 +566,7 @@ static void build_module_fabric_dependent_bitstream( fabric_bitstream.add_region(); rec_build_module_fabric_dependent_chain_bitstream( bitstream_manager, top_block, module_manager, top_module, top_module, - config_region, fabric_bitstream, fabric_bitstream_region); + config_region, fabric_bitstream, fabric_bitstream_region, verbose); fabric_bitstream.reverse_region_bits(fabric_bitstream_region); } break; @@ -791,7 +796,7 @@ FabricBitstream build_fabric_dependent_bitstream( /* Start build-up formally */ build_module_fabric_dependent_bitstream( config_protocol, circuit_lib, bitstream_manager, top_block, module_manager, - top_module, fabric_bitstream); + top_module, fabric_bitstream, verbose); VTR_LOGV(verbose, "Built %lu configuration bits for fabric\n", fabric_bitstream.num_bits()); From de6956530f4cfac86f5e6f4aca46e0414fd33bc0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 25 Jul 2023 15:38:41 -0700 Subject: [PATCH 223/391] [core] disable pnr sdc for tile-based fabric --- openfpga/src/base/openfpga_sdc_template.h | 9 ++++---- openfpga/src/fpga_sdc/pnr_sdc_writer.cpp | 25 +++++++++++++++-------- openfpga/src/fpga_sdc/pnr_sdc_writer.h | 17 ++++++++------- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/openfpga/src/base/openfpga_sdc_template.h b/openfpga/src/base/openfpga_sdc_template.h index fe3a25be6..64ac33d9e 100644 --- a/openfpga/src/base/openfpga_sdc_template.h +++ b/openfpga/src/base/openfpga_sdc_template.h @@ -96,11 +96,12 @@ int write_pnr_sdc_template(const T& openfpga_ctx, const Command& cmd, /* Execute only when sdc is enabled */ if (true == options.generate_sdc_pnr()) { - print_pnr_sdc( + return print_pnr_sdc( options, g_vpr_ctx.device(), openfpga_ctx.vpr_device_annotation(), - openfpga_ctx.device_rr_gsb(), openfpga_ctx.module_graph(), - openfpga_ctx.mux_lib(), openfpga_ctx.arch().circuit_lib, - openfpga_ctx.fabric_global_port_info(), openfpga_ctx.simulation_setting(), + openfpga_ctx.fabric_tile(), openfpga_ctx.device_rr_gsb(), + openfpga_ctx.module_graph(), openfpga_ctx.mux_lib(), + openfpga_ctx.arch().circuit_lib, openfpga_ctx.fabric_global_port_info(), + openfpga_ctx.simulation_setting(), openfpga_ctx.flow_manager().compress_routing()); } diff --git a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp index 0a6ad650b..7a862ae73 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp +++ b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp @@ -13,6 +13,7 @@ #include /* Headers from vtrutil library */ +#include "command_exit_codes.h" #include "vtr_assert.h" #include "vtr_log.h" #include "vtr_time.h" @@ -338,15 +339,19 @@ static void print_pnr_sdc_compact_routing_disable_switch_block_outputs( * 3. Design constraints for Connection Blocks * 4. Design constraints for breaking the combinational loops in FPGA fabric *******************************************************************/ -void print_pnr_sdc(const PnrSdcOption& sdc_options, - const DeviceContext& device_ctx, - const VprDeviceAnnotation& device_annotation, - const DeviceRRGSB& device_rr_gsb, - const ModuleManager& module_manager, - const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, - const FabricGlobalPortInfo& global_ports, - const SimulationSetting& sim_setting, - const bool& compact_routing_hierarchy) { +int print_pnr_sdc( + const PnrSdcOption& sdc_options, const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, const FabricTile& fabric_tile, + const DeviceRRGSB& device_rr_gsb, const ModuleManager& module_manager, + const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, + const FabricGlobalPortInfo& global_ports, + const SimulationSetting& sim_setting, const bool& compact_routing_hierarchy) { + /* Fabric tile is not supported yet, error out */ + if (!fabric_tile.empty()) { + VTR_LOG_ERROR("Tile-based modules are not supported yet!\n"); + return CMD_EXEC_FATAL_ERROR; + } + std::string top_module_name = generate_fpga_top_module_name(); ModuleId top_module = module_manager.find_module(top_module_name); VTR_ASSERT(true == module_manager.valid_module_id(top_module)); @@ -453,6 +458,8 @@ void print_pnr_sdc(const PnrSdcOption& sdc_options, print_pnr_sdc_grid_hierarchy(sdc_options.sdc_dir(), device_ctx, device_annotation, module_manager, top_module); } + + return CMD_EXEC_SUCCESS; } } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_sdc/pnr_sdc_writer.h b/openfpga/src/fpga_sdc/pnr_sdc_writer.h index 00c1642eb..abf995866 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_writer.h +++ b/openfpga/src/fpga_sdc/pnr_sdc_writer.h @@ -10,6 +10,7 @@ #include "circuit_library.h" #include "device_rr_gsb.h" #include "fabric_global_port_info.h" +#include "fabric_tile.h" #include "module_manager.h" #include "mux_library.h" #include "pnr_sdc_option.h" @@ -24,15 +25,13 @@ /* begin namespace openfpga */ namespace openfpga { -void print_pnr_sdc(const PnrSdcOption& sdc_options, - const DeviceContext& device_ctx, - const VprDeviceAnnotation& device_annotation, - const DeviceRRGSB& device_rr_gsb, - const ModuleManager& module_manager, - const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, - const FabricGlobalPortInfo& global_ports, - const SimulationSetting& sim_setting, - const bool& compact_routing_hierarchy); +int print_pnr_sdc( + const PnrSdcOption& sdc_options, const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, const FabricTile& fabric_tile, + const DeviceRRGSB& device_rr_gsb, const ModuleManager& module_manager, + const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, + const FabricGlobalPortInfo& global_ports, + const SimulationSetting& sim_setting, const bool& compact_routing_hierarchy); } /* end namespace openfpga */ From 523cf83cc92ae57d4aa75c19db1e0555ad09369b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 25 Jul 2023 15:39:25 -0700 Subject: [PATCH 224/391] [test] disable pnr writer in test cases --- .../group_tile_full_testbench_example_script.openfpga | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openfpga_flow/openfpga_shell_scripts/group_tile_full_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/group_tile_full_testbench_example_script.openfpga index 00f0d3f0e..33ab79d65 100644 --- a/openfpga_flow/openfpga_shell_scripts/group_tile_full_testbench_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/group_tile_full_testbench_example_script.openfpga @@ -56,7 +56,8 @@ write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VE # Write the SDC files for PnR backend # - Turn on every options here -write_pnr_sdc --file ./SDC +# 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 From 0db4ef62e87f753756a1e54ad4b5be74c1fb920f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 25 Jul 2023 15:48:14 -0700 Subject: [PATCH 225/391] [test] add a new test for tile-based fabric: using preconfig testbenches --- ...reconfig_testbench_example_script.openfpga | 73 +++++++++++++++++++ .../regression_test_scripts/basic_reg_test.sh | 4 +- .../config/task.conf | 36 +++++++++ .../config/tile_config.xml | 1 + 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 openfpga_flow/openfpga_shell_scripts/group_tile_preconfig_testbench_example_script.openfpga create mode 100644 openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_preconfig/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_preconfig/config/tile_config.xml diff --git a/openfpga_flow/openfpga_shell_scripts/group_tile_preconfig_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/group_tile_preconfig_testbench_example_script.openfpga new file mode 100644 index 000000000..2bd89a97c --- /dev/null +++ b/openfpga_flow/openfpga_shell_scripts/group_tile_preconfig_testbench_example_script.openfpga @@ -0,0 +1,73 @@ +# 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_tile ${OPENFPGA_GROUP_TILE_CONFIG_FILE} #--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_preconfigured_fabric_wrapper --embed_bitstream iverilog --file ./SRC --explicit_port_mapping +write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --explicit_port_mapping + +# 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 a576bdd43..a590bb7b0 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -175,8 +175,10 @@ echo -e "Testing tiles with I/O in center grid"; run-task basic_tests/tile_organization/tileable_io $@ echo -e "Testing tiles with I/O consisting of subtiles"; run-task basic_tests/tile_organization/io_subtile $@ -echo -e "Testing tile grouping on a homogeneous FPGA fabric"; +echo -e "Testing tile grouping on a homogeneous FPGA fabric (Full testbench)"; run-task basic_tests/tile_organization/homo_fabric_tile $@ +echo -e "Testing tile grouping on a homogeneous FPGA fabric (Preconfigured testbench)"; +run-task basic_tests/tile_organization/homo_fabric_tile_preconfig $@ echo -e "Testing global port definition from tiles"; run-task basic_tests/global_tile_ports/global_tile_clock $@ diff --git a/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_preconfig/config/task.conf b/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_preconfig/config/task.conf new file mode 100644 index 000000000..024c2e58a --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_preconfig/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_tile_preconfig_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_file=${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= +vpr_fpga_verilog_formal_verification_top_netlist= diff --git a/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_preconfig/config/tile_config.xml b/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_preconfig/config/tile_config.xml new file mode 100644 index 000000000..1a1f3f6e8 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_preconfig/config/tile_config.xml @@ -0,0 +1 @@ + From 83428a209e114c03e28b65d051384a378fb00e7f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 25 Jul 2023 16:03:50 -0700 Subject: [PATCH 226/391] [core] fixed a bug on io indexing which causes tile-based test cases failed in dv --- openfpga/src/fabric/build_tile_modules.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 22f957af8..6f7d834c8 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -1313,7 +1313,7 @@ static int build_tile_module( return CMD_EXEC_FATAL_ERROR; } size_t pb_instance = module_manager.num_instance(tile_module, pb_module); - module_manager.add_child_module(tile_module, pb_module); + module_manager.add_child_module(tile_module, pb_module, false); std::string pb_instance_name = generate_grid_block_instance_name( std::string(GRID_MODULE_NAME_PREFIX), std::string(phy_tile->name), is_io_type(phy_tile), grid_side, grid_coord); From 589d73d7aebb1c630ab40dd07175bb7d9d75f207 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 25 Jul 2023 18:52:56 -0700 Subject: [PATCH 227/391] [doc] add file format for tile config tile and new option to ``build_fabric`` command --- .../figures/tile_style_top_left.png | Bin 0 -> 101674 bytes docs/source/manual/file_formats/index.rst | 2 ++ .../openfpga_commands/setup_commands.rst | 8 ++++++++ 3 files changed, 10 insertions(+) create mode 100644 docs/source/manual/file_formats/figures/tile_style_top_left.png diff --git a/docs/source/manual/file_formats/figures/tile_style_top_left.png b/docs/source/manual/file_formats/figures/tile_style_top_left.png new file mode 100644 index 0000000000000000000000000000000000000000..1e0f66575713f3953dbb849c85056ba381c067d0 GIT binary patch literal 101674 zcmZsCby!=?6D}=QiaROp?i9D;Qlvl&#oZ;idkOAV+}&v@uECw6#Y1ro9{i@?@817z z^5i+^*|TSM&zzZe-<_F6s;kOlzJ2!=0RaJ1Q9(un0Rb6|fPhH#1`*z}G#O zzPchHQ1t!#y>gs(7l-#iQ2(eU3%@uqF%j_cvQ|TPM~CntE{-t!8bSZ%-)9Hm>O-(vFI_>T4=jZ3Mv$NPpi0SF+h)9S^N=j&GZ=nka9@ei6 z3=E#0pQNOu=ouIu?jNl55b*H{`s)!`Xc6_c^mB7_OOg?$S`nDvq9EY&BfUmQPENk# z#8g#PMSt@O03f@)xs?=prHYMla``fRJ&cQkw6n95;)#d5fd887wQtqSE4o(*8VLOS z4&4hcjysNS6(eaOj?F$n#S{RamJ8U8RSZYqBzBs_mQ@9ysI;oz8u&8LA7 zR^Gp7_kKa1MFo&xWJDquWxR;JMYgxMf80c9zif|`066Qwc zGB!piL9oF^nf?2sl&Sk8hh{Fpwyh#L`p}h#=r2d z3hht6QcO#ulA=O0eK8v!C*oFaXlTG>vUx}foR~0=J5CrYCnR-vK@ckV_U)UnhsUo1 zg!%b-O~QBHp5D{b(>-YvSQamlR8(85D+n^Z-#cGytT;f2;T4gD4jUT^f*2bV2vigZ zN>T`DZxF=3y+}!gyOq5h9UUPVBE7fadjL7}5_3{elYjluuB@CTE-v2M+Bz~a66Es6 zXWusf52>vLK}kv4%xv_dD8`so7qgad1qJS(KYt`8C6ABYd|)tr4TQzTMZKRo2%O=~ z&COElGI~5J?e7p8%Q@2s5Y{%f9`oX5WMsr$g2lwhH8eEF#>R5vv48ydac&|Uma6aq zJ$(`rlbV(Z4id$;d2uRwLBL^qfvmiFyy`)~g>GUTl+QViS#&91Es`+8jb8hlBsm#-I7ucKHBBsm57aQW5BMxO+) z`fRqz@jPCxs)8woZzRcm@{3e4oT{Yt>3tdrF87co(5 zZBr6Tm|wc4Af8M}r}9X0uhIym%yq@WWH z@>|s{f3Xp(C#hZVW0%9M4xJmZmC=1M@yhtYCkiSP6?Jfg}@e>zmA5hr?qY8dETtL}`w4pA6^cxT!`7!v#Lk`a*8>v7;M z)mp>kUUrjUT4)-?aPfUjSwAOdn%ok>c^G+e=Utign~%(voB-`U9?@D)dtLk+NlmUt zvpTarjBv`W;@3u9I8J}ge|PlqtRDTaywx9z*W1%IlV-e(j=LFGz9A54UH6Qg|3Yp9 zFmzx16B0U$IMD+fXk>#AOm5lY>$R6eAKfXk#^VPZR3?-sXCx7y)cGY{Y{`#{x|EdV zQ2pyrVGLT?K1oEDO<|1!QoB}L>wyj+CxM?tw|`(@yD7|~^a^Gh6im#?-FqVg#Chol z2MUDH+c?acoaxPG-;F?99WrtXYD&gae|i_j7YN@1Z|>z}B7HnFE6&zTZ)!0h^#GFE z>IG9{7$0@`k%2Lw_E!E9NnJiMdG$&siBz-teF4t-F zDJCP>^-yd+qp>`x#%_El*tTP{pT5sld&IGG3DE$*)Vq@a`mv)cN|lr}cTBxaE{Y4# z*1@Isih;ixjU>Qe=8PH%ro>p8mU5E@B+KWLK4h6vU-20@NDzQ zGzg(D7#QK7gSb~8#I~;G->Ff8xPzgoPz3b^KeyqKQdyV}ibF#@ufJ#4M z!OfF;(|Z}d7>LGs-u17wx@fMxa(+AZ$)R2G)|A%5L@ndi)>*$hYCS7^^e+OmdfuG3 z$Tfupy->7aOBaoO9xlJ-Yt`P226G~k#4~0aMjOP?MMQhDNn$6Ba=_tQ!ibDtBMEj( zRaK-Hm_+=8jWf|1Qs=BFp+=f8l~e!Qc)u5`YP){(cpQ`}f3m3s&XOY^ow22&^q)~6 z6!2Q1%m+U3-ex;*y`xtn{*7|AoNW7eDA9C4Rk%w3AHi&S=y$eQ(E$rD2UST36)_TZ?gI6|WlZ)5OlBD09 z1sN^5anlDO`WI`GO-)Fo<51Ecg2E!nWY0NlO^<1nk*Q#(NsWz&;jtW|G3du9Ah99R z53*;=w5R|%VpoMxNbOkiQwOom2ZK?*``A0Ywa%+r0hoAq+QV{MO&jXq%=hFh=U|^J z$`ZiG{@|Zc=xe0GU~@G7?~o$+II{Tvq>njhsjz{&(@VN=6K_%HY#K1SO?*MQUie4o zv6{b29{Hu;7a0|MV15GOfA^>Th5(K>f8xq@`qD^dSl<11)P;1V!~G~Yh+A%2Mqgj+ zU3$jH{t{=w^y;$677H(;;_bZKDS_vM8?v}e5Co9fu*O67ohR36@r}W#vl1cA81q{{ z4FQ}#QI)5`1Ag-zCw?`h?uMe?A(n2shU}Z9;jWnOe7MaRjC^YzPQ@#^vd`l^ty*85 z*wV+W@)S~=NJ3)a#g{auv87W>Nr-LALsr|hB4$E){6?cJ`N*EhyAj~=$Yov}_*IRM8U zV8?qifOv+JMS;3SXj9D1pnJ=c17R;kOI%%a_|a1B|UfH@m@;4u>Iaz=$vV&-#V-J#wx zQFSLO$4so%?j?jjS5%xkz@QXJtK`78edZ)Wrdmxs$dkQv2fmm>l0K6wiTW$r+iR5V zj-p8!fOwEk+VDTw&2ZTeXM!wvqU_EnW-Eci4M7lAc z)D||mjZ^tB;X<-HA79s;0bwK!JfiUudDw3KpNRbnzmJr!H`T!I@t>i|3xBtB#WHUfl0-%c# z+-dt`cL7dzmgcL^#^}U;ms4a5iA!{R`lXc${`A<{4B@F4ZS=+_5MZ2p|LcEm;Whj% zMCO^v9c9*_vVqT|FxWv(w?Egr$R>~H#x;I*d#&916+3qC46Hb|%Z^d?wmqe-%wIZ=Lm z(EY#DH3f(lNzy1YTK=s4_Wi`s(&n%B^8*Bofh z96a*|7V~!69Z|RnA0Y8uNAX>*m-{CYUOzBwx(U(LFHmoVaV~fB9NU!%P>a`NnuI26 z)YNU3{mzQoQ_3Zr!@TxqB#^s-&C*cXi@<*R?%NsB@(woE5TfLV?)m{WX@9TblZ}V& z9_`%Q6~=%YhV&#o!GpFVjcT&EW=S^i5F`x6>htE(=ZW3_R3rwX?G1)R8N3~|?|u>? za6Q|f5%{e#^rmA+Wp7=g(`&_v`%ooqqPbh)3*BVAO`FD4Qk+XFIasR zThQ_fU-FuA^%-};|3}0-|CO(0ek+)P;zxo4zYAVsZVuvHIg2--j2FU_YgfiC5ZNFx zlAlG^0V|?CSD1mIHyp_9X#v5phA}WlP zr_1L*M>d*pi1G>dbG9&U3VPMYk22+k;+Om~tH?8DO(DYXs~8A!m$|c+d@}e>zgb`Z zcPw~YJF#Jxn$N53VB#RlLWl(`=+0e<%CP({%!g7z=4Ex z!^}!I?WW&@L6CMz`2is4b6>T31k2yqO%xeUmZ@GAl(o)HQn7a*=VzYKI#wrFxIM1e zslr^DKkl>uqZ=$F3PaRV$Uh-2ynZ>To5Y}uyoZ)hKlbx<%*8IEXTK<>|3$9SL37{Q zl+iC#HUq@11*IsSly9I6aUicme8g9Gx6Wk<69aX7-(m{nvte{FWGuE48_EC1FJdE@ z4S{^KqjSvI9K{Z9S0r|B2cvLBoSsRZpX6Hv`*lkuygnG~`3l^B0Qf{en8M9gm{?Oo zpf~O67^ok_wca+q1wF9uf<<4^OXhOUvNZbrB0$U%=Jlf+f&6?csNWxh;d#56imIJ} z?^|oP!7td;D35s^WFhD=g7hvw3Af@#%T%2t?hT>J)ebOjMf{$4j$@B4Y9jD?Hv0Re zT$I!SX}>P8d#8_6gRjB+3cM{po*aZZ%quM6~G3p^< zO*HqA97grblHR2cU=Fy4N=yHQar`koKPr4g6rH>sC5gq7?x+v|%O{Z;=A)mGbUpq^ z(A@6xlwu=h1+}Xw!93Y%zICj0{$%gRBjpwpGX}&xt#4zX22=zp@a_Vb1u$G%UUqSL z8n2#^U=bbFnJDZ+Yd_hPXZ6HB(`s}PUk1j<$cTr;SZrVKpLB;U<^)P@Oi{)87g5Kh za^{l7Ie@D-#c7V$RhAvP39>O5(hmd>iZ<-Hi%oVm13(fe0Seu3pb371I(ez!_g~Cm zL@VB>Uzb2cy*SHwHsVa;M}J3-7Kf9<&L7HheTJNC=a-EULzfWYTL>M4w;qVeG&^bc z4(i*$lHe{fENfc*w-pP#Z~MmuLMp$Jj6)GSQFbjsMP%Zc^q&^g{zfai?ZnfGoefaw zCiZOP1$v3>Zm2p-DHQ`U|rbV*q$ORJWK+m37vq>^xep3zftVcj3JZ<42kAkjK-69jIPJ znt%%j7o>}vb?I4*rc%8yngzMm>1nd2c|FQlJUaf3EY^^Hv#FSMTjP`5?_;loa31&- zwYNm4tsFs}LpJA91VnO-57N#b&*o~#YB&09cA(%zm85)>ft%uVpC?QvLpUk^jM7s461$Z_F;W1QbVw098N+i`{ z+;Cnu7sUr7wh?UcvDSItedzc+l_Z=q2bpx*F{x1E|Im6}6ffQecn*D_=93E|Ux_#k zcWd52wliK+mUvwZ8Ld%AdHL|QF=0k`T=whq;6-<;1j;_t6g=YogPjcE0yA$TyO(?W z=Tz!_K*3MmZG|&E?AcEJGkr2q<3F_F|JJYe;t_zVxpa_X!l=I_R=73Ffx);`-Rk}d z^`5+d=m$@W6pkG>Ucakb9hUHp;`)HZw+EN>BX7x5)-sb_1b@DMQE&~}-EK~F7M1}h zAF%H>_IZ$FV@zS;?on33AxF`Jx!^%US;qPAv$Mb&-$Q0ZnN?b){krhYKWDa_EWPB4=^iQem>df2>;NWWwE?q=s8RJz>pzy=mCccZ{V5Dc}&a{XM%LSvUa7u`0QMEuK zYn}QhTD! zx<3o%$FIhK$Mp=BEsyJ=L!zedK$|a4Uv>Ro{}l{noWH+oo^AiAmEz?h$l&852y-g& z(Mfj^`F8g=8wc$$>BNdTvBVQi!}1aTdn(zN{azh1p9gs@=yBH1aT*!NMde8Q#sQiL z!l&2#D^A6~vAU11@Tn{QQ6eun%cbLqc{q#m63hG!hut62aW-t|6SLp6UNftv(aFoc z@uquvD@2x@;+dHc$+u}?EC@T34f$=$oR9MY@Cy5#Q}cxj(`82Lv#|}<-T9esvou@u zpIcLuD8e@UMv-lWuTjbJ(#%ZNzeqxGt`V~--ePtTGf%R1M!e$=T!RyC=&n?fgUTR0 zGBt!{X9@=ltZf=E>Ym~}nyo8{Auds4;`m!|2^ohQGaWU2E5x8yzh_b92-0Y7kbESc z`KC90^H|1~jm~>@BGHD4hc;;1Nih66j_6ZyMw*UCyP>T}dPv20otMs(SNxlf_{qFZ zWuf^NQG$FYhg~<-sbF3nu%%P&ztN9o`Y~>IfY=szwxUzy2r#7n`jyk*xgmRz+{xjL7xA>>+^}{zOl(-x2?F6E4 zej9O2y#~luT!?12(<-3}`B9XHhHBKi(1;87tYPdpS#VP5wh8|uUhDb_jWr`n|H!O~ z$~bT#-1p6;C=wr0gH49hH_HA7slRN0<9IjF>5#O$7O4$T*aEK+^{t@%NrA5tH}}{C zul*nPDO0;cc|>gL&geh?s{iPG+Ib!>(bDe6JI}J=v;MfnF42*CP2Q41O$NeNSvk3t znmbwLD$!~^PG9EGt=5$e!K6_;mT`Rt{q$))srmn78&L*PLp&Efpx~2~nO&I{N*eid z*?cNWnZW)}LDIU6L{iO#0b6oWU%ph~RfTPwq-@BXzq9jseWQ*qoqRcF_7oG+!hG56 zDff~9P*rktzLMyrX$)A&XQ?Cb8$Jm7-9Jq*M)_l0gwWF07ecpuv0cL|2Vye`$M zy}f6uw#x!qPXsoq%B412f;D>Vx=DV1w|idC>V~ajZzASfIbAcrfKh@a!n~`akclw5 zmCTrb#DaVQZrmpt2*Dw_``m(~J?BvZWWkXI7%3{nJ&q!kdOde{zg}J!%AjQYBJV;i zkTt<#p&4DHs)W4$8HNEPL<7=b-_6_pN+wm=$i(@R@_K=T9vEF_aLJ-RjuNi?4=hvd zO+j`pE%_A39TK2y1!FQ>b-v>A)?qbob2(h~tsYkV&O#okQ|jKS!9+tmM&s#d4L_X{ z%J>30s~uphcp604Gp6I84lfxj1rNYQfW5MO?B`^%;~^H&Vwt{@^i*`<2L_uKb1qO7 z#779TxmSPPITQ_Nmuc0dhwLVPb8ov4ZY}fS@uJ6=RlL72oLoT4LfOuY7|euA%g9_36YRcO4Hi1@K~Sy5})*0s`{R>s!)s(cA!gxTCS0(=72N6ddJQ7 z%&N%qh3xg^;y-Ms^j;$UVr372^SX2w!CtJOUGCV(G8FFU;Sa4lD3?LJbl#s*!Y(WH zIAlvwW0?j3sa^+!2LybUNdPZS>Bg2-vrms|lb4d^{~QulPqREM0vH44K?Ka5&Cwn1 ziB{yl+CtDcaPxR`)P`Ebl>I+>2NuxpQ?o0g6%P|m7WOk4mMoCc6wb44#*ZVTz2w}} zZz&^@r@%TT-5Wk+s~nDFtv_hi7j6s^VbE>;ejVqkSQSxcBBR%o{TcOGWTLX&(Tm_G z92|$w_zsB1e{yq+Y9eO~r9nQ=5pziW@awP^+m=NYWp)Ys!NeV_z zi8uoiysUvVfuZ(^>-zwYpI3tOes-7zEW+3ehYw6;58z-j+kt-QbI_AE;J*I)6=bIE zP?co!N~oiK)Rtx&TJiad08f-G*JI}4WEV~nSfr|Ixb;i>ZKiDc#rekxe;#@FBaX_` z5}V4b>R{Vi4;NZtE_eG>L^_LxFcD?uhebGC*qV(7U|NdQ-d=n+_Gtrp))~fPuyYo( zO3=8fM3O+xwEx&do7as!*!glJ1qN++I8CoHkdYbpLS& z=xjI!Jj^*9gk|>32Uf6r=&0x2dQDj6$G`+OUh+ZLq!pg1shoYA6CQf4VyOFh?_}}F z1`DKCcc>+2Sr8UYE5q{CH|gki=9f>&&0`LjH2C2h!h&9Wp$4N10xpV)N_Be+`K8Wox*#tX^0NG! z>XtJNGIE=h3Qm3)-YxI^m)gjq#d}^qYhMVu)VzP7Mtg~7_Y#u%^oL)m=FeM})d4ds$=5Gv! zF(_Xr!K9`qh;oid_6I!kcYXTGu!7DsXBGOUj)7s|9`NQ6Jf3^R=O(WI@UfkmN-Rl0|w(AzcI}fOTuOrTcJoYz%%d<5v*lC8=wIIDBzI zy3n?sK0YrnBsL;ejC#)i8o@vdJpxXxr7kN!$Gl0NSph)SVEn6 zM>S^@OmY}!f2pmG4I{H@9$guQ!jN8H62po%xCyGk)mx&lIJty z!?Wt1H`mpo?H<%U&>A#W#{rJyG=Jeg3{@uhu8JL_nJqch5_(*ByvJ0TvH*R| zK9Yz)Q~nbe#iLU8arMT*-2q9fG>5%OI-9+zNZDbC<}F`2xw=5j(jFAo^S3O{nsg^s zTX+C}RFy|29<0*2LATlciMetEhd^lp$QsTlUgk1aQoTV2qp?2=;*1>PdTfgiHQU6) z{1bs))jrGoevC9@eX2a7&88bi)Vc|B&(pE|1!?r0hLS*lm&z z8Sw|={2c9@C&W|T+9-l&*a!yjr(+Hd=bYWB!&$zl`>M57^httIY?()d4R95U!W+{T zH5+e2oq!Uv^~(vxMf7RG)M`W(e;(eui8W!5Us;)uNTeTt)BdVFC2SQ>pKDh?7E&K+ zv;3dn*|JAv8Ieg&ilaACuz{lqD6$*+e%7ha8m`JXRB^Rrq6ezHnqq0%;%7P`r|30J zGHGVuhK)Is*ayvV`}GA&%}*gwTofA=2x0!@N7R05SwSek)8V;`*Ae(`0-C|66Oxq` zgZYItZ_)`sv7yEmE=lE$KJ#n_`Hh9d=ryxUF>^cp+!Y+IZ5WJl{T?JFp}ZInlJjvg}x?)GT=1b;3}yevWm$gqx=8Xo#j*Zeg&IQ>81 zERwAM`mXg84~S_A`{143(^bD}=f+Mk0-cn+ZbBvgyumgGRp0n0tvDE$nWQV?G+{B+ zh(h^Q6K+~q*WXz$p^o^E8d%v&^a;LcAR@uef+N3pZ8E0y0)gREvP#;q8>Mono`IQp&v@i>gT0w zw1b54ZH*}nA545o=Jf4=N;XP6;C<}p#m@jkL$OkhxQ}T$aff=AJO7j+^q*Q4{^v7A zraRE}+tezZXn(}*dLTxg$1|@13=E_7;DzyP6R#2tw<)AQ?BKjM$M_*2&Zw24(Jk)hll+*>aZ0lI zbCWS>|8p?9`87m|b7vf?IloSoxHHJiXm$f%Dhh?%QE0(p2@By6c2@nQCi-93PlQ|T zer0Id*_4!{FhQ~8)xwJ3u?78TroT6|N6(iM5Jq_-J z41F4pzF?L3w2!0>iX!p$!ym?z=n7j9p5jb*vGlsq^S|~O-csXt`+=bryWby!0;;d`nCB(}&6K#=`#X1HNUA{cV}B>Nl9!Gs6BD zru8HNI++HUH`VGJgNj$vq*DQgKktO!av7Al^bs8APyL~6i1pi@h-H$TS=5+P+ha4| z6WSF;79aY?OeT@aqf(5}>@UG6V^5p4gD)^4AS4G>u6lU$geAhXft-v~gG{uZl`(vTf zcPhD;H4Kt*hJp%J_>rlek}!s=E(39H zml;&~SkBEEmYGOn%C!x24hfj8y81LVFN?*9TL`U^fiJKanPjI2>ej`5rQ#TM)xbKh zVJsgOF%FIoRU;75@wydh&-lOJkWS%2>{h`zQM+a0!%I2MX7=$|h%8=K6aFY|{RBe! zi2I=H=%o&Uy;23Q#mK$o3W>dZ6x5i@PbtcyWJIb^`-$XKM@aM>P{QT6_U&sgM2`~G ztmQ+AQMiI#_j}#O7F^uVDM&g2yfr|yw6tTdphnn2@}Tu}F#Hvkhg4@S4)F-Z3T@)O zBQp{j&D^Btty8T?v5xUntS4vggU2Hj>B8Z*OVjb0M}M(k7^2SzYQAa(p^3+wkDlTM zObtFH-zv|zAgR@n{RU#Sn#&aCRdRyRw>k`@SUpNZ zMfJ{cXI6h=MQ_S#ug9mLba#R87#DX5-@tjQsIBCz$b@>J?2c+gT^>i65zjww^J8Dv zMENYW`GxhTT4fkSURgxv5X;Fo$~p~i2nx-wkN~g!o-A2sCMNobPWj{T`v-HFXZOR} z#?JMv6ySkV35%0$cxI+O2q*bFMAOCX>MzF});k4<^?L_~DZzJ9*^S>Hn*!A?Q>`od zc|>uORdRw!RKavM(n+>W8>UCBHibvoKG zNP~-4?lBqR5cDyZu82df@m#Btw01q?ZF212S$EnMK%xYHrf@fh2Q#H6s*ZsYP57u1{$96vEx@qiDz#6!^eQ|2(||oZ=Gc#E8KdgAJkBV&U8!vI zPUvw<#Os2Z9OXzOQwpmr@26Xs%bE<}7e6nU{2wJMih%GA%EVN@C;vUc{5!tb7JwCX zHX*f@76QBH)mC~_{yS1(MT)kY>H-h*DPg2wSUg8#cB<(%E=fU-(~2lQ6%6sw@oy0} zr#by4hv?7511GPMZ>%1mM{DZ`0vU4ZoyXwdmDJJu>r8)0<(hmPS@27HeEAVkrL@>@ zCx9w(6>Vea>W2jIlxY@?J9AW2!OZ&ZVmLlH??EvxZg3384CZ2cwQ+pQm&~N<0=s?_ zI#MQFF*~P14xo6%$|4vVSU=P#3?@@md>r6<#yG^LVoq(2xC&sEGNLta^Sd=vP zPNaQBRlBWM)Yx2e#ce+9c0HM-&A1gLdWD{KY;9Iqk0x4#lS7VnNUK!xJj z|6;;2AkrP6>99GB#pQ`)eO7sJlOiVsKY$I~S*r-Kkpt-o>Erf}*zG{~3+&;&`N}47 zsKZ_N>O^Ywzv=G6wD|Xt!ZXBPMd*Q84RZD!XaE`e(T?b>4r*@dYdVlk)fSu}p1yIVV8wFjPlKA5jq#j^TLk36;A< z17s@uY~b^E_mIj50M_|jTAHva8C(QA`QbdzTu@Q1a#yu!Zqn$nWj1`^o>@(#@H)M)@nG_QC=P5jos)3CsO~q3v`*7s$)gVY3aa z!p4TXzs(f&$bb8hA{P@c%p(D+{Rrj0 zSZW&!n`*`#%+>$oTU=(cupR)7;sxDqy$}Z-66iGs~?|!HLEz6J};8`6>g&-ft7YssG z?SIaf(1~l;XZ<|*o(L-+;IQZkOV#kfwm&^OT6y(Sd(}qo8D1v;A>P8ve25f}4vd{_ znOa+5lgtS2wzCZ))_yZ7?WE(SO4gSnHSJNH2w^Z4G>CvpKA%1evG7Xf^7F4lrJ0h{ z=T&aNxD{rKmFMxxL{%)>8sJ^Ep<``oQv>++MUxe{z!K)dX~8vc(CpK*0Xivz zV|BNoL_#A&5((l1Lp-Zz83f`ZK+i~2#?U?cTo>3G1}3!VA{i_&2(ND8+Z5o(VGY;w z_u3L3I7&#rZ=}MzyJn4}goEQDCMV zo^43PurehUOXidL7qS_gDr^Can&E3gOmF<6H~7~XcHQs7wJ^Xh=i>{_>&rf;l&@X% zCg*PsCO8Y)>T-LRyHThJ%Tc=cJy--CE z*P8byVr>t+6FW06<+1yW9tAckN1A(?H4zz}z$&k3 zV&|rtBN#W;r)CEePv#C#cpa(@lzubCn+W0&udN#xoh_rUci!f#?5%5yc|2?cjdt0t zMY-2*hlBl5bQgqd$F%*3#-y$T?#2!6qmE9M#mhR}nEMY1^Wb}_PG8App5m0mA*f9< zk%y&w-b4!Lx2!}+e}w`_U>u7cnuKRfA+oq$t@vSD}er#bM*w1vgMikcIH)j#y5ww#U#M` zs0VZFzDCUc4UIerm^kdTKnqu+3l6KZXgWfx>1TJhlH2YRpVRUFyyYxF1E~7koT)`H?Ci_;L5=) zNci;^W@8;M6?>6N@3e6vE)&a9`G~ZdjO~9I@U)7x6yDv)-T^b08V?b&r$Yt*h{Iq! z(GG$z-hIw{_!vACwn@n8F6OvuYW$8<#T~ z>)DW4bze9>z2_)=FsVL7zIv)nc74V|WL`bnm2G_}o+mj-a0~L8c z%-3phf0L28pKD~3V4b{?ctiASDhej>ikCC!E&5`RQy<%;z<|1Z%NMU~zAUobILHGn zr?P&Zpm#W&3)a#`nNICf+UlRoNY*&brcGh4HuPM^YR$HSYpvCx9U;NX?INcaY}* z+&;GBvUm0j8|r?<65VN>y@Q|Z#BYe{apcMByx`igPMO_!nt*-;e(ZG`zi9ss`o6c2 z&k_jv7B_Ht3!GuT+~YjeL2%l5+XG})3k-4F#yDY6NBPSUM{~el$(^5$-S+89si4aB z@i0uk((d^1JO@U{#1_;sdyp~Rg_U`ZCc!-`stlz%ckvR^Hh3$JJzFED7v&4?BR5|r zVuD{h%CY~T2KvoXY##L51rBfg3~ z*X~KFP|0@jVMNt}UaQ`I$MC+nudeB59vDW=$=8(}UOAL{-H9EF5k_qvDKWA*s1gMqgdsNp;_=Si_Nsxo|7> zk{JF&jePksl?l>Vsd$Jp}-!l=}!u- zp|+X3;=s-1L2C?c{D!s7R@|7^!vbImzXS-*awqW?n=n%O%$+mq82PGyl1AXKNq>E^ zzv>OyD*vQuA(EdK%M^gR((s+NPqMxR*Ad$OPGO~NE6afx7B^0G=abx7-QI+Es;Z)>TA+5S zH?t`!!&KE1YBUDvy%)?=3WDwGI}$SN!+U1!EnRgjC4D|?CuhV9yGCZB3vIU<$&7cn z)OnOOArQUj0X*&Qar^;y(CrxuQF@Nui4mH}86y&9J&RDsMKQ=hjLX;*hLBhCs|lkn zl&to;9PfKPc!Msy&K)V`B}6E@{gh5W@IfLyjXRfiMd-BsJ2kwpOWP(Cnxt>vsC?kT zfP?7|N>=Y|NZv6LZJX`F+LD_v4^-df4=WgKX}tmMAq+!u9{11l=yhDfpM4QniB9sZ zR4y#%Q0I=zRcc)X-_sC+8ReP;sJmWaw0(+^W&fgMpAGn=!fI*l6|rI^VTb4mOrs}>lf9=wE)e%Fh`Ckg7wU)|Ecj&t zB28#^i>enn|D)JS6#9scLym}gwv4vj;dL&^-_H#d&|)>GBzzE4QNViE)BQgA$xviJ zpO`p-OgyppKC3*%UcMg16;?Psq5c<~Rk^#IcP@MMQ9S8UJD$5uu{b{2gLz^QXn-YO zK$0`9#?^nt-hGWU!Cx-KRmDeVrQ*}@ZQC;9%rsT5yZD4Vvvp&66pwohg5;QQ^Q zwUrI7lojbR?@j>ke0yRaQ(D!X%eEg7M!R_Y#`GLY%1+XCMuO7x<=`Ibuihm?bhMP; zsjp*Sl?2vP@|T}@cRm^woD>XUM=aqLBjvY;5j@Y9*0uP8KM_9BI!v(QaifGn?C6uf zZgXrflRTf4^h2FqcG|m>-+e!$0rQSR5;3sNPPcwLywTq*=~I2H_Q4-E5PdmhpEV+c zBm#E-W>((?c=K_DuB+Qv*}!owoU*uxI!*E}nXR{D+vV})qYs8%gZ){pt zU@#KmWv!{tb`ymcX4lQi>a)eYYo|Q`?b!_O^1@k(Kd==#nhI_c-!-bI{%gj1gU|V? zit!#d(@D6*uxy~@?Z?s(@`{VnDWs4Iq+u0z-_lpt$>1qeIBkRykgHzk7Sx2BTf#a- zES8O6vZWY(&+R($azc-(K)W>K_YD4mI;Xgz{vdP~Rr&HLzESF>3?!A0LlW;Qk`&j& zqLRRz9I2e9J3nN8+RfP{fGbpe5`wgOvcYJ2+(W2S>)Rham?eT^49=dm>jIySC|ln6 z1YVHA#v&}E*mxhftNxDj;ERwcx0(qwc4sPJ<;h@L7M|Y}cVy1E2tsO^t|rDmeCa4} z6yRM=rCS}Hr?Ky?a1pH1m}7ix*A#}8jcnQrq%Kf47&WZKs052uDdMckfugH@8^~qP zQ`AY;)qsJ~E}!%|jc`Z?Tws3U;V=g!P}Vjy?OM401JcF`#SD`37!>KlDqDEGu2kk^ zMEh(HFn(qnIFt;ghHx5ND!d43Dqy|*^&C?BdQzd=k+}qR@sg4LcvQkwiu)}o=XDg* zd5j*cjpQ$ zNj@&TBFJj_)VIji4?QW8FMF{2&Otv8RKtpY9>-GdkV@M@sOx1XQB|PFX%lSC)&EJx zL82P=&2KUuyWlo|DAd?X5=*;!-sDF7*S9YRzk};WAT_3++#J*TfZ`+)v@JEo`NTDW zIUL4ffYZ!vXz<#*dg^*GdyTsLA-qh5JDZ}~5C7e-k$<&)ROgh$xnk%v8wOiVj3W?> z^AP+u8{STL?HlYVXV)^`v0E^HzU7SF1%+kr<13mTCYgQer8qS%{ZchDZ6-I4Ae(IT zc;BE@n<5Smx#rgZ3KyNEoFU*R)_zCQDMu6-*acQ{lvo@#nN#@MKYdO7kumHmB8GWN zXMv?gKq%gTGuORX3@h)Vc7a4EnPKVi?nJd}3^uo@iKx%WimQ1`57QAdQs32@gaS@# zG!_r|YmBAATjhEwtaJ=)s+fxmokYmUw~d^UKYbb<_N!s!{vU|EiP zObF2(a{*VjYmFZwx$@QFS%M!taVKeWTS>7|qdSjk(D`Af&7#Fq^Sc<_SIjOAn9ZaQ zal_hBgvjO?>FUr{qzvFz(`o({YhdzXMXiM63h#U;80<^#WUDYE?aD`+8WS4E9>ozp z^;HK*RNL-lg2`oHs*8UYmgw1nOkn1drl7duoA#!hc6CwZq}T<<2LGo|SzC5rMPzjF z-ycu%5ocrKYL|=#7}jc7#27CNu5gagM$Ca74cRaSStWxRaX0!%C&W7<-I&WNjsk0? zeCn*ljtJ)nAXe@z`p<`?zjc2Ii*&+GB(6_@G>jK^Cy$`bupwdV(qi5B#jz;rYog0> zjfJdby;km#iy*H)(*q&Mbyj&SZN>=A;}CJn{jd}sd$}SaSP$J&RwEw&)8N$9oS6o( zbn|VH1Er_^zK&k%4_M3v)Q&)Yi~AA%%_wKPh6Ey^`M)3R-h^9;x$C6DiWewriP>ha zK~ruz_p2!S(!p4FNVWk}z`1 z4FK+`Ao?dIIygvTlV*3t+n{loN04Kmt`cHdB;j16IUoiwd)5vNLVX~mihE47qCZ>6 zm4Cy9Vm_X18xfVbUj;!xOwV#}d`>gm8+%;#Mk?2OYF!n^&erO%d!E#kOusF$W~6g5YDBYT z#hUCQS&^OQxIGg2V4$x;h74ZgSmyB+9rbhz5#r}JlQGVh9cc-_y&^AJ&&=R%Hi*{4 zd;zPYHcbYv3{6R?F1=V8;?s)Q=p969zh$O_PwlxC0m|Sg^o2)x60&qqa7e|-Yj%jh z!`m>s5uu%`{rk~y-e<}5zu05sailRE{?ir2JId+aB8<8-sM!YoJ(z&d(uZ`lItHcx z^#c61bXP}f3XV)A4N`dcV*<~^NBn-fy~g)`){e@XLai3-fgu^g$<5hZtlfc=m#`qq)S8-l|a;aJ$ohw zCMPD~Jow@%$kv@OdeWIQ(;K~ng&gVUJK=?~eexfbBr+U_d!gXC!Q+C57@(S76Hy77 zFENdLJmcT?3*!y<;<+mz-r=*pAb?rx3k*;C6*nh$vy_$(qAcoTxE0+`omAu5!oj>v z+-XYX^=p$Hzn~oTfR*!WWBAYf9_Tq+SWsE*_T1jOT{1FSN|)qXVNXZ4>3JoXf&VGc ze8rAX<~Q-S-*zT`a9`2DIom2>g z_D=$FNjU1#?R(~CeCFV)1Wl|$pLdL!O(hD5}AH#tRwjBcwxvR^iOpUAB5yayr4wg7kc14+8^ z1r0BY7sUoQyfK`&UW#sb_B9csBH6qpjLqT5N7)2YX)~6&=iloGwg%&#uiPZQq5uIwsBMXPF zzp-~FD_4RX-Nok<+O~Laigd5-=4P!S3Wh5N2gt(rJY1KjJ#VjagJl`QconlZkOxmc z%w@@j1ss2wsGv}s%IyKFMa?wD9ZR#5VM$#Lo9|E)gxGb;JZon2m$(hX_oOkUVTa5& zB0ZG7vvC8`M}|<4s+f*`ev>-lxD=%7Aa7UH540^Vc{$AvXHO?R4}9ZkbUc!x)fp!^ zA(NbS%N#f7WWlYf^YOCp8B#hUhq+COe~GRA?3KfX(jdxLIuyJTJK?uGE|!}*OPlI- zl2tGE&Cc^d>hlMlD=JCP-7bnNQ zz_ETmJhwI7G)2!f4tZPhzGIBm~R~i?VMn*#%K=b~ zo;1=V@axB=fXi28_uAJ#Z)te{YD0U*Jl;{e|2p;D4g9crol=8iWD|iBuxe5Xp}!Z= zNEOVcEg42tR#uQ5r_?g>h6v-UYs6owTdne*oqpm_v|Q%#{M(5OGav$@tr5%G5?J1$E96;1toh&$&K!wjH9v-7DC3-xIR!|p z(s=jhXb?(DJkxZDcIYbZ8l3h_ntLm)O-TkLl*0JvfUfC z2jaPa%%g^Zg{qWp7B2tW&r>5A<;)2AOiU8hPm}2+Q`ypq`>*j9&M;#+A4WZz|I{sp zw`u3yZy-XCAS+FZ!A@gKw)#Cwnv(+Ni2E2rE@!Vw?ReS}KFl6B4j-n)4zvAQXf|uI z=BOig)IBrI!%X?MExf&+7QZNpEu_ zd6+Tos;uM=H!=lzEU3)iwiUJLr#pE`;JD(F=GnZCol#>a$nL><$b(F7?b#F&YWpCd zAt@$)Pm_JCDWuA1X4je|QMd=snrz>_c-!RWE-u41_n2mdk6XiJD_x8YMS#cPaEZzEv5Glz zB?Q=^$Ax?SOL+PZKcOP$P?ZRu@E09<&l9EHVTo@&S_!TVXR>u)9E{07IX4GhrJB%v z2nQ2+HMfdi4tY%bcS93~7Pu8VL=Wu$63Aa6YS^1xFFv%pB3?FEJb1NKKQQ7&()Gs97 z5k(YDCG5OlWO7Heb8V(aG#<<+`NHbfDmJcUnB0KV7G#Tc?*{R%PF0nR3$Ei40=k4{ zVK>&=d+QLPS3>NkaBrYizFiGZyJsH0_|`P<(>yi0X><-pWE`-L2wNWLg^6>%QF*!D zr=9b(KO~=mc*{n0Xq8isLY#&ncWgX9a&@V~j2&GGKywzH!6$S3@{R3*#G zvQnCFAQ{1URW@(D8~sTh`zg+)8^ip>Viv3cKkdBV)S0PYTys#a6rU=Q575Cc?0roc zZsvvOaiB9YHQn_q>ga3072CA)PE*f^NR8hF7amn;(NpQyZ_QO0M@OW}waD~n!&gFm z{eLR8DEGp0XvUx}Eu&D%Q9W29BqPjqwro6o6dsWLWrz8CjL5jm`j_H?;YR0_f;QJ$ z${-Qp*kd7Sb~rP|RxR@&9r^RSBRe^(hty}xRF^B4k9*!m{V>MqS+;dO-Vr_4=3ab~ zcbS5CSJ1yU%XicGm-)*pLat6sv*_aRCg{`s*B-@Vk}q#pco0$3rV$7BoAyGf2d5k> zTKd~B&Bym~X9#4CJ3^2d{&t2nXXg;-KC#+C%IgTWS@p_C1j-zdtr#5AN{qZ|g@4Im zu50giA*5GxjBjckiZ-;d8Q*Xlh*zD2-=_gaBv02}5+zvU1mc9hOuQexJz>u)B>k4C zSgSrD{mWYoSHZjM4!RlqD6wiCOulr;?K*cEF=6kPtRcyP^1+u6@Qvhz!nYB2o!2fD z`|IyuUT+u!-RNaye#}(eaxN6tylqxoA_wgdlndQnDp)0>6b@Idb?|MZ?#U!SlKzzO zPlT7H7c{&&b~-qt;T_11+#r8KcT0y2!{Qn{Fvk0$bQ!}3mX~h!aDC>d?$?(jfCEql>4Cgm|zgm@y{^6(Q??jU#(LkKdUzltxLw_yd`~?bL2s(6X~21pS$fS{Z0 zPwm<#clVk-_}0gbN8k@X%ZaY{P}5srU48-AS$`#|dDE(52u-}eE@_LcN4<>^*!8T| z_wLYyrw7+)7h`=Szk#Z-NqN~5?ts0HNi`dY#T^}!z%E9zOFO*LOBgd3exCZhP(8-c zi6I)g7uRnbU%;3Pw}Mc5$c~5=anm7IXRmG+I9dC(apzL4ZM3q(E8*sS**%Q-{Obh_ zsqQ~<4=?Sg9G-TY#j8Iy){1%(-qYHC^0X2W6>1%j_o5^rGgb9?F#q58IC`?51DYZC zOXZ>dy5-se)eJ)6OZ(HWg_0?iKG|F7&%zt4FBEJ{XKL49A(GyL;77j`yGEPW8u9N< zYCs4;gPx0R{ki##ADFg-TFLl2Hg3bW4yiy~RJD-XOsMr0q4gtIBPgdq7O>O5jE|N$ z1(__;DO~aDm1F+s!DM66cdr_6FuR7nOHRQ#*71KqrBAm_TjNh>2530p;L#uZ?!IA= zTM7SJ$sp6M|Mu=PwpL9^J#KN_2AP!q9Y8@oOf;JOQ`A&F#`H`LC28*;fAhUGgb(d& z_w5T0@qNUNJuU$m;J67B9mPki{56bH7r{SJ4Q41=6l@~`5ucS(WoXTb#G--FL+-;8J02`I94TNB9Lg=Mkj>+Xu4@O+!}2)0$U| zME?||*iTz44tCR`w)nu)3@>%x>>`g#UKjwQt|t14;;sud9lG4h=KIA3aeSo&Q|B-t zH4c+dF`n--M|yHNP@7TV_v=nR{8AhLS+RuUvNhCRz&Y1xN24Q}U2tzc(Kj-R*4lI4 zg#ajoLll}ss7J&$x~ciRIct_zG1)^SFdwpnWjmf2g0kP6BrqdLcqsf5FZbj$Nayi$ zce3%!lLf=_qYN?Npqlsei1aTKXZB00jOVjr{s}Fd>N*y+Rn?6;x9w-n$|E+ulHtcr zEdWz67M5z?J`|(b2sJ{r^RP?PCW{{1`6ykZ)eSGeJA!ttM&rJ zvs?+Y4SHv7AnR}EgtbnMNv@#{to9zH=X6dgpd;q zHhhE2cLTBXSn&_@27?lo&@AG-#JCel!s=fjSBHgX(`VoG4q-d!I7`++?0B)P=BRWpJEwRNc}z65>#A79+<3A1KWCL?0Ueu zPG|9bvKpzU=T;By06*EZJdZ7qlHWxdb0$-;Rgp&%ADWF7g;97)HqMOG{9w8;mRObV}dFHzWhwN5(?As|mv`3wjz_EMF=K5kJh?~*mlrWYIKi4nzxRt2!xZo?xwO$SR!0IljY7ZX&xpD&p4 zS9_j%>+j_c_{(R{{P^>Dp7PwN3odbMtN50Od;~07Im8HjzdeFn!>E2J|M96t)0Dx~ z_BqK$OSwNWjXi|B&Mcf)TM8D&uf41CUC3nm>j8 zaMF4dc?39-Oq|Xc}`u!_hC>zp72bY_vb4baNZJ z<;~i}1A4*)P8dI3iRXK3e%{NXmbLqJ)+c_dbJaYa$9h$;4<^ex(o+8FF0*Xi{rc_< zWkY`Gsp@xnBx|43>@WGm7zMWt;s9MLx^l#yzE64vm$?^7L4|U{8`r@Z$&=pLDxmV> z!BT0$E%%fm^Wy0DR^l%dMt;m;WP-~;XM0xHuqQW|+O4)t%1(7`At=7ra zB;oPV1o##jM@4EmUfA*52Jb7*ni0QP!rr}C@g#%glq55+rhAXYz2!ZYWRdeK65uH2 zp^3MBc`hZfd0SsT#g9s9G^2TMDbYes9ig5I?PBZ-+BG&VFRiwp?Kfq1I*P}R!-MvN zHr(({<=zL4>AF$4H)%T3CQ-BI2WVN-9NrE!HVw-)TyGW{Ca?M{(xUGZjZw(c)>uM2 zKMnhHu9hZ4nnQ#y#r7qYGSZDlh7NNOVziVy@D7$fanP4W!z*0xOGcMb_Gh$b!||jR zvNVnxCAe@V5X>q;5)A=Kurz$4l-2GX{^T3vrsb#0{A>ly-EDOr%z;f}w#*dcJ;@i# z;_im?l@hwpS-iKAF~i@c-$XU6=03w4Zf}2|toiLZ1ifS!`thopm?l-W|jMu&)DRfsQGGB*0+^wS5Mdc*Z!R)=~Bv7 zYv5u_q^U*+cD^{vjB-_O;^i(d2q9r&JP?1P9M34mq)6W9jcp3}ye@UihSrE^-z3wHPnj^9tl=Nboh?*(Ca;Sp>bJjptZGWQqRk3 zL&@K4PZUybH>qz5Jl#sMwvD({(2$AZ;7YK4-NcyY-c&nz+1MenK|G|DW=MdO)cF7n zrisn=yHX7f`anL~AhZ=EmnXwgI-dFnE)#jaKph*Z^}O+Hn7!F`4V=Ag)im(S?z9>r zErev9d+o=JT-IKs@Jx5``%GCnqpx2BG6?>FPN#u<9IJTR+#d01v9LE1)dN|Q^6pB^ zhxi9&7-OoDz za<09=y+H%@uFA)itpe_qlvWZcBuKELtR;xsc&wQ8pK7k z>o2>-F`|T~H$MmmhdkC*R{BoEFjMtUHwZ8@;KjvXpyb4Iqki=u2{!WlaQSFRC%ey^b` z8F}E2#(5I+HZ^Y|O{wt7Nf-5pZ;*_mf$ofrDVruyv0_OHld4!!Fo;}q7zL|H`&1YY z%8tDJr8t4v){PLmdhXaH;Qw?JtpbBT+QAwtE=Hv2Z-BnRY+mqepE_BYR|NP{|9hu` zP$)Mpn9#iLb1NY+A=j3R?Zi_i)|o0I0maE(HI`s}r>+X2-FuCFDY_OPPY8(2T=4(q zmHVjw4872)PaKCNdh{#f=h}AbsYkYlR->78+g!tYwKFLRCE%;522mp@H6wPp4&0&H$8}>p zr~Z9Ea*RU;tG!$le7k);- zpQGmRia4awdMo#Ss+Q$QKq!m^rCB=!jBVGxaAuYV8mcqXRAuN_%n7zVj)|Iq!;>U! zQkk}ly*7U^_j=fhrzcL;F@dNcpJ8*~6TnwiUEg z{lZ0MwYA)RH+D7Zk>-4?=BD_j85JO9T>_KS*amkjzk8=|V8|JD;9(`=KK|uW3)|&G z3~$w|)I8~;@VPlSRyUWuT43y?OCn5)+rxl!Kd#5h5QQBuf zSZhYw;&GkQJ_T05ue%23?wMX?2Bdl>ZTi&XO(u-JGn98$5xBJ}`*UVSr>R;JMEYI| za-Ov+QT#=o4TEme0IuoF{DoqUMnG471VvRRnrVKX0rjlMa&uhd+4X!tvqXUlp=Z)TAiTlUK%r=w$6JsPSh!vJt%53$3eVjGX_}fHKyEr8v zZN4b?f0GR{kPPU7>f-J(2Co-4^`f{$TP{es`d;3VY*$|9M;@hY2j1a#qy3-nIwU3h zDa7=b{|N1bhPed0LQ0yET+^}qrctvr3s^u>g7o}%&yFA+nTFGA)y>itSDhvFG*(7=5t`tn)&P-598+X)N-4&O|HUSQw#u+pf3JW~y+Ma~&9ubJN5vEd) z5UG+$fAfMvqNeavRkZd3rKX$!Y5VvWc{&3pdk>6M*k9WjyX(0U4P`Z#ebJ8`G#amWYZt9@U3g zv5OhSx~#|xJ%L*d4rlfGMnie`XVA}{yi#2Zg|&V{lxA*4)8&<)Y-zG^U0CIX0Cd7% zyE(X6oT*yJHMB*%bQ9_mOfsPwKlx{-QVQwu6}jC0J!iq@d%30WR&OYVf;@UK8Ja<* zgHf{>Pz}#%H}2{fxa(1W5@*{Kva8_wKfi_ceag*?1SG-WnkPs$i1X0e+b+fDL;8yW z=VcmT1@LF>9H(;wxza%8ox%{=i^Zf8YvveKO;;SsYA}N{HE!hA>#ugJUqHpJtqTBv zY630>u-f8eEi$f)+0x|BZNvj~5%KQ6ozsO1(X#_t3zz3rh)(=6753|xo6&zb=EdO@yw?pKc+J=aUhG!1M1JSrW;+D++J!?D{^KX0_TdTd~PH!is3 zDX-8c0;EO2yj!l#u1pfr?GS0l!NM3uZIJ70z(v{VN|PZD>C@+Ia*=5u&c8h5X&Kf( zEDSQfZvQ98P}q6fvb!!8w!GeaKD|ob$WNFry0e?3 zu<|7kYODS8%BTIB8i*wMC0caY*#K!SJ$WCK30A<)*r+a%`|ZnApK7uCPeCHWk3eAm z2LRM#MH+lsnvp>qh6^ou)t-YNwbW}BooDy%+CBmf<^F#g>MvYhRfQ%ljPhWebbk7x zaFK`4=!ej8kmra#XiOja@B!#DN!TXrd-sUlU)YpqAqI8vZBnr2fzN z(3+-d5nkS=H*PHEit!x6LbLL2Q7%_~fvdUv$Q3q6+8Y2`Pt@509z(Ly?2RpyzSLt>RTU%A4-etAXf?6&%V&v!n<^2=>K{F z*v!Z$9gFf_mixh$?wn=U;mxu29GHZG?9ipA4s~Ua~(#d-iGK{0eSZ<9)591lZ2uqRUUs`^jT{?9a~l3jtv2B57jxxZ6?L6ME@SD}OV1giHf*aTBa^s8FyJ;wFj^b#^r zb^D9J+X*i+#gB{$a>cz;Ci_mmU|gnPxn{)J$XuiLq&_Cth`u+t=@K`DIV?vQl*492=>+XQnw^piE1eIa8YQibMk5>h~<8$2~^;YD7=+$)PbiIqaJVBO+pwmVVq} zXv;2#*vKII`Exk2NGmz>gl)l+GSoPRACT`?MD^+0+TYQZMnMLzrTF3WMVX3VE&7>Xrg# zmf{0DrxLg+$UZUpbq+Z`>Ujv*u>HQ5;+Hx1d8OK^T3un`4!)24Po9JElvbt_V}Nnc zk@fovh>_I2$&^o#QH7jIH?2d@axk4*@f&saw-0SoS>Fce;nti~rfQ#2uH9TJ$A(!6 z7UGR5)Pb={Yr>aV)jX>aFq%N~oD~?hA(7VM>%m+B|2{nZZJnRU@-Lh&dYp)g`CNJh zYT9c6;FVkq0o@J^w^TgiuI~e>8=NXFY zJdB4If82TQKwDdO5uY+V(gxbhx3G38$6=q9a9*W;j=&!IKD6P@C`^^(H#`q3^b3o#(fayFR-l&l~5EQIB^R4pZ9a_u; zTS#lllV;-bZF zc06vjZTg!ke4_yCq_a}xB{_iYx5=h5!-;BN%*PeW8@flnqjr80_LiveRG5&SEP^)F zpaj^8TKeT7QYV9IH}h(5s2w#-E*ZnjDTg{K*&&To20UkLJXU%VCRc6~m#X}1ea0rvx%4ZJBqod}4J7M9RHjLese@=MHll~6 zWd`~E)I?s<%OY-k(XG2~hk;THsb?9T5dZhT3%_2MkE=Y!cl(I&20a~h@JbA@3F{`v z9RI}OK0)@*eU`F9F@w;qVV?Ss>*7yxv)by=qB2 zs7x;3OA$~dYV5Ho;O}CrzeCU*3Gz{n$z2pR@YIx;v3ryvWv_aF29{kt;$A2slC!Up zSr=Cy9Y%89+-^Z7_n#LN?-)?in(u=xU{sxN%N2oTV~5Wzp-sJr-MN72a~n=SWnWog z7a#TLH05tkAF83_feVU%AKvFEn1%pin&9VR{mFVZE+Xu+Mb4gMDM;1g@ahyfUUEM3 zjbB6?^oMgMQh` z&TSYFE0?Xj?oAO-ZNlvNi+zzOs#}s+tW&hlo*Q#ZpTRNgefUefF|lm9-*6X0JoHY8 zKR5brao5c=3Nw08Hl2*Afst-e*?!?}$z!bY<6$$6$U{3@S^Uk*80eRsto$MX8}_X^ z6%luGc&u(hq)44V?#w`%)7k648X=bSzPfgfKS0?c-GiP?uXWhF{rFtatXtIcszu>8 z;W2NvN>2(*Z~=Y<+j(Q{&B`oMWd)TJy5I zRdc&Iw$*MOXS8BHQTQl`Q^ow?srfDBxX!w)LX<-mHqiKWbe6v{%p;%ID3> zu?(!sU&gJwmy2fcnH6u}oW_(Txw15*PHge@sEs6>$4xjw8PAPXJ~Yo^#{n3sEJEtMq{rY$s3`!v!-X)v zb7Tz(T$uZ1%Nf~1zMMSze`kx

_-9LG%m=>dE&cs@N?boa4*6bvi%P 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 literal 0 HcmV?d00001 diff --git a/docs/source/manual/file_formats/index.rst b/docs/source/manual/file_formats/index.rst index 1749fc68d..ac57bb1cc 100644 --- a/docs/source/manual/file_formats/index.rst +++ b/docs/source/manual/file_formats/index.rst @@ -37,3 +37,5 @@ OpenFPGA widely uses XML format for interchangable files clock_network io_naming_file + + tile_config_tile 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 fc552656c..add629dc6 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst @@ -252,6 +252,14 @@ build_fabric .. option:: --compress_routing Enable compression on routing architecture modules. Strongly recommend this as it will minimize the number of routing modules to be outputted. It can reduce the netlist size significantly. + + .. option:: --group_tile + + Group fine-grained programmable blocks, connection blocks and switch blocks into tiles. Once enabled, tiles will be added to the top-level module. Otherwise, the top-level module consists of programmable blocks, connection blocks and switch blocks. The tile style can be customized through a file. See details in :ref:`file_formats_tile_config_file`. + + .. warning:: This option does not support ``--duplicate_grid_pin``! + + .. warning:: This option requires ``--compress_routing`` to be enabled! .. option:: --duplicate_grid_pin From 5ea9090714208f1dfdc72ee2dff37030e98413fb Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 25 Jul 2023 20:28:49 -0700 Subject: [PATCH 228/391] [doc] update netlists to describe tile modules --- docs/source/manual/file_formats/index.rst | 2 +- .../manual/fpga_verilog/fabric_netlist.rst | 23 ++++++++++++++++-- .../figures/fabric_netlist_hierarchy.png | Bin 49128 -> 55692 bytes .../fpga_verilog_commands.rst | 2 ++ .../openfpga_commands/setup_commands.rst | 2 +- 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/docs/source/manual/file_formats/index.rst b/docs/source/manual/file_formats/index.rst index ac57bb1cc..0cd489293 100644 --- a/docs/source/manual/file_formats/index.rst +++ b/docs/source/manual/file_formats/index.rst @@ -38,4 +38,4 @@ OpenFPGA widely uses XML format for interchangable files io_naming_file - tile_config_tile + tile_config_file diff --git a/docs/source/manual/fpga_verilog/fabric_netlist.rst b/docs/source/manual/fpga_verilog/fabric_netlist.rst index f09575a84..17b64115e 100644 --- a/docs/source/manual/fpga_verilog/fabric_netlist.rst +++ b/docs/source/manual/fpga_verilog/fabric_netlist.rst @@ -5,9 +5,9 @@ Fabric Netlists In this part, we will introduce the hierarchy, dependency and functionality of each Verilog netlist, which are generated to model the FPGA fabric. -.. note:: These netlists are automatically generated by the OpenFPGA command ``write_fabric_verilog``. See :ref:`openfpga_verilog_commands` for its detailed usage. +.. note:: These netlists are automatically generated by the OpenFPGA command :ref:`cmd_write_fabric_verilog`. See :ref:`openfpga_verilog_commands` for its detailed usage. -All the generated Verilog netlists are located in the directory as you specify in the OpenFPGA command ``write_fabric_verilog``. +All the generated Verilog netlists are located in the directory as you specify in the OpenFPGA command :ref:`cmd_write_fabric_verilog`. Inside the directory, the Verilog netlists are organized as illustrated in :numref:`fig_fabric_netlist_hierarchy`. .. _fig_fabric_netlist_hierarchy: @@ -24,6 +24,8 @@ Inside the directory, the Verilog netlists are organized as illustrated in :numr An illustrative FPGA fabric modelled by the Verilog netlists +.. _fabric_netlists_top_level_netlists: + Top-level Netlists ~~~~~~~~~~~~~~~~~~ @@ -48,6 +50,21 @@ Top-level Netlists .. note:: We strongly recommend users to turn on this flag as it can help simulators to converge quickly. +.. _fabric_netlists_tiles: + +Tiles +~~~~~ + +This sub-directory contains all the tile-level modules. Only seen when the ``--group_tile`` option is enabled when calling command :ref:`cmd_build_fabric`. +Each tile groups a number of programmable blocks (:ref:`fabric_netlists_logic_blocks`) and routing blocks (:ref:`fabric_netlists_routing_blocks`), as depicted in :numref:`fig_generic_fabric`. +Tiles are instanciated under the top-level module (:ref:`fabric_netlists_top_level_netlists`). + +.. option:: tile____.v + + For each unique tile, a Verilog netlist will be generated. The ```` and ```` denote the coordinate of the tile in the FPGA fabric. + +.. _fabric_netlists_logic_blocks: + Logic Blocks ~~~~~~~~~~~~ This sub-directory contains all the Verilog modules modeling configurable logic blocks, heterogeneous blocks as well as I/O blocks. @@ -63,6 +80,8 @@ Take the example in :numref:`fig_generic_fabric`, the modules are CLBs, DSP bloc For each root ``pb_type`` defined in the ```` of VPR architecture description, a Verilog netlist will be generated to model its internal structure. +.. _fabric_netlists_routing_blocks: + Routing Blocks ~~~~~~~~~~~~~~ This sub-directory contains all the Verilog modules modeling Switch Blocks (SBs) and Connection Blocks (CBs). diff --git a/docs/source/manual/fpga_verilog/figures/fabric_netlist_hierarchy.png b/docs/source/manual/fpga_verilog/figures/fabric_netlist_hierarchy.png index 232eb940ce75d555f549acb109e7bfd9a284c237..27811bfb703b127266730c1a67673d72db4c7e66 100644 GIT binary patch literal 55692 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 KsWJ(Zfd2>ligfn? literal 49128 zcmdRWbx@XF_b%Wg5+Wg?bfSJFqg2EIfZKb1C-mxrSVpON4Y;Su3(K}FyYzz?iHpW(r0e7M^;&*9)S z;0gZoSs$MEzvY9?;b4Etn1J8VKPm7Z)c*7PRyzEDTTDmzr}Qnf^jrUYhVO)`G0(kL z2fvW*q_iC2;85>F|KZ^hlOMprA)uP8J#~62FURx3)|$!C$o9E0lbf|2R11#JjR$dNHG#$@YY%FM#e&CUFnm6?^55tLwbbhmLbbYrw} zr1;&)KkbMaJHBu*w{tSLwIPMtHGFRC?8Hw_4t4aOf4|4+WNz}`o@^ZdObbkq8G6Ia z!t|K=KW&4me9%)KMF(?Z&@V@F#nXQ;Bejk%M+KMnkk$N#gIsI4{V)X@NWui{m1Klf`poY#aT+O z+6l_J{Zp$lV;7fYUY+HhWH!|m3?v~q1dP0h&+9I`&vhop@Ls5Qe{UqNqQ3WXqz#M0_#1j4~cl^9`t)o%$s7T-zM>AB8SYVXD5d5H}g5TTuo zv%dItm&U=whz{YN;A@d&MN}bEry1@0ten(4Nv*E(l08!Pn z#hVyp80Z4D4EK9j5<)VfPFJTZI^MW+cz$ccxsn^j)u|P4R$rkqy{1-7B@83vX2s{U zdRoyz{^sP8httu0{3@a{JBbnLpEjW8WE9}CKjssRjx)nSC4W7rym6 z%+%HKjF%b+x(R~T`fC<|d^v=+XZ5h*$X zEt>mha@w20#Y705A8lQ>(I_Op=fLbu;&S!BgYLpL>y}w==e!sAfZZ&Ttt}%qr6Qa0 zcw#S*5$}6h#-DjD3c+QQ-I0R%K}+SgcmMr_;}4%xaf*+ekG3W&9eH;e$~}QlrUQ`g zulMn6JgS~{=wAC8UPbu4d!y=NJdWSW{KX&9Yaykx8^O2Q+$fI}HN0bJAFm-QbHhj8 zi`~{49oxnweeXkc(<^kFRQKg~bD|_VB~?poIzbad)40EdHH7q7Q4}A8lME6X=>+WR zVqbB*(ero$i*7FHWT;4st-07Yfw9tTu%*&!VyMz$w5!s5Si%Rxxu0)eZngI50C~gL zxD>02t~lmD3-yc|J_ga|_HTuLfY8m{+_1xVvYHto6Km27C-4nde)ZTgpGl+v5)6G9 zoQ!C8?1Qx%m28C>X|wxWxCtWb68eXIS?4+=!Ij6#J$$T{O#K!$`kQj zG^8&upYtuQRd z<;?vEdv&9fLzqJ5v_v}3gWzBmuRpu5q^xY$Z7@}XA9aUgI{m#|b6yHKvsMlrWWhDz zM7(FMhBX6LhUHu1ksqRV(-?JCw=|i%bCPV<1uhR%eLHL<#x|`XJzlg}`;Ns1Y?CN#yXo8}O-DW4wr(va*`Z`!lz^h2 z|2WxmCB-$_gRqN{HFnZCUT?a7r-@fqWwO$8g<3u_X4162Mvv?xW1U(s z2oWKIP$#rNto2i+(xjBECZ#c)oT3ErEYM|3!2 z+S8fERg?5(C=N#a=$VU!*u_w~IsBn!7AVBX zr!$QpG=jL$9q{n2hMx1N8Q82LMUqBw!zG|OvjpR~kUG~L;;p)kl6QxMV$D})mLtu+ zzG-+YPhBRADcFYhR-D&{+I9rjvZ6D<*5J{$EUvt8cA2u~^1Vp1Z3;P>1EotxHFSE* zhx~>Wco}gFaurf8xJw)MWDa`R`o;Va(e~!=pc4}k*?oo=NIi+5V;W4fEJ^6<-<@y0 zoaaCK-nv7;@?4DX#A>6fwWsas_i^>d*t5%}3ug<*PV}Ylo`)`{dTu|Q*lc;1KrN2S zRP)T}=)6g#ZmVkQY(a(m6)amAX9EmrLeq?q`&~>V=|)j`xM05*UITgh!c3j>8!MBA zn2hIF2m&?oa<~cT!cG^z+9l%+d};;z<+4T84zHSl0tz(zjQ^SWq_RXj);nY<&sx2u zxBqjgY`&7dZOwd;+KFXJEw#s5cKijmeYP;K%Xh;&BsPIK8oTW{@juxOUax++L-Mxh zxNXOGzn3dh98YhAWD3ZPVnaQ|>d2%tvZ!K6<{h_+q~}hfYsKZ!)cNk3AWf>`MGvERSb8cb3+!dnRE=}6UUF|x8cHPO(H#5qT zL`x~}D=!BnN!I#1E2PSfCN1l6G<64g9d!ErqZ4A)7cW^ZSKlA1WmPROho{Omr^IJU1&`+b8*FGO#4GBGe+doRV6pd+9bOX;@TV#m9tSIi{A-Awg8 zX$2_uobOsN7r}-@D!iJL^GU9p^z?nHuAyZd_hRy6>Yd{biind-#~zko)uoHV(k6#J z&kju|pK_Dl2G(>(4!#1y_x^i!-wlILouusFl~*S`Sn>=}a|`37Vqv20uZzj#qGVN<8v^fdyRQ$9 z_`{#`q|m|Kf}BV51B~P#v=!{6@n>R;+86wsf%F~r$ox3Jynh`sH(#)h7N*alh0ymE zYgdNeVQ`zdz*Si(FkK9xNwstE87upYfSMJ)RG&fY54@I>VfKtD55kyutBiT$UhEzc zjR=1avodKV+L5U;uq#hDj8vt{+P?VQoSY|b;AC#OB0wX-!WdYz!S@-tAec^Zo#jrV zQ@)5=r-}?Plrq++0M0?1f0p*8>BPe+CGk4vjWM5yQ*1XjD(irn7s9C8>ivPs0g*6A zlHw%hfMKP=UkNeBeXOk z4_c1eB&!``a*B5eZ@F>%9em8Re1Em!U0yXFO?IE7no*ok8WmgwkNGCt%fGEBnks`G z;c@F~KkeGBt&Q;Y<<*&w+As8slbAHSUOS>*VKU`ewN7XQrG))ljKx7K#{Zi9eg^3I z7FTO+s47;;{iDJm-!HfEPTZ`VC`hOe3SffokndC1u=PD@z1=1xTGJ&c>}c45Cn^)i zt#&TGXZ1Bt%OvSuBii!IZ7XbQ5<$V=XTtB}ftJ$`QBtuC#mOkvMNm6J;$o0ih*Q+5 z0&KGLOVQwaVK((!RUPzSd_IjiTe3%7tP1Ja)Mk?ucl4NEn8`v89LYoJc8 zIi9@PpJdA8Hx%X!~9sdb_1$V#YfVIXH6>AgHgHvE(jj1@Vny6ynos zyg9|rYB8tP+D}4~4($oyW_6@ZL*FlbU6hfAQm7omu3IP(GsMwj+7hu_(Jyd*^1jwlB=qUV1=3vz8&#obV{yMW@uC zUM8f-m~$y9H#J|BchqMQ#VYd3VzVt9;I3nw_-B zttaO<+Zqn+>giih+Xcxlsbx^ZClku>U7z+6(xYrJt8F>Eq}8C{v3hWG9XA%a!9`Ow|{|8_Hsyx;p_LHGf(#%JunSs70zW!Y6 zl5%aCVQHVRi1FS~+B`LRF%k|dT@ z5Rf^$*z1LVNx~kIgwL%=@4pAdE(Yq8i4fk5-cN({MnC)}sevX;Eh%n8{V$3<3)e-! zM7-&?ARb8mFpiNM(qW}Xpvg9JX+EeQ`WWyg5SOqMIN_uN0kBE-`N|sXn{}g%6nX>8 z3855DkAV?aba2rx-F^*hh}6r}Ao@pYWiX(Gq$CK=r!eYAu!5IDYPa1L4r+20EW5CbLrf^5pp zJAX@02AZ+hvk=_>Gzug!DgqvA%S3tok;+Zzlr9G9|6euw^5KU<(7z-n!uh11-0>;= zr~5e*XRp6=7GmV7Bk(Z;5Mz(=mkf6SQ1YaGzeVr&ZUIi3Jl6LX zhZK1&q;niZ5Qir(xX&})hzcxSIyG`dnXa#?tP;l1wX>Wd@O6Kbn)kZuc`ZkJG8nuq2X7NnkUgQb8exa;iMFUav83N0H}0^`@qYN~6Dk6SKcHEJO5r z4mE=-{S$&fl;&aU^n90;z8VNq-Zrbm}#hMNKc}%ljg-Cr)7NP*3btGVV;U&i*mqSU?5I$ZjhK81>lN zd%Zw4)`q=D4*h8=+nrRE=ej7alUio{icyLcxuU}8^11AW5p`*X3RR%{TgKz5JrbQP zC9oXO@Td{g9(Tin0+%jyUBiK6BKohlpfkLt{g~EW6_vXFz;)KPtXIm+6n=IbYX>Z($&IJWf6bPCpyf3D=Z+Fk}4W-kco#_kT_oAsblT(~o(w@k~dI z67&h5-?O~PI9*AbOR`P&Ia?c~h~UFYvH$jPaNvVzX6nV*r#-~*kiYYuClMdaCmzRw*E>q(1x(?D6%ZZj2ygqY& ze?w|;eqP8S;#@%h|#A7LscaWZ-wO_*lB2DYHP&hMnG(eK>?WpJ2J zMUstVZFM4ZpksH3XlhrOZG5jZeMLx&t))_)R1eNBpAQSDDpk5(lbI0br_yr z(7k=r7kx6EOskdp+pgI`5H{p#m7Bctz;k$)=5t1;g5GvCWlJ(qyN2@hNqsYOg|W3x zqWVc=>f&N1`=UcRC&o7q?1rJ_tywPLbhCri!NIeQQW8yKcB}-s!!znrL^A8&Ti(wB zpi*Y(FkT%)qOr9P|(iLYd(}Ufu6JF4$#z$8P_@f#$4;Sv1@hWk2EuG&>MIpH8gOtRdk7E z+cH&sf8O--pbgJ}ou0uW)*A%H85H49$k%5Z>m!D@&vvg9ibthphAJpIu6B`8sFAyZ`WyS2$T;-Ukn6} zMi)=^cK&?BybS?9)pI`Q+yoq*2@$Q29=B9oExbu!ul>kwJ39@*h|C~XawyiRb|LZF z^&{YR$nw1Q4x3JlO0(WF(&;M~k7dew4Aj%7axi)tK+oDw?czJ6& z*|9HyJZen<{kSHm)*1jdY|+%N3t?P!;|Aj+)cWk^(*jk~2jkSuzqc=vpOz(A;MdRG z(q!qWLH4w0(sbe9ax-0bW^FdVe+rABZGJ#p{X^91l)rfxi9Z^gxtKPA09hLA*HQ_{ z6Tx2Bn)-%&%J&CSh64+WkWO`g?SYS_{Wy?%!}t0;jm=b15HtCqbq}um_uT7~4C#fI z3=K_r(p=wtN4rF^Zi#gJXc|Qq!HZr0q$ltt3>KD)JzYCY^MH1Cw2$^>Ts@yWP z`mTPw+lqdE&I3a`uBoal@zg!8y5bPd*tSnv`7$?`_~;i1!jBp)Onzc#oJ?vQq#Ch) z6$Bqqkjb_%B&&AvJ}%Xk(A-c6DEXV*C%1IceLPrcAzXO zLZA~DfC?+{`#dDeyoHE{k6qTHd(RJc4b|PFd^r?J^Z1o$dLr_7BL^~1)Bv0-GNm=x zasmyv6?2ljVB(4BJsGIV=zMKIScQ|L9hCN!j4u!+AppZLznX%?)D1(Dc3$VO9RkI$J z)$>6Fb@H4OUl|*ATWm(vHJxP{D`UDj!jqZANRiluNIU!A$C!oH1w!ZQOQ9_Ql;$5?UN>zWcuY6JK(wSJ?dej0#z z8)G`Q`~ZB}KQ)wgP7R_FCmq0sMP0nj$5RM{CE8*9P7q8|M6y0BX)KCf@Ip45efju} zU3)mujA@#8oGrFImi|$9B9X>E)*`*@oOd;P!LK<0n3jgT7(L&GjvtP(rxfF6NO$}s zFs0Vna!&aHV2GDHFAMwt$S;sbIA9+!sa;92l&`AT@8iwwn=%Y0nr7?gTi~2^djVxg znv*@ZYiE;9L#X2H@bujb>5DY_xK{{sFW)dX)o`6pci9t7k`sP}XE&o|4jrfp{G+}ZMOq4PcVNi^`?X*~84Kp6gP2u{_5w=A)VXj-G2 z&}tAP9yZ->)9z}OAh2GnEyPneuGwCT)zI!-OVx1eM5alO^x#T%8j`-6*ckZWKjl2G zHv=%ApRHegYmOJ#pZ*C@V#0;+-tepsWxt_bp|86Dr}iKynOjSG;Q*|Xgofd3o94@- zktP7YGS&g@%^WM%IrrvF`lQ(2@s8rMDm>{_oZW5rS#u*OA+BK`LF|2?-0a=M(k`a) zCA{e8PBo4z({r3FGXS866$O)d%m?CSyolf*ODnAif56frX;|JO!MXVyLW0CO9j%N) zrHyb>I<%ECHTB)8)~9RB1>5OY-pL~@wN{u9f7@ZZ-O_FkPTky}Gtb{Jl4l!?e>9X) z?Su~H%cl9(V96Kl;fEW+gbuF-Z9>J2a0)w2O2a^QbI0s_t6DJy&wxdo%sC}D)uW39 zpTk1Uh@be^Gb$}EaVA|)$8LfsL3sIX+)C>y&YeB!bv6@zmq9eHX^ibR9Wg&wv*Y#m zPT7oGcDUNOhZ+kSHi(BDOZJP}`*+ybBuIi!g-@;dO%t-zj;|nSr_^$dm*<*%>F$lL zz7%rCq|F~nN523WA54LUcVWqk&8Lb2r$!cO*l;10B4^&BL3e*mjn(Z`A5WN)G<%=S z2cKJ)M;uM%rnn@|arz=OTNfYu=-|B86~l32kTD?BbW?vSVoXJg2F|${^cVN01-bxE z-Dg|K%JfMe9+b}X(GldFPng4t2?BP04%8)8fBh3B8k@E=N|tG@^=W^wVlZ}s%hsfL zG`0NRd2Anmw-^$i*gvPFqKv*c7*?cfqoXCfzE}~=1iOevbvI*M)ZFYq_QSJm;oAu# zrad0QD#ts(Lap!Af~Z@da;Kfd_aM|oPcLQec<%aYJXq_Q-|dSdfKIPemo^@0Hy7K@ zC;Yl&auu^aD+yZimWM%>mvC8n8YWKhEcOLSyxdz#);m#;YJR-Bn$uRf^gS`3Yicxz zmGRh0Wh3$>v<&Zl|K112)yab29T6cHTKStP=mL~#H?ysht}ex#%ntB-(0Ujb?J z+aGL({FwPpT04HO@2@}#??Qk(6rJ$N09}z6gOe_`a#UeSU6=L8FNm}G(>qeJLS)?qK-6{n95)Y=2#QOcO)x5=XrIEw@2`aWc%{L zT@C0|pnMAs-xicC{HXg9YA98`_^Il#@NDLG_@1*KG#7cYV?Vdc3sC|qzj~4&SSB1a zp`Y5FY}fC2?Sj;@DL>;0PRIBU2zZ4&g|uK{bB3TiEbNjGCs{~qJPxFz8D5T8W@wr=|9{>+c~bm2Sf773Jm{;IhOy-uNT z7$y@@siN(6Ofy;pAZ1*l0UqWRYhE38UIv2}a40XX0cyHMl&S8nAzlkyi#{*(hSpL9 zK9Ge~xYy25^zjw%;fSi~2G#Dnk^NziyYW2$LQiCjq2I^i){+G#k!8-I)LGqwok!)9 z7(cy(PuzPtWqNIHUD*$A+qEmxmZgo$A|Ba%(x31JeHz%J86k{3t8ZO}CCC_+6qZJaJARj4L9Dx5t3I{0+i&A-`Ml9Ai%*^RdxXJU_4GShn#+rYXw)1-No*&4<^PbbEapUrkB zWw$Ek?%~<*+|87=F7M^cccSeZ_-S!(yaWhkGEj}%FH7LXmq#)u99_|;NHrfsgL(GP ze^F(aLg|R34OZc(Ap*RF@wJ4(edfWhA^ZJ;g2z6DSQ|H9A)FsIxsr;cS(uVhIh7}k zM7JVk?$Qm0Ikq<<5Oy~UGqgwTOT;6iSeRwZ+l*mFL4F*G6$|Aj1T7q8q1CRtvE{qd zj{QWPm=hQ9TulBNTD$Q8UvkasDV%yqg!Bu7$Q)WfB2(cm~$fQVaLR|@tH&|O8Rs;Jt8jow4suxhWz%; zspE-qJUdocOm+>eRStV#mpY#n-MBJ7{-p}-Q}Ygu5^0ZbMKpa3Ki14E^@F5j>tNaQ zMdf{=J?wO4sjUa3SKM@mjS)iwtLKSGkRsRMK7J|m0_iClsdL>}Yln1xN)@Zs_s6D< zNVAV5sgX5=cTG?dA2Gd6$Cl7TUI`r3XmMe>W<4GxM{c^kw$j-n(l?MD_pA!DS;6jA z)u;7^`pddQ+MX&!UUo8KV8)^j-nJ1XEM#Lzh==YB*Bv69>2>+f!fOu&dJ3i>O+h*p zB@N()ge4Wse3d|_RKWJ|{w=p+Ws zs~K-p*ap8H2RkQ?^q!*ySOa%i9!7uqu)J3H!m)I3;2GQ=TIjiinkx4@Jjn7?0`ABM zT$x%i_ZZQS^jpB+{M6h6;;oH}fK#j%V~CSsT28{t&5`sloUIui@|BFY(2!Qv42t)@ zNOLy{7O5g5Q=XJcAjDq0O<;akj*O|=mLZH{rLjN4$?$t`z$3*33u$@b_iV!DlOvq4 zv!Bf~)+vhO%;+0;qX?jdHI;@yzAqe`h$H%q7Yymh4eot-|5<9#*{K~i^!lHN9ExDb z%eL@LF?+ikD`-3z8>Kr4_9YSe+QQ`jT91h_SB8Q}*Oa2Sp+y@<5T;am~XXH>j z>yW7zBniKXEdIPnUq-F~?W!aG06wstUcF0kneMxk9P(X&F>sD%7o72^=E%)+%PApW zpDaWm%b-c$;9Lm+=VF`4;<@F=S+(;*2e;4etsX{4@MksD>8Uu^eeRHtsV`^qcfJqZ zCk~-_+^SNM&q#4Uo)rs%bJGz30<9)Qw)9Sn?jGtQW@1p=cZ(&iVCF^ z2b6>hp#>A_Pxy7rzLgDLXyID}Yn(y~R1|G!~&Zj8Kcvb%rg5Q-dt{^f4> zi>IYOH={U^*y-SVv$`EevpQ;@qG9}~E$|beeghOJ18KE!>XDjR2FtrD5ci}%g&~fN z*0Kz~e=aV`hhgS`aBCg$#8QB7$#A?tzL2`vCkK^Ye(*tRp`6tX;+BpL0|jh_iI%0a z(`Az(ttfc?`M`X*ke18nH+F;qK~6{-Pcs;M-H-Ge$Jhjd4b<(<@x??M4m zG^gJkjiE2nWCP@M$Yeydm`@x!Fph`vlxuHOt4$yWbbjhe#lV8|lfX#N{eZdq1P6f) z`7ZaK6xVuo{IrDkdEK4Eq$b6E$rnMxS@Jz8yGV zS2J6S2+2T^ci*DaL79DTCbwnu1fCm>pVy9A!7#aID`dNqH}#Q%(N_RSyCH~b=%8U? z2(KN|=@b_Mjhw^pO_KqxnTNO28U{$&p*i`P`ShL@) z0Nx--C}q{uKKRF=LC@(UfT>!FeMon6K>jH%6b(derZTO2_wTM^B|%qvU2n1fm64K$ zL`P@Cg!p$?T@(Wudr`VNbi`F>>)TVv_ooTBCQqprud=qPWVbBGK@hrBv!GjUvii~w z{kl^4*$HRsO#j?9pN`43?T_p1qsp|>MgpJq-9NTG@zrO}*5LXb-_*-X_TB3%AHm-7 zrpEf#7BY-H7+4l$=ImJjL)ZvxRZOUGFCR^*o+Db-GopxwA~IBprMDswO{=;JaOlLf z3$Soj+Lqtqk}WQW@&@VoNROVOd4PB4=W8LKe4EqmO}{->RgJwfZq5gvm_yM2F^i?B zaw!h&TXWl&J6sQf;4tze5d2*Urk_mQjim9LLFBsd>7tTLji-77laT;R zWn}i|{x6kA&-&Z-&fWMEDu5{!w=vGVVSi5u;7SLp!>!IbB|2k|)}KbidK&Tf-7H`> z`9Q8DdbKY9oGp>gL<=cK9yXXCj*^O{^(XN$wW01v9JD(Y0iS>9l~*qhb}LbE!A}GT zi6C!kE85;_yo=1p9u*r4M5@8w$sO4%W}X^OlydP918IcSPnZ|Yp*OMrihj6BnD<_R zG5aFm7t*r%r67XmYMym=>CUUcl ztI~)t@~9B@nb=EpoOcV#jonAlG@WmfLKw7oeu2v=<7oQMN5{iKRf_ExNNQL>6H#c3 z0ZX-dc{!;+YG|2JyG9;>RB_%`Lg^aFMy#S;6{Q-dk+wh>UNF`KGUG|jCTs^|q(!iX zWLyfy=&?2#6=NDYwWoOcHJfYX@EB4Eem+SDj*ian8Dg=EkEhH1tzOY;@N$I zqO4Y3AjgU(mRs**iur2n=79}DF&)n`EvY%0EV=aB&n~H@YAwon`Vqk|u2{RykN2*e z--DV3pJOH3=v(d8bDZ?LgJGn&Gdgovk_|PoqJ}vhq)pKypjO1#x7IpQ*gC4J_ zF}Nf{ORtGuaopG>vYW5*G#t7(2!Rmy@U4zk9}2b1-EwW*3tjekSW7ZmPjBSzD{^;? zJYfXGKE!2{ye}=K8IP*-G8)vpH8k{X`NwNLU4yiW&!VXP1F_PB|D@w%65;soW>d>u zWJ5Vvix%;>&BlcqHYY@`k9#aI6R^~sF`wxNt&3IC@Z zBbw&(Vn9B++exd_MfPw>DO?Sl6PZ_l5%{(0sr6ky0q-;RWv4!QQux$}pExM}lE0o} zscvEZ&{Jz-U71lAIFhbm0Vj|c|*94Wi`NcO+Y9Hk#nbSxP^cIZHXwjor7 z^a~I-mhKZuwEsQ>=L0D~bOTgK(f(5oRFQfN#0tZ^Cw)V^_+!6>u)rZh`2kz%*576l zOAaXGmq5{>KMvweGm((#`F}rDe8nFTf>(A6?Gd(KVqgC`9Sk8e$Z!ETP<4&Rvr~s_ z!|lva2CjP6qfo~mhMvQV~{ zDc6)0TlIQD;aS#-D#>>MIqCsI`hu1T5rX@DvveVoOf;jWN1naOYyv9Ugg2n~+f{VwasPXP0CnxVAe=gtk}N(8vX3XQfDlHpDekR4&>kH%@I zSLbBE{#;Xb3Zl#2z5?l~8)W_*3N)J-tEY$1h{X*Wmb{{&>`ma&H?9he*?%WN5}5V?aMl^SNHf2l(;~&TuMqi{WawN>XS=tCs}$`XUdm$S^>B zhLNibzu`fLM$Gs9oSU{51i^P_!f=#qD1R{{03L3x!6``n%zz!T!qj-6a{b8z(0*sY zwS>)x{1wvoSos-O07g8!wAhM}-Q7n%kJEMNR6|V==nEB%y*%qvEt~|ei8FTj-6oN4 z-E-QRof#b+g{N$00x=-fIu94=q%7XSUJ1 zw8-P}h}0_~QUYLZt&Fgm1mDZhCVaUdpO>a~$B@DVw;8Cs3X=%W;`Cg!XdGXXmes4j z@d2EI%Mv@e*yEZk+xCd1I;N>--n9OVXA$P8f4j;c8R}eDrYT>fnEE5B)S@{r$gT1DmL{vZfo8FE0^}F*CaK| zd+k(uPUC$|2c<XaJ_H`vKg#HrsZ&>%=jUJ7 zQZmM_Y@08uethL@bFKQ|I;SXj86)Gn$|1dF_EqMAx`MJ5P?lNUb&%zD(iX{-rpQFv#o9PO@l_>T1#u3;{i}dSf~R-I z-_xNaNaeSX1&aHlbr>p#kF9@`1dfdn)&qWz3KF@S{1NiVKA=NijhC+MUzv67Sc&d5 zJuAqU^SZt~7GR#Ixk~M(@#QgS#QowV(>Eb3aJEX3dzyg(h%ld5r4#aE+zl@L(26n0 zq(6*`DI{Z6dIfEdV+8pkQO16TQcKae0$pS&4$tH0K7+hq^d7?i3pbJL##rwiRL=Uu z87;iYc=&7YCq#r|(inMo9#^t}-sb zsT4wye*8*%+1Mh}b_tlq9bAyaOY75}B_R_98**VMZkGv}I%59mcakdtkK_Dy^)VPe zy?S`JgNF~uG3gp z?dpenebO>v#6}YY1w&=v=Ed3kq}nfth(8Tw*sgv>_{InhW`STAV<58dOTHG#F-Uhz zqk()v+C+gmgDc2zohnC<*Ap7%4ND1qTGg zoAI6|;&nM7nMluD^)FNwVc)lm1T@?>2D%Sz3gyK%;3fdm6E$M5-7jQ$K;$VT=4&8< zan$-b=iWz1!Ct+@M;m#Xj(D3Jqwp1x_PV3HFZ&U%=TfX@&XZOCOvFZzULM#P_LSZ4 zc#F@`Wh;w3@OTIxr4CrB*+7rnwZ=@toWt-TT6>l7Mc;JHh?xGMJ({_YXX{ym8)G0( zdEGR!JM0mx@7T-X^S~m;~g_B-b!Lo67;QOu3vI#jwEbesiU@zB$LICAwVxXO_eMC%JrQG61$*( zknz~D@`kZS7bj+OV0K!SF@rqGT6H+|tT*+EedA7@N62GgYhf4Rkz!8}JaX*btj zcR9rh>cacIj@DPr^CBJ4^ol$%Egne05JqK4qsuV%3I@eTT9wlBVv9a88YjXj@T21I z=*?WQVnz;Sxhp>q&KEc%^cOls%5znY+9DbnIM1A(o3L8bNF71K3i7tLd>xRutT^f?v;}us0)Kwl0NjE9M}SFl%xiT+{)cd+HA^&RNHx(;WA-+YCV>i zcpwJ#LawGYY{&0Kg{PPKK8mCN4r7 zK%D$PZPpJ;v{A^&(IFI~J&e*e&2~TrVUz6=wZIoT1M3b8WCbE=f1y1jtC4tDWh@!v z4vU8RD5ZN#Qlc|GZzNu14X5wL$iaxI){(o^-CdZ ztUQ9pLJvzbb3br)r|JHUxsaLyU!OaZob>Mq#R6jdSjX|!{tL+I0~+8UAujY^u>eXY zxYign`N-7)?Qdx%0~}}ZZRpFtg$-yGv}l!D+l%;r$5PxN7VCJ31OG3~ISF)RQ9j+t z@%NY%$XBH3r%XHE&m?VA{1Yls=@w68@GlOu-AcR{O>V!B!GGL;9|FOOe8> z5+kp0pF6+&dSs|5!7?Y?-Elm_Sav)^onhP~oLe`Ed&SAIkH0#}^XgQuy)R|P3zd13 zhq3B?Qtpd(iw}cYr)~#1k~5iHg6p~?S@BEHPccq5e(m=Ar$(6cCefea39Mr@>KCP{Hj5C~D;z7{t3@&Vvr6&`q|Diddl70-|&KUUL4wzRImz;>3rbm~)G`eI~~rDDyP_hF|dcL1dn6{gu( zM#4s&ljgW%Y^qIQrG^~?y6%LtF08FCsw=Vrl6&551ozRQoXgW()1fABPuF%L*AWDg zy$2K+;=Ww=mi&*{OnU5wa}`~ECog{iZd5Iad@V$cn)&|e#f?*b%#i55oY0=OKa4I| z<62xT)9tURzgC*AACi53-hv!MaT@!-ZY6t;$8cF3A5xz=aJCZt&b&HzVH=q9en4-f}9 zfS9_v9t13Ul5Dabz?Yf|76sENviil!b1}I=x79YhkWaaAb2_ukE6@ zX{#J}VJI#S*RcuXm?q45I#L#}Vr%_kC|fpOTS9rz{Tf7L)4+k>wy9mhtqz}=s-AI4 z@`NG)5Z~>@Zn7wb+oxUbur*KH_Ysxo1-sZ(hG{rna;~kM*?b?bOhGlS%dmyRxwge0 z$p%doZb8s{3%RqFIxC*}&~;<}B zTL{EAie`R;Qkc5h=cG2A08qLDLhs!P^jME|MJ8yP=cf~#c zSecuzn-))tmQnM_n`+Ro*`8&`PAe65Tbbod78mJ8gJE`b^U+B^BxM8{xQe%h)ns@H z5D_lu$Z(ka6lD8nAifg(yf@JJ(nJ%zJ@KJO0?OgGt=KHy-bK`qZwk7EWjI;~jRjYU`9N zc@#}&5^5EExAZYJiHQPt*UM+i^0MwCrs1C^bYiPffAH1b*;*Kb%UlYuEOj>Bs!p4l z-qWSS@_2QRZRvyO@pf#~rbMo)aXzuZC$=jj!t!vecS>#s<`vUe{ifM*$Sj>?4Vk`i)>wuBon>Jc)e)F zsG395gD%34Y1;(QkF8Jb7Y<9xlC`x(-HKhJ2C6(~+Ng4O2!kzmOJrT*vLDF$eDs5N zz)1+&u9~v(NnkY&g%A_3rFjSmOKU!^tzSCOxhNZWj#>*pbviO&5l)Hs(M0b0lf0HPaDBBV$)>I|DY%R9;1(Oh#Ltt38IQ5g4k3py=E~I!oah~h zI?CXN6a52=@&Lq%)x0M^{AYMf%IX2Vu3na@_G|o_0Jx5>Q0X9G2>66d8>I$&slmIO z$-r2bzt@T*?N-Oj#`ZX^Hh=cvu&9Z30G}VkzAmMU239g3;+#DX9fGxO`!LFjBHdMM zvoGjqx!_CYC}AS;`Pwp}WcVS{B;nxJ_2HT(2<5NUjO zRCLW~B=IdJPYchC(v8YL~% zWCdugRf^Nl1+;L!Jt@KUtabL*AT-CVp8k2{@`CS+?_cODs-7{|9_t?$a^S|QURmgW zTFdwbu8j3L=)BR<(Z?SwH0{jgfGUN5PbVXF4q~_JIUbJAie&^Ja=v}6T2sj=hh@xI zNGSNCv1{Epj!vuijRfV05DGq@@XoD}RQIqBmxe=f=?{@bljXU*t@tB|PYazt87r^_ zp7r)v`&52`{k+A4eaH8w*harTs?Xa4d?6cO!h=awi>+ z=H`0Y_LDJEuZ=L@4{K)_-!J{p%lp*-y;xW4J>Jb95cArgiYB3du6wz^tc1?r-}-^$ z>8D4wjlZNEkXE^4_HxCk^MsA!F3~-SN24i8;()KvA5rA`t+=|*u{Y|uCz~mc)s%Ho z1>gRjd88gCf}aRdp86`ICD{-mn!R*$?$0Uh{)#**sz(m=DEH0GFFeiH9ay6ptF;9V zSV#S7#h0L4_u$HNh%5E^K;2>l%YKgU)Z;fuur82g-JQ4jSUI%jgBIm;ZRzWVSW$AW zKQ(_9Z?1ZAHO`jqX&_p+KC7viRM*IX5WRgZm~%C+@O>TTdR{YCoLiioA!ZTq(&IUm zLzX;VW7;S$RsDggB6pjfb>I8UqFjrwg4b)DRhaIPMp#Ov8DhrxF+q_kvBy_AC(>jw z4|lXQs?aZwo3}9?GiHY`E)>`tD^I~+o`9`1zlBZq5F%<_S=(omQA+c7LYI&Y*h^z$ zK+3nSd*+#dwKiwjU|jf&yJ>Y@LQbsqo(ld?y_IZ*2fs8){FdT$N;VTz0@qm_-^Z!` znCjP*HObwoYyJlgUbINMrh@B*OW%H6&XI-?YEJHsS*QJ@flN_T<4eic^ z7`3P*iFd`0H1tO?X0n$bduR8;XSz$5eAGYgJv;7qp+8T0EhHe2<}EKAxf6=-*C%-G z_%=|+!d*pQXDJ4K z0lEyPkohUF?C!OFGfnKq^jP`FLT)9nmkzgeHbSObV!opGHY*xpJ1vMqqy-FJ>jNvh7}27P;i>>FD=kX%0wzLz2MVv!M! zeR5y%nJ_`W2QEu=#3xR#!Z>&n3b!me>|%Pi_`ax8JjHw45r>9%NCJZRh)X@OYuxgHuZwv2o4Le9T+kjNd4;(JR* zqs#PFe4)5kg}dg^r>29t6w`m1tsF({dtq-7 z`CR?{fA_p?yYKtFbYAEA{T|2XfMn4cXbe%+bxIqVR{7D|B9P)V zj`inc5cbnj!Agp=7)wE60<_S)gi2o`h1d>8NzSE(dE=->nwK6MMjJ>|8u8t8f0ed}h(il>5EP&&zZvAsva^=#U~u zj4caGng8WNqzFb{#wg^|$3pofP+GcZu=}q>eAGikwL^rqMM=Rb=68Elu3FuBKfhLz z*e&#!iv8bvC=66nKs{~f%Ma_wunsryOoW-tY<-6|+-yd#B;CB_8f^_PUh5>)bCRKq2wRX;sqnrfV`I`3iH?v7%u92~Gc7edb zQ-NV<@R(J{DZUnQp=LZ%Vk)g+V-NAzBT|%;Lzz!;^=re&Vvw+vHSL2XtflsC|1s^{N*0S%4O0g#w@`&sYUGKH?P1 zq71(%A!U4iOkcn1rIAZcBlj5hrd!rF0Qa>9jc>Ji4x{=8JVc=P_K*H` zNv$fkjFjSNGiCO4c{0E}sS;3InC<-F_zO?fWUc>%hD(TW~TeGk9pTm@0ig!^sX;0@B*z->&xqT;7)xe4^YPhDz<>x>hf`jEJqflIlh#;&3f&*Y1R zgk6{@uDU+g`v$aR$G;PTD2$2|?0q3x&Vb(Gj59}6CDtu;F)KsWt*2N; z14!MU`V6onwdFlZF*3$KQ-2!HA?wZ--U&*1njmv|)Zgm$)U14Lhp{q`3paPbMkInsfsd=@SG>ZS z<&nLLn>h;jKtb&)0Fi0;dX1GJJe+kb@&d^ldsOWUO;qhIOtO9S@_AEehN&d)i!;A; z*yf*n*t3u*DUzJ1EA6U|c|ZiSbdqjw%pg3v^Zxf~k7Kd?q+}1Uz^HbqN7?C9FB24CEF_NyW~Zd^o@L zkt^Vxig)-e>jiVd2;#0f$jdN?zx-Kmao9c~eye06gULx_fYva$M{1Bytvab< zzTixeW1?3#D9>)n)!x~rQDpNJtSr^TdXB5<<>xq+zqJ6>;> zgKym8bhiF}ByTH5DvcHdvX~Yd(XOuR7;=Xozev>{c%0_ecE0 zOeNc3j3B4jtDH>fTZh9=6^Ncz?g+UI86KzTqYSbsk5fRRae3S-0JN`My9m75 z=^02z=x6vOgvF%Vufhm^DP%d45bU7cbRZq@qfWW3F5VaLNUk+e6aG+67|QUPVZ(a? zoAsxHqZGQdEA2iOD=#^t$O+QJ9J&BYl55wz1gu9j@%PP|G!)Wt@wg4rgIcpMLeEN2 zToIGxFvc{Pc%xtIH#Y$AV5hTQU1{*~;p)xz3lzPQw}6>y*>V+b5OPp*acx%guOiXA zF*MZ⁣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 E0C1yyS^xk5 diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst index d29c4c172..a21c4dbfe 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_verilog_commands.rst @@ -3,6 +3,8 @@ FPGA-Verilog ------------ +.. _cmd_write_fabric_verilog: + write_fabric_verilog ~~~~~~~~~~~~~~~~~~~~ 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 add629dc6..7aaa2f7e6 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst @@ -255,7 +255,7 @@ build_fabric .. option:: --group_tile - Group fine-grained programmable blocks, connection blocks and switch blocks into tiles. Once enabled, tiles will be added to the top-level module. Otherwise, the top-level module consists of programmable blocks, connection blocks and switch blocks. The tile style can be customized through a file. See details in :ref:`file_formats_tile_config_file`. + Group fine-grained programmable blocks, connection blocks and switch blocks into tiles. Once enabled, tiles will be added to the top-level module. Otherwise, the top-level module consists of programmable blocks, connection blocks and switch blocks. The tile style can be customized through a file. See details in :ref:`file_formats_tile_config_file`. When enabled, the Verilog netlists will contain additional netlists that model tiles (see details in :ref:`fabric_netlists_tiles`). .. warning:: This option does not support ``--duplicate_grid_pin``! From f5e8f175fbede3ef610a0036f9f81775dc69df23 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 25 Jul 2023 21:27:58 -0700 Subject: [PATCH 229/391] [core] fixed a bug which causes flow failures when group_tile is not enabled --- openfpga/src/annotation/fabric_tile.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index 39bac50fc..bb291db07 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -80,6 +80,9 @@ FabricTileId FabricTile::find_tile(const vtr::Point& coord) const { FabricTileId FabricTile::find_tile_by_pb_coordinate( const vtr::Point& coord) const { + if (pb_coord2id_lookup_.empty()) { + return FabricTileId::INVALID(); + } if (coord.x() >= pb_coord2id_lookup_.size()) { VTR_LOG_ERROR( "Programmable block coordinate [%lu][%lu] exceeds the maximum range " @@ -103,6 +106,9 @@ FabricTileId FabricTile::find_tile_by_cb_coordinate( const t_rr_type& cb_type, const vtr::Point& coord) const { switch (cb_type) { case CHANX: { + if (cbx_coord2id_lookup_.empty()) { + return FabricTileId::INVALID(); + } if (coord.x() >= cbx_coord2id_lookup_.size()) { VTR_LOG_ERROR( "X-direction connection block coordinate [%lu][%lu] exceeds the " @@ -122,6 +128,9 @@ FabricTileId FabricTile::find_tile_by_cb_coordinate( return cbx_coord2id_lookup_[coord.x()][coord.y()]; } case CHANY: { + if (cby_coord2id_lookup_.empty()) { + return FabricTileId::INVALID(); + } if (coord.x() >= cby_coord2id_lookup_.size()) { VTR_LOG_ERROR( "Y-direction connection block coordinate [%lu][%lu] exceeds the " @@ -148,6 +157,9 @@ FabricTileId FabricTile::find_tile_by_cb_coordinate( FabricTileId FabricTile::find_tile_by_sb_coordinate( const vtr::Point& coord) const { + if (sb_coord2id_lookup_.empty()) { + return FabricTileId::INVALID(); + } if (coord.x() >= sb_coord2id_lookup_.size()) { VTR_LOG_ERROR( "Switch block coordinate [%lu][%lu] exceeds the maximum range " From bb837f4f792685dde5a8db874cec14642cc9c4c4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 25 Jul 2023 23:39:59 -0700 Subject: [PATCH 230/391] [test] update golden netlists --- .../device_1x1/golden_outputs_no_time_stamp/fabric_netlists.v | 2 ++ .../device_4x4/golden_outputs_no_time_stamp/fabric_netlists.v | 2 ++ .../golden_outputs_no_time_stamp/fabric_netlists.v | 2 ++ 3 files changed, 6 insertions(+) diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_netlists.v b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_netlists.v index 662174af0..2b7055cd8 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_netlists.v +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_1x1/golden_outputs_no_time_stamp/fabric_netlists.v @@ -48,6 +48,8 @@ `include "routing/cby_0__1_.v" `include "routing/cby_1__1_.v" +// ------ Include tile module netlists ----- + // ------ Include fabric top-level netlists ----- `include "fpga_top.v" diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_4x4/golden_outputs_no_time_stamp/fabric_netlists.v b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_4x4/golden_outputs_no_time_stamp/fabric_netlists.v index 2eac9b4f9..130506763 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/device_4x4/golden_outputs_no_time_stamp/fabric_netlists.v +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/device_4x4/golden_outputs_no_time_stamp/fabric_netlists.v @@ -55,6 +55,8 @@ `include "routing/cby_1__1_.v" `include "routing/cby_4__1_.v" +// ------ Include tile module netlists ----- + // ------ Include fabric top-level netlists ----- `include "fpga_top.v" diff --git a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_netlists.v b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_netlists.v index 246169183..e9c113a25 100644 --- a/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_netlists.v +++ b/openfpga_flow/tasks/basic_tests/no_time_stamp/no_cout_in_gsb/golden_outputs_no_time_stamp/fabric_netlists.v @@ -58,6 +58,8 @@ `include "routing/cby_1__1_.v" `include "routing/cby_2__1_.v" +// ------ Include tile module netlists ----- + // ------ Include fabric top-level netlists ----- `include "fpga_top.v" From 34743529ae7e58bea39873f1f20ee3e587cab5e6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Jul 2023 00:02:40 +0000 Subject: [PATCH 231/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 1c9ca43f7..3cff6746a 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1214 +1.2.1281 From 5685fbd5e843683ecb5ff67220732a1e504533a2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 26 Jul 2023 22:17:39 -0700 Subject: [PATCH 232/391] [test] adding a new test case to validate the tile modules on 4x4 fabric --- ...reconfig_testbench_example_script.openfpga | 2 +- .../regression_test_scripts/basic_reg_test.sh | 1 + .../config/task.conf | 38 +++++++++++++++++++ .../config/tile_config.xml | 1 + .../config/task.conf | 2 + .../k4_N4_tileable_TileOrgzTl_40nm.xml | 7 ++++ 6 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_4x4_preconfig/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_4x4_preconfig/config/tile_config.xml diff --git a/openfpga_flow/openfpga_shell_scripts/group_tile_preconfig_testbench_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/group_tile_preconfig_testbench_example_script.openfpga index 2bd89a97c..b635585d9 100644 --- a/openfpga_flow/openfpga_shell_scripts/group_tile_preconfig_testbench_example_script.openfpga +++ b/openfpga_flow/openfpga_shell_scripts/group_tile_preconfig_testbench_example_script.openfpga @@ -1,6 +1,6 @@ # Run VPR for the 'and' design #--write_rr_graph example_rr_graph.xml -vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --device ${OPENFPGA_VPR_DEVICE} --route_chan_width ${OPENFPGA_VPR_ROUTE_CHAN_WIDTH} --clock_modeling route # Read OpenFPGA architecture definition read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index a590bb7b0..92c041723 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -179,6 +179,7 @@ echo -e "Testing tile grouping on a homogeneous FPGA fabric (Full testbench)"; run-task basic_tests/tile_organization/homo_fabric_tile $@ echo -e "Testing tile grouping on a homogeneous FPGA fabric (Preconfigured testbench)"; run-task basic_tests/tile_organization/homo_fabric_tile_preconfig $@ +run-task basic_tests/tile_organization/homo_fabric_tile_4x4_preconfig $@ echo -e "Testing global port definition from tiles"; run-task basic_tests/global_tile_ports/global_tile_clock $@ diff --git a/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_4x4_preconfig/config/task.conf b/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_4x4_preconfig/config/task.conf new file mode 100644 index 000000000..dc95f9ab2 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_4x4_preconfig/config/task.conf @@ -0,0 +1,38 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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_preconfig_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_vpr_device=4x4 +openfpga_vpr_route_chan_width=20 +openfpga_group_tile_config_file=${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= +vpr_fpga_verilog_formal_verification_top_netlist= diff --git a/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_4x4_preconfig/config/tile_config.xml b/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_4x4_preconfig/config/tile_config.xml new file mode 100644 index 000000000..1a1f3f6e8 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_4x4_preconfig/config/tile_config.xml @@ -0,0 +1 @@ + diff --git a/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_preconfig/config/task.conf b/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_preconfig/config/task.conf index 024c2e58a..79d45fb91 100644 --- a/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_preconfig/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/tile_organization/homo_fabric_tile_preconfig/config/task.conf @@ -19,6 +19,8 @@ fpga_flow=yosys_vpr openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/group_tile_preconfig_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_vpr_device=auto +openfpga_vpr_route_chan_width=20 openfpga_group_tile_config_file=${PATH:TASK_DIR}/config/tile_config.xml [ARCHITECTURES] diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_TileOrgzTl_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_TileOrgzTl_40nm.xml index da117fa5f..c51132506 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileable_TileOrgzTl_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_TileOrgzTl_40nm.xml @@ -87,6 +87,13 @@ + + + + + + + | 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 259/391] [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 648ce0f12a4a3fc4359d545a9b0bf0ae30f3a591 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Aug 2023 06:15:05 +0000 Subject: [PATCH 260/391] Bump yosys from `b04d0e0` to `f37ce5c` Bumps [yosys](https://github.com/YosysHQ/yosys) from `b04d0e0` to `f37ce5c`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/b04d0e09e83102e14a53bb8b8dcc8c35f63b2fbe...f37ce5c839c5a5e21fb74046375e5928ad91d22e) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index b04d0e09e..f37ce5c83 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit b04d0e09e83102e14a53bb8b8dcc8c35f63b2fbe +Subproject commit f37ce5c839c5a5e21fb74046375e5928ad91d22e From c05f12ac118d1578d8ef1f399530aa25fe3bbf47 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 2 Aug 2023 12:24:16 -0700 Subject: [PATCH 261/391] [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 262/391] [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 263/391] [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 264/391] [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 265/391] [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 266/391] [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 267/391] [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 268/391] [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 269/391] [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 270/391] [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 271/391] [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 272/391] [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 273/391] [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 274/391] [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 275/391] [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 276/391] [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 277/391] [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 278/391] [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 279/391] [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 280/391] [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 281/391] [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 282/391] [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 283/391] [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 284/391] [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 285/391] [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 286/391] [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 287/391] [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 288/391] [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 289/391] [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 290/391] [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 291/391] [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 292/391] [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 e70a67386f83338955387063ed38e7d13e8d1557 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 06:13:52 +0000 Subject: [PATCH 293/391] Bump yosys from `f37ce5c` to `e0ba07a` Bumps [yosys](https://github.com/YosysHQ/yosys) from `f37ce5c` to `e0ba07a`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/f37ce5c839c5a5e21fb74046375e5928ad91d22e...e0ba07aed3118502e623139048a24a0cd433d85f) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index f37ce5c83..e0ba07aed 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit f37ce5c839c5a5e21fb74046375e5928ad91d22e +Subproject commit e0ba07aed3118502e623139048a24a0cd433d85f From d579bdba39eaf929d61ca48225c60c0286f24ec0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 6 Aug 2023 23:16:32 -0700 Subject: [PATCH 294/391] [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 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

;uHa2P8WU;Um6l*dVcXCF`-Q9 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

a~>1OXC;wDkzi+oU-CUke$@Mkg9M3OL!!FA> zNR5Z2)g_kQ*FQ7jr&vEx7+K3C-Eg?Eq%iwSTHyWi%WF^f$`HNWsG0uaqoXxPCL~Bn zlmCK5G-Ki6gGdDb$0PjxyS-#6iI9ev*2k^K05oegZaz+X&f7Kl#Az+o9_$dq#nkq} zMcx)CwZL2E=^i!954tPJciy=9Bwp-szVp_3HG+M4qsmD{&K99qF@MZJ{NX{k*ih;M z8y&4@P410nx9Wozo=zI$g&z}35_{k*+q;w5&hh)Na`Q5T-Q%QYO8jKisuLTr8s5Ge zI!&WskxSM`KhB0oGaJp&6#h2!pLMqN0S7M$qlElu(sl{=n z81Z&YQtFwF^C_&KeCgTQ`cP|En0TaVxUCx_MayLyQ5W)ckiM=i#KcSkvMXosI49@- z?e4gX*&Pl__|$ir>Kx&V&KHOL()~rA-?CM#q^qNr0aGkxqIyk5ruv&{fAW_ITJQVH zCD+y(ed(_3K1JR&q~iQeI`c2*!#EEZPk}qD?brt+ukK8}u2KCkcMM>&zbefCb))^y zH0N6l7MMvgXIdP^jyEG8y|OOdSW%d}U^9NoRB6)RN>7@E+iUsf^2} zlQXrl(G!ut(!MxvIeyH9;6r-blt)~W=Z9PK2>BnC?e2@|S-9#H zv-o;X5CmU$C-V@& zD+yA=dV2fvHrkg5Td&2j67u<^?|yXh>76^TqoSwBPFzv(f%CsUb#G&gL$OQ3){tvw zym#%Al5~gBw|!ywmhG-9gyOlpedOn}caIzvdc_hO$pP%W&%eIpdQrhl<(A7Ot)a7) zY8%(yKBqYGOj=tbeD|+%&S9DCmqYx_PP<2yw}z&O^Mj?g{mK9Q0I>sLn9wEW575(7 z5nWOVGTIS8-W3mm)Q@j1CNl58t?1FU68>{qa%T&F5mJ=BT!m!ad9sLM2ivms*b8JL zYzezaSuO%pdj-GS=6A!w{qzsS^bkZDGr!O7Pk+~>|9D@pth^`-u9A@u-ACefjSu4< zH&|QB!C+40?-Cidm1oocKjldyuZyL}bhm85R#;`#%l1Xi#iIKhJ7*IWg1fCARolXK zndzFz4*ub}-~p!nDiHLlHq43e^}7MIz89e)c4a2TYS@dd?AQQsKE@TX!EkR~n4U>= zO?a_gas3K6GAtAFU$O1!snftlq!R=H;+_p88oLgp;%}g+d@NorTq7^i!FHnk+(9Y< zF58)sFD?xThmlPxP^aG+8!UJ0v1tkyfqLE8tIKZG^*(BRhQGDPOn%6X!K+Y%Oa z2ivjyMu-9J;H)-Km0QC=aQ#^;Rw5Xx?7}7tBaN$IW?}ilJChw7XnH2ohTz~Bjh3E_ ziN}IGf#x%r-NT`{1orU0bxYv`(*$>tZ3~VJm=ZaQlmOk|@O@wGQc%jBy{dVoIGy7pXxzU0*{`b5I$#*kGwW!|Thyp~CAY=B7o6lT+i2gDhw zbE~z%y!WCS!mj~wYBvlu_8Pt2*+{@zKvN&gNe^RLY9QO;;T;#@AQC3r7JObfuf1g4 z70C@C_;kKcf03A;Ku;M!2x`Fx><`(K34-AkHbCxNdoT<5ip>b;e%SX&p9*BwTOXv!kK)3*v>sF5K)&>6N4vLZBre@VL>PFsM!s9WQix4Vw zj0gUaxa*e2l|2OrlQ6}@aL3P&WenCA6el9r`$peD;|XeYfsiM1Av*Quh~tH znMxbW{sEaP!_0z{`Q3hfKiMZ1Cc1#1Ii%g&FO!elgGNW?zEULbN~y6EfgAHcj}7f2>dluB!1Xm%#ewGehvUMW+U}K1t%KS2@Qz%cDb5Ie^1=|G zg-3DwexS%umpNVLE;rI<)KsnjMX}aTsv)n=>XZ zAqoQ(t6BA-%jvJHDW*+)>T&+0(rk^fK5yy}qCA?n5g>)68S&~?<->W*`Zh(shDpSbcS-MtJaC>+{s8+HT=BA`bmVf6fnFr)_Qx-~O>-&7L)4V68 zB`%CYNxiM9MMf#2XI?ZOk4(7nQpwvEy=D)@NF6LrnssQ**+5W;X?eZLU-~DZX9XG2 zYBuEpFbcEwq`x@|iF8=wN86wsF=XbT-gzjg2_5*8C`W~pWS(njLGrJh_dwH{wwtAV zhueLyOVdX7xTBY&EwdbS}Bn*-Dmo*B^vKqw)?oFArkG}Wy>LRhd567%o&0f;^DR`5>aQ4TYl zBKf@K#JbkAlfrK_%+gdQ8$LpL%WR68Ltn~Ufb4GpNXI3bhKk3i?EhZa`IfK1$zEE0 zbutt+pOg@R4>ND|p+^W3SE<$fc`&6o6FO=;jb)g4ULZ7K-TGDfdT)B(-i?6yhp(y? z({sb?>Rz#B*K@pMZ5%ot=$7wf16iU&fLXKZ24GjVK^tX=QycvfzbLHsGdG1``DZ+a z?Sl}D!JBii zH7yuS{Bk#M`@1`||ug>Nvh8ZIoy}1wj^W^zi z2QO*0wGCFct!v2B1meX1{x%GQVUx#nzXzgx_`S!JL(rrL(Hkx zyHc<=k@9fiBYLZ8;9iBaO8C7KqW;W^58N5BJaCkW-#sezcy#4(bocA%i>FP^U!P^X z+5Yw-m#H>;w;%=C>-)Ekl1jWj>rSS6;ZhuKE%sdhmU>e^r-PB9k$pnb>|K$%KG&wz z0hiH(f=wbul^!^F*Z2);No@dA+jI3L45lwv&(eQPh_)59o9XL;=DmgnRhMvnoA}kG zk5>0bp!4-HMXS(tg(`$wyB_j+diH&{FSkLhkRs1Vo9fU|2^isefa_j`i&hU4v9G46 zrbRnQ>B@=Mt$X5AL~j#3eUWtEWL+a7Z|xa;kTE91R;mxf%m|AeVQ6qxa8cDq_GiDb z+J5nK-x5^7;+r3@l|PE7OfaOGYpSx(_zwi~*!VhP;t$GU2jf9Q7=qQx>p2BljGBvC zVSgiFtAjsPJ^W}<<4tJuLN|~>Rg?u9PO-Ia z9ppGxd6xaXI$_^(QUdLj(GAe*z%Zhhxep<07VF0un|=C0-)&Uql)<~r`N%EvqvP}M zpsmcn|5T{Q?~BOBzzkcxYnOO~&wbt(u7Zvb5IySuaS@s~p=U_aO<^G4N@v&;Z=(CP zUNa+(N&1a^)*uHG(XH><{NOE`chGPyw)+P;BxG0F#P!6Cpscw+W@ z9jE=&_4S%l8VK}Yz-F{uD)s%kR{AuGxcL zdk~74Wvdo>Q}9}vbDecAi5_b1$u>HWQtU8m3DlX{H${3LXOwUD8sjeVSo8)#A)U6T z-(y-eUDE_vL9V;cz<-U&nM4LO1CyHobjR%5YQx<829{5Owvj7Oi~t zYG{5XzR_{@jJwz&9B6z?N}V4eQz_tj*fG?;_sn7g_ywYtYBi>Y&IdA`3-B*JHWf$z~BxM!6DvLz5lp|82E`B5Fwtcotu zt=54C?U*PDHPVv8%KelDJ*F9OL}3b4nqMb}UeeC5y+8A;dze8?vC2O|5e9l4h?y^g z=uV;d9A9p_gm~t9`Ba+4`h*7267mdJwA?ovgRU+)%xs2;x|Q(*8N0Wq{8s<0s}T^e z;O9S4!zO;e57VG8lEdJM=k>dMM9Jc0d(ww0A(`W}UMp{_D}JK*%9m7XI)8vx`ce#x zUP6T9moKO{B=XIlKfm*mg$sTa=$t+FuN>#gh0y{JDg|)dk$GS|>|dd6<5_Jc($IkY z(Ih(lQ$5+xSl~V)q2@?g=QZdS$^UrLdF+++Yz(0myBF5A{9aS0Y~Y|du+B7T%*SMu z$?*Y?H?^2Y&Au6-aQz_Ma}aq9ns7u$-BOznrK$1YS*CwV54I47~YB zxl1Ilp{=2dOMeAIxc+!aaZ)HbXjk6NA{iMOu`ig9mkkY3#u8IY(=bpLB@8HoPDBdA zRRI`Knb^iCZmjiW_Ek|_2vrX2sG$zBk0?gWwc`3E2Z|7_dlLYxF@^XOmlO1W67;(# z6I75QqWa?rfg5l8cAaylPaeKUxk+nqlQ_Btldb_p?ekz^mplL#h}Xm1 z05b~s#-`W%!@r3Q>L9@wQN5Nft{r`)Ss5;IMKGJ1hx0GX+*`28k?UZ4g1OCJ_CR)_ z0igr2Yq#Ft2ub1~n9}$L1_HKPEoS*}5H_7_p1 z_zWui;IsPm%A)A-m+nHJlgll5_@FrU!r%5 zIIKMHi!1Sye>{ymPP;Z3D4UIN4lC1c+r>bL_Y#A-OngmXc;qx2xy$zN{8#}$>D<;a zZ;Zwh+PHF5(3;U0#LRr@mn*X?koan1n1PdB8N5ifJ@=-UYMMq5b7;C9M90DWOWk@6 zSq63HQC~jLM^35YWVlvDlhgU#Zl%`F<*7O{9tMrBuS^EZN{@rTDj(=b0`kcNko6oC zUG1S{-L|UAWY?%TtgE@Q7EfkhL-Te}XWES}H2c(jM4IF;BVxDUz@(a*Ss4l-_hewZ zEFc^(2jCA?26^$By(8F`zCx1bS;qcH;@H;c83Ai_Hx{;awY~1S-$Zl!59BkkzIyuE z5#CWh$1Q5qcw!!U5PGf#K(@{6<5@?oY(sA+m?&|7^(D)V5a^fV40^`hlB&*`4H={T zDUh1e5+rrbjPiU8BQru|SPS1;+Q9%7HG+$Tv+4Dr_yfw-vN#ru>`KS^s@@R|wHJYB zeYQl}dmd&*hC2;=^Lk7p<6mxu;BDWmDg5TaxbpHgYTJd$uBMXBwMjH+AzmOlc*<{H zl%;PbWrzRU666`MSlzRE+|0EZEMg-boD;(^($(fQ1%mBg?;x_!x1{ohAK)bd+w+n`oWtw@_kLy5kIa>65zD`SQwZN6E8V3rf+B@p;z=qYY%-7GdS zx4_NK8AwfX0NvHIV8j#qIJjwu!s%|83*pzK;uAlG9CjMRTT}EYPaok|xutpDe zoSPOnT%q3E71{awLBl%5Uf==y3W?!JZ9qKC&a_|odc9$u)7o?c2;TMg@!Ya-fnY?E zWfL-gB;3?i=+x{+4sS}{L#j-EXlRSQzj~ddb}7ld0pWeOU!-#A({@z-bx;-FzVsRWi;jQB2&00D_>CDHIKk5?MVx=cfbFmVo+>SIf};FjdX7 zz5OQSQROmLZoF0lLfJJZUAm*{+vEfrp!i!;AHvPFq9EA_+z^$J#C>}LJnto<(@4`g zI;Hj#xH5TI&e}{74QCjaX0<-*Wm4}QsQeTb>pptSuy?L;1R3|Rt;q+tW2*mt*sv#8 zF$sldJg7stOpe%4vMg{~Gb*70$gF3X^6CweHcGT3q~v)T@{BhdEP5!CLAah4)f#T! z`@wJl->6i1D84Z+Rmbw!GU8;MrHIfB=5bk?G8u506?X9fZhC{z?52Rj%!%0r=F1o;W&%i5HY#J^a!BEuE?|xf zX*;!d*Ky8(#8+x2`=%JL@Xp9EVGF#aU`_d=Zw&2hKXlvfE3iZq1DfjD3pT(b6vS_X z?$%(+{0dFEc~xExz%|PF{*A$q`9MiwAy}Ij&r4mmxz0`?nROc&-Vr>f6ANQu7voh@ zR1IsW9Jp?nzkUE~m(`>t%vghPD+K1ryg8&&X4=7M1LK}UAi>^h4)j)ea*=MEg@kWx zFNAwjaC5*_=7J1%ioK@6TstuuKlbkGyLj7NUa7ZTc7SKkR_E@xoS^8hj=1@&XEM!uIMjkys2lsq zOMX}fTE-zPzW&)$GX5yT(N|)n^5M=7)4hxFHjVrVlvSb+^n)a#aB-l+#p)%q`mNqP z3$qBAJ$%~ylq|xL3;vD+)<)vjBqi)I}E|oN}7q7p9Vsao2C?RY(a-B z^son%WFqNtelc#ezfVC2Dud)GKMLI&>`|U?a<3fM<~~MZmVU3Pk06g zg^@VIo~`4QDnC%@KE;wfLWbjb=%f&zDM^P?)mwTcQu$uEx;K>wPYFWscT;5GEixUJ z*UzNn9{%k@54XCyx>c=!A|Gi`=y1#LAo5*{~V zp~YNwx^EQgpDtk0Z~$}Jr3tKBXq-&I@N*Bb8Ej1*d4>DmOt*?%A*;)pvT|*CtWV_` zC%E!?*nMtectiCs`9?1wTggC+xz?jt)SE+wiH)ySQED_VGheSGjk9?!d(>@no_Zdv z?lCYn*A>N!%ovuf(e?FRk2qr)hg@I3pFN@&pyz^phe=E)P|Iztc*;3p-F(4roP5Nw zhuC^{o^15AJ16B(*qP&)SbTm8w?ci%tN}%$rTk$4i%aM-7R9|$m|xDpqLIU17s%G6 zz{1CZ6@Zn_mKelDMD)t%@IP-G+|c8Pn~##LDJ)&vwOQ{NK@X&=y|n*veG!>BMNn|6 zeQUXVi|h_(qRV0YcD6S>EoTvP_?t2K*MFwE6AV4Qbovp^b2TvZ=#G&nj(=SLJ(cmN zAB>YSFfnC6g#G|60iwFs#ED3pYe)A5<4?c*KfkxiIK*L{?5h8%zAHsJWbw3nHdlsV zn&kh(I<8!-AQ}Q@(@nJ_7V$40oI=dGCcWw7f12~ZE4QDklPACwb@w|MmW!bu_!JL3 z)$CxNq;Rqb}j#4l%b7=wLJ)I$+JC77kWe3qvL0$^Sg7y&s5~5QN>SJ!c40 ztJG8(G_`w=#kh|iXYZ}V zXne%f)zvdSPqpvEr~4)b4z^GM0`}Tl!T((K&mYimV!6V!2|iA}dId9j5RK`+?%xB& z|2UZ_3~bmXcQ55=m9y^Im)L|F{-0#?0>~y@vbbjb-s?1Q&B)(Bhz{A?$N%0GkkBLo z|9}7-!SjDPi&b7ai9}Hcj>0^WpR)Vkuk6r>e-TU78rA@QOf8(~t3(U$-=S z*YjJz`=7fabszT8MC=Rk@x!=8uS7PX$FZ+KyVN;`^A69UKO`uAEbO<(+}=eoa-8oN zjZ$ZQ-*MV7Vjn>+8i`ed#y_t{loSrQ;rarQ+KWw#QOAwFEY^evcMM<6y)4~2gNnN; z0^^?6QYt^mFB{Z@=yn3R8sZ5|91T znHfIi-LNXn<%f8J#Iowj&NEz*aCgm9PUo7wgn?2#Y}^|A*kYo{Zt|*=lT#8`sQqbq zdd%g%_Hmji(6j0!)=kX!v^wAOD;&lx7>&mbxk&P>EsMIiEx`2omqZZdd`<0s5zTW+ z6uKca6nqpqySLnCo$Un{kV^3tAgA0BZFzj~q2jCqOwM58`ukSAe{rT^y>Py{(VOb{pcZz)cUe;O@@0`@POEg*=dhjiJW*{T^rv&|S4R_Lo zM=F9H0uy4HE@2o+>$p5i^F~2^7|N@KA0gMJ?m^0R9QBzraWXR%e<*hEea*pL>}F;D zN2(3B<$VM)bg=(}wjIe&PapTvwt6Nwc2PL$=4m4AH<)45DhaeQFTY&qNCcG=D;@;{ zm*N4|k}Kf9)V4{wyS(VynK>GWYK1I+&aK8Xd^0WeV{MwT8+))g*(5 zgp`J!hp05}#pUbI^cA83C~!p}+XxzE>qXY5^jHgqux1%L=+#oM>jIa%BB#|r$pq6d z^LTG=N{h~`T;rD0jpPAdTTA)n!GSuiJ>MZ;gO_i^1OjBn3al(K!}N?WsV)enMZH=32`PdQM@LDHKdx86tuk7| z3s^<<>R}|x_jh;++*j{EU5tF^XfWNIyVP3>Inu8@NqKi6TI7Vbm|znqJ>=SLdH~5q z2n;0(wVUi*^%|DTH}7c$j1NDOpSYdh@&rQHU~s)cAxBK#*O*qHFsiq)wwSIVU7oxN zFT4tcn=aE5G+pkPR(RiZS%vAywH7z!wPrWve)>50n#=Q5IZ2c{cid>vX&D+F&ad_P zUi~z$5~nC~uj~rC$Rt$JO(cwDawRMUpQ$ zion;I{SnVHaheV^U0#WZ+Dbff?!joxX|MKiGM3FuV0un|$@$>NT^Pa!TcJOk*H>0@ z`nfd9`Y$b;@T)o(=>?w_mD#4BbGNDWKZk}KvQ!ckk?_KEq{dWF7ixaQp&UZzAeijP zJ+pS5shxHDC>y)%I8ZZ7K**GB>z=4MH2AAMerg&dJjJpz#nN~cqN#Fq3X1v zyK$C8O@NLpW?0c7NCP-!J=rsqx~z1wU_6Q^|U;wEAT9%v$f)Eh4?GjD{^y##w?0qKIdVsALz| z-CB=99Tknat!KOc8ofUnqZsvb&xcm$aFXL?zHCubs^mDDzazp7QwfYXx2Gg&i06i5 z;O^hl-MRZrOq&!Lxo%Z-$uV{Ae#Vq(ZFA4@huYnIe9y7&Gm0LPr80uv0Zy0cT(v7G zuQdX}>A=LX5`Y~#)*k00f^_<&hq;sfUpYDshC#kW)4N9a^P@9}$%G%96?FpOv7FFX zmV2KO08dCd)hEd=g_`b{>!4RqaM2HgSF0V$wp6BmsO z*%>f;hK56kT7~?<=3V97vzw$Fm*c$ah$XfZf-j_zu0311Cec9=L8PZRZB%SK?yFdy zdNAy68OIXQvm+blpA|!)X08xG>zzajNnlp5ydy_RiOYPl7fnvgtl^`|S!hbfAr>Es@C z-6c*un*8<+(tT6~gQ_oj1xsL{E*_OP><5;hAYi$UuZ~CNY@#84=URm1`t2J$yY>)O zdvIwXyAN+qXa2YaU=a$50l!%;6RV_sd()>dao^}AnzNaix5~sN&J!Fp^@WU1nOKbv zm4ydu#9^HWCfFQobJO{QAy97YCLuh**KB;r1RtyLi1ob-?^f%oTGY#(0bQz8YaE;+ zX|0V-EF~*e8HCR)9d9h?!Rz`-YHCXR8;f|(`JN?{zD|pI2?z5!epH%!WPSpDx zB$%?s_mn5vjZlsDb(nncrFD|$PKK=+Cm-u8%pM15)SD{P(!7{Gi)adNRFczc?mg4+ zKf;ZkBTz>K$AHDI(eEkZCXeddBkO0-L_r)(n|fDvl4PcS&o|VJig(S4U!-lkJpZT1 zo}iNtP))Y1(Y4yUXi;O>6kM53gcijRjTP11XePuzV4cn|)+z6VO`NC@rBm)&eT`sx zHfluU+IHdxN^ZS)%}t$+-le~6!Vj0J^)#_`odO^3-m2tdu~@a~j(pNndZGQ zP~%~a6CjC!ip2@xHZycyo}sdFVd-+-b8gz@z`wP(ocgI{|F|<^@i>|o^vC%)SsK17 z9fnhl&tij86By^Wlx5rl->@v`I(8yT8hWg+Yi*8oHj2`6e~JaAHFVsgZVIG??*&o4dUY6LeytH+V&t z{uIPY$=-z7Z1E6}OfoJNvE&N(Q0#pU0}D96GLz0nub6@J;m;%?d{`<*3~XM5JLbM< zpF}5?ggtrb#~w49MAr=b>G~z%{mdIo**TfIWc?5;c?~GaKt$-TT4iFPO+cI$&=vTw z_SAEJ6l&iANh%>B>RjLByV4*fP1Bd*IjSUZjPf6K4dk}V-xq^KZ2-#eClKSgSX63i zeYyeeBDPd&Z4rcF>hz<3;eL52hSUgw3eSisMy)|_c(=1Qgl7ZHF6Zm}JlC9JhCt&Y-y3CO@?N z=?4Asy+kiiEy4Zw`it!B_z$k1ey)o0d==we1R3em04VG4c{NhMc^aBqxj0?FJ7&W; zm8--I6ey@|6&d$l6u)u<0hsK0cX5OY6=?~PEJI$GJBIO_o2uhw@xvKU66w2jt!-jv$6p8!0CI9zP5bk4LtlyY|$9d!&Ybwck_`PN1gmuY+A zu~6wy{HswK7)GcL(a8?GU|vct%7FNj_{%%RfZnYxqWb? zxR_x*Z$~w6LpJZJjGyBJ58k7!nn8|dppg$YM_)Eduf`|7q(sPUbof#*SyW_0D%)iQ zH=+oyWmTR||Lrp&&Z$5=XMlMfO641#CKJ7v5lwe#QyLY%J@ijj`ZNG zlk)5@^dRkPlmZcggU;()5)sEjxhGf^y%E>W`(C;ykf`Ei+kew6&9T`0<1I{DGlr0`DU zx+Eq?_Uz`*U;N*%#LjKU$PGrgi|!tePY}R44--`UzwHeN(~uFOX|p>|2s#BG%tN z;SX;|LxzmIE^5&EEn}d36v?fTb8IJcec;yCf5jPE_!rH#v$J9rIMqH>X3&~^&+Of1 zO~=wP7!|X74E`QFf}>dQ`9X^$79YW|fWm)oPgIB)R8^!;g)K6plzeQzlK{WDwSOx|L7YqP- z2YHnmB!jhrW$pZ*TMl|{@4-;@_}j{*K9&3OHSH)}*b2$_Y{jUtRJh*ei6~ zHBs(uh)qXwK##-VJ-;4r3)jPrP*wH!TY?@(w1?w!;Q!=5aIPT@{G-T_CwHb9qOPw~|TL{DTeOlME;U6KvqI*)WK z<&Hcki7F}SgI2hAC6H7>Lx9Q)^SL=(W(pII117Ar@j)NYgmyQ$Qx9x5z?rZ~8#D4g zSMA!_PX+j`7$+E9+5m${%|zD5&e#k%jRwP1jKYpz@@s$6R^Lx=*O(4#62B)vDs9VW zF8h4XJiShZ8OC2C1m-UH0*YJZf;v8+MV)QX!A6}qCY7ns5z*8TA8Kh@TU(1C-H3o` zogjK^s10ChKoAR*wquOx2Yo+~<5(a~ZfK||=1coWuQs`DW)q^7iThw8vEDvmvV&^x zM?QP8@jyF{jx6|@4ZzoF>;oMnf})mveDm-rq0pK8?u31}u6i+>W$`qR4S?LM&msem z8UWRJ+(wIofGnsOyrU3A@_Nw_zw+YG^qFTjB&RXu;M6`(SYHK4EZaf_!xC{%5rhW< zl)ZliTBk0w0bJ~&8O0*Y5zIC9wI0WOjtz6Ps9Q@@b@965jdalyiWO+iCLuj-r;&gm zqDQv=2=FBL{=Y|X@-U24NHKaP?8v96V5igaqSq!aa0{VAEsH+v$6~ypSmpWY*bO~T z$ouVE_78r2-Gg^Az5qZJp@72dvz_X0*+1iS)=4l4BL_%p-jsmF;`#U@CDG&SAz;bi zrCe57pN4)wL+B>q1>OvI!ht>*KVklS2+)-l@sb8At(l)8&6I1>68&Hm8Y3GZg)}7` zwT~$%TfUBZYvr;{hl*Oy(@+>T2s4pQ{dM;{kp|n zIH7u=8!+n3(3y2~l%PogiCxIXuTF?eyE`>zl)bj(_tNj&M>G;CtRuh(Nq~o&cGSA2 zU#)xmGmQM9*+`$dmvI8$0==NBa>3eBFPt`f#vh)IzV=Q%4qeJGeg{+=-KhU!8tKo%_!_`SzZ;WT0j}8!b7(Hw2F5}7z=_QM@kv>HATYIr zz&Fzi(G(wFihln@)cJ<{D(Zu2;6eWY7242c2>_)vKogQF>CK5isEw^;pOe+O^MO_Y zmDHJ=FutA*%9O9F2;^?*lM4 zn798kPaklbaMs><{fwiWY2SBfBtL9du(>kaLKKh zViL>uyJ-aQ-`^rN?5~tG*FtrK4!b+yYj&BQ`xvc*WT0bSwRxp+!Ieyw{ ze$_zi4sFZaIAo1Efu4ousFv-E7Wp>D15~!psWugLis%^u)b+ZhIcDahyn9BdT#imz z+WOjLhDDxihs<$aZv8YJ$a|Y2+G-=KQpY^&o(=;+y3T^oghqk}7ikHBs@=Nj_1gv~ zSmL9Zvt~lh6|8J+&Tb|5M@VtDlT|Fmhlubx~Pf>f*BbWfe^+z+Xuk=KG^bGuX$DfYJuV7BiT z;NPBzdg_DFHD#(+No_&1Bp%w!ec z35BADcm&gMBGX#5R@FnMHbV{x^}vO=(krhHMT$TjF-A-^0BNV7ArK^4qYqquhrrK} zM(jii6KF5JOOl%!f1I!;{b};2PjATH2Ti-ThfHV$E&|0;690ItpNBYuiwT^;eJ9>m zH~pZT+S`MGCBe2drhO(ur9#=rFubIbJ~mR;+sCyt=+>msa6cx(5}tX*tvyB*f2Wz` zPo`n}dvD2`rM%PG|0?nu;_gCRhc*1Od|O~xSp)|2r~@8RlERnm1%C&t@6Za z`}A0I;AmUIHD18w9#Z?jFvJu9(1?T}1Z&&NTD~!DmBPaG1*bMX2}6wx$E3QBHM#ue zS1mwK5ft{cmsUW8O)!ep+C=9Dg20saBrVe0;eZgdI94)lyb8?9-AhUQ;@JEu4!3p! zsHYZhTc;4m`rdqO)7Swf;{?c>(3M)fvK-@>C)3#2FpEol@!FGHtvT95SxaH*9b&wy zpUpT-nmSK+zm8Ez$btrgt{jtrv@Yv$n6;m~*bA!8n3URBNmS96J##)FN9TMSzWA5-DF?Dwsz*b zYN%BqJUa7sAzSYBmiC63glOo>T5}UcXiI4jm@&1x9my(a00kaWt*OJHHizj?|8%H+ z`iGz4TwksbK2K=KAg@>j#xl#G1Ur@_({r^hy*VZqLV+{PatVN!HzOW@Kio{*4AX*e z8DmeiRE;2h4>ESD{WJ7Yc9}yvkV2hrxWZgQ7Bg8daUX?%Yiq3Z1x_NUP}~$4qyyF* z=^L~LvWvi(!N)Wp%6v>e7fSDpc?ImAtXsx@|s-o5>VsvM{vEBj$Wei84R zesTQ!y=ksoprOX5@MOHLBr()5UK?SLaGI%}50ynhbM?Da%|Z^o5m3T9DObHKF)wb{ z$g+)^cwp3_+mw?(o&qKcJVG7gy0_T~yprn3-GLG(Nm0ZPTZL9e3r=xHzNTlI&m7L; zyiGa@I^qRY#2Of5UU2sw=R39EAtYJrykUj2ZkhFg&Ty>xm9$a3X1QxA(!)1(zMUNB z!yyW;_j#WH)sSNm0)8t$fCcJ(1ZR%fS@Ongbfw)eGD7kqT+Ge|nz!GPRnE0)uwZBN@*pV6c%DJ`{ z<(q3}oOTn+Hj5oZpgVpQc)lnTQfVSPdnc-a$ojSdA&^R6Do- z3NT7pL_P%Xw`NRaz78Uv?>!U=0lD86yL9rGhY^J6{fmpKhX7pswpK3rU?Awu#|Sq` z>MYE5&_@O-4ha<{*!8zJBE?4N2zx&^uvmw5!y%s8Jd7dj9hDaEuMr5u;s6Nr(oHpk zhq9#RCl3uBwAaT1JXCWNT=k-nF(bXn<|fr5{p?8Zd$f#!YgGx8^ULYQGa<${o&TDE zpk*6+<~vEB%;8QPo{HI$Ae&Yo$iC7lX|rf%!gkS9z<052Y{^cm?q|A*L|r>Aty7 zaCWn0&If9C&w@qnITMRbbb`#*Glc5MN?dsCwpyoC4;4PkhkB}jd`@t}!Lx_H!IL_e z55GRiB0pCkhxr#HK1C^@{;xt8c0JM^2Vn-uR5C`s<2&k36`in20^4$Cjhy#F(6qF9 z+6KrkstC3~Q#Fk~e8Fl)&J4z`UeM7G9Z-n&U zVDI8oPuoBf6s(^JxMnqFXE|yEz!_b(K9hZ%l}zlM63&%tVSFuzl+ukfFbD9fOFns0 zdQ2`=5s6}t+M=@8=5B**g(4lv*Aqsy#7m#SaE9wkK)ey85F;53C*R3h45^!GyAGGj z1qi=K^;QaLuI%iAuUGhn(MVR_O6ld9=L(q1Va0xD*ENf#^-4QON_p>=6qRMPfQ(E; zL`M>es<+HP00>1FpL3%dwIGuFg_L^?@6AKMnt;7{_s92Wu2fvRbQ!~ll#r936xXfJ zD8|xX#nbl&<`#i^M{GKZ7C%)8!Te_3VsuneUq%ASY{##+kj`;R-m5u{FqmS!7vr@f z7PPzj5q2uuiv%EbT*K?i`(+Kf1c#q;PTesuQH+Yl65fb}p5F?D`$@9$cCw!iDcAVr z;`K?cG_ltRYTpn$Ovouq;bz}9#QN-NHKkSekOGc;GAmG79GX2@&m{I53i>?VvOUBB z7Plepm8>+*@Pc4l0-zjChJ_^`)Jo0x%a`?~Z2Nx)L%#Hm=c$8?Vx->3?*2W%h ztR3UH3>^_EodZMz$}S57uZBd`{2wc#+!5Dq<96L*a_V!-)mhhN!Qy0|WeI_q%jMc3 z_Hf&r8TqUXmbMYznu}U=+*hkOToFKurDjG3W zOZ{xL)w*`Q$-O)hOLpTTMnXoVEf1J|J+ro^YWuH&txfTpg-y$CTpQ-!Hq5&l8<2bY;sx9Mkh#5I?=i1p@C`5X2 zbkD8a0p2*IHAJD9ks!g9v^_pb7UwBr#W(2n@$O|+h?l{6bFM-O^93XRni7UCkoVr# z15TRG{jW_>S(iP%unq>G=75#sb&8z8_}O{r#qNIk0zXyRJ;n3Ml2>C7cvWr+>}gk{ zScenIJafs02~VF{hI{T)lU^*7>e?{Awpk~NudU*qoK?wnJMrL)(JWcj9EnMsjKbjCFbW z;)RIRnA74zakZ$NCj<0!vLX7BHOM#|``}H5{R)IQ8DeHQg48f_M^gQzpzRp3dCX+& zH_%HTzr)Ome9X>}0SIzEEJ%k=uqIDJ6i&+ADI9Gc_4o-#>RKFFt4-!Ms86`N^6+qO zn42GUJGWszEwa4o330)4cG(TbUZRU#Mgf;1UsbJh{N5P-i*JDV3;xM~b4n^WijTF5 zVD&^aAIMX^!Mx3FkV7zik~`&T5Pr~i@Xe;~;2eW~Yj1^*#*)G02M(P$#@t2x`BStW zwC~*JNxVAA(`PLXC7Kn}DsDz%MNGZ)9OITZ49zlCA{iRFwK>oZoyLLhz97Sg(VEsq z+=m|zK!&cV=@s8;10PqHw)3rd|xW~N{acvN; zwT==}sbeV&j?HYYLuuujZ6ErM1{YA<%|I;PYtMMS!{9#R$g1{4)5zkWJ-s|wxMNGO zoF4*{h<>i@qLV7dS8*a?eWHx3g+-Uia>fGz*@B(@>C<5DMNG-`vEU*gn4UTxkfwB8 zaglkI`ij4W3aSut8PN(i>?{Bj)0}8l+@X4-P<($zQ9R&J@z4POQ;X3Z7$y@Yg22Wk zQ)61OQw1NZhg@p0@TzjTZP~Kn=X)%{%SyRW=xI?&*9>eln{Jd8yP9H~{t5h;!!afX@ zqH$Exxx*yYk&*DR(<#i(V*%1O+|!;2lr_txL@0a(?j&d(C(m|?!1`~MIWJJ z$y&v6qVs*pDooiBo^~a_L^5Jj_`TDqK>VC*Hfoqs%BoewWg1NI-IgHXYVc>e>B+;F zPaNi+HS~WhFCJPP9hC_+p5mTFHBlG&l2_DLNbYfKWBKC-*QGCT#}5YxH&e-vwpcgT zvL{!3fbhRqMN-Gs4A-hdRzlp3>ArDl7SLJVe9iH=8ctO{+to1px=ut5{?Nk1;X`uD zHII?=y!^5@Y~o2ThtoIx$ypEHT)7Kg0K{m<*(x+}4k&ihp|34Vn)WMXZSaI{ceSUaKIeM|A5AyOnXVN1m)xMj>N9hr^Y(mWQ@l?B(WE0Hx*&pnZ ztCL!xgyXzU+kqixfE7pc+yYAE6Q38dJ7nL;z}1tk85LRUaSK&m>zTVZhF+{WwaG^M zy@}b9@|Viwu2&V6gIzLv9v!G!XTq`55Wsjq7)G^+uuvd_Fd zrA&E=lXM2=A)n{8D}$^m8u|whE_pAGWM@(N0MA>ua%hR`uvpuy;~M#@E3 zdxZujmy|5wlQ35xgzyP9`!S|RDwK?8VzIQfkgm->{oF478G>p5nRSJly8!Mp_iTZ& z+?V>rT@BeS@~uBzEbWednpSI@;-iHb$Zo@E7cE-N_2Ootj8<8rfnk7Z+QvlgE4Z%Ba4k>P+Ya0#~arsbchT(RGyDO_KJ!-r?A{RYis~!-Jp@)dj#Aj825ED zeaKkY zhI#6`Lf!W!<26n7Hfs5|f`{trwqZ=S}5fMmkbsbl{vk+A@4lm5`+r-0311bpbx~$pDq{Z>C|0O+di;H3jY%fiGm$6|dL|`U6zN*6E4148=LUUvo2$zGeaS?- zs&V~7rAB<}3s+*x5{!4&L)y_8a$6-|X9&Ut`o4rp!bcl6fnfRH)2z z$vkFE#t0=-rftepnMLN=zg~6Dch0FZ{MUD#>(jLN`#$gUu4k=#-S@o)Kfaven=JB? zb3E5{{|hzRPY)ED(!>cbZ9+M;O@tlBui7^(9APRStvc7K6)w3EXNtC5sd~w!U)HPF z1G?sAUq8%%N=>`1fP(d;=|J1YwEpAd+p6^^;*-e5+l7sH$k{X9Su#C@x8Joq%_D45 zGLn-xXFOC&@bDQmjbCF~?Wz-b_l0|`@_GTkj$khnX<$)I~(U;-pK0);7ik``2OS=X~^ii&a(%YsZ}c| zguWCAO{C}cUaeQBXqY6u;sDfNt+*#ld~x;eo1wK|;$Y(Nsm+Nw4+`x3glX*j!4Xm{+x{E( zI0|Oe&tG1vy?;~R%-6m@02Yd<(FwBC3I_mbdL~1lRidw=kn4pFzT6hPjnvAXo-Vg| zVgErnQ*^#rDiL_)0@NeWl(PnegEx{`xhd2qC*bt`DNxZG(PU5heHlay02{gqFrYjF zW-E(ZgnOOM&tR`ZrJwYMD=yziPpz|xJR!IOL~A?W+^jmUiKJrzh#vC*%}oCl2$fG_ zGgeIEWBHFU#;*NxSwj0(`O%)HnwKIAU6ot6f9>A+q*hqq-46xmAF~!ED%)KXfYE!W z>F&3N27W;1<5O)bQrFnyV{;B2)0P9rL|!;aii`X|%OK4(8~3?`rw`7l66?4h5U>i9 zuuMAd&tMKIWs|{n8OM92X17R|CpY8Auq*z%Q15B3CK?uA!CG(C99)&EsJqcDNm}$a zS>4#l{v5Gw{;p-1g#1+G&H)(ykk5R!P{{=8K1JqNT`W#sGZ^HewkTvGp__g;XFDuC zwFza$an~2yk#db#E;Y)h;}FbQ%XePwVU09`?UHI89~Tg+=gg`Rb7I0MXUTg{NFzi^ zW$q!w6A8T5%m8S=U@ttN^MG!|Fg%Ul1J+9 zsDCLZKS3wuQlB43?vZA0k)*+sZ^@SR-&m|UN#dXTC)B&vktD}mJol^R1rzm3yz#Xn zmksDpBiWg=|T}(ge45X;(1_bNL-7>qNOz|#`>vOQALeC@wKItl+ z;}{KP?ugjy+-g4Mz@tBJyk90L|9v~pdaB*>D^}=e**Q^*WKGc!O%PZgmV0CffaPBN z9nPUkEZlOyheBniJVo^(&P@R=b)IK3PLGo(U)H&%nF^~Z z`e=rxsPf`jJ*R}TCT0smO%;TVYN5HZV;)hi>C9CoUWsT7{i*re%ZXj@En zc;#y1p1xk6clf zyj_=Cgb3!Jgkz(U#}McWhzJKLZe?%{TC#11HX(j6cU8}$*;nxaAY)D*%XTdFn$$6= zXp-3K-6`~v5ov^+KCs{n=6(?beGf4U&Ga~>LcIRg*`5^ZI@?i77WZnbdgp+I#~X{A zy=Su!2aop)?Fw^W`V_R0+_@g~A417>5W%Qjh5*&M;FH-U^6`(tzJcyC4cDGk^Djx?kuv ztlGQSlk5XlJwzXm@|rp65C4#W{2?+Dldz$CFC^}M(#ZIb3pJP=)sJ+@c|rd&(*P2xu7R^3cwubxMFi&Cvq8jjS~S<^sQSU6E=6N-QE^Pl&*E&=Z>wy+ zpA*PN9d|Hs9a0qk?x9*HkNXjXdc-J?gZxPjGn=5zSWAKcPTs-sT!*`Wj0>h_y}NXPtAbDT{!1_VZOau) zD$Y~u@QGg4m?7&A}z`0&6$sNH7WNC?)t@`OC|0f5)WJ&c0)l;A=tLLH;7gF>Eap`Cf6DTKkkm~YN31z zduLY7?;7bn?47i91ePlD^3 zSL)caNP3D42nP7m4=3FQ9WN3#l0UMt_X8v60ZMVhr7>DIYcQ?~$zo23n5W->YDAht zE%Dvi1klsHvYzN~59MP}(d0&&54i7;~aGk=z#_SA_CEEL$p*dI+
    l{gc0+LTp<`z-^}YJ;)nqg=(A~e0i=Q}g0)p6UE6XoD_u{Yr`fniMAhv;H z1I5}ir%uC-0Kiues^5S2BAUmj_Ms2Fx*kQ>87wCJA;dhcvGU1Srrnga*JjpzXgXpECAxbh$XqrdC&JG=Y3zxkbSscz-g@U4M8dmlgc_;*s@ z*@&TT+4IV4uim+J8|_6(RcRY@8ag|CUI^8NCKsm*Mk(MDTX(k4 z#3x%?S~oMv&c+?tRCaYPmW~&o827}{PiX~n1<>uq*yd{T&h>lU-Cek74;?;H$m?JI z&L2PZ)KmCU>>D@?+2@gwkprBLB;mwBqt|G`iN>+KwGsUS5GC z@671*nFA*$?~UgZ*>bitKQVh?U?0!ff+dC`u$isx&}L6G_zG@$8`ZYmcegjYYTa(| z^A98|4>soQ%J#wR$9`LTO4B^V;|R z#XtX-sOl%j$HU>s==k*UlgHcv*X+{VI3%9uW`W2NL|EVXHsWcBMW++VxtZCAjy=S& zHYPj^X|<$flV^)N6AJ{E2lpH&Kd_APm59GxEF%KIbQoSxCe6ula*=fLEGWe|p1;=47}z4_jo zzxRt@zB77fa(1ql~ zLwgPmU%Z7bgpk*l#%7Kkgi5xM)~UlK{<%p z2{khSC*U+nkRg_!XTy?IDpJfJQvnPE-m-x4nfd(d&jEXbQUnCxR66n9%YOy7q(gD8 zt;U)fnjzPG>&7k2!!ZVj=vO|UogSOox33?kt_GTqGK7Ydn^$h4DGRNphfki}-MK55 z&JA4}^m_fEeJ)Ha$lY>ROV{GmBCVC8P~TaLff$bQ(19b#SPIp3F;(bp+gX6DC4xBO z5qT1e)Q`1TH&-xztd~Q9&u5G2sl|z8c5`fDJY%Grnp(~rKb_f3w{^8fH=^%cdJpCL zHn z?RmH$X&~-8RJi6ChR-H&A_g&^KG@h&wI&eJ zRA%K2b?)Qwl2bInI4a`9C&U3nFCm_(l&rkq<0fWCSMpIKYrwRUO=^;o4q}I@C%2bYVj+cNg7;r?+KKE|%t1K{i}`+0oOnx0JCu z+M1z$>~q#6*7L8t@D`drb%7Sqbh)HJd?klI$nwkzU*vK9nLVsxk3hp zj}+F4(*>Y{CCEx9o5dx8+%8XY&hYicY1bh`!UQcL>B5uMt;JLK8#QE6N3N1Dq><7= zDwQcAOLkp{w9i&Q#?Q7?rrp4{r8493%?WO)ZTIe1UU_YFV_ui!_FWy%MCyQvxHmlo zF*ESE&YU?jbZcl}VDJ3m0_Sq?KXiO)VPQvS@AA@y-y4|USi`ZjwROkinP@a#TT|1} z)Sl1hKy&c=p_3i31)yGZR9QO(M!q9a={l%ONJ`tJh4hT8g0h)|$g`iGak zEvmlFRDK!eOH_DgJG**!7YaO-88i757Q?>&Js7tl4C*BaQ1kD;c?}7V&l`H; zp-;Crbb*0xwT34x-6e4pG7roaAQ#LabzuwUg--6ew7zz|4Ub z##|=D8V90AQNm)p5)oM+k%uL6n`haJZ0jWR#Dvx%R|Wn~eM8IG{n7F0419a{AJ}c^ z!tmX@OABiRAxvhUdFD$P1E9oy>N8JIEzH)}H@OSKG~b!I^XR9}!TpLZ0fY8igm4EUg(;Dh zhsY8p5{;;+KagG;YnW~aj_v#HSYY3?e=;~UN@(8xRRuTn;R3i)LQ|xApzgKH>}nZY zEr6@r$K#5yyLTwzu;S?D5&$!xee`yA&&7jfqHA-Y_U-Q5 z8LE$b`}yz4E+v-ORNXFe6xd7#d{(433{uU_TQ}Cz%Vjz9aL=BZnc1D4O&8vH=X1|| zetvFg_U?S9oNe9N*4^9*pY)N`SWO_JrHVmSk(1hzXFMOvj?XW<II%LZXg2$ zDhe3_fyxM@8i5%gL6JiI(F#J;JE(PW6X?B2TFFE&7^^1ql|h1b8g+HGKe+L{a*qpp zgSArQY;juPMX%4bHo8)R$!tkKe(*>^d|rO$`4Xv)GdES&N?)Zo3O59U3L)kgoTl=L@%b^Xm3#U23&9ta$B#ZFl{#heCIYKS z$1$Oj+stG_{s1Jh5%Lh?iaeOtWg@8-OA0{jW%EI;3-b2@v|bRFmSfCHxl;4sIhYS4 zR;f;2GF9GCOQ696Kqa6DA_%aH@Q4^r-k7`f=qEo>P6F#zo{5jH#Fh?A$Mj;pYiGyn z@4Q?#Gzeh&nw(ch-vgO_Y-0S}xpQa_UA*?@=;f4D8QIon{>bEZ z`}e~382oR1&PV@Qt$V9mb^8ena(`%q`Y>P97G$F49}V?b@oQKmI87jIBOHvZ-phs3 zrmT(Ly&q9pQexb}mwCMaF$0}f)45{6y$W;}Rx@32lp&^QXzObW7Nqb6N1~uXE18KY zh&EAzBmjAsqAl6AjlB-okK0<%WkXUpvlrmjhv3C6eP}z#pQAt zm(z)b6z3X8YLjL~>4Or{WQ~AqK8Jxb)rSUr0~H3M!Q=ojRWBF?d>V5LbC3%s&_%{-!nmBM>1w;_x>0jDH|}Y(bm1-i;GK{K$jxsJc}P;Oh4A z4NjB?W7>vBAQ+qEP^aryl{QwQsOSLJZER_V4$$SHt4s3>Xsn|5wm84A>)fM>*an^t zqVL$`#N6D>!>3MxD~l4Py|V+&zqYPUzt4AK|MAhO(V-hRK#0XGD4WPM*R>>4n{Yij ze|8`I>s?~K#Hk=;%8Z_4ufR-spLWa+i4_@!A2m&76(U^YX$-_Hlq`ZunNae=I%~=L zWM<-_(+}m##ZR31q{_K;XJK??3`xAgDTGISj;=?-f##ORL?X1lx^(LJ$xD|m z9vB#?X$e7i?2R|x*gMdhT#XYDOVjpr?{&Zn1}s`c6YJeSC2 z18$GVz~CT;=XB&Uy4USgOgcSKEMc~;{C2D{j)y7`O~>Gx0mh#vHa-1$Jf%U7v6jo?+m`xBac5)TVL~u#>XLk4!tf&8AI?x-n|>; zBHj?6-IGr~i9?39)%o-1uSRb?_Qd&?_6AST{p|9L67oIybDsjLVt3!Jn!3p4YuBUk zIH+r8k}j2ox`v#UfpIjfhIcu7=TdWvi_3dD`@QX6HrXUPvrY5D$Qtbc1&Izo%z~6H zbI^)%5D^O(GYNs@^LoGqdbExiKnd#Z2IrF4f$<;hOxq%sKa{qFuY7cJ?Tw-9z1=-8 zzWw5*q3e0Abh!WEKv!>aBe8Gb;NtR1DxQQUT<`9_i#M;SJ|BdyF&V}<8fOEi0`Gt| z1SBqqoE7sWOdepW1SmB0JVBMmr;lt>GmBViaPg=vh2i!14}V>&TXm~$e_yL#&K9-$ z;ngpRUnHn_h6=L=Ro(9wpFF9wIrd(cER+H0()XBqVJ!Yur;v7V5G0-(d-jL2) z0T+3~vdAXnK&sJ7J}5dND#$9tnj9v1|JuRAH0=T4hjxzbgJl=M0uxEHNl1nWd@*?< z;k`AfylBmgua0A|{M2VY?d5#3M|$bSmyaGgAy3{Z7qp`X4>#5~)YsHD*486C0+R0f z_3OQTeXX_ai>oWBbs@*~O0#zMu$hB)!*IKlg_8n z4Xp8cv4lc!ly@ire-K2fhOXv14k5G^LK}zr4@->rB9Z|Rxg?_xp4BM=x|xV#K@@=O z16D|^P;2Py!n4q+DwVX)=7vUr&Z?OgjXqUXCxAVzW{tiun&174=^2Eg|R0hW6Fra4<+0R9A{L&W!0@( z09Uua&4#rRnLQReU@1|Op$wj#or6hCO=9Se@+8r!z=u0WyNU zC(0gVw6|~H{@mxDhEm_!M*Oj}Puv-~4PnEO*WXxMXTWPlGJ6O5;Ifn2Os%Z1Ev>K4 zFD~v{2shX5G@Nd;P}J}yq&{fCWe%DYL~kJx2dalf^0lERrb++&5w^c+0*uFDotG*l zL!mHga;Mw*t>?dWuK!U$X91%EixrAN@BzR=jNCoCwu0dlXtAKO#M2w^U3nJ)a&CFH z6F$G4Ew8`+Dlj=yqtmUm9W6Dj$(9_tn8|o*Y;5${G5^fW^w`LKR5Gu=cmchG=5W_~ zdYz05L=mU)+O@&9?)Lin=2$YWf?Z_6*@h;-a@j#j9vF`qrmX^n*apENk))L+^1a3U z`tM`{Typ6(n#{?a`krX6|N6>QuK!LY5=xX!26&GgS zP~a$91}5Rff)hCry3?^-9x|Rl#1S$Y?R@I&^Ku0uYgGK7D_v~@59>xwq1w<5TmdG) zXk$BoX~H>nSx6~xT;mgCux?(NU&eXHccEky6qf>V%eUTr2UB0$0`S&_Hw8rg8Vrm3rCb$A18F^}x1^@c{rCf>PpdH0T;ZS~DQ zhuaehtmiT}h6b0$R&ZLbOk9*a1_qMybs=b40ct8M+B$^eap;p@2on7r}&)FZlXU?A8nKMQljU-ai ziWEtaA_>9~0D*>Si|$5uS9Mi)dGE6-y?2ojX=3jeQB`O-jljX#9dYtwqnTA*6`2tk z`Mr1FyYF5u6bVkw^XJYz`Y-?Z|M25;y6$mUY<7KxTFxYWF`L=uuBuAQSbQ9+ZN_b1 ziqdFsM+aRl(V!`a5mc%(hJLy+;%qSvBWx&Qw} z@w~jstGxbyOE5l!f#shTrxbfdT_tfqysv>wWsw|TQ!5EYzjE(%&_x|RI#~xRk2oQhawHHi! zlbkO_(G70xTv=HIZws}{+hcd|YICK+lxk*c$K!N4`v(TDyn7)Ti7unZt2cDCv{%?1 z8*6JBOdal0$WS9i z5t9-PkuXUnGK!15z#*=bNNCFA$Vx&NmZ98P-(9mATK7i-N)5xN@O!yrD)s)&o1n`* zp?$)j)p@sfkWOf6AsUWlQrUPQi05kVYv^uh(=lr3Yd{Gm$Fltg2a$9ko_01gV_rMt z58-zu2@Im?EfnSVwRy{{yvhK$ygnZQ4BU(Sn+O}^^No#-lao`M+ne>(wUBY@>hAve zD=!#r=Emj*Xa(K9bGPbH<>14Q-nelio6cA)CV-E={{D{cu6wgH>nrO|J@yoOYsiBE zUP7Uuj4!k`HD~jgnTbg#900c|7^TT&H)12qW@C{!3$R(xqJ)=K<`vLxfSLoyoaFA^ z1;8?Kh*uy$_;a8@lkdyvXgDD-1QLPgT+?2A{+H)({c^PbOc#>=Bg02)-POn_@purQ0ALV`0eEl_G`9gA(F{as0DJ+o1T{uA+PE*h_TvBi2Y-xw z<=yM=(u{0Wraaq?j+FG=|FBty}NI-UIilZ-)%SS65cyUCVPAXfDtS`op(m z>60?%@+z-5GqmW0|4e^bRwBm6w%xyv4BvZiC$BlLLrn;p!3lz2P)CN5t?KqE+XE7Gzu%* zGaJVaFX3TK4l4`5jF>jz2nVOYD2r5z$&gu4nzGt$qwkMx`LcR~z&GSo(%bV*nOY|#?G!buWYD`8F6SLE_R;Sl%Au)aV z-HQlVkcaR_!wCOIy~$;B7>pKoRb42RnO>ZK(MnJZn364R9Y+%e9cj(rMw_ z#{&T8WU7=%fyOCO`CLwe|B!^JByVKW*#-qVoh%Z)>pVH}Jzf1>Z`-0M1VxxdD8s8T zSp$HqRpTgmQI`~ML^)KA0e}@00n!$e8Imz3y#c-fD2QV$NU5dd1yyHL&)AJSZkPR` zhaOs-U-)mn@b&j6Mli>O%N~oxQ}L9=YKGN34f%<9nwG4!stw2)09Xg!>*)yXA1Vg@$aFqjplh`030Eqy&B;sKr zyFducM6dp{17M!G+3x{hOt6S*2{fiZRR#(bO-Lmjgn)yqd;a)LsQ}I7 z@T(PHeSwg*Md@SWUyrF*27XZz|M}xH!152L692fQgq5>2jOi7SBQ|Sn2RevB(cS#3bM9o?OP#E|%E zZfjXsT-2EiXC6Kij)XSCTPTH8R#mJ`&$qU=yQ_JP$;?6rlh3!bwH!HlBodD$g7MDw zo>N0-i!vvgi(1m@MiPXHBY85LMGcq|DbQpFSO68gs6|m)#7dcbfut#v5(H=Qfn0R_ z`NY(UBvpJNMVB;W_E{xSpbJ31xOTjZ%_(zunb`slCNrjzP~I-cxcatw^t$^G4FHWF zt~`R)CAyj>gR!%{)9G-642*XJE$F8D28_JI2282U_YNQUlmGaqk9R$)<5`Er>a^H^ z80%V^0qkvB|c*4N!f1?17W;8&mhomxko#;7Y4^Nf}d zyOrWu$GIHRDYcSG&A$K=?efdP3UhZ$f_rUVSuGIG@IKTd&` zfbK5GZ%_7Ve;7FK+WzTFFPJMTlt+|NU)WaJkPIb3snck+H?Q36YwOlinkCPM!9{9e z7tg;naAN4@?VH*))FNaRc6(!GvnS|5#mD7vff#~18g>R~!lPAUFG1#M|E?T?7!lw`l1rXm*dDofEDyjq+Y|)xg zc4v8VTn!o01*Hml2oz8fyxADS9Ygn+;)sR@ufe1RW~(S|Nu|~(O8cGO`xYrM-(Mvx ziNeUDEu2DCBr?c(kyNBAf|f%Gv|!O%@Wah*%|pFM-hBHlomI~m)#+TO!B&0l^dpaV}YbM8jk?l>QpB9<9U2u3OSudT~csJ zb^J-cciVTc?@(xU$DlO;P9ueAaMlN-zO6vO)&j+>czsjz`i^IPWmO^8to@-No6Rb8 zN|HeVy5E=h-S0dkl-t4O_35iDO;%q2s6{N)R21e_fMx*#MHxT}I1O&4TD>lrO<{+E zH97XCh3Q$)doUThxwh6&SzlXKjdT{<(2m~+ix&)RxGLS(-oJr%!@p}g>yPOR_JI-?k|$u6Y^u=C@BKLzm7;gAzz1Sy-<=8_g`t{ zKk_xoqEfGWd0`Qosl(wsar9(MON%e)%j7bk@;0?Lqv;2T2`(qjZh3ie?Cv-&4v-n3 z5m1oDXrU=J*xhOs1%&T(y3d|{P_5SD1&4y+Q%6ryGR4Bo95kgYMpJWRi_hn8X>Het zdc}AmD2?H=EG@0jlt!!3BSg!Tq8G4i&(K}58<3^HL|=R261(S-BvFAr`18U4f0l)3 zAsUh#x3apFNF~8;byd0Llrox3q~JcN5MaZAKK9|GN6_Hy>Fq*=7+zTrzUu1eL|BDv zIFK{$4ImuM^y11uulVl7_}$6zmGxzan&Cp>iV@>zo&L!up9W5bA1@Z_DG!`JgZ4Wn z$uZ&;3&$K*Cq*V(WfCrvauJ-6sMWFSM~}dZv_vxak)*=+B`EMy(j>4nvP9;*MXs-8 z5wZV={5&L=_O7u~{CPYRrKCWYo1C7)S)i?r7mQ9gnRRF6Mu&Oi?roJC;+e6VBe!-u z+pzor-p0!Ybvzn~n2lz9ejw?A3}$6*adB;-cWA&>?KT>8Y$}7=irwjq#NzM=;#0X( zCr`Gtx3zb7&d$%g^uqJi?&^@&-`m{_Z!nYITH~t5t6?aSkSsZ4iA9cmm8Ly;WI=kb z_jh@zYa!{lkSidU37=94kq&jgnW6Me2@F@*osT^HDBkPog=xK3yRx`Kt7yI62s)oZ zZ>p=VvsVq23TJ5hb)e)nCWoX(V2dHveM;!oL~O2lcFkaviE5qy2QbTH`8h~gSxBte%r zl}}+?&WTIfMo6<)l&!*p-7ojn{^R`zd2vY%-u-lfWEvm*(npR10LWu=Mc^afFNlAj z%*o~u+E4&Rjn8B)z27wun?({b&Il+MfyOOyeh1A`_G?$vA(8u zYHAwQZ#W)f6NW4^GN|3qAO(j#4JVnXrjH`NeAft1rrZkw()KH%U)lZfMezY}!1X7= zch^c#Jnn8#!G2BNBejMGjca9T>CVU~t_Ds4xuS}uw!L0ZqFj|uM7oKoN#e|05*Qfw zF$@Nhfw65CdwWw0st#4wYB+Ap%rB@IHEOJ8gApwjyVVBL8IlU5Nst#I)6E5f)k#Pf zc7eJSDLx!5%3!CwKDx>s;PU#%0bqe7^w_mA){7BP$n9Vc!n|KzC|$VtE@3|F3^evx zsP|Gd5JGE1GxFLU-*zYzK6v;LhDK2S3`e6F8^-R2Y0}oF=43vFZ#vwrTp=rDWYJhG znNETH47TLsPd+g{GY8SE4pIFG^raF!>xn;Ufq0QC3C<%?i0s`0>Dh|X$E1iU@So|t zl!WC2ZT}*n2$MV;{}1#~QmeFFj$^qT*_{PYf9DqB%Z)Xy)3JLjm-YC!FJHZ+LS`Lno0^-P1sp&F={m_L=(!^=c58`;&o9?bRjv1s)SzSyES@qZF7T%Vj1Z-v-V6$~4%dO>P&G-HNEJgG<)TDii@c};ts)iT(mq@i zgS@1S!pgxED>5L2m7wt{%ai^*_yOwT=++oy+XjXYq0FIGFb_Zc@Z`+6H{=H!xu&uj zC5%`wj*kYEv0;gnPG=FwJKMX^28SUKgqqu0+Sb?C@+tvdSr)ApTGbgu`y9WrvI@%B z(BOb)V>6tLA8j}_xj1wF_16IuE1PNz4kPQ%fj*W^WZGL>83lt&k>^GED>2jDDtZsrO1%`}zP^g^5z%9O#H1F-NB#+;0ib8sRo0E%8a>$3 zQ}3?#%q|pUkTZ!Ned0;a>Z;XdN1B4!1#k&BHa6QE2TE`O%a3qL>BTe*seR!U(_AWd*DQ^*#ZwccokXR}tA7FVI{gzjG{o0k*KaK7DDbTn` z_C9Rc%1|Qa4~Jy(g2iS*f&u##1bYFZkzF3+4*z?K`8(>&w_);ltb9+OoN{1n4`^hbm{RzP1)qTz5vs zNeWR_2jko=pGTEYPfSmsdu!Dj*Oyni`};Q6)<;H0>+5S#Z=KdnYmEktUbE%*A|VL` zLg)O3wf=;Z>GCkraI(zCOLz1`jF#Jm~? zf)E6OS__$^&1A=b&&I+!eix-=BySaFXD|?eBn28IXt%p6D!}eeCQ}xp0W?FJqA(_d zY$27(3=Vc6{_-5x-QG<^r;C!1hk_>JSBhP4_)p~xl`(L6l>u;h{R%gVoZKo>?+Y6c z24kfXESN^Z)>|f zI`ZYGz8H)}fE)8gVPS0rLdtN)o0^@WG-^CW302oT*Vxd6-l@Rr6$NO*5?S9uQHlK; zhUSoeA}9d$N6bBvTpehbQY$r|5df?11Hh_%01W=CPOUd<3y? z8cd$l=>5KX@dB9F@Gt-!y0W@@ePfNVqbaqc*LmpBp>!fOJvjqTR#BmI*_>JMWQ)AL z$~H4Muhc4PTn$rGGwls+ja9HtUD;S&>+3(@sB}%tOoz7CH~pKqT)jR0xO7H?DHF@- zw0d;W;Y81s)CC6fmJ|cCcVIaM4}eCdl1N4%2fS2^9)_Qz4U(Q$%XCF6ZN=L}F^WVs zo?=s4tCrT$d?p9hIcglxtPaP*5H|+p>F>VxgJ+(32HIC>175#+1Hl#0*J-W5Adp(E zfwHgN>F{iC!>G2mw;vq%8hZ^cFG$kQ|7;up_;BB_PH#c=7~f7BB3J;xI)4y)D_ z-13E@v4cZHv7Jzb-c+Hta&9h^OTGQxMJ|&utC?J$jh52&mU?Z6esg85rJ-46#MCZ& z;7SZz@w}wjxz7WT?;BYQyLfn)Rwz9XDa~r6AR;whwySO``&10P|ILqlKM~97DZ?YD zddA1@Ev>IMwzP6G9)s^Sl~r;^&dT!XLM|GPU|9Nv`f zN?S#R$vQkZgkw(Ly=QT{@;MCC77Ti8ip%OO#`SGauD~B399~$OuW`DuVJ@z(rV_F0 z%EL$ooOZ|3`bu~sh4GxO&K?9U_%$rfE(3f5HFwqvJC178IUod#xDiy+<#3B|w7I!_6@bWNNL%f1%f_oK`ZIVI(ofn;2CsM0m zoxv+9q*DcmEh6%6ZEimOjVJso{+YW|AcSLVY9|~_M3QwC4Hlaf2S;@PhK4V^@ssQj4LZ=f!m=yjSjF% zESM7eVzI#ep#WFrLYG$=0GHQ)Mya-hFmvL>2@LN^Ik&_)fu<3vt;lp!@fbQ<7PA@5 zMw7vOZ(`!ekt3kO-kBJ0?reo7L~DE7_{~vld|>kUgTB`;z7AxR;p%y*f8t>_91J7GB8=g0TzPd?o5?3Avm#xD7ZT=JONhSksU;&x zGGarYX0zf9Fv1cUU;SJFaJon*vay}m76wMU2YNf}dN#ctN<&rERDz4OIJGc5Gz_w? z!DI*p!Vq6VK=SSQ(EtRDIdSYHt{%<>AvTtX_jmQ_jD|boqg75f%jet37#4VVGW@p&CXi7H{TXeI+Rg_K6pOhQzIyRgxymwm$2SxNj2PAO>6 zM~1C~5?;%)0%a&pnLBs;1`br%E5d>B*%PM|z92^BgXt)(p@ZQd z`V>}&@y6K5G3RNw$#rLX9P`5{c&`K(QR*QvJ|h-9c01P!>0C(EmwFTm5zX$s^T^h+ zY!MfM*n5CIh$(q|XiI|f(=Bi%=nM3YL=EYn)HlXWi0{x!4aV>*Ev`g;)8guKJfB|o zZyXrvWz(6AkS%Hzo4)OADr47LQqfe`qy6hh)Y9p;*46^YW56L82p94tOnQ3*evxZP zieiux!;!UWt%96{Sd|d@grcen>0=i~b-PdM9hHmvSH82PRbaF$mVIgH?bz>bmJafRaj4Po*1?&<<^GLDfE%iN-Zn766e zw4%hQT{Lv3vWe98#^$JEM61^tHF`l`aGD*liWH|PY=w3j+8cr1EG8ppX4zEQRpIt+ z>|h#JuQsl&Y=TL2`qY_ikB|3z?oH1eJ$~Hl_s%ZO_ceEfw!O)bTZ~B$#VoWIJnQQ) zNxC;X+1l6L*xo8|Fy71K3V?bI)-iGtJ|mzS9v66EdI%80Eh=3g1@oLz2Dr&8C=?~7 ziZH~Yai-YkF3 zm?8l{GvnxiqkK9ShQ4zw2G(0VmVt6=A;n{)?!x7oZ zTPhM_JjP~3CiDmrB|9QYlGureU66*jv|&R;tU{X?cm~}t2>sn2yA8GxHnoS&oV{@I zqD5yiH<*AozxQ{4YqDFP`{Hxo{`R*4t?d;K%zPd_d<-<3xv6Pv;>k?9u10IAs1|^& zkn)$H-p@f!mdptXN~r@Z0bnpC)o9KtKOYe+bZo0!h9Xl?(n^@up$ggD))ZWf9XWms z7V|hqo6$ZqIfK487*}dWi#{;I#>V;h&75J(u#$=$tOh+EKd zG^q7x&7z3wS=$5{!ySbfX}4Dd3q|ySGtuPiZfW*<}KkcAb89CxPXkkcbIN>1jx4FNsPY zcpChI3@1K4H`m`-4l@*881FAy=bHhjrDTYCC2HNU0L#esF}y+)MkUf{!u}MNY(HC*+$=xqhh6G; z;Lb#CO9G<1FgUN!QU(+VDxH-`sYWKpLdlTMtjTeiD znWRpysi?58EwAASI2t}F;b_FdTf9DBBpL;Y4rw2wAani@?AqGi!2BD=3P{(TjCvEb z8%Y}AlhAK@l~;NFx-8+N2}cof!W7wv_(GlrFQ=5x6j_ z4QHM1o`b92%{Bi#ws+(M<_ZjKxfDFBhcOX?lM=y_(HK~pj@Td(O$3n-j1I724*=6y zUJVC4olXM4(kQG_qyg`LXTLru{SC23HFP^^nv;%oL7S(ae%hk79~wA- zG1r4V15;B|NPFr#8z<%_!6C{E1umbhs&d2Kz~}Lz)mi0qA?{}v09Y3sn0&gAqA z$YuBDX6BdYtLkc(?oDIoG?+}a^>q+ROa=Y*^>tfoYvFM4zx%^K+zf1=eemq9cQ0df z%j&RNbaqIzATd)~)X{LzYOzu9Du(biRG^{c49zgPpoRvApdcMxl>*zZG~0~Qqom9) z^?nH}N)jVfk~ad9kYbTl3KFDTNq($ER9IjpjKi{pEIPn)S{9CnP_>60AcB4{7JyXM z%KAz?9>q)A+VnvcMV=F2a1X&qyibsiLG~Drrcvwc=<1xfdk>zMI4puiAQW_hVCC^3 znM4XvTV1`m;o-8Z#-xP{Nn?E@=!YhQ26-llgW|NaWMNSLJe5}&0GHS234kU2Lr{0X zP$e4;O!H)_WFld<7@eTDdOX;7oE7fHg#|JaOsS!FHM_KcFor=6jQh5Bw4NG1j>o@v z?WJd*{vwj&rj{mX*}wYw>mV;RG&O;_3cnVqqzCapXfIVb-S!HXL1{s@t-+HiwV3%K zp6hZIL9Ba06OzH`62X$-GvCl6LM#%+U!0!Ek|jF9&{kIA&^duebI$~|A_F)+Apfv- zr}g;JW8Q7AEH94)qYs>X5PG(#8Q&Qlg*fm7hadP4-~Q7xCr$(4LI3H|bB{*jzNY$S z2p($nn)!uUE}tJhI0UF~qzsLf^=%F9fn@Od*maY|9E*ny23=ikgT-aJc<~}?oP&oB z{pi&nRn%7a{9bF5kO;vCpOXr-3}kmI zPivUzx#@gPKG1Tox~f*7Nzu2;U0GLCk71;2p8d*KzVg~jueUU});p`~>+1jRM?VH= z7d69`xGPY&g`|hy8)$884f=^GZhKQ3uJe2Ez2`DJ+zr*7N&s*C*2QaETVPLb;|Cg= zYUdZGDl6R>f|^}iMxgc?+v)_%X~>$iG0r$N4|UdqRA0z*y13QLjD0<6cqmu z9waxpoD`E40!G3M1XbgwOQiWgsy`ady~zo?))83sn)Nz-6DB=}hYou^aIFr2&81Gw{2Z?T0B8wDfQ_nwlEm0v!wmn;M$! zc56HY?MUH02$0(^nC?FKpsM&G08Baod>%8vG$fW4S{MP)C5F^c zsgkqO%~ENtMvdw)NY1?jeSC)7T-q#F030fS-kTtsLaQh5+oJddW!b3L<#O2r{r!gz z4O+HrmoB_x(HM^mbez9(5v_=LB8Eyd4T*jR#BZEgp4dk~7E3Js>O=t)y+Q$3d60~O zfpJbH;wM2=Gc+28C@_dR6QrPw30pCUiZxb|PAtE#f=nbH3&diOQW)hXQh$(f>0Aoo zqbQ0m0^CBctFftJC*cD{j-gaM&qa5_+9O9`agU@?uQ#||b)&am0(o0Wy3VrI)m3DJ zT1pK+VO*}6x!D-J1FUv~)(93f{@`h$wb)c$k9QnWTM!JBUKg3p`&b%SHq0xpG5{{G zUwxH`{^IT!3~G2{7p{m7Fel_8Z}7+i55v|7dWL{K*zsXnr&7`a?Av52biJVel!(Ql z_psyfWD7Zf$^M~%S1-K6m~;<6`tW!D>Mxh?uC_KeLl3#mRRw#cV}zO2Jo2PP8(kz-8mpmGRkhhxhrlp=xb1fpS)ha@3Qg96Qdrt|tlAK()4RZ zLC}@F-ksU$Y4m|3oADxK8j?An-&`iAC@SGoH$OkWw6FkBg{yJ?{CU(nLy_Rmp8qL) zn?w)e?Bx9T*@w;|65PIVTLu4KC@N?9x6i-z@R@TrcSaKFR4^7^SYEn1dWANt^K9;| zH{YCEnW}I))A1ZE0q@=$r&p;aTU(pDU8F##R7T=@BzAz&tjvqAStfOWB_Ci?`vf(Z z{P{dOzzXG7er$+pOIc-M7c@fYktAw`Y9~^%nR{fWfPywIIVE zJ9Z4Y4hrgH_r}5a9iO`Ug=e2b^Yady{q5iUEqoswEuL9_bW=VS=DsYTiA@&tbfus735P7_@q)Q3S@ADZ2foc-9e$T zw%XO%0Xaa;*xWQyk>;j)hsF$TMX1MP->};q+3m>6(sE3mRit%6f!9(31!+tL+~Ama zGHcR;Y63YZ%&9Wy3zv9SD*|9arAe}R4Mp_orJko``Aho1MD6L5cTCL8$~Zx-V@ed7 z$Q+8B$`S>tmN(#Goa)($xuidXJq@`*G9J%o3#&`s6UUDGx4lt+Sfip-k;Gf?yzR6( z!Fc-K_rHtgK`P5SUG5+J`0oxJ8q^te?_IhK2}U>=V6gbw)oTwNIX-)DI+ritI?OH2 zBV0f=YRBvS=}&*U?%%1XaoxUp{fn;O9vQjqw7L_fc$2*)8-vRc4XQVMfDzYtIl~Gh z9RPyV6!;vmP~s)ME0WQPG#_mE2-ZbWd!Kk=h^9wGA`8&IDCH1E$qiT%pc1EVlxPoN zRJcUmsk~B+cP*bxq_s`@*7o+~c5HcNW%%&0C%kjxy=y{I5eYU`*urZ-qb?MS&Wc(neBJcoNU|Iv@Pqh2MTgHA} zC`mRnM2!Fv$`Cd%nso>*5Ng6irZ4Q5tCSN{_gvL34Wl-|4vxzo>pPs`)6pPODaJJt8azO4zLu}0%GyE@xCXQ$?>E2|lbLUj|CMTiF- zUBgo|6VXH%nCFRS9$Q;kcR8FqlN~-WeEnP3^^C5ix)svFm|}uN@WvYEe6nBY`BG@) zI)dMU1B3OAHMj5F)-t*S1B0+MblIyRwWZfUo_p?v7k_~g{?f!e^pa|7+mO7>EG%Q>nGB=K$=feSlvasKPD+svERvn} zo4TMzOL|wNSqlFLK@k0Wr24%~(z@K6ewE+5p;3Ag=0G1aEFs)1E@}NLV09bNC zmjE!)M-)D_fSya`U^OIY(L~NOMYRG_T59h6;_LWwC+Pj>zxO{~xqKP3UEAB8H1~{ zvS)ArvC>!F#KzJn`Bzoeta>(s;Ye3Ud+i}tE}KE286+vWTKT2l{37OH5gDP@aP-8{ zp6(tl1-5n3pjS%(n3Ge`!NRF&WhJ@*`*@{vvVs^hun*&;K*K?wR@ftfO8^*@Y&@o= z(yBE7OSH@65&$mpge6Xz=kOpFz$Y(21#EeB>H5|^$GM8sPMjD+QWofXdLw?j(K&o* zDCv($Zo~K{Iwd=?5Tv>y$tbvHs5oFQ23#;a4a)k6p4l7=svQm@QKK2Ccq#ElF+{zR zRJ#RuBYjY}Eh}D?R~Z18*XIs^p}T-B67@GsbYL?gGr*{rst7R^yV%K-C(*=<$CKF9 zDk@wYm&Whix;cUY54lS5(4!B$^2#e~JD%B<#mAn0RBtofnHpPNUQ(2b|LgzwM?d+| zPouFQh|{@j7Nf6I3zL=#bCbO(kxZ_vt+DaVLRw}q7~r`p2w60?&;gbJF!E{QZ9{~8 z6qE{LNT00(tSVB_ZxjJA!kx&RB@JN=0?Xmqn<}O!rzgCnZKUac{WpKDPU~9wnn2Rt zTHf;S_+In=0=!mK1mdX#KyNG^Pvw%x`xz5+ee{}V(^FqlKfke*ed&1=S#1W3nKH(= z!hui(GGJ(^E^n^?pWphwpstLI!{_qX-@O6Z!_MK(tGBOidAH7z z3j}?E?Eo$pEvIRLy7KOop@YL?x5i_^#Px|QXyJkhJTpHH0c5MeJUloY3I%YeYggYx zq{qEcUtbT_0Cb^1q|fA%JMrK~zbGN6yhFg^$3!v@~etPok|rGqdSS;qJ{^+ z;BIs;6eZuI`!XByB~KH^Hxw!TObfltSKx2M zJAJmLqy56jHH|@c{KScgo3|F`=Ist!OQ{uZx`|-a+|+UE=;?QET*XM0(Po;vyXdN_ zgeVsVVnvm2@>+|s-BNeyV-zp3^;?oWlaZhN%8*eKLB&a-CxE|+Y!#O($n)V$HttCd z^&LQ|zOBC1sMkY&I~_^MvxSAJ8Cu5#;-SsZ2Bb|MdGz6b^RNC5G+-KQ8i*z~t-+>& zT>>9hjMiftnHaz45BLR9Y(`cf+5zNAp{^=dfKHMpvww7X`z^2X`ZZs|#|m(S_ACSX z1-_YInoFh=gGUa*L@mq5FTMYs$!P97*o$A9Tb?RrG5n+J?&w|fZ(z;>GgfQME5<@{ zHSYDxa{B-n9pFTY1pr0|7)4M3;9OA-0IZ|Hk|_ZI$5|c%#`gnYQ9K{9RgjzlK9muO zrBnD3V*FSM7a>476$}W#BvuL5QYaqW+}PCH^nd#8|5)G(ZCx#aNa*Is?ajHB_)ZA> z9ulOWpZFCuHa5ewutY0Bh&zAzZR7`iC4O;bP9$pQC|+jMnuJX8`sjOhlTA;nH&!?P z?Z5r^lulFObRl25H8SdUIC}aAF5kGi74jE)Gq?e%Il2`+Uj})nXQYy(9j}gS`DoMH|0IU?R z3|xq4Q5Qyr>0j{SWE9wLa&+aP@-Q|#Ua4~;E127w18JzKqd5{tv^BT9JNGsOUod+d zNktbt3#9_}@FVBG^PN9K?Y^U_g;b%53%q=FbvYD^f&Q&iX*L&EU^=K0Uo(tJ5~(!O z60y^wXlZFfCkEnKGLbR#31d!WP+VSR09;bsYLR1Tjw;*W-6+1Pey`$s?h$)Ehu|J_j8Nz06&HZ#}`3G~gWRM-Ip zlmPbuFtI@bQwT3XQTU7ic)tU@3xM&ZK+un%rL{n_ua3SPi2C5z{?*67cITaYCypP7 z&kbQ-)isOlJ{F>d=w)ei^!nzC*cV5hdWIyM$tUt0aWuYJS2>51nu$B&;}*;qj<7p=3Qp+Rrd z+t=4K>>Ofp5W!+;BNzJDD!bWN& z<8(OPT3Axa(C(|Rt%F!w&$pw2gr?cl*3o{n`)HNUX|Pwbsf<;lBjVoxN-_Ze*re0LDOV|KQ$kBT%^}Ot zqf~<3Eh(ll;s`6ru@F}A)&Kw#ZRb5;xR=?GFF947feAEf<)~^a;JL-ZTZzPet^7Yc z|K|r(!|z>v?@-4f%w;Xl&BE|0;ty|cZm+slzx;(S-Z+PFUY{)g z*1H#Rt1nO15AS1=;_}&sOjf z+rN9=^<7(FJkaWxkUz5N^R?7BZY{5Z3UPY)B+S4UW*6K>s;8y%@WAlj|Kj;0r;km~ zOxX-3M~w?}0Y{G=U!7Y*838~#nN4DP-0HUH@*EODP@sv6Z1p^>WD8&(GJR^R1 z*Kq`?o8l8iO-f}I=<+JBU&Des_8-!IC7w15%kw)v@7Yslu@}VH_#3ajTw7N)bZ{sq z6MP{*JOaUx?riByXW?F}M{<(NP#~K$Tf1pq8&b-&%6$s7GLgz#^q@fJNr6zQW(zU^ zU=xj<5`aw`&E%vG@V@3VfEFp9DrKJrfR%_8IY=@Jd4_{DhEg26$*K6%`DB4OH`34k z#&c8mruq)_fTT@pH3vHSF#&Y(@+GjPAP>Q))yr$^C~iLS>{CSWDG>05d^^D%SC#Xz z$IkuaCqMc9=e}`wdgkDv;ilTgpf}Ld+HO?q;DMyGTaYHLtga6Y9d4|v^@jqz{R8e~ z)MYkQMcihmosnz$`+A3(hU9q)f>}0`UP-hcRYgIOE)d?6o{Y4?wTKud%SfSt+27|e z#fI6OlE@_{R|?P`5@f{qSD?kXDgBd3fmTQW7!Ns?C#*Rwjfr5k$fA4iu5!Qs+SG*$ z7fuhHXsc;X#N&tswT-nqYMA~8x;r}2P4V1x=a-hP7H1s>shXS4oq1qxVhXPT z&zmb@VorZ%pf{VzIV`rQHz3;`%$BDU84My3l`?Um$3RTuMRO)ad0wWx$^f{$KF39h zaWdKL>?|1sWm!yPB5j?RoWN`z1aEe>JlMGa?J&02e*nExwX~DI@WKly1X|6O3s*0> zt11veo_gk~KmX331J~V~yqikI&pq@&7)m&S0GmwPEmoH2w2T&sFPuVic^+E%H*eqa zS0`w}0`ns+JnrS(J_Q;87>K4w#!&=P1%%F{N-ZyCJ|h7BXa`sV!0?ylvU$eFEH2K= zDoS8aV@|K>M=fzc8K`5D8CtsO1i;5J|h#+w0+Zj9W3iqD;i zQMq!+UQ_w@rMLb5;Ox}&g?TJE(wOk^HC{yg!Kz9M~ zhrawrFNRU%SRTDxh*bGk0w$~ZOTY2Oh1GdTVK-LSXOmg2T8AlL-?q=c6MpdMxkx+$ zLF4t6b)3Z?|G^)B_ebCR-VeT8S5pUZOE#0mq$_+5L2X)HSwRDE$LoP6Hja!^0{E^L zi*2a?P&&(AxpGCx$j&`_(zCsKZ*CfQUA?QtXY;iWwkr2B4+ZIQ6USrH*A;^?a6kUA zMBhYCQ^YJ%M(iF+3du=!ul_4CqUEGbF1wEjy$65`Xr3#S(!Qv~96Q1)ET_>kkQGqj z#ygHQ1+kgt$U&i5GTeLmnWvt%*vu%mqb@bvWeogV1LKV5tO8dzHg`woH9kIVuL zw@X(pRTwSMJKl`?zV_8`#FPHN{qFZ>8*1Zu7MLI9aH=4jJ#+Tf#Q2Wa|MJVfc>3Wd zTH0G=acbty?AtfrL6hN`2cMM_DdyeLCUG+V!yu4k2YW4)J!P5Y*Is@NVzQiNscJ=lQcJ<|C`Hih@jKjntkz>7w zfB>VB6l1_-s{+HK(A*qb8)I0LPswRPQwH3WEB8CV3~Yf>R#Pwlz=d4K%%J=zlK?Oc z7o$A@EQoSY1YN22L&OJaSW*~LgF{?WV!k%3Ov)HIT@dZ4qD=_L!kn6G>}Mkf|r*rT2g&-OF!uI>Mh2{Pd~mCUcY|n zP4K5C7U$>^{m`Lva8_4R0O?9mFH#b+mVFQr5C=tVXGBC4+;>m0gvYcvNFx`7521Y$ zIEf0@ZZT5iNGo&WRTfhip(43GJq>$0n)-XFa*IM1w*W2d_Z6UkE&onhQ zzVi1kx}7e}!YJ-3T~*a2K3=+nDI4fbNRD%UC0Mp`3q`HBi_p5|Xa|I4cST9e+e9rg#jel+hY z)Xw$A#YfLQV0Bym^}qjDUwrNhCr>>4gTMLV)r)uhe(!I6<2N@Wo4*`+L!(ny)wzvP zLn&0S=#+!~eR|rJj1_EVRiTi<9Iyb+kU+2>YK`KSM{{`;B?THXS@#J6$v#AI-An5~ zj03@S#^|r8=PwiGuh37HtBN!sn9>5PV8N#hM^rZ<8U3BV_%__0PCaz0ueleqi?=85G*nbSbM~RGhKA`{yP?{6_0l_c zmZ#|Tnd-V~s;ErFvkI+l)3;M=b1U=Q_Qtl!=~S5LxR7zR*`}5z?9Phmh1uRC2hsD+ za7B}bQ9_-##DSZ|D&=b8>8BK(Zsd||FYKU7#ezZv#>6)YBRMkC3M`2s0Wtl45G)t= z!dpp@$v#0U3BM1DdnycC?vBZzomd)a*=jIXIo_KZQ&7hGs)lGVpk@SvyW;xPor%!0 zDQ!ZR*ivJio}Y$ueP3rEqgDUx=RZYoYHex1G;+nWv32tJDQl(k=f8N_VXKf+v|gd) z!r6M4+n4e%c}7WV+Izb(n)LQ-Z@Ju#>YB#ZgMAyxtypn~EpF*mU8LGU%rNkxK~z@4 zxVNB^5ymqnnlOF@27sXAQ5S(&l%g0H@-%EAXe~?w!3)p9r-7ityc&f0BI%Qp*nK~U zETSEN=5L`W&jU(lp}m!dwFQqHs=2w{s59?)w_0lIQhe^z0}sA;^=4Cj)AKKn4OshQ z$wVxbs4>?RvVwwT>`G_RSHvJwPi+_VkaBi;+85n%R=dFR-CEvo$PH$hHWCG-HCAdW z8|}2Cvf_>NzeEGWGB=$LMQx0s_mST~BC#~HSX)!cGNHUa3tx*h@A|>20rdHEsiL~5 zCPvuk(o>McDp1gof(a39AfewzjG)B7OGP+-Ku81ya=yqY1dQv$u|k27fm}(>po3nL zf6C{-1XZd+z990(LDAm|Yy5D;HRa~(L9z4CWyr!bG73NChjY5)x3Raj8 zW$i@NjSM!+(MVBcSdJ%-9V)q$wh82Rl@qNmGI}P~Hxy;Fi}EV3|AN*h)!KxAh<1c7 zuS&*~RJ5qdLp_)V(+l5`%XvBX+Lc#r)s`du$24+HBpbRidiikINvFe=m1S<-zJXv@ zRZ(@K_vq%1XK{JKRp+EkMK@J3Ilj|f?S{_0Jg@Mwa*GDeuzBL8B&af3)}jX@<`5IJ zf{HDW5~^8^bOL${R4l`(RZ2>Pf=Yr6W#AWIULx>94U_9gFuCLHnAxb5F*Xl%~2Yf{~hbHP>IB9?NJHF0)-0Of~l$oE@L8 zvbdf&^3aji1Ae$1hlB05x*=z8bw`ik+HE7PyL|i7)!Uc03;uK=n_6dQX2)wQ+?(@$ zqoYE_=oFcve?3sm*s2dVE`-+gVWzsmy|K2~R?|tz%&<2$7nH?9t|ZG9lscXxu`aKN zj%YTKrFgDjV$=neSPSNeqKZ77<1nj2TJRVtk`=RgPR~#>o{XjB@ITlILz}Ebw%&rG zD2av=KgzrU9a~VEfvPz!g@K(&#=pKaKit{d-_jL`hP|F(VvVcpu+e72(%Q16)p6H9 z$(v+HPMuhqSO{-yx4K-Hg3F4sv{GfkHGtA+F)-Y7iRH4MV#aE#g}@-aqu< z;P-#{{VuBO@duxThb_8lMvDjna z4R)isFm^Xeq4_ib3^j}U0q{pK@O=R9egI5B{yqRET;UQ8aV;4mi>u<0;-8qk2TB!A zAsmXPa%spRP0mcdff?V8O|HNv)2Y?1HJI0-2KmxUFQJsHrZrH!2<&)4(E;z*Na?P7 z{HYYXvbAyH>SaEUNfjDIWq%+LUt3b*r)kLiR)gjB=EZm02Kv1a(e=Vs5ji@}x2iE}zMY88s9*a>`ONm!)M&o7yOsW#Mm`VDnH<104d_gexiYO47fzn=lN< zLO(lO!g-a98j8V$*QeNKlc8Ms?g*Sjghp*1ui?tZRSK1;WtD{n7{$=| zj7%)u>lkW-(dF%%Bg0)slHRno6J5%YXWMw({0At^P>{E@SCW>t|ne6N9%PV+@5v;7Q zqBV*(pR=7{NzH#BTpt{+X+VL3+>#U7{_a< zZKz+ETc~Yrn4X-et*TY#OKq(!Yu@eK*Kc&TcSZd{|K@g}Dxi{6pxC&NRJ{1&ixyKA zEm&1K)kZ$Yz<)qd;)--EO^kf4Mkq<9awUB+$D4H+DU$Q0Viwyk1zE3*S*wT4BDw&a zypZKNy-tS!3INQ@+4})-aSs4vW2Yd5odsSlsZe2{@;O!Z;c3R-#J96{Z1BXwpt!!4-*B0*;*Vb1sZAX$5Ob}7ZR5qnGXp1=v zy%n=uuCAf>!j;QC@_t66&Xr)oOiA5o;sdhRss0sw@5`$UfXl1AKCPrnc%hq{8?Z4U z#w}|rQiMR5$+21F{bGwoE~T?zFRP$eVAf+e7LvbP#GMbNM;UTuo83OWGzFfs!|YJz zDX>ZZ>d*i3=&55CyA?h_$!vPtw^Papy{&z`z)eg|^z?PNHFh58Jz!8(U{FOv%P1v8 zjxg?vjx^!)0sxC5jVdlrQg&qY)JF@^d*SL+_C)ZN2=iG?uuIs@Wu%p@D3QZ(MZQ4s zW9zqD546lJF0|L$ax6P|>!A{4GneQ`qsA=SC)qk4)zU;GGqQ2qRs8c%JiSo0mcPRQ1L*~d*RyKh(|RwwHwf)#t&O9HVv&Y8O&jS z$fPw74Gawq3{o=Y>92p`gTY{oIzzI1c=*V` zP`__-2j8r3Y^bVoiw3!h&hCzzcWyScHuiLMUAS=xcTr7c?cB^_Z(lc31!F;r%KgIX z0z;`M?@iQJ*A4d#XJa{`MBA+fm1xC?EG~|Gqo^>0s$Jk@6qC#eTD4r{4TI0GLVqQZ z%Hg6xW(pq`WS1;27|DmFB>x8}@xs0j<}N12t$2ZZI|=LRSRh?YA!mXiXL2=F*{a@P#_mBTDNtXK$%)`f zi8z_jm{=yyLxviVGD<4e5*n9%(&x9#ax1U$`Y#jmei{t^_-7asKnmCwBHC~SeU*sK zIk&j7P+_a+XzVP=^5g5{*Kc3_%5z`00!MLa6jhO9oIZJimeJT%)|WS%n_IETs5I34 z>O3~vl<42q-QBHL=|h~th@X=YCQCt1r8BHqk0;|mEa3zyOFD#tSuHW36bZHnJx`R; z#e{)uewk!Ouh)?YqTLZ9;UfT;m{EwqHDAg>fJ9axdRx4fJrjFi+B?0o;YZyZHSuPI z48By~fc@J&*xL_5UhE zIW#y3klfpO;Bfz8i0FeLRG~*@9J9X&m`oxo&H$r{j*uP7yH`SMVwQk0Z?nT3=!5&K|DAm@%QEGD6mmqhZRP$IB6{|$$>l|p$soy?VJHTHGL}wO zSJ(P?ydcAq=_E?2)~dm5!oPxT4GLb7yfupZ?lHV-K~b)jmRA`7m)Eb1cRz8VkcsRc z%C|(EfQJ&5iYw$Xl7V5)-B^Zhn;I-+IeyY$Hv{&}&CNiUPp{FScId8lzw+~!AOC|V zp#INi@&=WGF4F(||MoAx_5b`yM}J56K-XLEp665CiDM_NcFT>s@54UFRZj$R@Dv5D z9-PlSF(1vM8vF4MFi!%UfN@@BDgW6{7sX2e9bs4hdDj4`q=E{xN=LD(+|G_~=)u8j z*RD;*c!$o#=J-%NG%+&)_u5LAb98*vU^aDjcL88`c60~*L5slztK0EA6X^XSY^d#O zbYnS2@#EKDj)!80i}{y-{z7X&lZ7~ogLGJSekwA3txq2y)We*#}un?8!}G5K;Upg1C))_jCOr_ZF+p#X0%k>YhuATpUro+ zcD;7v&E~e|L^h(7(>Wm@Oht_rlUc6=z?)r|fBx-P!?6h1Tv|?%%W^3;Crc#?>4ZG5 zhO;3!yd@djk68>~7x)x92ze0zg9u$J7@!Ib`I8vSLbpS~=+N)QU^n<%Ssq=`0<=Kz z9kh{)9|piEt3yi+?bzWfsOw6zBP%;Z#s%aJb? zHauIc-7QfTurYxf9%Ep*;5a%&K4Xd4!ord-?9(tzrL&3;acqHwpRg|&1h-wSP*C82 zt4eUufMZgL0$NTh7=4N>Vw#N>h_Mj*9=w7=5?11jG`X$e1F1j4MQBCKJjxQ zc7XQ>;c)KcvzN_6;M%A%P0mgJzwEtdlv`(-CUygI4h5ic&N(pWAc{(((CX>w*$%rt zYtQcN%sQO)*`HqfkG=mK+T(S5z1`D2+U}liwWbv*k&>9nVzF}0g{lHjg`C5^xWM)E z-HR$x64mzX%#V=|u6QW23Pj;vTzudAyzdiCS}PP>Xc$4{*4ENGd}IV^XXw{VtxV$+ zgxR$cmU)b-v$Jz-c1plT86oDKUY_0y`W%%`EGDv!Hw)ei z58DtRGoF%yf03!ff(cZ}fiWflVC?k;>Tm*_-`38h%V*zv|NY9^y58Zz z+)ZB1AWa=*l_9tvTyU@I>}G#Fn2ds+7N7J?=ry{qKlJB+{^uXQ`w3nh3Dyng)CD6H zU7$7_3F(4$X zs;j}8hNf4;s+&66#-<(xG%Aep?EPL!4ffYh`7*rM*od zr^HKd4ED_&mqVFBJ@LONhX9w4G5{_g|0xIaRl8u5*^FG%R5q=IVIWIdws=rOT|Ks_ z{T3b8D1HsHXT+}pU}%8zTwZI@1%>_X&8^N@yR*XSG*@Bi|Lo4EI*oR5ZDAUbr`2^h zsY<7d3-WNo!s6T8-MhWH4VB}}#1!g#sFh-c$i~*@?8;IsnG$n_|M^G%%L^ASp|}TE z7JxwurBROe?Rx#;(Ab9`2Ga47u3iQczvX5B-hO?Zy}P|@b9Do*xogWSbW)Ye77S?R z765tBXN95_V@%M~ zcrvIBx70P+tQDbD0&UtK|Mx3)GnMkmn%h7-6TAlQ}1ta)s7 z)MT{ORMqb8cp*RC_xWdpnS734UtWjJ^)K$d^>6;mf4#Z81@B83Xt4!!7`Pw(=tr!g zSl81GB-v0~@6=nBS;}IxxVJoZ){u*ZV`P?@Cu(#?o#uFp*~q7(z{He74Y&#bjQ5D< zGQfM7Rs#S>)OuBsIGb@S1*fZCmjM{WNQKW5@5^-Jei*s85m^(S^H>+ z!Bklivy1|3XUn}bzp?AttJGHj1Xj+^0~>El!Uk*O%*nH;yU^@F9|RC_eDZN99_}6L z#e0e*4}j$D-ECZrfA@d?_{h-k)clOWZ14y7$0sIK%bG|e1o$^KJ&lcjYH|keMuJHO zeY=&lE=?*98%#JSBSD%AgIZC9`5eQeWsgocmCW;qQnaejILH71g95Eqf|G`sDn21f z831P!=BEL$0cvZo0>7q4IJ`F~iCreA0Yi`A+4Ctm<>}tD2tx`)0wB73_q~}MK9veY zK)Yw$_$RB&Yv23!4?q;xbz*qJ!^>oQe-{}QW}_(+OI16na-7%a_gA`Vm8?>dR48o# zz)Q0uCy9%RY&VUch{)wgH0rEyd>u+JALXNbe4~#7o6qO+I;9>xvK*D&jcl(kY<1Lh zH8>lRR2)%ikx2Zd=l?*<>Vhf%ho66Vs{d?jTT4nvMWZ2<7U;3I)wjh`34hoRA_+%T zpzXdiw@^qHqPt;((&*3Qso`DBVM)PC7)vHiYOI1;jottXG^tby`2EW>C9krM)df+A7j(&dDvJp)=sbB^%%QGQu$g!=ZZyjPn5Tqv zo=~7=0GwlUg@Y(Yt_{aRiAu*U1!z;Cl|RQ8hiD%^;Sc3BAQbo{T9_X z%V375R_bH`O!g)52>@1oIrNnI3IHzte*wU1g*L^ftU60?cb`#bM9SRj&%bf=+MN@p zPD0yLSz9xR_HZiQUfm8l7W&QDeb1jiUu~#H!a^>U#o#L%ioWs28&eyTQ_Bl5f~v9E z+8bJ4zx=|jkFV!)8J!soP90Wl?5{{J!nS?&r5E47c|)U74YYI^e3oiwMJ^ZF5AIdF zs=Dj?2Rnz9f;vks2zWn5LeFAJ2CzcHZbgYAnn**2q#=fuWFq|Z$r6@UfrL6h%fw@h z&mJ`DpX?IPvWOr9(L5CvqUp_a4wjmFIBqz-KHtR)7m}%*n5K4?HkzBNVau5k(xGSu z2)n{zL(D@^(U=Ez8tCm`SzgPgb2Zh~NJO-oZLN*%8%WUU??#v82jBZ+yUPxD zi$E-**K5soTPB^n^wRSfFZ;s#oqR4Dk6}CO?CyQ#<=1MgH5$r}eUXrPi=fDY0!_!$ z9FYK1#C#I|WxNQZ4+`_L-MO217P_J zvMd%jm~5ojU~!FkK~Y?aZLUYQ>gyYAT7!FU<4^zePk;N)`)|Db+T6lC^ho)5M6J;D z^z{PoHP+T!Oy*mk-P+pNeEx+iGxM{cf+O2G5{kkg1P}20gWr1n4cs{hjeuSnb6})B zRaRF)4!*UuO^~)#edW>>fa>wpY4y-hD7+s@r(U>x6+z7X?fnW$nd3yQn$W`FPKSUp zpnA5TFhc6A#B7^Qb-izN~T3zmJYlJqw zvZ0zzCh@Z{?b&p1;d2Y;73e2`_5pNdRXp6(VF+(?8fb~WkKPA9`{0sP*zK|fefcOK z<>Q-oJhSsmAO0V+Kd@AwO<^{+H$f+X%BHf>70!egRu(F2oXDJmy@)UDz46)g*I#|Z zqOs+P7xW}HG3+l+t1}o4PEF0!Hr8s4no3>e($XT>RTa7lyV?qhnVM$}u+@U?vjl*n ziG*1LZ$u>bs&n8RLGT2!kP6dIG)`59Y#y8%0AP89h$ENIU=;$}7R%8y0Wb&F8An59 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 Date: Sun, 13 Aug 2023 00:02:27 +0000 Subject: [PATCH 320/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index b2bca563d..5349014bd 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1384 +1.2.1394 From a1d75ad4e06abd15469112805bbc2f25ac9240f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 06:55:05 +0000 Subject: [PATCH 321/391] Bump yosys from `2829cd9` to `008b725` Bumps [yosys](https://github.com/YosysHQ/yosys) from `2829cd9` to `008b725`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/2829cd9caa5729b3b7e2393d3428e265cd038ea6...008b725c1d7c44d6724a30a5953a5f886c93d699) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 2829cd9ca..008b725c1 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 2829cd9caa5729b3b7e2393d3428e265cd038ea6 +Subproject commit 008b725c1d7c44d6724a30a5953a5f886c93d699 From e34de286a4d3129a34584af99b1673ac3153e075 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 00:02:20 +0000 Subject: [PATCH 322/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 5349014bd..4bce91c1d 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1394 +1.2.1401 From d5cbe591528d31cc54c35a0bebe763ee6826444b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 06:13:24 +0000 Subject: [PATCH 323/391] Bump yosys from `008b725` to `cbd3ff2` Bumps [yosys](https://github.com/YosysHQ/yosys) from `008b725` to `cbd3ff2`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/008b725c1d7c44d6724a30a5953a5f886c93d699...cbd3ff2d3a8d29fda2d796e4db5feb384990812c) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 008b725c1..cbd3ff2d3 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 008b725c1d7c44d6724a30a5953a5f886c93d699 +Subproject commit cbd3ff2d3a8d29fda2d796e4db5feb384990812c From 517d608e23a16f89116cba133a802b2155e03d35 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 00:02:17 +0000 Subject: [PATCH 324/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 4bce91c1d..7c037b6b1 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1401 +1.2.1405 From d44f34323f25522321dff78841a823594b51fada Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 06:35:41 +0000 Subject: [PATCH 325/391] Bump yosys from `cbd3ff2` to `4a475fa` Bumps [yosys](https://github.com/YosysHQ/yosys) from `cbd3ff2` to `4a475fa`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/cbd3ff2d3a8d29fda2d796e4db5feb384990812c...4a475fa7a2788679b4c575dc3c1231a065d35271) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index cbd3ff2d3..4a475fa7a 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit cbd3ff2d3a8d29fda2d796e4db5feb384990812c +Subproject commit 4a475fa7a2788679b4c575dc3c1231a065d35271 From 414f7379c67a6e03883e8a660784988dc1512c4b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 17 Aug 2023 13:52:21 -0700 Subject: [PATCH 326/391] [core] fixed some bugs in debugging messages --- openfpga/src/annotation/fabric_tile.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index 7f190c7bb..d8e4025ba 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -374,7 +374,7 @@ bool FabricTile::register_tile_in_lookup(const FabricTileId& tile_id, } /* Throw error if this coord is already registered! */ if (tile_coord2id_lookup_[coord.x()][coord.y()]) { - VTR_LOG_ERROR("Tile at [%lu][%lu] has already been registered!\n"); + VTR_LOG_ERROR("Tile at [%lu][%lu] has already been registered!\n", coord.x(), coord.y()); return false; } tile_coord2id_lookup_[coord.x()][coord.y()] = tile_id; @@ -401,7 +401,7 @@ bool FabricTile::register_pb_in_lookup(const FabricTileId& tile_id, /* Throw error if this coord is already registered! */ if (pb_coord2id_lookup_[coord.x()][coord.y()]) { VTR_LOG_ERROR( - "Programmable block at [%lu][%lu] has already been registered!\n"); + "Programmable block at [%lu][%lu] has already been registered!\n", coord.x(), coord.y()); return false; } pb_coord2id_lookup_[coord.x()][coord.y()] = tile_id; @@ -429,7 +429,7 @@ bool FabricTile::register_cbx_in_lookup(const FabricTileId& tile_id, if (cbx_coord2id_lookup_[coord.x()][coord.y()]) { VTR_LOG_ERROR( "X-direction connection block at [%lu][%lu] has already been " - "registered!\n"); + "registered!\n", coord.x(), coord.y()); return false; } cbx_coord2id_lookup_[coord.x()][coord.y()] = tile_id; @@ -457,7 +457,7 @@ bool FabricTile::register_cby_in_lookup(const FabricTileId& tile_id, if (cby_coord2id_lookup_[coord.x()][coord.y()]) { VTR_LOG_ERROR( "Y-direction connection block at [%lu][%lu] has already been " - "registered!\n"); + "registered!\n", coord.x(), coord.y()); return false; } cby_coord2id_lookup_[coord.x()][coord.y()] = tile_id; @@ -483,7 +483,7 @@ bool FabricTile::register_sb_in_lookup(const FabricTileId& tile_id, } /* Throw error if this coord is already registered! */ if (sb_coord2id_lookup_[coord.x()][coord.y()]) { - VTR_LOG_ERROR("Switch block at [%lu][%lu] has already been registered!\n"); + VTR_LOG_ERROR("Switch block at [%lu][%lu] has already been registered!\n", coord.x(), coord.y()); return false; } sb_coord2id_lookup_[coord.x()][coord.y()] = tile_id; From 399f087c50649196028ccfb2a854e251ef0fb331 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 17 Aug 2023 13:54:31 -0700 Subject: [PATCH 327/391] [core] code format --- openfpga/src/annotation/fabric_tile.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index d8e4025ba..b9c6eaeec 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -374,7 +374,8 @@ bool FabricTile::register_tile_in_lookup(const FabricTileId& tile_id, } /* Throw error if this coord is already registered! */ if (tile_coord2id_lookup_[coord.x()][coord.y()]) { - VTR_LOG_ERROR("Tile at [%lu][%lu] has already been registered!\n", coord.x(), coord.y()); + VTR_LOG_ERROR("Tile at [%lu][%lu] has already been registered!\n", + coord.x(), coord.y()); return false; } tile_coord2id_lookup_[coord.x()][coord.y()] = tile_id; @@ -401,7 +402,8 @@ bool FabricTile::register_pb_in_lookup(const FabricTileId& tile_id, /* Throw error if this coord is already registered! */ if (pb_coord2id_lookup_[coord.x()][coord.y()]) { VTR_LOG_ERROR( - "Programmable block at [%lu][%lu] has already been registered!\n", coord.x(), coord.y()); + "Programmable block at [%lu][%lu] has already been registered!\n", + coord.x(), coord.y()); return false; } pb_coord2id_lookup_[coord.x()][coord.y()] = tile_id; @@ -429,7 +431,8 @@ bool FabricTile::register_cbx_in_lookup(const FabricTileId& tile_id, if (cbx_coord2id_lookup_[coord.x()][coord.y()]) { VTR_LOG_ERROR( "X-direction connection block at [%lu][%lu] has already been " - "registered!\n", coord.x(), coord.y()); + "registered!\n", + coord.x(), coord.y()); return false; } cbx_coord2id_lookup_[coord.x()][coord.y()] = tile_id; @@ -457,7 +460,8 @@ bool FabricTile::register_cby_in_lookup(const FabricTileId& tile_id, if (cby_coord2id_lookup_[coord.x()][coord.y()]) { VTR_LOG_ERROR( "Y-direction connection block at [%lu][%lu] has already been " - "registered!\n", coord.x(), coord.y()); + "registered!\n", + coord.x(), coord.y()); return false; } cby_coord2id_lookup_[coord.x()][coord.y()] = tile_id; @@ -483,7 +487,8 @@ bool FabricTile::register_sb_in_lookup(const FabricTileId& tile_id, } /* Throw error if this coord is already registered! */ if (sb_coord2id_lookup_[coord.x()][coord.y()]) { - VTR_LOG_ERROR("Switch block at [%lu][%lu] has already been registered!\n", coord.x(), coord.y()); + VTR_LOG_ERROR("Switch block at [%lu][%lu] has already been registered!\n", + coord.x(), coord.y()); return false; } sb_coord2id_lookup_[coord.x()][coord.y()] = tile_id; From 85bc89000907305286b554fc7711665c58b02a24 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 17 Aug 2023 14:52:30 -0700 Subject: [PATCH 328/391] [test] add a new test to validate comb options of group tile, group config block and L shape fabric --- .../config/task.conf | 38 +++++++++++++++++++ .../config/tile_config.xml | 1 + 2 files changed, 39 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_Lshape/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_Lshape/config/tile_config.xml diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_Lshape/config/task.conf b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_Lshape/config/task.conf new file mode 100644 index 000000000..33f1f8764 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_Lshape/config/task.conf @@ -0,0 +1,38 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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= +openfpga_vpr_device=4x4L +openfpga_vpr_route_chan_width=20 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileableL_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_Lshape/config/tile_config.xml b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_Lshape/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_Lshape/config/tile_config.xml @@ -0,0 +1 @@ + From 913c232556b4dfd2cc400a5a21fbbbdbb851afa0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 17 Aug 2023 14:54:24 -0700 Subject: [PATCH 329/391] [test] deploy new test to basic reg test --- openfpga_flow/regression_test_scripts/basic_reg_test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index f345d8428..8803cd5fd 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -190,6 +190,7 @@ 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_Lshape_full_testbench $@ run-task basic_tests/group_config_block/group_config_block_homo_fabric_tile $@ +run-task basic_tests/group_config_block/group_config_block_homo_fabric_tile_Lshape $@ run-task basic_tests/group_config_block/group_config_block_homo_fabric_tile_core_wrapper $@ run-task basic_tests/group_config_block/group_config_block_hetero_fabric_tile $@ run-task basic_tests/group_config_block/group_config_block_hetero_fabric_tile_Lshape $@ From 3ac3eb4624425c01977a267870e88c76ad95bbb5 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 17 Aug 2023 15:08:27 -0700 Subject: [PATCH 330/391] [test] adding more flavor to the L shape --- .../config/task.conf | 2 +- .../vpr_arch/k4_N4_tileableL_40nm.xml | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_Lshape/config/task.conf b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_Lshape/config/task.conf index 33f1f8764..e18407d92 100644 --- a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_Lshape/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_Lshape/config/task.conf @@ -21,7 +21,7 @@ openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_ 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= -openfpga_vpr_device=4x4L +openfpga_vpr_device=4x4L_io_boundary openfpga_vpr_route_chan_width=20 [ARCHITECTURES] diff --git a/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml index f1fc5abfe..c3f54c448 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml @@ -52,6 +52,23 @@ + + + + + + + + + + + + + + io_topL.outpad io_topL.inpad + + + @@ -97,6 +114,15 @@ + + + + + + + + + From e9fd22790dafe111ab7264a15f67dc0c78759dc5 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 17 Aug 2023 15:26:32 -0700 Subject: [PATCH 331/391] [core] fixed a bug where pass thru cb blocks are not connected in tiles --- openfpga/src/fabric/build_tile_modules.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 621621fc1..1d104fee4 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -881,11 +881,6 @@ static int build_tile_module_ports_from_cb( return CMD_EXEC_SUCCESS; } - /* Skip if the cb does not contain any configuration bits! */ - if (true == connection_block_contain_only_routing_tracks(rr_gsb, cb_type)) { - return CMD_EXEC_SUCCESS; - } - /* If we use compact routing hierarchy, we should find the unique module of * CB, which is added to the top module */ if (true == compact_routing_hierarchy) { From 463897f78edc909f4acd0687df9ab3055bdb250f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 17 Aug 2023 15:28:59 -0700 Subject: [PATCH 332/391] [test] fixed a bug in arch --- openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml index c3f54c448..cfcf662eb 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml @@ -116,7 +116,7 @@ - + From 4afd48d9307ce4c86b5a04eec8c15572650ae02e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 17 Aug 2023 15:33:09 -0700 Subject: [PATCH 333/391] [test] format --- openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml index cfcf662eb..73abed84a 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml @@ -62,9 +62,9 @@ - - - + + + io_topL.outpad io_topL.inpad From aa216cca0b6f694a1e379b5311ba2ef9d74f97d7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 18 Aug 2023 01:36:29 +0000 Subject: [PATCH 334/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 7c037b6b1..e3a3377e0 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1405 +1.2.1418 From 48e458cf7230b909bbff93bd5843f374e61090d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Aug 2023 06:07:44 +0000 Subject: [PATCH 335/391] Bump yosys from `4a475fa` to `6405bba` Bumps [yosys](https://github.com/YosysHQ/yosys) from `4a475fa` to `6405bba`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/4a475fa7a2788679b4c575dc3c1231a065d35271...6405bbab1e8afc587febf41339aaf8b514c8f202) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 4a475fa7a..6405bbab1 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 4a475fa7a2788679b4c575dc3c1231a065d35271 +Subproject commit 6405bbab1e8afc587febf41339aaf8b514c8f202 From e82e4f487e99e778cd913b6f2c0a3100374c6280 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 18 Aug 2023 11:13:34 -0700 Subject: [PATCH 336/391] [test] add a new test to validate io subtile support --- .../regression_test_scripts/basic_reg_test.sh | 1 + .../io_subtile_strong/config/task.conf | 36 +++++++++++++++++++ .../k4_N4_tileable_IoSubtile_40nm.xml | 36 +++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/tile_organization/io_subtile_strong/config/task.conf diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index 8803cd5fd..2a3687033 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -175,6 +175,7 @@ echo -e "Testing tiles with I/O in center grid"; run-task basic_tests/tile_organization/tileable_io $@ echo -e "Testing tiles with I/O consisting of subtiles"; run-task basic_tests/tile_organization/io_subtile $@ +run-task basic_tests/tile_organization/io_subtile_strong $@ echo -e "Testing tile grouping on a homogeneous FPGA fabric (Full testbench)"; run-task basic_tests/tile_organization/homo_fabric_tile $@ echo -e "Testing tile grouping on a homogeneous FPGA fabric (Preconfigured testbench)"; diff --git a/openfpga_flow/tasks/basic_tests/tile_organization/io_subtile_strong/config/task.conf b/openfpga_flow/tasks/basic_tests/tile_organization/io_subtile_strong/config/task.conf new file mode 100644 index 000000000..8f20754ba --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/tile_organization/io_subtile_strong/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/fix_device_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_IoSubtile_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout=3x3 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_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= +vpr_fpga_verilog_formal_verification_top_netlist= diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml index 857064fcd..0119b1f14 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml @@ -29,6 +29,34 @@ + + + + + + + + + fpga_input_center[1:0].inpad + fpga_input_center[3:2].inpad + fpga_input_center[4:4].inpad + fpga_input_center[5:5].inpad + + + + + + + + + + fpga_output_center[1:0].outpad + + fpga_output_center[3:2].outpad + + + + @@ -89,6 +117,14 @@ + + + + + + + + From 170a49c34fde2f8d0e4b52a477c7741d4b1697ea Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 18 Aug 2023 11:15:05 -0700 Subject: [PATCH 337/391] [test] fix a bug in arch file --- openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml index 0119b1f14..392c2bfdb 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml @@ -32,7 +32,7 @@ - + @@ -45,7 +45,7 @@ - + From f69520d0c3e1d88547ed5cb80891c2ab61fe6ef2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 18 Aug 2023 11:15:25 -0700 Subject: [PATCH 338/391] [arch] format --- openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml index 392c2bfdb..ad58bdb6b 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_IoSubtile_40nm.xml @@ -51,9 +51,9 @@ fpga_output_center[1:0].outpad - + fpga_output_center[3:2].outpad - + From a8ef24150d90a1a4fcb3925451e894340f9c04d3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 18 Aug 2023 11:38:32 -0700 Subject: [PATCH 339/391] [lib] update vtr --- vtr-verilog-to-routing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtr-verilog-to-routing b/vtr-verilog-to-routing index 8f4c5567f..62fa48399 160000 --- a/vtr-verilog-to-routing +++ b/vtr-verilog-to-routing @@ -1 +1 @@ -Subproject commit 8f4c5567f4ff92db0659d54dd1eb6435d3b7a9f6 +Subproject commit 62fa48399eabb8788844ad852fca6cdec800accf From 3d8f76269ab49236c2e970bef749f83c070315a1 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 18 Aug 2023 12:42:15 -0700 Subject: [PATCH 340/391] [core] fixed a bug when io is in the center of 3x3 fabric --- .../fabric/build_top_module_child_fine_grained_instance.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 97152ebb4..47067a2cb 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 @@ -390,7 +390,10 @@ static void add_top_module_io_children( } /* If width is odd, add the missing vertical line */ if ((grids.width() - 2) % 2 == 1) { - if (xmin == xmax) { + /* Note: Do NOT add a coord two time! So when ymin == ymax, should skip this + * point. Think about a fabric of 3x3, where the point (1,1) is added twice + */ + if (xmin == xmax && ymin != ymax) { for (size_t iy = ymin; iy < ymax + 1; iy++) { coords.push_back(vtr::Point(xmin, iy)); } @@ -399,6 +402,7 @@ static void add_top_module_io_children( /* Now walk through the coordinates */ for (vtr::Point coord : coords) { + VTR_LOG("Adding coord [%lu][%lu]\n", coord.x(), coord.y()); t_physical_tile_loc phy_tile_loc(coord.x(), coord.y(), layer); t_physical_tile_type_ptr grid_type = grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY grid */ From 395e9d8af3e6a0af9edd7b4d6cc4d308c0eab126 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 19 Aug 2023 00:01:29 +0000 Subject: [PATCH 341/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index e3a3377e0..2d155e4d7 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1418 +1.2.1428 From fc523bed32986e06d17d81421e9c87e5e0685df3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 18 Aug 2023 21:04:37 -0700 Subject: [PATCH 342/391] [core] fixed some bugs in spotting the correct pin index of given subtiles --- .../build_top_module_child_tile_instance.cpp | 5 ++- openfpga/src/utils/check_tile_annotation.cpp | 45 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) 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 9b190c646..88c0e0731 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1341,8 +1341,8 @@ static int build_top_module_global_net_for_given_tile_module( return CMD_EXEC_FATAL_ERROR; } grid_pin_start_index = - (subtile_index - sub_tile.capacity.low) * sub_tile_num_pins + - tile_port.absolute_first_pin_index; + sub_tile.sub_tile_to_tile_pin_indices[(subtile_index - sub_tile.capacity.low) * sub_tile_num_pins + + tile_port.absolute_first_pin_index]; physical_tile_port = tile_port; break; } @@ -1386,6 +1386,7 @@ static int build_top_module_global_net_for_given_tile_module( generate_tile_module_port_name(grid_instance_name, grid_port_name); ModulePortId tile_grid_port_id = module_manager.find_module_port(tile_module, tile_grid_port_name); + VTR_LOG("Finding global port '%s' from grid '%s'\n", tile_grid_port_name.c_str(), tile_module_name.c_str()); VTR_ASSERT(true == module_manager.valid_module_port_id( tile_module, tile_grid_port_id)); diff --git a/openfpga/src/utils/check_tile_annotation.cpp b/openfpga/src/utils/check_tile_annotation.cpp index 6117e3109..68097d2af 100644 --- a/openfpga/src/utils/check_tile_annotation.cpp +++ b/openfpga/src/utils/check_tile_annotation.cpp @@ -128,7 +128,6 @@ static int check_tile_annotation_conflicts_with_physical_tile( ++tile_info_id) { /* Must find a valid physical tile in the same name */ size_t found_matched_physical_tile = 0; - size_t found_matched_physical_tile_port = 0; std::string required_tile_name = tile_annotation.global_port_tile_names(tile_global_port)[tile_info_id]; @@ -146,6 +145,7 @@ static int check_tile_annotation_conflicts_with_physical_tile( /* Must found a valid port where both port name and port size must * match!!! */ for (const t_sub_tile& sub_tile : physical_tile.sub_tiles) { + size_t found_matched_physical_tile_port = 0; for (const t_physical_tile_port& tile_port : sub_tile.ports) { if (std::string(tile_port.name) != required_tile_port.get_name()) { continue; @@ -167,7 +167,7 @@ static int check_tile_annotation_conflicts_with_physical_tile( } /* Check if port property matches */ - int grid_pin_index = tile_port.absolute_first_pin_index; + int grid_pin_index = sub_tile.sub_tile_to_tile_pin_indices[tile_port.absolute_first_pin_index]; if (tile_port.is_clock != tile_annotation.global_port_is_clock(tile_global_port)) { @@ -215,6 +215,26 @@ static int check_tile_annotation_conflicts_with_physical_tile( found_matched_physical_tile_port++; } + if (0 == found_matched_physical_tile_port) { + VTR_LOGF_ERROR( + __FILE__, __LINE__, + "Tile port '%s.%s[%ld:%ld]' in tile annotation '%s' does not match " + "any physical tile port!\n", + required_tile_name.c_str(), required_tile_port.get_name().c_str(), + required_tile_port.get_lsb(), required_tile_port.get_msb(), + tile_annotation.global_port_name(tile_global_port).c_str()); + num_err++; + } + if (1 < found_matched_physical_tile_port) { + VTR_LOGF_ERROR( + __FILE__, __LINE__, + "Tile port '%s.%s[%ld:%ld]' in tile annotation '%s' match more than " + "1 physical tile port!\n", + required_tile_name.c_str(), required_tile_port.get_name().c_str(), + required_tile_port.get_lsb(), required_tile_port.get_msb(), + tile_annotation.global_port_name(tile_global_port).c_str()); + num_err++; + } } } @@ -228,17 +248,6 @@ static int check_tile_annotation_conflicts_with_physical_tile( tile_annotation.global_port_name(tile_global_port).c_str()); num_err++; } - if (0 == found_matched_physical_tile_port) { - VTR_LOGF_ERROR( - __FILE__, __LINE__, - "Tile port '%s.%s[%ld:%ld]' in tile annotation '%s' does not match " - "any physical tile port!\n", - required_tile_name.c_str(), required_tile_port.get_name().c_str(), - required_tile_port.get_lsb(), required_tile_port.get_msb(), - tile_annotation.global_port_name(tile_global_port).c_str()); - num_err++; - } - /* If we found more than 1 match, error out */ if (1 < found_matched_physical_tile) { VTR_LOGF_ERROR( @@ -249,16 +258,6 @@ static int check_tile_annotation_conflicts_with_physical_tile( tile_annotation.global_port_name(tile_global_port).c_str()); num_err++; } - if (1 < found_matched_physical_tile_port) { - VTR_LOGF_ERROR( - __FILE__, __LINE__, - "Tile port '%s.%s[%ld:%ld]' in tile annotation '%s' match more than " - "1 physical tile port!\n", - required_tile_name.c_str(), required_tile_port.get_name().c_str(), - required_tile_port.get_lsb(), required_tile_port.get_msb(), - tile_annotation.global_port_name(tile_global_port).c_str()); - num_err++; - } } } From 19d4d9a16d6f391dfaa3ebf932887955d60e9f8c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 18 Aug 2023 21:05:26 -0700 Subject: [PATCH 343/391] [core] code format --- .../src/fabric/build_top_module_child_tile_instance.cpp | 8 +++++--- openfpga/src/utils/check_tile_annotation.cpp | 9 ++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) 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 88c0e0731..e8ef67488 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1341,8 +1341,9 @@ static int build_top_module_global_net_for_given_tile_module( return CMD_EXEC_FATAL_ERROR; } grid_pin_start_index = - sub_tile.sub_tile_to_tile_pin_indices[(subtile_index - sub_tile.capacity.low) * sub_tile_num_pins + - tile_port.absolute_first_pin_index]; + sub_tile.sub_tile_to_tile_pin_indices + [(subtile_index - sub_tile.capacity.low) * sub_tile_num_pins + + tile_port.absolute_first_pin_index]; physical_tile_port = tile_port; break; } @@ -1386,7 +1387,8 @@ static int build_top_module_global_net_for_given_tile_module( generate_tile_module_port_name(grid_instance_name, grid_port_name); ModulePortId tile_grid_port_id = module_manager.find_module_port(tile_module, tile_grid_port_name); - VTR_LOG("Finding global port '%s' from grid '%s'\n", tile_grid_port_name.c_str(), tile_module_name.c_str()); + VTR_LOG("Finding global port '%s' from grid '%s'\n", + tile_grid_port_name.c_str(), tile_module_name.c_str()); VTR_ASSERT(true == module_manager.valid_module_port_id( tile_module, tile_grid_port_id)); diff --git a/openfpga/src/utils/check_tile_annotation.cpp b/openfpga/src/utils/check_tile_annotation.cpp index 68097d2af..929a3b24e 100644 --- a/openfpga/src/utils/check_tile_annotation.cpp +++ b/openfpga/src/utils/check_tile_annotation.cpp @@ -167,7 +167,8 @@ static int check_tile_annotation_conflicts_with_physical_tile( } /* Check if port property matches */ - int grid_pin_index = sub_tile.sub_tile_to_tile_pin_indices[tile_port.absolute_first_pin_index]; + int grid_pin_index = sub_tile.sub_tile_to_tile_pin_indices + [tile_port.absolute_first_pin_index]; if (tile_port.is_clock != tile_annotation.global_port_is_clock(tile_global_port)) { @@ -218,7 +219,8 @@ static int check_tile_annotation_conflicts_with_physical_tile( if (0 == found_matched_physical_tile_port) { VTR_LOGF_ERROR( __FILE__, __LINE__, - "Tile port '%s.%s[%ld:%ld]' in tile annotation '%s' does not match " + "Tile port '%s.%s[%ld:%ld]' in tile annotation '%s' does not " + "match " "any physical tile port!\n", required_tile_name.c_str(), required_tile_port.get_name().c_str(), required_tile_port.get_lsb(), required_tile_port.get_msb(), @@ -228,7 +230,8 @@ static int check_tile_annotation_conflicts_with_physical_tile( if (1 < found_matched_physical_tile_port) { VTR_LOGF_ERROR( __FILE__, __LINE__, - "Tile port '%s.%s[%ld:%ld]' in tile annotation '%s' match more than " + "Tile port '%s.%s[%ld:%ld]' in tile annotation '%s' match more " + "than " "1 physical tile port!\n", required_tile_name.c_str(), required_tile_port.get_name().c_str(), required_tile_port.get_lsb(), required_tile_port.get_msb(), From 5ac8919ce029cbc0d7dddb60dc6091bf6463bf11 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 18 Aug 2023 21:37:15 -0700 Subject: [PATCH 344/391] [test] add a new testcase to validate subtile with tile annotations --- ...balTileClk_registerable_io_cc_openfpga.xml | 14 +++ .../regression_test_scripts/basic_reg_test.sh | 1 + .../config/task.conf | 35 ++++++ ...ble_GlobalTileClk_registerable_io_40nm.xml | 116 ++++++++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/global_tile_ports/global_tile_clock_subtile/config/task.conf diff --git a/openfpga_flow/openfpga_arch/k4_N4_40nm_GlobalTileClk_registerable_io_cc_openfpga.xml b/openfpga_flow/openfpga_arch/k4_N4_40nm_GlobalTileClk_registerable_io_cc_openfpga.xml index 8722f8614..0a5978d27 100644 --- a/openfpga_flow/openfpga_arch/k4_N4_40nm_GlobalTileClk_registerable_io_cc_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k4_N4_40nm_GlobalTileClk_registerable_io_cc_openfpga.xml @@ -158,6 +158,13 @@ + + + + + + + @@ -175,6 +182,7 @@ + @@ -186,6 +194,12 @@ + + + + + + diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index 2a3687033..16845c00f 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -198,6 +198,7 @@ run-task basic_tests/group_config_block/group_config_block_hetero_fabric_tile_Ls 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_clock_subtile $@ run-task basic_tests/global_tile_ports/global_tile_reset $@ run-task basic_tests/global_tile_ports/global_tile_4clock $@ run-task basic_tests/global_tile_ports/global_tile_4clock_pin $@ diff --git a/openfpga_flow/tasks/basic_tests/global_tile_ports/global_tile_clock_subtile/config/task.conf b/openfpga_flow/tasks/basic_tests/global_tile_ports/global_tile_clock_subtile/config/task.conf new file mode 100644 index 000000000..d75851ad9 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/global_tile_ports/global_tile_clock_subtile/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/global_tile_clock_full_testbench_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_GlobalTileClk_registerable_io_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout=2x2_hybrid_io + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_GlobalTileClk_registerable_io_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_pipelined/and2_pipelined.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2_pipelined + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_GlobalTileClk_registerable_io_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_GlobalTileClk_registerable_io_40nm.xml index 1f8022119..509187cfb 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileable_GlobalTileClk_registerable_io_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_GlobalTileClk_registerable_io_40nm.xml @@ -34,6 +34,11 @@ + + + + + @@ -55,6 +60,41 @@ + + + + + + + + + + + + io_input.inpad io_input.clk + io_input.inpad io_input.clk + io_input.inpad io_input.clk + io_input.inpad io_input.clk + + + + + + + + + + + + + + io.outpad io.inpad io.clk + io.outpad io.inpad io.clk + io.outpad io.inpad io.clk + io.outpad io.inpad io.clk + + + @@ -87,6 +127,13 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + @@ -316,8 +316,8 @@ - - + + @@ -333,11 +333,11 @@ - + - + From 5f6050d40474c9a55f61828eb2d705134966b895 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 18 Aug 2023 21:48:40 -0700 Subject: [PATCH 346/391] [test] add a new test to validate combo: group tile, tile annotation and subtile --- .../regression_test_scripts/basic_reg_test.sh | 1 + .../config/task.conf | 42 +++++++++++++++++++ .../config/tile_config.xml | 1 + 3 files changed, 44 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/tile_organization/fabric_tile_global_tile_clock_io_subtile/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/tile_organization/fabric_tile_global_tile_clock_io_subtile/config/tile_config.xml diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index 16845c00f..4ec35395f 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -179,6 +179,7 @@ run-task basic_tests/tile_organization/io_subtile_strong $@ echo -e "Testing tile grouping on a homogeneous FPGA fabric (Full testbench)"; run-task basic_tests/tile_organization/homo_fabric_tile $@ echo -e "Testing tile grouping on a homogeneous FPGA fabric (Preconfigured testbench)"; +run-task basic_tests/tile_organization/fabric_tile_global_tile_clock_io_subtile $@ run-task basic_tests/tile_organization/homo_fabric_tile_preconfig $@ run-task basic_tests/tile_organization/homo_fabric_tile_2x2_preconfig $@ run-task basic_tests/tile_organization/homo_fabric_tile_4x4_preconfig $@ diff --git a/openfpga_flow/tasks/basic_tests/tile_organization/fabric_tile_global_tile_clock_io_subtile/config/task.conf b/openfpga_flow/tasks/basic_tests/tile_organization/fabric_tile_global_tile_clock_io_subtile/config/task.conf new file mode 100644 index 000000000..1c477f704 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/tile_organization/fabric_tile_global_tile_clock_io_subtile/config/task.conf @@ -0,0 +1,42 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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_tile_preconfig_testbench_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_GlobalTileClk_registerable_io_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml +openfpga_vpr_extra_options= +openfpga_pb_pin_fixup_command= +openfpga_vpr_device=2x2_hybrid_io +openfpga_vpr_route_chan_width=20 +openfpga_group_tile_config_file=${PATH:TASK_DIR}/config/tile_config.xml +openfpga_verilog_testbench_options=--explicit_port_mapping + + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_GlobalTileClk_registerable_io_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_pipelined/and2_pipelined.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2_pipelined + +[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/tile_organization/fabric_tile_global_tile_clock_io_subtile/config/tile_config.xml b/openfpga_flow/tasks/basic_tests/tile_organization/fabric_tile_global_tile_clock_io_subtile/config/tile_config.xml new file mode 100644 index 000000000..1a1f3f6e8 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/tile_organization/fabric_tile_global_tile_clock_io_subtile/config/tile_config.xml @@ -0,0 +1 @@ + From 15a8d8a76a8be493a88548c540c53b24cfb18cf3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 18 Aug 2023 21:59:06 -0700 Subject: [PATCH 347/391] [test] added a new test to validate combo: group_tile, group_config_block, io subtile, tile annotation --- .../regression_test_scripts/basic_reg_test.sh | 1 + .../config/task.conf | 42 +++++++++++++++++++ .../config/tile_config.xml | 1 + 3 files changed, 44 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_global_tile_clock_io_subtile/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_global_tile_clock_io_subtile/config/tile_config.xml diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index 4ec35395f..a96902f98 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -196,6 +196,7 @@ run-task basic_tests/group_config_block/group_config_block_homo_fabric_tile_Lsha run-task basic_tests/group_config_block/group_config_block_homo_fabric_tile_core_wrapper $@ run-task basic_tests/group_config_block/group_config_block_hetero_fabric_tile $@ run-task basic_tests/group_config_block/group_config_block_hetero_fabric_tile_Lshape $@ +run-task basic_tests/group_config_block/group_config_block_homo_fabric_tile_global_tile_clock_io_subtile $@ echo -e "Testing global port definition from tiles"; run-task basic_tests/global_tile_ports/global_tile_clock $@ diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_global_tile_clock_io_subtile/config/task.conf b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_global_tile_clock_io_subtile/config/task.conf new file mode 100644 index 000000000..a04c046d9 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_global_tile_clock_io_subtile/config/task.conf @@ -0,0 +1,42 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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_N4_40nm_GlobalTileClk_registerable_io_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/fixed_sim_openfpga.xml +openfpga_vpr_extra_options= +openfpga_pb_pin_fixup_command= +openfpga_vpr_device=2x2_hybrid_io +openfpga_vpr_route_chan_width=20 +openfpga_group_tile_config_option=--group_tile ${PATH:TASK_DIR}/config/tile_config.xml +openfpga_verilog_testbench_options=--explicit_port_mapping +openfpga_add_fpga_core_module= + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_GlobalTileClk_registerable_io_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_pipelined/and2_pipelined.v + +[SYNTHESIS_PARAM] +bench_read_verilog_options_common = -nolatches +bench0_top = and2_pipelined + +[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_homo_fabric_tile_global_tile_clock_io_subtile/config/tile_config.xml b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_fabric_tile_global_tile_clock_io_subtile/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_global_tile_clock_io_subtile/config/tile_config.xml @@ -0,0 +1 @@ + From 66cc37599670e93a4c07f31b6bf5b759c32d23bb Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 18 Aug 2023 22:08:47 -0700 Subject: [PATCH 348/391] [core] remove debugging messages --- .../src/fabric/build_top_module_child_fine_grained_instance.cpp | 1 - openfpga/src/fabric/build_top_module_child_tile_instance.cpp | 2 -- 2 files changed, 3 deletions(-) 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 47067a2cb..13c09952f 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 @@ -402,7 +402,6 @@ static void add_top_module_io_children( /* Now walk through the coordinates */ for (vtr::Point coord : coords) { - VTR_LOG("Adding coord [%lu][%lu]\n", coord.x(), coord.y()); t_physical_tile_loc phy_tile_loc(coord.x(), coord.y(), layer); t_physical_tile_type_ptr grid_type = grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY grid */ 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 e8ef67488..dd4669c3f 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1387,8 +1387,6 @@ static int build_top_module_global_net_for_given_tile_module( generate_tile_module_port_name(grid_instance_name, grid_port_name); ModulePortId tile_grid_port_id = module_manager.find_module_port(tile_module, tile_grid_port_name); - VTR_LOG("Finding global port '%s' from grid '%s'\n", - tile_grid_port_name.c_str(), tile_module_name.c_str()); VTR_ASSERT(true == module_manager.valid_module_port_id( tile_module, tile_grid_port_id)); From 5749db5bad2ab7d33d4eae288a614029b00db19e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 19 Aug 2023 17:46:45 +0000 Subject: [PATCH 349/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 2d155e4d7..a5abe3d37 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1428 +1.2.1438 From 9caa4d4bbb00ec3a8744d28088eb7628213695f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 06:28:21 +0000 Subject: [PATCH 350/391] Bump yosys-plugins from `f8daf6d` to `17519a6` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `f8daf6d` to `17519a6`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/f8daf6da2ccaac239fd07aaf4207bdb30087c9e7...17519a6ac96b649112a331364f59b416fe2d6874) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index f8daf6da2..17519a6ac 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit f8daf6da2ccaac239fd07aaf4207bdb30087c9e7 +Subproject commit 17519a6ac96b649112a331364f59b416fe2d6874 From a6d43beacac9200bb84add0a880e168285155481 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 21 Aug 2023 22:25:52 -0700 Subject: [PATCH 351/391] [core] now tile verilog writer supports relative paths --- openfpga/src/fpga_verilog/verilog_api.cpp | 2 +- openfpga/src/fpga_verilog/verilog_tile.cpp | 9 ++++++--- openfpga/src/fpga_verilog/verilog_tile.h | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 68eabe145..921e88176 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -130,7 +130,7 @@ int fpga_fabric_verilog( if (!fabric_tile.empty()) { status_code = print_verilog_tiles( netlist_manager, const_cast(module_manager), - tile_dir_path, fabric_tile, options); + tile_dir_path, fabric_tile, std::string(DEFAULT_TILE_DIR_NAME), options); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } diff --git a/openfpga/src/fpga_verilog/verilog_tile.cpp b/openfpga/src/fpga_verilog/verilog_tile.cpp index 0a3465c4c..400695554 100644 --- a/openfpga/src/fpga_verilog/verilog_tile.cpp +++ b/openfpga/src/fpga_verilog/verilog_tile.cpp @@ -27,7 +27,8 @@ namespace openfpga { static int print_verilog_tile_module_netlist( NetlistManager& netlist_manager, const ModuleManager& module_manager, const std::string& verilog_dir, const FabricTile& fabric_tile, - const FabricTileId& fabric_tile_id, const FabricVerilogOption& options) { + const FabricTileId& fabric_tile_id, const std::string& subckt_dir_name, + const FabricVerilogOption& options) { /* Create a module as the top-level fabric, and add it to the module manager */ vtr::Point tile_coord = fabric_tile.tile_coordinate(fabric_tile_id); @@ -69,10 +70,11 @@ static int print_verilog_tile_module_netlist( /* Add fname to the netlist name list */ NetlistId nlist_id = NetlistId::INVALID(); if (options.use_relative_path()) { - nlist_id = netlist_manager.add_netlist(verilog_fname); + nlist_id = netlist_manager.add_netlist(subckt_dir_name + verilog_fname); } else { nlist_id = netlist_manager.add_netlist(verilog_fpath); } + VTR_ASSERT(nlist_id); netlist_manager.set_netlist_type(nlist_id, NetlistManager::TILE_MODULE_NETLIST); @@ -89,6 +91,7 @@ int print_verilog_tiles(NetlistManager& netlist_manager, const ModuleManager& module_manager, const std::string& verilog_dir, const FabricTile& fabric_tile, + const std::string& subckt_dir_name, const FabricVerilogOption& options) { vtr::ScopedStartFinishTimer timer("Build tile modules for the FPGA fabric"); @@ -98,7 +101,7 @@ int print_verilog_tiles(NetlistManager& netlist_manager, for (FabricTileId fabric_tile_id : fabric_tile.unique_tiles()) { status_code = print_verilog_tile_module_netlist( netlist_manager, module_manager, verilog_dir, fabric_tile, fabric_tile_id, - options); + subckt_dir_name, options); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } diff --git a/openfpga/src/fpga_verilog/verilog_tile.h b/openfpga/src/fpga_verilog/verilog_tile.h index a38886136..03dac11ae 100644 --- a/openfpga/src/fpga_verilog/verilog_tile.h +++ b/openfpga/src/fpga_verilog/verilog_tile.h @@ -22,6 +22,7 @@ int print_verilog_tiles(NetlistManager& netlist_manager, const ModuleManager& module_manager, const std::string& verilog_dir, const FabricTile& fabric_tile, + const std::string& subckt_dir_name, const FabricVerilogOption& options); } /* end namespace openfpga */ From dde9ef04496099d6a4007da230cb4b9fcdc07cc5 Mon Sep 17 00:00:00 2001 From: Kyle Chuang Date: Tue, 22 Aug 2023 15:46:49 +0800 Subject: [PATCH 352/391] [doc] format --- CMakeLists.txt | 144 +++++++++--------- Makefile | 2 +- docs/source/dev_manual/version_number.rst | 2 +- .../tutorials/arch_modeling/quick_start.rst | 2 +- .../getting_started/shell_shortcuts.rst | 2 +- openfpga.sh | 2 +- 6 files changed, 77 insertions(+), 77 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 100351184..ff5475321 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 2.8.12) find_program(CCACHE_FOUND ccache) if(CCACHE_FOUND) - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) - set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) + set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) endif(CCACHE_FOUND) if (${CMAKE_VERSION} VERSION_GREATER "3.8") @@ -92,17 +92,17 @@ set(WITH_PARMYS OFF CACHE BOOL "Enable Yosys as elaborator and parmys-plugin as # TCL file/lib required to link with SWIG generated wrapper if (OPENFPGA_WITH_SWIG) -#Find Tcl - include(FindTCL) - message(STATUS "tcl.h path is : ${TCL_INCLUDE_PATH}") - message(STATUS "libtcl.so path is : ${TCL_LIBRARY}") + #Find Tcl + include(FindTCL) + message(STATUS "tcl.h path is : ${TCL_INCLUDE_PATH}") + message(STATUS "libtcl.so path is : ${TCL_LIBRARY}") -#Find SWIG - find_package(SWIG 3.0 REQUIRED) - if (SWIG_VERSION VERSION_GREATER_EQUAL "4.1.0") - message(WARNING "Using SWIG >= ${SWIG_VERSION} -flatstaticmethod flag for python") - endif() - include(UseSWIG) + #Find SWIG + find_package(SWIG 3.0 REQUIRED) + if (SWIG_VERSION VERSION_GREATER_EQUAL "4.1.0") + message(WARNING "Using SWIG >= ${SWIG_VERSION} -flatstaticmethod flag for python") + endif() + include(UseSWIG) endif() #Compiler flag configuration checks @@ -150,71 +150,71 @@ endif() set(WARN_FLAGS "") if (MSVC) - # Visual studio warnings - # Note that we do not use /Wall since it generates warnings about standard library headers - set(WARN_FLAGS_TO_CHECK # the flags to check if the compiler supports - "/W4" # Most warnings - ) + # Visual studio warnings + # Note that we do not use /Wall since it generates warnings about standard library headers + set(WARN_FLAGS_TO_CHECK # the flags to check if the compiler supports + "/W4" # Most warnings + ) else () - set(WARN_FLAGS_TO_CHECK # the flags to check if the compiler supports - #GCC-like - "-Wall" #Most warnings, typically good - "-Wextra" #Extra warning, usually good - "-Wpedantic" #Ensure ISO compliance (i.e. no non-standard extensions) - "-Wcast-qual" #Warn if cast removes qualifier (e.g. const char* -> char*) - "-Wcast-align" #Warn if a cast causes memory alignment changes - "-Wshadow" #Warn if local variable shadows another variable - "-Wformat=2" #Sanity checks for printf-like formatting - "-Wno-format-nonliteral" # But don't worry about non-literal formtting (i.e. run-time printf format strings) - "-Wlogical-op" #Checks for logical op when bit-wise expected - "-Wmissing-declarations" #Warn if a global function is defined with no declaration - "-Wmissing-include-dirs" #Warn if a user include directory is missing - "-Wredundant-decls" #Warn if there are overlapping declarations - "-Wswitch-default" #Warn if a switch has no default - "-Wundef" #Warn if #if() preprocessor refers to an undefined directive - "-Wunused" #Warn about unused variables/parameters - "-Wunused-variable" #Warn about variables that are not used - "-Wunused-parameter" #Warn about function parameters which are unused - "-Wdisabled-optimization" #Warn when optimizations are skipped (usually due to large/complex code) - "-Wnoexcept" #Warn when functions should be noexcept (i.e. compiler know it doesn't throw) - "-Woverloaded-virtual" #Warn when a function declaration overrides a virtual method - "-Wctor-dtor-privacy" #Warn about inaccessible constructors/destructors - "-Wnon-virtual-dtor" #Warn about missing virtual destructors - "-Wduplicated-cond" #Warn about identical conditions in if-else chains - "-Wduplicated-branches" #Warn when different branches of an if-else chain are equivalent - "-Wnull-dereference" #Warn about null pointer dereference execution paths - "-Wuninitialized" #Warn about unitialized values - "-Winit-self" #Warn about self-initialization - "-Wcatch-value=3" #Warn when catch statements don't catch by reference - "-Wextra-semi" #Warn about redudnant semicolons - "-Wimplicit-fallthrough=3" #Warn about case fallthroughs, but allow 'fallthrough' comments to suppress warnings - #GCC-like optional - #"-Wsuggest-final-types" #Suggest where 'final' would help if specified on a type methods - #"-Wsuggest-final-methods" #Suggest where 'final' would help if specified on methods - #"-Wsuggest-override" #Suggest where 'override' should be specified - #"-Wold-style-cast" #Warn about using c-style casts - #"-Wconversion" #Warn when type conversions may change value - #"-Wsign-conversion" #Warn if a conversion may change the sign - #"-Wpadded" #Will warn if additional padding is introduced to a struct/class. Turn on if optimizing class memory layouts - #"-Wstrict-overflow=2" #Warn if the compiler optimizes assuming signed overflow does not occur - #"-Wfloat-equal" #Warn about using direct floating point equality - #"-Wunsafe-loop-optimizations" #Warn when loops can't be optimized - #"-Wswitch-enum" #Warn about uncovered enumeration values in a switch (even if there is a default) - #"-Wsign-promo" #Warn when overload resolution converts an unsigned type to signed when an unsigned overload exists - #"-Wdouble-promotion" #Warn when float is implicitly propted to double - #"-Wuseless-cast" #Warn about casts to the same type - #"-Wzero-as-null-pointer-constant" #Warn about using '0' instead of nullptr - ) + set(WARN_FLAGS_TO_CHECK # the flags to check if the compiler supports + #GCC-like + "-Wall" #Most warnings, typically good + "-Wextra" #Extra warning, usually good + "-Wpedantic" #Ensure ISO compliance (i.e. no non-standard extensions) + "-Wcast-qual" #Warn if cast removes qualifier (e.g. const char* -> char*) + "-Wcast-align" #Warn if a cast causes memory alignment changes + "-Wshadow" #Warn if local variable shadows another variable + "-Wformat=2" #Sanity checks for printf-like formatting + "-Wno-format-nonliteral" # But don't worry about non-literal formatting (i.e. run-time printf format strings) + "-Wlogical-op" #Checks for logical op when bit-wise expected + "-Wmissing-declarations" #Warn if a global function is defined with no declaration + "-Wmissing-include-dirs" #Warn if a user include directory is missing + "-Wredundant-decls" #Warn if there are overlapping declarations + "-Wswitch-default" #Warn if a switch has no default + "-Wundef" #Warn if #if() preprocessor refers to an undefined directive + "-Wunused" #Warn about unused variables/parameters + "-Wunused-variable" #Warn about variables that are not used + "-Wunused-parameter" #Warn about function parameters which are unused + "-Wdisabled-optimization" #Warn when optimizations are skipped (usually due to large/complex code) + "-Wnoexcept" #Warn when functions should be noexcept (i.e. compiler know it doesn't throw) + "-Woverloaded-virtual" #Warn when a function declaration overrides a virtual method + "-Wctor-dtor-privacy" #Warn about inaccessible constructors/destructors + "-Wnon-virtual-dtor" #Warn about missing virtual destructors + "-Wduplicated-cond" #Warn about identical conditions in if-else chains + "-Wduplicated-branches" #Warn when different branches of an if-else chain are equivalent + "-Wnull-dereference" #Warn about null pointer dereference execution paths + "-Wuninitialized" #Warn about uninitialized values + "-Winit-self" #Warn about self-initialization + "-Wcatch-value=3" #Warn when catch statements don't catch by reference + "-Wextra-semi" #Warn about redundant semicolons + "-Wimplicit-fallthrough=3" #Warn about case fallthroughs, but allow 'fallthrough' comments to suppress warnings + #GCC-like optional + #"-Wsuggest-final-types" #Suggest where 'final' would help if specified on a type methods + #"-Wsuggest-final-methods" #Suggest where 'final' would help if specified on methods + #"-Wsuggest-override" #Suggest where 'override' should be specified + #"-Wold-style-cast" #Warn about using c-style casts + #"-Wconversion" #Warn when type conversions may change value + #"-Wsign-conversion" #Warn if a conversion may change the sign + #"-Wpadded" #Will warn if additional padding is introduced to a struct/class. Turn on if optimizing class memory layouts + #"-Wstrict-overflow=2" #Warn if the compiler optimizes assuming signed overflow does not occur + #"-Wfloat-equal" #Warn about using direct floating point equality + #"-Wunsafe-loop-optimizations" #Warn when loops can't be optimized + #"-Wswitch-enum" #Warn about uncovered enumeration values in a switch (even if there is a default) + #"-Wsign-promo" #Warn when overload resolution converts an unsigned type to signed when an unsigned overload exists + #"-Wdouble-promotion" #Warn when float is implicitly prompted to double + #"-Wuseless-cast" #Warn about casts to the same type + #"-Wzero-as-null-pointer-constant" #Warn about using '0' instead of nullptr + ) endif() # check and see if the compiler supports the various warning flags # and add valid flags foreach (flag ${WARN_FLAGS_TO_CHECK}) - CHECK_CXX_COMPILER_FLAG(${flag} CXX_COMPILER_SUPPORTS_${flag}) - if (CXX_COMPILER_SUPPORTS_${flag}) - # flag supported, so enable it - set (WARN_FLAGS "${WARN_FLAGS} ${flag}") - endif() + CHECK_CXX_COMPILER_FLAG(${flag} CXX_COMPILER_SUPPORTS_${flag}) + if (CXX_COMPILER_SUPPORTS_${flag}) + # flag supported, so enable it + set (WARN_FLAGS "${WARN_FLAGS} ${flag}") + endif() endforeach() #Suppress IPO link warnings @@ -301,7 +301,7 @@ option(YOSYS_ENABLE_NDEBUG, "Enable non-debugging feature in compiled yosys" OFF ## Search and link dependent packages ## We need readline to compile if (YOSYS_ENABLE_READLINE) - find_package(Readline REQUIRED) + find_package(Readline REQUIRED) endif() #PugiXml has some deliberate switch fallthrough cases (as indicated by comments), but they diff --git a/Makefile b/Makefile index abb6e0760..e3d23b82a 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ CMAKE_BUILD_TYPE := $(shell echo ${BUILD_TYPE} | sed 's/_\?pgo//' | sed 's/_\?st # e.g. make CMAKE_FLAGS="-DCMAKE_CXX_COMPILER=g++-9' override CMAKE_FLAGS := -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -G 'Unix Makefiles' ${CMAKE_FLAGS} -# -s : Suppresss makefile output (e.g. entering/leaving directories) +# -s : Suppress makefile output (e.g. entering/leaving directories) # --output-sync target : For parallel compilation ensure output for each target is synchronized (make version >= 4.0) MAKEFLAGS := -s diff --git a/docs/source/dev_manual/version_number.rst b/docs/source/dev_manual/version_number.rst index feabce41f..9e75fa9e8 100644 --- a/docs/source/dev_manual/version_number.rst +++ b/docs/source/dev_manual/version_number.rst @@ -42,6 +42,6 @@ Version updates are made in the following scenario - significant improvements on Quality-of-Results (QoR). - significant changes on user interface. - - a techical feature is developed and validated by the community, which can impact the complete design flow. + - a technical feature is developed and validated by the community, which can impact the complete design flow. diff --git a/docs/source/tutorials/arch_modeling/quick_start.rst b/docs/source/tutorials/arch_modeling/quick_start.rst index 1b31300d1..6f2d87f27 100644 --- a/docs/source/tutorials/arch_modeling/quick_start.rst +++ b/docs/source/tutorials/arch_modeling/quick_start.rst @@ -30,7 +30,7 @@ A summary of the architectural features is as follows: - K4N4 Configurable Logic Block (CLB), which consists of four Basic Logic Elements (BLEs) and a fully-connected crossbar. Each BLE contains a 4-input Look-Up Table (LUT), a Flip-Flop (FF) and a 2:1 routing multiplexer - Length-1 routing wires interconnected by Wilton-Style Switch Block (SB) -The VPR architecture description is designed for EDA needs mainly, which lacks the details physical modelingrequired by OpenFPGA. +The VPR architecture description is designed for EDA needs mainly, which lacks the details physical modeling required by OpenFPGA. Here, we show a step-by-step adaption on the architecture template. Physical I/O Modeling diff --git a/docs/source/tutorials/getting_started/shell_shortcuts.rst b/docs/source/tutorials/getting_started/shell_shortcuts.rst index cb5d01bdc..2079b48aa 100644 --- a/docs/source/tutorials/getting_started/shell_shortcuts.rst +++ b/docs/source/tutorials/getting_started/shell_shortcuts.rst @@ -46,7 +46,7 @@ Once the ``openfpga.sh`` script is sourced, you can run any following commands d .. option:: clear-task-run - Clears all run diretories of the given task + Clears all run directories of the given task .. option:: run-modelsim diff --git a/openfpga.sh b/openfpga.sh index 6d2900a11..aad70bea2 100755 --- a/openfpga.sh +++ b/openfpga.sh @@ -132,7 +132,7 @@ goto-task () { done } -# Clears enviroment variables and fucntions +# Clears environment variables and functions unset-openfpga (){ unset -v OPENFPGA_PATH unset -f list-tasks run-task run-flow goto-task goto-root >/dev/null 2>&1 From 972ccd2d83efb43e971050a3f597500cc9eb5fb0 Mon Sep 17 00:00:00 2001 From: Kyle Chuang Date: Tue, 22 Aug 2023 15:51:00 +0800 Subject: [PATCH 353/391] [doc] format --- docs/source/dev_manual/cicd_setup.rst | 18 +++++++++--------- .../manual/file_formats/fabric_bitstream.rst | 4 ++-- docs/source/manual/file_formats/index.rst | 2 +- docs/source/manual/fpga_verilog/testbench.rst | 9 ++++----- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/source/dev_manual/cicd_setup.rst b/docs/source/dev_manual/cicd_setup.rst index 773e07efb..92609ccd3 100644 --- a/docs/source/dev_manual/cicd_setup.rst +++ b/docs/source/dev_manual/cicd_setup.rst @@ -44,7 +44,7 @@ in which case the docker image compiled for the latest master branch is used for ]; RunRegression [ - label = "Run functional regeression test" + label = "Run functional regression test" shape = box ]; @@ -69,7 +69,7 @@ in which case the docker image compiled for the latest master branch is used for .. option:: Build regression test - The OpenFPGA soure is compiled with the following set of compilers. + The OpenFPGA source is compiled with the following set of compilers. #. gcc-7 #. gcc-8 @@ -81,9 +81,9 @@ in which case the docker image compiled for the latest master branch is used for #. clang-8 #. clang-10 - The docker images for these build enviroment are available on `github packages `_. + The docker images for these build environment are available on `github packages `_. -.. option:: Functional regeression test +.. option:: Functional regression test OpenFPGA maintains a set of functional tests to validate the different functionality. The test are broadly catagories into ``basic_reg_test``, ``fpga_verilog_reg_test``, @@ -93,7 +93,7 @@ in which case the docker image compiled for the latest master branch is used for How to debug failed regression test ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In case the ``funtional regression test`` fails, +In case the ``functional regression test`` fails, the actions script will collect all ``.log`` files from the task directory and upload as a artifacts on github storage. These artifacts can be downloaded from the github website actions tab, for more reference follow `this `_ article. @@ -113,11 +113,11 @@ Release Docker Images CI after cloning repository ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you clone the repository the CI setup will still function, except the based images are still pullled from "lnis-uofu" repsitory and the master branch +If you clone the repository the CI setup will still function, except the based images are still pulled from "lnis-uofu" repository and the master branch of cloned repo will not push final docker image to any repository . -**In case you want to host your own copies of OpenFPGA base images** and final release create a githib secret variable with name ``DOCKER_REPO`` and set it to ``true``. This will make ci script to download base images from your own repo pakcages, and upload final realse to the same. +**In case you want to host your own copies of OpenFPGA base images** and final release create a github secret variable with name ``DOCKER_REPO`` and set it to ``true``. This will make ci script to download base images from your own repo packages, and upload final release to the same. -**If you don not want to use docker images based regression test** and like to compile all the bianries for each CI run. You can set ``IGNORE_DOCKER_TEST`` secrete variable to ``true``. +**If you don not want to use docker images based regression test** and like to compile all the binaries for each CI run. You can set ``IGNORE_DOCKER_TEST`` secrete variable to ``true``. -.. note:: Once you add ``DOCKER_REPO`` variable, you need to genrerate base images. To do this trigger mannual workflow ``Build docker CI images`` +.. note:: Once you add ``DOCKER_REPO`` variable, you need to generate base images. To do this trigger manual workflow ``Build docker CI images`` diff --git a/docs/source/manual/file_formats/fabric_bitstream.rst b/docs/source/manual/file_formats/fabric_bitstream.rst index 6cd8085de..eb094d53b 100644 --- a/docs/source/manual/file_formats/fabric_bitstream.rst +++ b/docs/source/manual/file_formats/fabric_bitstream.rst @@ -11,7 +11,7 @@ Plain text (.bit) This file format is designed to be directly loaded to an FPGA fabric. It does not include any comments but only bitstream. -The information depends on the type of configuration procotol. +The information depends on the type of configuration protocol. .. option:: vanilla @@ -232,7 +232,7 @@ A quick example: -Other information may depend on the type of configuration procotol. +Other information may depend on the type of configuration protocol. .. option:: memory_bank diff --git a/docs/source/manual/file_formats/index.rst b/docs/source/manual/file_formats/index.rst index 0cd489293..6a30c2b8b 100644 --- a/docs/source/manual/file_formats/index.rst +++ b/docs/source/manual/file_formats/index.rst @@ -1,7 +1,7 @@ File Formats ------------ -OpenFPGA widely uses XML format for interchangable files +OpenFPGA widely uses XML format for interchangeable files .. _file_formats: diff --git a/docs/source/manual/fpga_verilog/testbench.rst b/docs/source/manual/fpga_verilog/testbench.rst index f4c084620..70e9b5f07 100644 --- a/docs/source/manual/fpga_verilog/testbench.rst +++ b/docs/source/manual/fpga_verilog/testbench.rst @@ -37,7 +37,7 @@ To enable self-testing, the FPGA and user's RTL design (simulate using an HDL si Full Testbench ~~~~~~~~~~~~~~ -Full testbench aims at simulating an entire FPGA operating period, consisting of two phases: +Full testbench aims at simulating an entire FPGA operating period, consisting of two phases: - the **Configuration Phase**, where the synthesized design bitstream is loaded to the programmable fabric, as highlighted by the green rectangle of :numref:`fig_verilog_full_testbench_waveform`; @@ -63,7 +63,7 @@ Inside the directory, the Verilog testbenches are organized as illustrated in :n Hierarchy of Verilog testbenches for a FPGA fabric implemented with an application -.. note:: ```` is the module name of users' RTL design. +.. note:: ```` is the module name of users' RTL design. .. option:: _include_netlist.v @@ -84,9 +84,9 @@ Inside the directory, the Verilog testbenches are organized as illustrated in :n .. option:: _top_formal_verification.v This netlist includes a Verilog module of a pre-configured FPGA fabric, which is a wrapper on top of the ``fpga_top.v`` netlist. - The wrapper module has the same port map as the top-level module of user's RTL design, which be directly def to formal verification tools to validate FPGA's functional equivalence. + The wrapper module has the same port map as the top-level module of user's RTL design, which be directly def to formal verification tools to validate FPGA's functional equivalence. :numref:`fig_preconfig_module` illustrates the organization of a pre-configured module, which consists of a FPGA fabric (see :ref:`fabric_netlists`) and a hard-coded bitstream. - Only used I/Os of FPGA fabric will appear in the port list of the pre-configured module. + Only used I/Os of FPGA fabric will appear in the port list of the pre-configured module. .. _fig_preconfig_module: @@ -94,4 +94,3 @@ Inside the directory, the Verilog testbenches are organized as illustrated in :n :width: 100% Internal structure of a pre-configured FPGA module - From 34f9067a2e68b84d2b0920c7d64ba343e2fffb28 Mon Sep 17 00:00:00 2001 From: Kyle Chuang Date: Tue, 22 Aug 2023 15:51:37 +0800 Subject: [PATCH 354/391] [doc] format --- .../manual/fpga_spice/command_line_usage.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/source/manual/fpga_spice/command_line_usage.rst b/docs/source/manual/fpga_spice/command_line_usage.rst index e7c261ab3..2592d2bb5 100644 --- a/docs/source/manual/fpga_spice/command_line_usage.rst +++ b/docs/source/manual/fpga_spice/command_line_usage.rst @@ -23,16 +23,16 @@ FPGA-SPICE Supported Options:: --fpga_spice_testbench_load_extraction_off --fpga_spice_sim_mt_num -.. note:: FPGA-SPICE requires the input of activity estimation results (\*.act file) from ACE2. - Remember to use the option --activity_file to read the activity file. +.. note:: FPGA-SPICE requires the input of activity estimation results (\*.act file) from ACE2. + Remember to use the option --activity_file to read the activity file. -.. note:: To dump full-chip-level testbenches, the option –-fpga_spice_print_top_testbench should be enabled. - -.. note:: To dump grid-level testbenches, the options -- fpga_spice_print_grid_testbench, -- fpga_spice_print_cb_testbench and -- fpga_spice_print_sb_testbench should be enabled. - -.. note:: To dump component-level testbenches, the options –fpga_spice_print_lut_testbench, --fpga_spice_print_hardlogic_testbench, --fpga_spice_print_pb_mux_testbench, --fpga_spice_print_cb_mux_testbench and --fpga_spice_print_sb_mux_testbench should be enabled. +.. note:: To dump full-chip-level testbenches, the option –-fpga_spice_print_top_testbench should be enabled. -.. csv-table:: Commmand-line Options of FPGA-SPICE +.. note:: To dump grid-level testbenches, the options -- fpga_spice_print_grid_testbench, -- fpga_spice_print_cb_testbench and -- fpga_spice_print_sb_testbench should be enabled. + +.. note:: To dump component-level testbenches, the options –fpga_spice_print_lut_testbench, --fpga_spice_print_hardlogic_testbench, --fpga_spice_print_pb_mux_testbench, --fpga_spice_print_cb_mux_testbench and --fpga_spice_print_sb_mux_testbench should be enabled. + +.. csv-table:: Command-line Options of FPGA-SPICE :header: "Command Options", "Description" :widths: 15, 20 From 1b132fd667945ad3ca9d207072393380e585d082 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Aug 2023 11:06:12 -0700 Subject: [PATCH 355/391] [test] add a new testcase to validate the support on different routing channel width on X and Y --- .../regression_test_scripts/basic_reg_test.sh | 2 + .../k4_series/k4n4_chandistr/config/task.conf | 38 + ...der_chain_mem1K_L124_ChanWidth0p8_40nm.xml | 723 ++++++++++++++++++ 3 files changed, 763 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/k4_series/k4n4_chandistr/config/task.conf create mode 100644 openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124_ChanWidth0p8_40nm.xml diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index a96902f98..630de10ba 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -147,6 +147,8 @@ echo -e "Testing K4N4 with LUTRAM"; run-task basic_tests/k4_series/k4n4_lutram $@ echo -e "Testing K4N4 with multiple lengths of routing segments"; run-task basic_tests/k4_series/k4n4_L124 $@ +echo -e "Testing K4N4 with routing channel width distribution: x = 0.8, y = 1.0"; +run-task basic_tests/k4_series/k4n4_chandistr $@ echo -e "Testing K4N4 with 32-bit fracturable multiplier"; run-task basic_tests/k4_series/k4n4_frac_mult $@ echo -e "Testing K4N5 with pattern based local routing"; diff --git a/openfpga_flow/tasks/basic_tests/k4_series/k4n4_chandistr/config/task.conf b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_chandistr/config/task.conf new file mode 100644 index 000000000..99478308c --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_chandistr/config/task.conf @@ -0,0 +1,38 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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=vpr_blif + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_L124_40nm_frame_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout=4x4 +openfpga_vpr_route_chan_width=40 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124_ChanWidth0p8_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.blif + +[SYNTHESIS_PARAM] +bench0_top = and2 +bench0_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.act +bench0_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= +vpr_fpga_verilog_formal_verification_top_netlist= diff --git a/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124_ChanWidth0p8_40nm.xml b/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124_ChanWidth0p8_40nm.xml new file mode 100644 index 000000000..35d56c98f --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124_ChanWidth0p8_40nm.xml @@ -0,0 +1,723 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + + + + + + + + + + + + + + + + + + + + clb.clk + clb.cin + clb.O[3:0] clb.I[5:0] + clb.cout clb.O[7:4] clb.I[11:6] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 + 1 + + + + 1 1 1 + 1 1 + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 195e-12 + 195e-12 + 195e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 56cedf6c8b84a4c446e19103ba272b2b4e1f3163 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Aug 2023 11:20:14 -0700 Subject: [PATCH 356/391] [test] added a new test case to validate the support on different wire segment distribution on X and Y --- ...n_mem1K_L124X_L12Y_40nm_frame_openfpga.xml | 277 +++++++ .../regression_test_scripts/basic_reg_test.sh | 2 + .../k4n4_chandistr_segdist/config/task.conf | 38 + ...ain_mem1K_L124X_L12Y_ChanWidth0p8_40nm.xml | 733 ++++++++++++++++++ 4 files changed, 1050 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_L124X_L12Y_40nm_frame_openfpga.xml create mode 100644 openfpga_flow/tasks/basic_tests/k4_series/k4n4_chandistr_segdist/config/task.conf create mode 100644 openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124X_L12Y_ChanWidth0p8_40nm.xml diff --git a/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_L124X_L12Y_40nm_frame_openfpga.xml b/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_L124X_L12Y_40nm_frame_openfpga.xml new file mode 100644 index 000000000..f489b97f5 --- /dev/null +++ b/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_L124X_L12Y_40nm_frame_openfpga.xml @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + + + + 10e-12 5e-12 + + + 10e-12 5e-12 + + + + + + + + + + + + + 10e-12 5e-12 5e-12 + + + 10e-12 5e-12 5e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index 630de10ba..749765a33 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -149,6 +149,8 @@ echo -e "Testing K4N4 with multiple lengths of routing segments"; run-task basic_tests/k4_series/k4n4_L124 $@ echo -e "Testing K4N4 with routing channel width distribution: x = 0.8, y = 1.0"; run-task basic_tests/k4_series/k4n4_chandistr $@ +echo -e "Testing K4N4 with routing channel width distribution: x = 0.8, y = 1.0 and wire segment distribution: x=L124, Y=L12"; +run-task basic_tests/k4_series/k4n4_chandistr_segdist $@ echo -e "Testing K4N4 with 32-bit fracturable multiplier"; run-task basic_tests/k4_series/k4n4_frac_mult $@ echo -e "Testing K4N5 with pattern based local routing"; diff --git a/openfpga_flow/tasks/basic_tests/k4_series/k4n4_chandistr_segdist/config/task.conf b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_chandistr_segdist/config/task.conf new file mode 100644 index 000000000..91eda2fd4 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_chandistr_segdist/config/task.conf @@ -0,0 +1,38 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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=vpr_blif + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_route_chan_width_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_L124X_L12Y_40nm_frame_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout=4x4 +openfpga_vpr_route_chan_width=40 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124X_L12Y_ChanWidth0p8_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.blif + +[SYNTHESIS_PARAM] +bench0_top = and2 +bench0_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.act +bench0_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= +vpr_fpga_verilog_formal_verification_top_netlist= diff --git a/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124X_L12Y_ChanWidth0p8_40nm.xml b/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124X_L12Y_ChanWidth0p8_40nm.xml new file mode 100644 index 000000000..095a82096 --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124X_L12Y_ChanWidth0p8_40nm.xml @@ -0,0 +1,733 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + + + + + + + + + + + + + + + + + + + + clb.clk + clb.cin + clb.O[3:0] clb.I[5:0] + clb.cout clb.O[7:4] clb.I[11:6] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 + 1 + + + + 1 1 1 + 1 1 + + + + 1 1 1 1 1 + 1 1 1 1 + + + + 1 1 + 1 + + + + 1 1 1 + 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 195e-12 + 195e-12 + 195e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 1ee46523233438eb0c2f98d728abc46ed63388f6 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Aug 2023 15:15:55 -0700 Subject: [PATCH 357/391] [lib] update vtr which supports different x y axis in segments --- vtr-verilog-to-routing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtr-verilog-to-routing b/vtr-verilog-to-routing index 62fa48399..ec634df07 160000 --- a/vtr-verilog-to-routing +++ b/vtr-verilog-to-routing @@ -1 +1 @@ -Subproject commit 62fa48399eabb8788844ad852fca6cdec800accf +Subproject commit ec634df07a5be186a0e7b66f2789c4daf2e4b44c From db0bb291c2c6b11909ccc7da4461bd178897c664 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Aug 2023 15:22:48 -0700 Subject: [PATCH 358/391] [test] update settings --- .../k4_series/k4n4_chandistr_segdist/config/task.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/tasks/basic_tests/k4_series/k4n4_chandistr_segdist/config/task.conf b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_chandistr_segdist/config/task.conf index 91eda2fd4..e948a73b5 100644 --- a/openfpga_flow/tasks/basic_tests/k4_series/k4n4_chandistr_segdist/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_chandistr_segdist/config/task.conf @@ -20,7 +20,7 @@ openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scrip openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_L124X_L12Y_40nm_frame_openfpga.xml openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml openfpga_vpr_device_layout=4x4 -openfpga_vpr_route_chan_width=40 +openfpga_vpr_route_chan_width=60 [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124X_L12Y_ChanWidth0p8_40nm.xml From fe69bbf57f12fb1f2e4d5c9621e80fae6c484a05 Mon Sep 17 00:00:00 2001 From: Kyle Chuang Date: Wed, 23 Aug 2023 14:22:34 +0800 Subject: [PATCH 359/391] Revert CMakeLists.txt from tangxifan suggestion --- CMakeLists.txt | 144 ++++++++++++++++++++++++------------------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ff5475321..100351184 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 2.8.12) find_program(CCACHE_FOUND ccache) if(CCACHE_FOUND) - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) - set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) + set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) endif(CCACHE_FOUND) if (${CMAKE_VERSION} VERSION_GREATER "3.8") @@ -92,17 +92,17 @@ set(WITH_PARMYS OFF CACHE BOOL "Enable Yosys as elaborator and parmys-plugin as # TCL file/lib required to link with SWIG generated wrapper if (OPENFPGA_WITH_SWIG) - #Find Tcl - include(FindTCL) - message(STATUS "tcl.h path is : ${TCL_INCLUDE_PATH}") - message(STATUS "libtcl.so path is : ${TCL_LIBRARY}") +#Find Tcl + include(FindTCL) + message(STATUS "tcl.h path is : ${TCL_INCLUDE_PATH}") + message(STATUS "libtcl.so path is : ${TCL_LIBRARY}") - #Find SWIG - find_package(SWIG 3.0 REQUIRED) - if (SWIG_VERSION VERSION_GREATER_EQUAL "4.1.0") - message(WARNING "Using SWIG >= ${SWIG_VERSION} -flatstaticmethod flag for python") - endif() - include(UseSWIG) +#Find SWIG + find_package(SWIG 3.0 REQUIRED) + if (SWIG_VERSION VERSION_GREATER_EQUAL "4.1.0") + message(WARNING "Using SWIG >= ${SWIG_VERSION} -flatstaticmethod flag for python") + endif() + include(UseSWIG) endif() #Compiler flag configuration checks @@ -150,71 +150,71 @@ endif() set(WARN_FLAGS "") if (MSVC) - # Visual studio warnings - # Note that we do not use /Wall since it generates warnings about standard library headers - set(WARN_FLAGS_TO_CHECK # the flags to check if the compiler supports - "/W4" # Most warnings - ) + # Visual studio warnings + # Note that we do not use /Wall since it generates warnings about standard library headers + set(WARN_FLAGS_TO_CHECK # the flags to check if the compiler supports + "/W4" # Most warnings + ) else () - set(WARN_FLAGS_TO_CHECK # the flags to check if the compiler supports - #GCC-like - "-Wall" #Most warnings, typically good - "-Wextra" #Extra warning, usually good - "-Wpedantic" #Ensure ISO compliance (i.e. no non-standard extensions) - "-Wcast-qual" #Warn if cast removes qualifier (e.g. const char* -> char*) - "-Wcast-align" #Warn if a cast causes memory alignment changes - "-Wshadow" #Warn if local variable shadows another variable - "-Wformat=2" #Sanity checks for printf-like formatting - "-Wno-format-nonliteral" # But don't worry about non-literal formatting (i.e. run-time printf format strings) - "-Wlogical-op" #Checks for logical op when bit-wise expected - "-Wmissing-declarations" #Warn if a global function is defined with no declaration - "-Wmissing-include-dirs" #Warn if a user include directory is missing - "-Wredundant-decls" #Warn if there are overlapping declarations - "-Wswitch-default" #Warn if a switch has no default - "-Wundef" #Warn if #if() preprocessor refers to an undefined directive - "-Wunused" #Warn about unused variables/parameters - "-Wunused-variable" #Warn about variables that are not used - "-Wunused-parameter" #Warn about function parameters which are unused - "-Wdisabled-optimization" #Warn when optimizations are skipped (usually due to large/complex code) - "-Wnoexcept" #Warn when functions should be noexcept (i.e. compiler know it doesn't throw) - "-Woverloaded-virtual" #Warn when a function declaration overrides a virtual method - "-Wctor-dtor-privacy" #Warn about inaccessible constructors/destructors - "-Wnon-virtual-dtor" #Warn about missing virtual destructors - "-Wduplicated-cond" #Warn about identical conditions in if-else chains - "-Wduplicated-branches" #Warn when different branches of an if-else chain are equivalent - "-Wnull-dereference" #Warn about null pointer dereference execution paths - "-Wuninitialized" #Warn about uninitialized values - "-Winit-self" #Warn about self-initialization - "-Wcatch-value=3" #Warn when catch statements don't catch by reference - "-Wextra-semi" #Warn about redundant semicolons - "-Wimplicit-fallthrough=3" #Warn about case fallthroughs, but allow 'fallthrough' comments to suppress warnings - #GCC-like optional - #"-Wsuggest-final-types" #Suggest where 'final' would help if specified on a type methods - #"-Wsuggest-final-methods" #Suggest where 'final' would help if specified on methods - #"-Wsuggest-override" #Suggest where 'override' should be specified - #"-Wold-style-cast" #Warn about using c-style casts - #"-Wconversion" #Warn when type conversions may change value - #"-Wsign-conversion" #Warn if a conversion may change the sign - #"-Wpadded" #Will warn if additional padding is introduced to a struct/class. Turn on if optimizing class memory layouts - #"-Wstrict-overflow=2" #Warn if the compiler optimizes assuming signed overflow does not occur - #"-Wfloat-equal" #Warn about using direct floating point equality - #"-Wunsafe-loop-optimizations" #Warn when loops can't be optimized - #"-Wswitch-enum" #Warn about uncovered enumeration values in a switch (even if there is a default) - #"-Wsign-promo" #Warn when overload resolution converts an unsigned type to signed when an unsigned overload exists - #"-Wdouble-promotion" #Warn when float is implicitly prompted to double - #"-Wuseless-cast" #Warn about casts to the same type - #"-Wzero-as-null-pointer-constant" #Warn about using '0' instead of nullptr - ) + set(WARN_FLAGS_TO_CHECK # the flags to check if the compiler supports + #GCC-like + "-Wall" #Most warnings, typically good + "-Wextra" #Extra warning, usually good + "-Wpedantic" #Ensure ISO compliance (i.e. no non-standard extensions) + "-Wcast-qual" #Warn if cast removes qualifier (e.g. const char* -> char*) + "-Wcast-align" #Warn if a cast causes memory alignment changes + "-Wshadow" #Warn if local variable shadows another variable + "-Wformat=2" #Sanity checks for printf-like formatting + "-Wno-format-nonliteral" # But don't worry about non-literal formtting (i.e. run-time printf format strings) + "-Wlogical-op" #Checks for logical op when bit-wise expected + "-Wmissing-declarations" #Warn if a global function is defined with no declaration + "-Wmissing-include-dirs" #Warn if a user include directory is missing + "-Wredundant-decls" #Warn if there are overlapping declarations + "-Wswitch-default" #Warn if a switch has no default + "-Wundef" #Warn if #if() preprocessor refers to an undefined directive + "-Wunused" #Warn about unused variables/parameters + "-Wunused-variable" #Warn about variables that are not used + "-Wunused-parameter" #Warn about function parameters which are unused + "-Wdisabled-optimization" #Warn when optimizations are skipped (usually due to large/complex code) + "-Wnoexcept" #Warn when functions should be noexcept (i.e. compiler know it doesn't throw) + "-Woverloaded-virtual" #Warn when a function declaration overrides a virtual method + "-Wctor-dtor-privacy" #Warn about inaccessible constructors/destructors + "-Wnon-virtual-dtor" #Warn about missing virtual destructors + "-Wduplicated-cond" #Warn about identical conditions in if-else chains + "-Wduplicated-branches" #Warn when different branches of an if-else chain are equivalent + "-Wnull-dereference" #Warn about null pointer dereference execution paths + "-Wuninitialized" #Warn about unitialized values + "-Winit-self" #Warn about self-initialization + "-Wcatch-value=3" #Warn when catch statements don't catch by reference + "-Wextra-semi" #Warn about redudnant semicolons + "-Wimplicit-fallthrough=3" #Warn about case fallthroughs, but allow 'fallthrough' comments to suppress warnings + #GCC-like optional + #"-Wsuggest-final-types" #Suggest where 'final' would help if specified on a type methods + #"-Wsuggest-final-methods" #Suggest where 'final' would help if specified on methods + #"-Wsuggest-override" #Suggest where 'override' should be specified + #"-Wold-style-cast" #Warn about using c-style casts + #"-Wconversion" #Warn when type conversions may change value + #"-Wsign-conversion" #Warn if a conversion may change the sign + #"-Wpadded" #Will warn if additional padding is introduced to a struct/class. Turn on if optimizing class memory layouts + #"-Wstrict-overflow=2" #Warn if the compiler optimizes assuming signed overflow does not occur + #"-Wfloat-equal" #Warn about using direct floating point equality + #"-Wunsafe-loop-optimizations" #Warn when loops can't be optimized + #"-Wswitch-enum" #Warn about uncovered enumeration values in a switch (even if there is a default) + #"-Wsign-promo" #Warn when overload resolution converts an unsigned type to signed when an unsigned overload exists + #"-Wdouble-promotion" #Warn when float is implicitly propted to double + #"-Wuseless-cast" #Warn about casts to the same type + #"-Wzero-as-null-pointer-constant" #Warn about using '0' instead of nullptr + ) endif() # check and see if the compiler supports the various warning flags # and add valid flags foreach (flag ${WARN_FLAGS_TO_CHECK}) - CHECK_CXX_COMPILER_FLAG(${flag} CXX_COMPILER_SUPPORTS_${flag}) - if (CXX_COMPILER_SUPPORTS_${flag}) - # flag supported, so enable it - set (WARN_FLAGS "${WARN_FLAGS} ${flag}") - endif() + CHECK_CXX_COMPILER_FLAG(${flag} CXX_COMPILER_SUPPORTS_${flag}) + if (CXX_COMPILER_SUPPORTS_${flag}) + # flag supported, so enable it + set (WARN_FLAGS "${WARN_FLAGS} ${flag}") + endif() endforeach() #Suppress IPO link warnings @@ -301,7 +301,7 @@ option(YOSYS_ENABLE_NDEBUG, "Enable non-debugging feature in compiled yosys" OFF ## Search and link dependent packages ## We need readline to compile if (YOSYS_ENABLE_READLINE) - find_package(Readline REQUIRED) + find_package(Readline REQUIRED) endif() #PugiXml has some deliberate switch fallthrough cases (as indicated by comments), but they From c7fb73817e5a9f55a5928b0060791c33d24d5758 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 24 Aug 2023 00:02:24 +0000 Subject: [PATCH 360/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index a5abe3d37..b6f41377e 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1438 +1.2.1455 From 92f92658c9fab330b7f34d67fbb883d15816bc7c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 25 Aug 2023 11:53:49 -0700 Subject: [PATCH 361/391] [core] remove useless errors --- openfpga/src/annotation/fabric_tile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index b9c6eaeec..6357f9813 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -400,7 +400,7 @@ bool FabricTile::register_pb_in_lookup(const FabricTileId& tile_id, return false; } /* Throw error if this coord is already registered! */ - if (pb_coord2id_lookup_[coord.x()][coord.y()]) { + if (pb_coord2id_lookup_[coord.x()][coord.y()] && pb_coord2id_lookup_[coord.x()][coord.y()] != tile_id) { VTR_LOG_ERROR( "Programmable block at [%lu][%lu] has already been registered!\n", coord.x(), coord.y()); From 55e5f738ce55f1ece133ba36f454b97f5189391e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 25 Aug 2023 11:58:15 -0700 Subject: [PATCH 362/391] [core] code format --- openfpga/src/annotation/fabric_tile.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openfpga/src/annotation/fabric_tile.cpp b/openfpga/src/annotation/fabric_tile.cpp index 6357f9813..367fa5cef 100644 --- a/openfpga/src/annotation/fabric_tile.cpp +++ b/openfpga/src/annotation/fabric_tile.cpp @@ -400,7 +400,8 @@ bool FabricTile::register_pb_in_lookup(const FabricTileId& tile_id, return false; } /* Throw error if this coord is already registered! */ - if (pb_coord2id_lookup_[coord.x()][coord.y()] && pb_coord2id_lookup_[coord.x()][coord.y()] != tile_id) { + if (pb_coord2id_lookup_[coord.x()][coord.y()] && + pb_coord2id_lookup_[coord.x()][coord.y()] != tile_id) { VTR_LOG_ERROR( "Programmable block at [%lu][%lu] has already been registered!\n", coord.x(), coord.y()); From f4fb470420ba591386670a4f22c7450b3a98b081 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 25 Aug 2023 14:51:45 -0700 Subject: [PATCH 363/391] [lib] update vtr --- vtr-verilog-to-routing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtr-verilog-to-routing b/vtr-verilog-to-routing index ec634df07..4014b3297 160000 --- a/vtr-verilog-to-routing +++ b/vtr-verilog-to-routing @@ -1 +1 @@ -Subproject commit ec634df07a5be186a0e7b66f2789c4daf2e4b44c +Subproject commit 4014b3297d8cd3e12d52a44a42d1809ea4147729 From 89b392a51ff930a51cd46f4bf0616cbbff663798 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 25 Aug 2023 15:13:00 -0700 Subject: [PATCH 364/391] [core] adapt changes in is_sb_exist() --- openfpga/src/annotation/annotate_rr_graph.cpp | 1 - openfpga/src/annotation/device_rr_gsb.cpp | 4 ++-- openfpga/src/annotation/device_rr_gsb.h | 2 +- openfpga/src/fabric/build_device_module.cpp | 2 +- openfpga/src/fabric/build_fabric_tile.cpp | 12 +++++++----- openfpga/src/fabric/build_fabric_tile.h | 2 +- openfpga/src/fabric/build_routing_modules.cpp | 2 +- openfpga/src/fabric/build_tile_modules.cpp | 4 ++-- ...uild_top_module_child_fine_grained_instance.cpp | 7 ++++--- .../build_top_module_child_tile_instance.cpp | 4 ++-- .../src/fabric/build_top_module_connection.cpp | 6 +++--- openfpga/src/fabric/build_top_module_memory.cpp | 10 +++++----- openfpga/src/fabric/build_top_module_memory.h | 3 ++- .../src/fpga_bitstream/build_routing_bitstream.cpp | 2 +- .../src/fpga_sdc/analysis_sdc_routing_writer.cpp | 2 +- openfpga/src/fpga_sdc/pnr_sdc_routing_writer.cpp | 4 ++-- openfpga/src/fpga_sdc/pnr_sdc_writer.cpp | 14 +++++++------- openfpga/src/fpga_sdc/sdc_hierarchy_writer.cpp | 5 +++-- openfpga/src/fpga_sdc/sdc_hierarchy_writer.h | 3 ++- openfpga/src/fpga_spice/spice_api.cpp | 2 +- openfpga/src/fpga_spice/spice_routing.cpp | 3 ++- openfpga/src/fpga_spice/spice_routing.h | 2 ++ openfpga/src/fpga_verilog/verilog_api.cpp | 2 +- openfpga/src/fpga_verilog/verilog_routing.cpp | 3 ++- openfpga/src/fpga_verilog/verilog_routing.h | 2 ++ openfpga/src/utils/device_rr_gsb_utils.cpp | 8 ++++---- openfpga/src/utils/device_rr_gsb_utils.h | 5 +++-- 27 files changed, 64 insertions(+), 52 deletions(-) diff --git a/openfpga/src/annotation/annotate_rr_graph.cpp b/openfpga/src/annotation/annotate_rr_graph.cpp index b58dbfd8d..3a904aefd 100644 --- a/openfpga/src/annotation/annotate_rr_graph.cpp +++ b/openfpga/src/annotation/annotate_rr_graph.cpp @@ -436,7 +436,6 @@ void annotate_device_rr_gsb(const DeviceContext& vpr_device_ctx, vtr::Point(vpr_device_ctx.grid.width() - 2, vpr_device_ctx.grid.height() - 2), layer, vtr::Point(ix, iy), include_clock); - /* Add to device_rr_gsb */ vtr::Point gsb_coordinate = rr_gsb.get_sb_coordinate(); device_rr_gsb.add_rr_gsb(gsb_coordinate, rr_gsb); diff --git a/openfpga/src/annotation/device_rr_gsb.cpp b/openfpga/src/annotation/device_rr_gsb.cpp index 7374ab793..131533eca 100644 --- a/openfpga/src/annotation/device_rr_gsb.cpp +++ b/openfpga/src/annotation/device_rr_gsb.cpp @@ -78,7 +78,7 @@ size_t DeviceRRGSB::get_num_cb_unique_module(const t_rr_type& cb_type) const { } /* Identify if a GSB actually exists at a location */ -bool DeviceRRGSB::is_gsb_exist(const vtr::Point coord) const { +bool DeviceRRGSB::is_gsb_exist(const RRGraphView& rr_graph, const vtr::Point coord) const { /* Out of range, does not exist */ if (false == validate_coordinate(coord)) { return false; @@ -93,7 +93,7 @@ bool DeviceRRGSB::is_gsb_exist(const vtr::Point coord) const { return true; } - if (true == get_gsb(coord).is_sb_exist()) { + if (true == get_gsb(coord).is_sb_exist(rr_graph)) { return true; } diff --git a/openfpga/src/annotation/device_rr_gsb.h b/openfpga/src/annotation/device_rr_gsb.h index bd190cec5..c08cde3e8 100644 --- a/openfpga/src/annotation/device_rr_gsb.h +++ b/openfpga/src/annotation/device_rr_gsb.h @@ -58,7 +58,7 @@ class DeviceRRGSB { const vtr::Point& coordinate) const; size_t get_num_cb_unique_module(const t_rr_type& cb_type) const; /* get the number of unique mirrors of CBs */ - bool is_gsb_exist(const vtr::Point coord) const; + bool is_gsb_exist(const RRGraphView& rr_graph, const vtr::Point coord) const; /* Get the index of the unique Switch block module with a given GSB * coordinate. Note: Do NOT use sb coordinate!!! */ size_t get_sb_unique_module_index(const vtr::Point& coordinate) const; diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index e20413f3e..715c0c12a 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -111,7 +111,7 @@ int build_device_module_graph( /* Build tile modules if defined */ if (tile_config.is_valid()) { /* Build detailed tile-level information */ - status = build_fabric_tile(fabric_tile, tile_config, vpr_device_ctx.grid, + status = build_fabric_tile(fabric_tile, tile_config, vpr_device_ctx.grid, vpr_device_ctx.rr_graph, openfpga_ctx.device_rr_gsb(), verbose); if (CMD_EXEC_FATAL_ERROR == status) { return status; diff --git a/openfpga/src/fabric/build_fabric_tile.cpp b/openfpga/src/fabric/build_fabric_tile.cpp index 465cf7cc9..f6e0ce070 100644 --- a/openfpga/src/fabric/build_fabric_tile.cpp +++ b/openfpga/src/fabric/build_fabric_tile.cpp @@ -30,6 +30,7 @@ namespace openfpga { static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, const DeviceGrid& grids, const size_t& layer, + const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const bool& verbose) { int status_code = CMD_EXEC_SUCCESS; @@ -48,7 +49,7 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, */ if (true == is_empty_type(phy_tile_type)) { skip_add_pb = true; - if (!device_rr_gsb.is_gsb_exist(curr_gsb_coord)) { + if (!device_rr_gsb.is_gsb_exist(rr_graph, curr_gsb_coord)) { VTR_LOGV(verbose, "Skip tile[%lu][%lu] as it is empty\n", curr_tile_coord.x(), curr_tile_coord.y()); continue; @@ -114,7 +115,7 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, * +----------+ +----------+ * */ - if (!device_rr_gsb.is_gsb_exist(curr_gsb_coord)) { + if (!device_rr_gsb.is_gsb_exist(rr_graph, curr_gsb_coord)) { continue; } const RRGSB& curr_rr_gsb = device_rr_gsb.get_gsb(curr_gsb_coord); @@ -124,7 +125,7 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, curr_rr_gsb.get_sb_coordinate()); } } - if (curr_rr_gsb.is_sb_exist()) { + if (curr_rr_gsb.is_sb_exist(rr_graph)) { fabric_tile.add_sb_coordinate(curr_tile_id, curr_rr_gsb.get_sb_coordinate()); } @@ -138,7 +139,8 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, * Build tile-level information for a given FPGA fabric, w.r.t. to configuration *******************************************************************/ int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, - const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb, + const DeviceGrid& grids, const RRGraphView& rr_graph, + const DeviceRRGSB& device_rr_gsb, const bool& verbose) { vtr::ScopedStartFinishTimer timer( "Build tile-level information for the FPGA fabric"); @@ -149,7 +151,7 @@ int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, /* Depending on the selected style, follow different approaches */ if (tile_config.style() == TileConfig::e_style::TOP_LEFT) { - status_code = build_fabric_tile_style_top_left(fabric_tile, grids, 0, + status_code = build_fabric_tile_style_top_left(fabric_tile, grids, 0, rr_graph, device_rr_gsb, verbose); } else { /* Error out for styles that are not supported yet! */ diff --git a/openfpga/src/fabric/build_fabric_tile.h b/openfpga/src/fabric/build_fabric_tile.h index 16b53f2f2..a63c2cad8 100644 --- a/openfpga/src/fabric/build_fabric_tile.h +++ b/openfpga/src/fabric/build_fabric_tile.h @@ -20,7 +20,7 @@ namespace openfpga { int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, - const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb, + const DeviceGrid& grids, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, 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 4693db986..1f12774c5 100644 --- a/openfpga/src/fabric/build_routing_modules.cpp +++ b/openfpga/src/fabric/build_routing_modules.cpp @@ -1135,7 +1135,7 @@ void build_flatten_routing_modules( for (size_t ix = 0; ix < sb_range.x(); ++ix) { for (size_t iy = 0; iy < sb_range.y(); ++iy) { const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(device_ctx.rr_graph)) { continue; } build_switch_block_module(module_manager, decoder_lib, device_annotation, diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 1d104fee4..f20215f71 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -69,7 +69,7 @@ static int build_tile_module_port_and_nets_between_sb_and_pb( const bool& compact_routing_hierarchy, const bool& frame_view, const bool& verbose) { /* Skip those Switch blocks that do not exist */ - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { return CMD_EXEC_SUCCESS; } @@ -514,7 +514,7 @@ static int build_tile_module_port_and_nets_between_sb_and_cb( vtr::Point module_gsb_sb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); /* Skip those Switch blocks that do not exist */ - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { return CMD_EXEC_SUCCESS; } 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 13c09952f..59936d473 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 @@ -187,6 +187,7 @@ static vtr::Matrix add_top_module_grid_instances( *******************************************************************/ static vtr::Matrix add_top_module_switch_block_instances( ModuleManager& module_manager, const ModuleId& top_module, + const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const bool& compact_routing_hierarchy) { vtr::ScopedStartFinishTimer timer("Add switch block instances to top module"); @@ -202,7 +203,7 @@ static vtr::Matrix add_top_module_switch_block_instances( * module of SB */ const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { continue; } @@ -454,7 +455,7 @@ int build_top_module_fine_grained_child_instances( add_top_module_grid_instances(module_manager, top_module, grids, layer); /* Add all the SBs across the fabric */ vtr::Matrix sb_instance_ids = add_top_module_switch_block_instances( - module_manager, top_module, device_rr_gsb, compact_routing_hierarchy); + module_manager, top_module, rr_graph, device_rr_gsb, compact_routing_hierarchy); /* Add all the CBX and CBYs across the fabric */ cb_instance_ids[CHANX] = add_top_module_connection_block_instances( module_manager, top_module, device_rr_gsb, CHANX, @@ -508,7 +509,7 @@ int build_top_module_fine_grained_child_instances( if (true == fabric_key.empty()) { organize_top_module_memory_modules( module_manager, top_module, circuit_lib, config_protocol, sram_model, - grids, layer, grid_instance_ids, device_rr_gsb, sb_instance_ids, + grids, layer, grid_instance_ids, device_rr_gsb, rr_graph, sb_instance_ids, cb_instance_ids, compact_routing_hierarchy); } else { VTR_ASSERT_SAFE(false == fabric_key.empty()); 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 dd4669c3f..ff2bae0aa 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -292,7 +292,7 @@ static int build_top_module_tile_nets_between_sb_and_pb( const size_t& sb_idx_in_curr_fabric_tile, const bool& compact_routing_hierarchy, const bool& verbose) { /* Skip those Switch blocks that do not exist */ - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { return CMD_EXEC_SUCCESS; } @@ -736,7 +736,7 @@ static int build_top_module_tile_nets_between_sb_and_cb( generate_switch_block_module_name(sb_coord_in_unique_tile); /* Skip those Switch blocks that do not exist */ - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { return CMD_EXEC_SUCCESS; } diff --git a/openfpga/src/fabric/build_top_module_connection.cpp b/openfpga/src/fabric/build_top_module_connection.cpp index c2d029d92..d49a6f1cf 100644 --- a/openfpga/src/fabric/build_top_module_connection.cpp +++ b/openfpga/src/fabric/build_top_module_connection.cpp @@ -69,7 +69,7 @@ static void add_top_module_nets_connect_grids_and_sb( const RRGSB& rr_gsb, const vtr::Matrix& sb_instance_ids, const bool& compact_routing_hierarchy) { /* Skip those Switch blocks that do not exist */ - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { return; } @@ -232,7 +232,7 @@ static void add_top_module_nets_connect_grids_and_sb_with_duplicated_pins( const RRGSB& rr_gsb, const vtr::Matrix& sb_instance_ids, const bool& compact_routing_hierarchy) { /* Skip those Switch blocks that do not exist */ - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { return; } @@ -617,7 +617,7 @@ static void add_top_module_nets_connect_sb_and_cb( vtr::Point module_gsb_sb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); /* Skip those Switch blocks that do not exist */ - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { return; } diff --git a/openfpga/src/fabric/build_top_module_memory.cpp b/openfpga/src/fabric/build_top_module_memory.cpp index 094a5ea05..a4ab23b2c 100644 --- a/openfpga/src/fabric/build_top_module_memory.cpp +++ b/openfpga/src/fabric/build_top_module_memory.cpp @@ -134,7 +134,7 @@ static void organize_top_module_tile_memory_modules( const e_config_protocol_type& sram_orgz_type, const CircuitModelId& sram_model, const DeviceGrid& grids, const vtr::Matrix& grid_instance_ids, - const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& sb_instance_ids, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, const bool& compact_routing_hierarchy, const size_t& layer, const vtr::Point& tile_coord, const e_side& tile_border_side) { @@ -165,7 +165,7 @@ static void organize_top_module_tile_memory_modules( * we will update the memory module and instance list */ /* If the CB does not exist, we can skip addition */ - if (true == rr_gsb.is_sb_exist()) { + if (true == rr_gsb.is_sb_exist(rr_graph)) { if (0 < find_module_num_config_bits(module_manager, sb_module, circuit_lib, sram_model, sram_orgz_type)) { @@ -438,7 +438,7 @@ void organize_top_module_memory_modules( const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, const CircuitModelId& sram_model, const DeviceGrid& grids, const size_t& layer, const vtr::Matrix& grid_instance_ids, - const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& sb_instance_ids, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, const bool& compact_routing_hierarchy) { /* Ensure clean vectors to return */ @@ -496,7 +496,7 @@ void organize_top_module_memory_modules( /* Identify the GSB that surrounds the grid */ organize_top_module_tile_memory_modules( module_manager, top_module, circuit_lib, config_protocol.type(), - sram_model, grids, grid_instance_ids, device_rr_gsb, sb_instance_ids, + sram_model, grids, grid_instance_ids, device_rr_gsb, rr_graph, sb_instance_ids, cb_instance_ids, compact_routing_hierarchy, layer, io_coord, io_side); } } @@ -524,7 +524,7 @@ void organize_top_module_memory_modules( for (const vtr::Point& core_coord : core_coords) { organize_top_module_tile_memory_modules( module_manager, top_module, circuit_lib, config_protocol.type(), - sram_model, grids, grid_instance_ids, device_rr_gsb, sb_instance_ids, + sram_model, grids, grid_instance_ids, device_rr_gsb, rr_graph, sb_instance_ids, cb_instance_ids, compact_routing_hierarchy, layer, core_coord, NUM_SIDES); } diff --git a/openfpga/src/fabric/build_top_module_memory.h b/openfpga/src/fabric/build_top_module_memory.h index 3f53e3a42..2ede0cbf1 100644 --- a/openfpga/src/fabric/build_top_module_memory.h +++ b/openfpga/src/fabric/build_top_module_memory.h @@ -14,6 +14,7 @@ #include "config_protocol.h" #include "decoder_library.h" #include "device_grid.h" +#include "rr_graph_view.h" #include "device_rr_gsb.h" #include "fabric_key.h" #include "memory_bank_shift_register_banks.h" @@ -33,7 +34,7 @@ void organize_top_module_memory_modules( const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, const CircuitModelId& sram_model, const DeviceGrid& grids, const size_t& layer, const vtr::Matrix& grid_instance_ids, - const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& sb_instance_ids, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, const bool& compact_routing_hierarchy); diff --git a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp index 28b79e049..0c1d7c31e 100644 --- a/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_routing_bitstream.cpp @@ -616,7 +616,7 @@ void build_routing_bitstream( * Some of them do NOT exist due to heterogeneous blocks (width > 1) * We will skip those modules */ - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { continue; } diff --git a/openfpga/src/fpga_sdc/analysis_sdc_routing_writer.cpp b/openfpga/src/fpga_sdc/analysis_sdc_routing_writer.cpp index b9e4c974b..8d3163262 100644 --- a/openfpga/src/fpga_sdc/analysis_sdc_routing_writer.cpp +++ b/openfpga/src/fpga_sdc/analysis_sdc_routing_writer.cpp @@ -579,7 +579,7 @@ void print_analysis_sdc_disable_unused_sbs( * We will skip those modules */ const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { continue; } diff --git a/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.cpp b/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.cpp index 3732815f7..1f56da15e 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.cpp +++ b/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.cpp @@ -211,7 +211,7 @@ void print_pnr_sdc_flatten_routing_constrain_sb_timing( for (size_t ix = 0; ix < sb_range.x(); ++ix) { for (size_t iy = 0; iy < sb_range.y(); ++iy) { const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { continue; } @@ -248,7 +248,7 @@ void print_pnr_sdc_compact_routing_constrain_sb_timing( for (size_t isb = 0; isb < device_rr_gsb.get_num_sb_unique_module(); ++isb) { const RRGSB& rr_gsb = device_rr_gsb.get_sb_unique_module(isb); - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { continue; } diff --git a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp index 7a862ae73..e5ec66bd0 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp +++ b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp @@ -87,7 +87,7 @@ static void print_pnr_sdc_constrain_configurable_memory_outputs( static void print_pnr_sdc_flatten_routing_disable_switch_block_outputs( const std::string& sdc_dir, const bool& flatten_names, const bool& include_time_stamp, const ModuleManager& module_manager, - const ModuleId& top_module, const DeviceRRGSB& device_rr_gsb) { + const ModuleId& top_module, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph) { /* Create the file name for Verilog netlist */ std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_SB_OUTPUTS_FILE_NAME)); @@ -124,7 +124,7 @@ static void print_pnr_sdc_flatten_routing_disable_switch_block_outputs( for (size_t iy = 0; iy < sb_range.y(); ++iy) { const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { continue; } @@ -212,7 +212,7 @@ static void print_pnr_sdc_flatten_routing_disable_switch_block_outputs( static void print_pnr_sdc_compact_routing_disable_switch_block_outputs( const std::string& sdc_dir, const bool& flatten_names, const bool& include_time_stamp, const ModuleManager& module_manager, - const ModuleId& top_module, const DeviceRRGSB& device_rr_gsb) { + const ModuleId& top_module, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph) { /* Create the file name for Verilog netlist */ std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_SB_OUTPUTS_FILE_NAME)); @@ -246,7 +246,7 @@ static void print_pnr_sdc_compact_routing_disable_switch_block_outputs( for (size_t isb = 0; isb < device_rr_gsb.get_num_sb_unique_module(); ++isb) { const RRGSB& rr_gsb = device_rr_gsb.get_sb_unique_module(isb); - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { continue; } @@ -391,12 +391,12 @@ int print_pnr_sdc( if (true == compact_routing_hierarchy) { print_pnr_sdc_compact_routing_disable_switch_block_outputs( sdc_options.sdc_dir(), sdc_options.flatten_names(), - sdc_options.time_stamp(), module_manager, top_module, device_rr_gsb); + sdc_options.time_stamp(), module_manager, top_module, device_rr_gsb, device_ctx.rr_graph); } else { VTR_ASSERT_SAFE(false == compact_routing_hierarchy); print_pnr_sdc_flatten_routing_disable_switch_block_outputs( sdc_options.sdc_dir(), sdc_options.flatten_names(), - sdc_options.time_stamp(), module_manager, top_module, device_rr_gsb); + sdc_options.time_stamp(), module_manager, top_module, device_rr_gsb, device_ctx.rr_graph); } } @@ -419,7 +419,7 @@ int print_pnr_sdc( (true == sdc_options.output_hierarchy()) && (true == compact_routing_hierarchy)) { print_pnr_sdc_routing_sb_hierarchy(sdc_options.sdc_dir(), module_manager, - top_module, device_rr_gsb); + top_module, device_rr_gsb, device_ctx.rr_graph); } /* Output routing constraints for Connection Blocks */ diff --git a/openfpga/src/fpga_sdc/sdc_hierarchy_writer.cpp b/openfpga/src/fpga_sdc/sdc_hierarchy_writer.cpp index e9d46fa4a..f2ae08864 100644 --- a/openfpga/src/fpga_sdc/sdc_hierarchy_writer.cpp +++ b/openfpga/src/fpga_sdc/sdc_hierarchy_writer.cpp @@ -31,7 +31,8 @@ namespace openfpga { void print_pnr_sdc_routing_sb_hierarchy(const std::string& sdc_dir, const ModuleManager& module_manager, const ModuleId& top_module, - const DeviceRRGSB& device_rr_gsb) { + const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph) { std::string fname(sdc_dir + std::string(SDC_SB_HIERARCHY_FILE_NAME)); std::string timer_message = @@ -59,7 +60,7 @@ void print_pnr_sdc_routing_sb_hierarchy(const std::string& sdc_dir, for (size_t isb = 0; isb < device_rr_gsb.get_num_sb_unique_module(); ++isb) { const RRGSB& rr_gsb = device_rr_gsb.get_sb_unique_module(isb); - if (false == rr_gsb.is_sb_exist()) { + if (false == rr_gsb.is_sb_exist(rr_graph)) { continue; } diff --git a/openfpga/src/fpga_sdc/sdc_hierarchy_writer.h b/openfpga/src/fpga_sdc/sdc_hierarchy_writer.h index 39c5fd565..c429d5e62 100644 --- a/openfpga/src/fpga_sdc/sdc_hierarchy_writer.h +++ b/openfpga/src/fpga_sdc/sdc_hierarchy_writer.h @@ -17,7 +17,8 @@ namespace openfpga { void print_pnr_sdc_routing_sb_hierarchy(const std::string& sdc_dir, const ModuleManager& module_manager, const ModuleId& top_module, - const DeviceRRGSB& device_rr_gsb); + const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph); void print_pnr_sdc_routing_cb_hierarchy(const std::string& sdc_dir, const ModuleManager& module_manager, diff --git a/openfpga/src/fpga_spice/spice_api.cpp b/openfpga/src/fpga_spice/spice_api.cpp index 99c346cbf..c31bea19b 100644 --- a/openfpga/src/fpga_spice/spice_api.cpp +++ b/openfpga/src/fpga_spice/spice_api.cpp @@ -95,7 +95,7 @@ int fpga_fabric_spice(const ModuleManager& module_manager, } else { VTR_ASSERT(false == options.compress_routing()); print_spice_flatten_routing_modules(netlist_manager, module_manager, - device_rr_gsb, rr_dir_path); + device_rr_gsb, device_ctx.rr_graph, rr_dir_path); } /* Generate grids */ diff --git a/openfpga/src/fpga_spice/spice_routing.cpp b/openfpga/src/fpga_spice/spice_routing.cpp index ec29dbfd2..bb59b86cf 100644 --- a/openfpga/src/fpga_spice/spice_routing.cpp +++ b/openfpga/src/fpga_spice/spice_routing.cpp @@ -259,6 +259,7 @@ static void print_spice_flatten_connection_block_modules( void print_spice_flatten_routing_modules(NetlistManager& netlist_manager, const ModuleManager& module_manager, const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph, const std::string& subckt_dir) { /* Create a vector to contain all the Verilog netlist names that have been * generated in this function */ @@ -270,7 +271,7 @@ void print_spice_flatten_routing_modules(NetlistManager& netlist_manager, for (size_t ix = 0; ix < sb_range.x(); ++ix) { for (size_t iy = 0; iy < sb_range.y(); ++iy) { const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); - if (true != rr_gsb.is_sb_exist()) { + if (true != rr_gsb.is_sb_exist(rr_graph)) { continue; } print_spice_routing_switch_box_unique_module( diff --git a/openfpga/src/fpga_spice/spice_routing.h b/openfpga/src/fpga_spice/spice_routing.h index c14a3d68b..ed4d2e476 100644 --- a/openfpga/src/fpga_spice/spice_routing.h +++ b/openfpga/src/fpga_spice/spice_routing.h @@ -9,6 +9,7 @@ #include "module_manager.h" #include "mux_library.h" #include "netlist_manager.h" +#include "rr_graph_view.h" /******************************************************************** * Function declaration @@ -20,6 +21,7 @@ namespace openfpga { void print_spice_flatten_routing_modules(NetlistManager& netlist_manager, const ModuleManager& module_manager, const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph, const std::string& subckt_dir); void print_spice_unique_routing_modules(NetlistManager& netlist_manager, diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 921e88176..94b406524 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -117,7 +117,7 @@ int fpga_fabric_verilog( VTR_ASSERT(false == options.compress_routing()); print_verilog_flatten_routing_modules( netlist_manager, const_cast(module_manager), - device_rr_gsb, rr_dir_path, std::string(DEFAULT_RR_DIR_NAME), options); + device_rr_gsb, device_ctx.rr_graph, rr_dir_path, std::string(DEFAULT_RR_DIR_NAME), options); } /* Generate grids */ diff --git a/openfpga/src/fpga_verilog/verilog_routing.cpp b/openfpga/src/fpga_verilog/verilog_routing.cpp index fe93a9fd4..1cf57dabb 100644 --- a/openfpga/src/fpga_verilog/verilog_routing.cpp +++ b/openfpga/src/fpga_verilog/verilog_routing.cpp @@ -280,6 +280,7 @@ static void print_verilog_flatten_connection_block_modules( void print_verilog_flatten_routing_modules(NetlistManager& netlist_manager, const ModuleManager& module_manager, const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph, const std::string& subckt_dir, const std::string& subckt_dir_name, const FabricVerilogOption& options) { @@ -293,7 +294,7 @@ void print_verilog_flatten_routing_modules(NetlistManager& netlist_manager, for (size_t ix = 0; ix < sb_range.x(); ++ix) { for (size_t iy = 0; iy < sb_range.y(); ++iy) { const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); - if (true != rr_gsb.is_sb_exist()) { + if (true != rr_gsb.is_sb_exist(rr_graph)) { continue; } print_verilog_routing_switch_box_unique_module( diff --git a/openfpga/src/fpga_verilog/verilog_routing.h b/openfpga/src/fpga_verilog/verilog_routing.h index f33c44d43..2766c1b2a 100644 --- a/openfpga/src/fpga_verilog/verilog_routing.h +++ b/openfpga/src/fpga_verilog/verilog_routing.h @@ -10,6 +10,7 @@ #include "module_manager.h" #include "mux_library.h" #include "netlist_manager.h" +#include "rr_graph_view.h" /******************************************************************** * Function declaration @@ -21,6 +22,7 @@ namespace openfpga { void print_verilog_flatten_routing_modules(NetlistManager& netlist_manager, const ModuleManager& module_manager, const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph, const std::string& subckt_dir, const std::string& subckt_dir_name, const FabricVerilogOption& options); diff --git a/openfpga/src/utils/device_rr_gsb_utils.cpp b/openfpga/src/utils/device_rr_gsb_utils.cpp index 31d1669ec..9c4bd3b27 100644 --- a/openfpga/src/utils/device_rr_gsb_utils.cpp +++ b/openfpga/src/utils/device_rr_gsb_utils.cpp @@ -34,12 +34,12 @@ size_t find_device_rr_gsb_num_cb_modules(const DeviceRRGSB& device_rr_gsb, * This function aims to find out the number of switch block * modules in the device rr_gsb array *******************************************************************/ -size_t find_device_rr_gsb_num_sb_modules(const DeviceRRGSB& device_rr_gsb) { +size_t find_device_rr_gsb_num_sb_modules(const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph) { size_t counter = 0; for (size_t x = 0; x < device_rr_gsb.get_gsb_range().x(); ++x) { for (size_t y = 0; y < device_rr_gsb.get_gsb_range().y(); ++y) { const RRGSB& rr_gsb = device_rr_gsb.get_gsb(x, y); - if (true == rr_gsb.is_sb_exist()) { + if (true == rr_gsb.is_sb_exist(rr_graph)) { counter++; } } @@ -51,11 +51,11 @@ size_t find_device_rr_gsb_num_sb_modules(const DeviceRRGSB& device_rr_gsb) { /******************************************************************** * This function aims to find out the number of GSBs *******************************************************************/ -size_t find_device_rr_gsb_num_gsb_modules(const DeviceRRGSB& device_rr_gsb) { +size_t find_device_rr_gsb_num_gsb_modules(const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph) { size_t counter = 0; for (size_t x = 0; x < device_rr_gsb.get_gsb_range().x(); ++x) { for (size_t y = 0; y < device_rr_gsb.get_gsb_range().y(); ++y) { - if (true == device_rr_gsb.is_gsb_exist(vtr::Point(x, y))) { + if (true == device_rr_gsb.is_gsb_exist(rr_graph, vtr::Point(x, y))) { counter++; } } diff --git a/openfpga/src/utils/device_rr_gsb_utils.h b/openfpga/src/utils/device_rr_gsb_utils.h index 0b7424b4f..3996ee324 100644 --- a/openfpga/src/utils/device_rr_gsb_utils.h +++ b/openfpga/src/utils/device_rr_gsb_utils.h @@ -8,6 +8,7 @@ #include #include "device_rr_gsb.h" +#include "rr_graph_view.h" /******************************************************************** * Function declaration @@ -19,9 +20,9 @@ namespace openfpga { size_t find_device_rr_gsb_num_cb_modules(const DeviceRRGSB& device_rr_gsb, const t_rr_type& cb_type); -size_t find_device_rr_gsb_num_sb_modules(const DeviceRRGSB& device_rr_gsb); +size_t find_device_rr_gsb_num_sb_modules(const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph); -size_t find_device_rr_gsb_num_gsb_modules(const DeviceRRGSB& device_rr_gsb); +size_t find_device_rr_gsb_num_gsb_modules(const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph); } /* end namespace openfpga */ From 717906ea17b331ba7cc0ed054afd2507d2203ce5 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 25 Aug 2023 15:13:39 -0700 Subject: [PATCH 365/391] [core] code format --- openfpga/src/annotation/device_rr_gsb.cpp | 3 ++- openfpga/src/annotation/device_rr_gsb.h | 3 ++- openfpga/src/fabric/build_device_module.cpp | 3 ++- openfpga/src/fabric/build_fabric_tile.cpp | 7 +++---- openfpga/src/fabric/build_fabric_tile.h | 4 ++-- ...ld_top_module_child_fine_grained_instance.cpp | 7 ++++--- openfpga/src/fabric/build_top_module_memory.cpp | 16 ++++++++++------ openfpga/src/fabric/build_top_module_memory.h | 5 +++-- openfpga/src/fpga_sdc/pnr_sdc_writer.cpp | 15 ++++++++++----- openfpga/src/fpga_spice/spice_api.cpp | 3 ++- openfpga/src/fpga_verilog/verilog_api.cpp | 3 ++- openfpga/src/utils/device_rr_gsb_utils.cpp | 9 ++++++--- openfpga/src/utils/device_rr_gsb_utils.h | 6 ++++-- 13 files changed, 52 insertions(+), 32 deletions(-) diff --git a/openfpga/src/annotation/device_rr_gsb.cpp b/openfpga/src/annotation/device_rr_gsb.cpp index 131533eca..963d53bc1 100644 --- a/openfpga/src/annotation/device_rr_gsb.cpp +++ b/openfpga/src/annotation/device_rr_gsb.cpp @@ -78,7 +78,8 @@ size_t DeviceRRGSB::get_num_cb_unique_module(const t_rr_type& cb_type) const { } /* Identify if a GSB actually exists at a location */ -bool DeviceRRGSB::is_gsb_exist(const RRGraphView& rr_graph, const vtr::Point coord) const { +bool DeviceRRGSB::is_gsb_exist(const RRGraphView& rr_graph, + const vtr::Point coord) const { /* Out of range, does not exist */ if (false == validate_coordinate(coord)) { return false; diff --git a/openfpga/src/annotation/device_rr_gsb.h b/openfpga/src/annotation/device_rr_gsb.h index c08cde3e8..245b1646b 100644 --- a/openfpga/src/annotation/device_rr_gsb.h +++ b/openfpga/src/annotation/device_rr_gsb.h @@ -58,7 +58,8 @@ class DeviceRRGSB { const vtr::Point& coordinate) const; size_t get_num_cb_unique_module(const t_rr_type& cb_type) const; /* get the number of unique mirrors of CBs */ - bool is_gsb_exist(const RRGraphView& rr_graph, const vtr::Point coord) const; + bool is_gsb_exist(const RRGraphView& rr_graph, + const vtr::Point coord) const; /* Get the index of the unique Switch block module with a given GSB * coordinate. Note: Do NOT use sb coordinate!!! */ size_t get_sb_unique_module_index(const vtr::Point& coordinate) const; diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index 715c0c12a..5279c9fc7 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -111,7 +111,8 @@ int build_device_module_graph( /* Build tile modules if defined */ if (tile_config.is_valid()) { /* Build detailed tile-level information */ - status = build_fabric_tile(fabric_tile, tile_config, vpr_device_ctx.grid, vpr_device_ctx.rr_graph, + status = build_fabric_tile(fabric_tile, tile_config, vpr_device_ctx.grid, + vpr_device_ctx.rr_graph, openfpga_ctx.device_rr_gsb(), verbose); if (CMD_EXEC_FATAL_ERROR == status) { return status; diff --git a/openfpga/src/fabric/build_fabric_tile.cpp b/openfpga/src/fabric/build_fabric_tile.cpp index f6e0ce070..a1e5f9460 100644 --- a/openfpga/src/fabric/build_fabric_tile.cpp +++ b/openfpga/src/fabric/build_fabric_tile.cpp @@ -140,8 +140,7 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, *******************************************************************/ int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, const DeviceGrid& grids, const RRGraphView& rr_graph, - const DeviceRRGSB& device_rr_gsb, - const bool& verbose) { + const DeviceRRGSB& device_rr_gsb, const bool& verbose) { vtr::ScopedStartFinishTimer timer( "Build tile-level information for the FPGA fabric"); @@ -151,8 +150,8 @@ int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, /* Depending on the selected style, follow different approaches */ if (tile_config.style() == TileConfig::e_style::TOP_LEFT) { - status_code = build_fabric_tile_style_top_left(fabric_tile, grids, 0, rr_graph, - device_rr_gsb, verbose); + status_code = build_fabric_tile_style_top_left( + fabric_tile, grids, 0, rr_graph, device_rr_gsb, verbose); } else { /* Error out for styles that are not supported yet! */ VTR_LOG_ERROR("Tile style '%s' is not supported yet!\n", diff --git a/openfpga/src/fabric/build_fabric_tile.h b/openfpga/src/fabric/build_fabric_tile.h index a63c2cad8..b2d21c9a0 100644 --- a/openfpga/src/fabric/build_fabric_tile.h +++ b/openfpga/src/fabric/build_fabric_tile.h @@ -20,8 +20,8 @@ namespace openfpga { int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, - const DeviceGrid& grids, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, - const bool& verbose); + const DeviceGrid& grids, const RRGraphView& rr_graph, + const DeviceRRGSB& device_rr_gsb, 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 59936d473..7640a969c 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 @@ -187,8 +187,8 @@ static vtr::Matrix add_top_module_grid_instances( *******************************************************************/ static vtr::Matrix add_top_module_switch_block_instances( ModuleManager& module_manager, const ModuleId& top_module, - const RRGraphView& rr_graph, - const DeviceRRGSB& device_rr_gsb, const bool& compact_routing_hierarchy) { + const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, + const bool& compact_routing_hierarchy) { vtr::ScopedStartFinishTimer timer("Add switch block instances to top module"); vtr::Point sb_range = device_rr_gsb.get_gsb_range(); @@ -455,7 +455,8 @@ int build_top_module_fine_grained_child_instances( add_top_module_grid_instances(module_manager, top_module, grids, layer); /* Add all the SBs across the fabric */ vtr::Matrix sb_instance_ids = add_top_module_switch_block_instances( - module_manager, top_module, rr_graph, device_rr_gsb, compact_routing_hierarchy); + module_manager, top_module, rr_graph, device_rr_gsb, + compact_routing_hierarchy); /* Add all the CBX and CBYs across the fabric */ cb_instance_ids[CHANX] = add_top_module_connection_block_instances( module_manager, top_module, device_rr_gsb, CHANX, diff --git a/openfpga/src/fabric/build_top_module_memory.cpp b/openfpga/src/fabric/build_top_module_memory.cpp index a4ab23b2c..a7d137a5e 100644 --- a/openfpga/src/fabric/build_top_module_memory.cpp +++ b/openfpga/src/fabric/build_top_module_memory.cpp @@ -134,7 +134,8 @@ static void organize_top_module_tile_memory_modules( const e_config_protocol_type& sram_orgz_type, const CircuitModelId& sram_model, const DeviceGrid& grids, const vtr::Matrix& grid_instance_ids, - const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const vtr::Matrix& sb_instance_ids, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, + const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, const bool& compact_routing_hierarchy, const size_t& layer, const vtr::Point& tile_coord, const e_side& tile_border_side) { @@ -438,7 +439,8 @@ void organize_top_module_memory_modules( const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, const CircuitModelId& sram_model, const DeviceGrid& grids, const size_t& layer, const vtr::Matrix& grid_instance_ids, - const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const vtr::Matrix& sb_instance_ids, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, + const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, const bool& compact_routing_hierarchy) { /* Ensure clean vectors to return */ @@ -496,8 +498,9 @@ void organize_top_module_memory_modules( /* Identify the GSB that surrounds the grid */ organize_top_module_tile_memory_modules( module_manager, top_module, circuit_lib, config_protocol.type(), - sram_model, grids, grid_instance_ids, device_rr_gsb, rr_graph, sb_instance_ids, - cb_instance_ids, compact_routing_hierarchy, layer, io_coord, io_side); + sram_model, grids, grid_instance_ids, device_rr_gsb, rr_graph, + sb_instance_ids, cb_instance_ids, compact_routing_hierarchy, layer, + io_coord, io_side); } } @@ -524,8 +527,9 @@ void organize_top_module_memory_modules( for (const vtr::Point& core_coord : core_coords) { organize_top_module_tile_memory_modules( module_manager, top_module, circuit_lib, config_protocol.type(), - sram_model, grids, grid_instance_ids, device_rr_gsb, rr_graph, sb_instance_ids, - cb_instance_ids, compact_routing_hierarchy, layer, core_coord, NUM_SIDES); + sram_model, grids, grid_instance_ids, device_rr_gsb, rr_graph, + sb_instance_ids, cb_instance_ids, compact_routing_hierarchy, layer, + core_coord, NUM_SIDES); } /* Split memory modules into different regions */ diff --git a/openfpga/src/fabric/build_top_module_memory.h b/openfpga/src/fabric/build_top_module_memory.h index 2ede0cbf1..163d7614a 100644 --- a/openfpga/src/fabric/build_top_module_memory.h +++ b/openfpga/src/fabric/build_top_module_memory.h @@ -14,11 +14,11 @@ #include "config_protocol.h" #include "decoder_library.h" #include "device_grid.h" -#include "rr_graph_view.h" #include "device_rr_gsb.h" #include "fabric_key.h" #include "memory_bank_shift_register_banks.h" #include "module_manager.h" +#include "rr_graph_view.h" #include "vtr_ndmatrix.h" #include "vtr_vector.h" @@ -34,7 +34,8 @@ void organize_top_module_memory_modules( const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, const CircuitModelId& sram_model, const DeviceGrid& grids, const size_t& layer, const vtr::Matrix& grid_instance_ids, - const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const vtr::Matrix& sb_instance_ids, + const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, + const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, const bool& compact_routing_hierarchy); diff --git a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp index e5ec66bd0..ab2a25aee 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp +++ b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp @@ -87,7 +87,8 @@ static void print_pnr_sdc_constrain_configurable_memory_outputs( static void print_pnr_sdc_flatten_routing_disable_switch_block_outputs( const std::string& sdc_dir, const bool& flatten_names, const bool& include_time_stamp, const ModuleManager& module_manager, - const ModuleId& top_module, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph) { + const ModuleId& top_module, const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph) { /* Create the file name for Verilog netlist */ std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_SB_OUTPUTS_FILE_NAME)); @@ -212,7 +213,8 @@ static void print_pnr_sdc_flatten_routing_disable_switch_block_outputs( static void print_pnr_sdc_compact_routing_disable_switch_block_outputs( const std::string& sdc_dir, const bool& flatten_names, const bool& include_time_stamp, const ModuleManager& module_manager, - const ModuleId& top_module, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph) { + const ModuleId& top_module, const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph) { /* Create the file name for Verilog netlist */ std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_SB_OUTPUTS_FILE_NAME)); @@ -391,12 +393,14 @@ int print_pnr_sdc( if (true == compact_routing_hierarchy) { print_pnr_sdc_compact_routing_disable_switch_block_outputs( sdc_options.sdc_dir(), sdc_options.flatten_names(), - sdc_options.time_stamp(), module_manager, top_module, device_rr_gsb, device_ctx.rr_graph); + sdc_options.time_stamp(), module_manager, top_module, device_rr_gsb, + device_ctx.rr_graph); } else { VTR_ASSERT_SAFE(false == compact_routing_hierarchy); print_pnr_sdc_flatten_routing_disable_switch_block_outputs( sdc_options.sdc_dir(), sdc_options.flatten_names(), - sdc_options.time_stamp(), module_manager, top_module, device_rr_gsb, device_ctx.rr_graph); + sdc_options.time_stamp(), module_manager, top_module, device_rr_gsb, + device_ctx.rr_graph); } } @@ -419,7 +423,8 @@ int print_pnr_sdc( (true == sdc_options.output_hierarchy()) && (true == compact_routing_hierarchy)) { print_pnr_sdc_routing_sb_hierarchy(sdc_options.sdc_dir(), module_manager, - top_module, device_rr_gsb, device_ctx.rr_graph); + top_module, device_rr_gsb, + device_ctx.rr_graph); } /* Output routing constraints for Connection Blocks */ diff --git a/openfpga/src/fpga_spice/spice_api.cpp b/openfpga/src/fpga_spice/spice_api.cpp index c31bea19b..7fa744de9 100644 --- a/openfpga/src/fpga_spice/spice_api.cpp +++ b/openfpga/src/fpga_spice/spice_api.cpp @@ -95,7 +95,8 @@ int fpga_fabric_spice(const ModuleManager& module_manager, } else { VTR_ASSERT(false == options.compress_routing()); print_spice_flatten_routing_modules(netlist_manager, module_manager, - device_rr_gsb, device_ctx.rr_graph, rr_dir_path); + device_rr_gsb, device_ctx.rr_graph, + rr_dir_path); } /* Generate grids */ diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 94b406524..1dbf64dc9 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -117,7 +117,8 @@ int fpga_fabric_verilog( VTR_ASSERT(false == options.compress_routing()); print_verilog_flatten_routing_modules( netlist_manager, const_cast(module_manager), - device_rr_gsb, device_ctx.rr_graph, rr_dir_path, std::string(DEFAULT_RR_DIR_NAME), options); + device_rr_gsb, device_ctx.rr_graph, rr_dir_path, + std::string(DEFAULT_RR_DIR_NAME), options); } /* Generate grids */ diff --git a/openfpga/src/utils/device_rr_gsb_utils.cpp b/openfpga/src/utils/device_rr_gsb_utils.cpp index 9c4bd3b27..4f1fb8e00 100644 --- a/openfpga/src/utils/device_rr_gsb_utils.cpp +++ b/openfpga/src/utils/device_rr_gsb_utils.cpp @@ -34,7 +34,8 @@ size_t find_device_rr_gsb_num_cb_modules(const DeviceRRGSB& device_rr_gsb, * This function aims to find out the number of switch block * modules in the device rr_gsb array *******************************************************************/ -size_t find_device_rr_gsb_num_sb_modules(const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph) { +size_t find_device_rr_gsb_num_sb_modules(const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph) { size_t counter = 0; for (size_t x = 0; x < device_rr_gsb.get_gsb_range().x(); ++x) { for (size_t y = 0; y < device_rr_gsb.get_gsb_range().y(); ++y) { @@ -51,11 +52,13 @@ size_t find_device_rr_gsb_num_sb_modules(const DeviceRRGSB& device_rr_gsb, const /******************************************************************** * This function aims to find out the number of GSBs *******************************************************************/ -size_t find_device_rr_gsb_num_gsb_modules(const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph) { +size_t find_device_rr_gsb_num_gsb_modules(const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph) { size_t counter = 0; for (size_t x = 0; x < device_rr_gsb.get_gsb_range().x(); ++x) { for (size_t y = 0; y < device_rr_gsb.get_gsb_range().y(); ++y) { - if (true == device_rr_gsb.is_gsb_exist(rr_graph, vtr::Point(x, y))) { + if (true == + device_rr_gsb.is_gsb_exist(rr_graph, vtr::Point(x, y))) { counter++; } } diff --git a/openfpga/src/utils/device_rr_gsb_utils.h b/openfpga/src/utils/device_rr_gsb_utils.h index 3996ee324..453e93d61 100644 --- a/openfpga/src/utils/device_rr_gsb_utils.h +++ b/openfpga/src/utils/device_rr_gsb_utils.h @@ -20,9 +20,11 @@ namespace openfpga { size_t find_device_rr_gsb_num_cb_modules(const DeviceRRGSB& device_rr_gsb, const t_rr_type& cb_type); -size_t find_device_rr_gsb_num_sb_modules(const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph); +size_t find_device_rr_gsb_num_sb_modules(const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph); -size_t find_device_rr_gsb_num_gsb_modules(const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph); +size_t find_device_rr_gsb_num_gsb_modules(const DeviceRRGSB& device_rr_gsb, + const RRGraphView& rr_graph); } /* end namespace openfpga */ From b8c66b06a0019044d60b72de39527e36be328c3e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 25 Aug 2023 15:17:52 -0700 Subject: [PATCH 366/391] [core] syntax --- openfpga/src/base/openfpga_build_fabric_template.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index 453c4c3b2..2509b96a7 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -69,9 +69,9 @@ void compress_routing_hierarchy_template(T& openfpga_ctx, "Detected %lu unique switch blocks from a total of %d (compression " "rate=%.2f%)\n", openfpga_ctx.device_rr_gsb().get_num_sb_unique_module(), - find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb()), + find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb(), g_vpr_ctx.device().rr_graph), 100. * - ((float)find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb()) / + ((float)find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb(), g_vpr_ctx.device().rr_graph) / (float)openfpga_ctx.device_rr_gsb().get_num_sb_unique_module() - 1.)); @@ -79,9 +79,9 @@ void compress_routing_hierarchy_template(T& openfpga_ctx, "Detected %lu unique general switch blocks from a total of %d (compression " "rate=%.2f%)\n", openfpga_ctx.device_rr_gsb().get_num_gsb_unique_module(), - find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb()), + find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb(), g_vpr_ctx.device().rr_graph), 100. * - ((float)find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb()) / + ((float)find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb(), g_vpr_ctx.device().rr_graph) / (float)openfpga_ctx.device_rr_gsb().get_num_gsb_unique_module() - 1.)); } From dfe5447e2a10c62738a0dc98d3111d8fa72c4b0c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 25 Aug 2023 15:21:24 -0700 Subject: [PATCH 367/391] [core] format --- .../src/base/openfpga_build_fabric_template.h | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/openfpga/src/base/openfpga_build_fabric_template.h b/openfpga/src/base/openfpga_build_fabric_template.h index 2509b96a7..39e181b6d 100644 --- a/openfpga/src/base/openfpga_build_fabric_template.h +++ b/openfpga/src/base/openfpga_build_fabric_template.h @@ -69,21 +69,23 @@ void compress_routing_hierarchy_template(T& openfpga_ctx, "Detected %lu unique switch blocks from a total of %d (compression " "rate=%.2f%)\n", openfpga_ctx.device_rr_gsb().get_num_sb_unique_module(), - find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb(), g_vpr_ctx.device().rr_graph), - 100. * - ((float)find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb(), g_vpr_ctx.device().rr_graph) / - (float)openfpga_ctx.device_rr_gsb().get_num_sb_unique_module() - - 1.)); + find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb(), + g_vpr_ctx.device().rr_graph), + 100. * ((float)find_device_rr_gsb_num_sb_modules( + openfpga_ctx.device_rr_gsb(), g_vpr_ctx.device().rr_graph) / + (float)openfpga_ctx.device_rr_gsb().get_num_sb_unique_module() - + 1.)); VTR_LOG( "Detected %lu unique general switch blocks from a total of %d (compression " "rate=%.2f%)\n", openfpga_ctx.device_rr_gsb().get_num_gsb_unique_module(), - find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb(), g_vpr_ctx.device().rr_graph), - 100. * - ((float)find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb(), g_vpr_ctx.device().rr_graph) / - (float)openfpga_ctx.device_rr_gsb().get_num_gsb_unique_module() - - 1.)); + find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb(), + g_vpr_ctx.device().rr_graph), + 100. * ((float)find_device_rr_gsb_num_gsb_modules( + openfpga_ctx.device_rr_gsb(), g_vpr_ctx.device().rr_graph) / + (float)openfpga_ctx.device_rr_gsb().get_num_gsb_unique_module() - + 1.)); } /******************************************************************** From 0cf76576941dc212274493a2263c9183b3c845ef Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 25 Aug 2023 15:24:33 -0700 Subject: [PATCH 368/391] [lib] update vtr --- vtr-verilog-to-routing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtr-verilog-to-routing b/vtr-verilog-to-routing index 4014b3297..690f7cbe8 160000 --- a/vtr-verilog-to-routing +++ b/vtr-verilog-to-routing @@ -1 +1 @@ -Subproject commit 4014b3297d8cd3e12d52a44a42d1809ea4147729 +Subproject commit 690f7cbe80060e911f007146db73db7c0452b9e1 From ced6b361ed284fd3bc6a3837a5365d2a31801f0a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 25 Aug 2023 15:37:02 -0700 Subject: [PATCH 369/391] [lib] update vtr --- vtr-verilog-to-routing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtr-verilog-to-routing b/vtr-verilog-to-routing index 690f7cbe8..3273c292e 160000 --- a/vtr-verilog-to-routing +++ b/vtr-verilog-to-routing @@ -1 +1 @@ -Subproject commit 690f7cbe80060e911f007146db73db7c0452b9e1 +Subproject commit 3273c292ea75010409c325ec573d95ebec19ae67 From 41c3bb7c8c567711446c7b837acb1b1d82160896 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 26 Aug 2023 01:17:22 +0000 Subject: [PATCH 370/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index b6f41377e..9ef47cba0 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1455 +1.2.1467 From adae7392e5a6b74b53f9cb954384685487d325bb Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 26 Aug 2023 12:54:12 -0700 Subject: [PATCH 371/391] [lib] developing fabric key assistant --- libs/libfabrickey/CMakeLists.txt | 1 + libs/libfabrickey/src/base/fabric_key.cpp | 29 +++ libs/libfabrickey/src/base/fabric_key.h | 5 + .../src/utils/check_fabric_key.cpp | 102 +++++++++++ .../libfabrickey/src/utils/check_fabric_key.h | 22 +++ .../test/fabric_key_assistant.cpp | 169 ++++++++++++++++++ 6 files changed, 328 insertions(+) create mode 100644 libs/libfabrickey/src/utils/check_fabric_key.cpp create mode 100644 libs/libfabrickey/src/utils/check_fabric_key.h create mode 100644 libs/libfabrickey/test/fabric_key_assistant.cpp diff --git a/libs/libfabrickey/CMakeLists.txt b/libs/libfabrickey/CMakeLists.txt index ac43927ac..8c01cc472 100644 --- a/libs/libfabrickey/CMakeLists.txt +++ b/libs/libfabrickey/CMakeLists.txt @@ -20,6 +20,7 @@ set_target_properties(libfabrickey PROPERTIES PREFIX "") #Avoid extra 'lib' pref #Specify link-time dependancies target_link_libraries(libfabrickey libopenfpgautil + libopenfpgashell libarchopenfpga libvtrutil libpugiutil) diff --git a/libs/libfabrickey/src/base/fabric_key.cpp b/libs/libfabrickey/src/base/fabric_key.cpp index b80d87204..12b92846b 100644 --- a/libs/libfabrickey/src/base/fabric_key.cpp +++ b/libs/libfabrickey/src/base/fabric_key.cpp @@ -55,6 +55,35 @@ std::vector FabricKey::sub_keys( /************************************************************************ * Public Accessors : Basic data query ***********************************************************************/ +size_t FabricKey::num_regions() const { + return region_ids_.size(); +} + +size_t FabricKey::num_keys() const { + return key_ids_.size(); +} + +std::vector find_key_by_alias(const std::string& alias) const { + /* Throw warning on empty alias which may cause unexpected results: whole key is dumped! */ + if (alias.empty()) { + VTR_LOG_WARN("Empty alias is given! This may cause unexpected results, i.e., a whole data base is dumped!\n"); + } + size_t num_found_keys = 0; + for (FabricKeyId key_id : key_ids_) { + if (key_alias(key_id) == alias) { + num_found_keys++; + } + } + std::vector found_keys; + found_keys.reserve(num_found_keys); + for (FabricKeyId key_id : key_ids_) { + if (key_alias(key_id) == alias) { + found_keys.push_back(key_id); + } + } + return found_keys; +} + std::vector FabricKey::region_keys( const FabricRegionId& region_id) const { /* validate the region_id */ diff --git a/libs/libfabrickey/src/base/fabric_key.h b/libs/libfabrickey/src/base/fabric_key.h index 7afd43af7..5ef9eb56c 100644 --- a/libs/libfabrickey/src/base/fabric_key.h +++ b/libs/libfabrickey/src/base/fabric_key.h @@ -75,6 +75,8 @@ class FabricKey { const FabricKeyModuleId& module_id) const; public: /* Public Accessors: Basic data query */ + size_t num_regions() const; + size_t num_keys() const; /* Access all the keys of a region */ std::vector region_keys(const FabricRegionId& region_id) const; /* Access the name of a key */ @@ -86,6 +88,9 @@ class FabricKey { /* Access the coordinate of a key */ vtr::Point key_coordinate(const FabricKeyId& key_id) const; + /** @brief Find valid key ids for a given alias. Note that you should NOT send an empty alias which may cause a complete list of key ids to be returned (extremely inefficent and NOT useful). Suggest to check if the existing fabric key contains valid alias for each key before calling this API!!! */ + std::vector find_key_by_alias(const std::string& alias) const; + /* Check if there are any keys */ bool empty() const; diff --git a/libs/libfabrickey/src/utils/check_fabric_key.cpp b/libs/libfabrickey/src/utils/check_fabric_key.cpp new file mode 100644 index 000000000..30b9df07c --- /dev/null +++ b/libs/libfabrickey/src/utils/check_fabric_key.cpp @@ -0,0 +1,102 @@ +/************************************************************************ + * Check functions for the content of fabric key to avoid conflicts with + * other data structures + * These functions are not universal methods for the FabricKey class + * They are made to ease the development in some specific purposes + * Please classify such functions in this file + ***********************************************************************/ +#include "check_fabric_key.h" + +#include "vtr_assert.h" +#include "vtr_log.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/** @brief Sanity checks for fabric key alias attribute: + * - Each alias should NOT be empty + * - Each alias should be defined only once! + */ +int check_fabric_key_alias(FabricKey& input_key, const bool& verbose) { + /* Check each key now */ + size_t num_errors = 0; + float progress = 0.; + size_t num_keys_checked = 0; + + std::map alias_count; + for (FabricKeyId key_id : input_key.keys()) { + /* Note that this is slow. May consider to build a map first */ + std::string curr_alias = input_key.key_alias(key_id); + progress = static_cast(num_keys_checked) / static_cast(input_key.num_keys()) * 100.0; + VTR_LOGV(verbose, "[%lu\%] Checking key alias '%s'\r", progress, curr_alias.c_str()); + if (curr_alias.empty()) { + VTR_LOG_ERROR("Empty key alias (id='%lu') found in keys which is invalid!\n", size_t(key_id)); + num_errors++; + } + auto result = alias_count.find(curr_alias); + if (result == alias_count.end()) { + alias_count[curr_alias] = 0; + } else { + alias_count[curr_alias] += 1; + } + num_keys_checked++; + } + for (const auto& kv : alias_count) { + if (kv.second > 1) { + std::string key_id_str; + std::vector found_keys = input_key.find_key_by_alias(curr_alias); + for (FabricKeyId found_key_id : found_keys) { + key_id_str += std::to_string(size_t(found_key_id)) + ","; + } + found_keys.pop(); /* Remove last comma */ + VTR_LOG_ERROR("Duplicated key alias '%s' found %lu times in keys (ids: %s), which is invalid!\n", kv.first.c_str(), kv.second, key_id_str.c_str()); + num_errors++; + } + } + + return num_errors; +} + +/** @brief Sanity checks for fabric key name and value attribute: + * - Each name should not be empty + * - Each value should be larger than zero ! + */ +int check_fabric_key_names_and_values(FabricKey& input_key, const bool& verbose) { + /* Check each key now */ + size_t num_errors = 0; + float progress = 0.; + size_t num_keys_checked = 0; + + std::map> key_value_count; + for (FabricKeyId key_id : input_key.keys()) { + /* Note that this is slow. May consider to build a map first */ + std::string curr_name = input_key.key_name(key_id); + size_t curr_value = input_key.key_value(key_id); + progress = static_cast(num_keys_checked) / static_cast(input_key.num_keys()) * 100.0; + VTR_LOGV(verbose, "[%lu\%] Checking key names and values '(%s, %lu)'\r", progress, curr_name.c_str(), curr_value); + if (curr_name.empty()) { + VTR_LOG_ERROR("Empty key name (id='%lu') found in keys which is invalid!\n", size_t(key_id)); + num_errors++; + } + auto result = key_value_count[curr_name].find(curr_value); + if (result == key_value_count[curr_name].end()) { + key_value_count[curr_name][curr_value] = 0; + } else { + key_value_count[curr_name][curr_value] += 1; + } + num_keys_checked++; + } + for (const auto& key_name_kv : key_value_count) { + for (const auto& key_value_kv : key_name_kv.second) { + if (key_value_kv.second > 1) { + VTR_LOG_ERROR("Duplicated key name and value pair (%s, %lu) found %lu times in keys, which is invalid!\n", key_name_kv.first.c_str(), key_value_kv.first, key_value_kv.second); + num_errors++; + } + } + } + + return num_errors; +} + + +} /* end namespace openfpga */ diff --git a/libs/libfabrickey/src/utils/check_fabric_key.h b/libs/libfabrickey/src/utils/check_fabric_key.h new file mode 100644 index 000000000..4849a99d8 --- /dev/null +++ b/libs/libfabrickey/src/utils/check_fabric_key.h @@ -0,0 +1,22 @@ +#ifndef CHECK_FABRIC_KEY_H +#define CHECK_FABRIC_KEY_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "fabric_key.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int check_fabric_key_alias(const FabricKey& input_key, const bool& verbose); + +int check_fabric_key_names_values(const FabricKey& input_key, const bool& verbose); + +} /* end namespace openfpga */ + +#endif diff --git a/libs/libfabrickey/test/fabric_key_assistant.cpp b/libs/libfabrickey/test/fabric_key_assistant.cpp new file mode 100644 index 000000000..77df8dc47 --- /dev/null +++ b/libs/libfabrickey/test/fabric_key_assistant.cpp @@ -0,0 +1,169 @@ +/******************************************************************** + * Unit test functions to validate the correctness of + * 1. parser of data structures + * 2. writer of data structures + *******************************************************************/ +/* Headers from vtrutils */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from fabric key */ +#include "command_exit_codes.h" +#include "command_echo.h" +#include "command_parser.h" +#include "read_xml_fabric_key.h" +#include "write_xml_fabric_key.h" +#include "check_fabric_key.h" + +/** @brief Initialize the options from command-line inputs and organize in the format that is ready for parsing */ +static std::vector format_argv(const std::string& cmd_name, int argc, const char** argv) { + std::vector cmd_opts; + cmd_opts.push_back(cmd_name); + for (int iarg = 1; iarg < argc; ++iarg) { + cmd_opts.push_back(std::string(argv[iarg])); + } + return cmd_opts; +} + +/** @brief Checks to be done: + * - Number of configuration regions match + * - Number of keys match + */ +static int check_input_key(FabricKey& input_key, const FabricKey& ref_key) { + if (ref_key.num_regions() != input_key.num_regions()) { + VTR_LOG_ERROR("Different number of configuration regions between reference key (='%lu') and input key ('=%lu')!\n", ref_key.num_regions(), input_key.num_regions()); + return CMD_EXEC_FATAL_ERROR; + } + if (ref_key.num_keys() != input_key.num_keys()) { + VTR_LOG_ERROR("Different number of keys between reference key (='%lu') and input key ('=%lu')!\n", ref_key.num_keys(), input_key.num_keys()); + return CMD_EXEC_FATAL_ERROR; + } + size_t num_errors = 0; + size_t curr_num_err = 0; + VTR_LOG("Checking key alias in reference key...\n"); + curr_num_err = check_fabric_key_alias(ref_key, true); + VTR_LOG("Checking key alias in reference key... %s\n", curr_num_err ? "[Fail]" : "[Pass]"); + VTR_LOG("Checking key names and values in reference key...\n"); + curr_num_err = check_fabric_key_names_and_values(ref_key, true); + num_errors += curr_num_err; + VTR_LOG("Checking key names and valus in reference key... %s\n", curr_num_err ? "[Fail]" : "[Pass]"); + VTR_LOG("Checking key alias in input key...\n"); + curr_num_err = check_fabric_key_alias(input_key, true); + num_errors += curr_num_err; + VTR_LOG("Checking key alias in input key... %s\n", curr_num_err ? "[Fail]" : "[Pass]"); + return num_errors ? CMD_EXEC_FATAL_ERROR : CMD_EXEC_SUCCESS; +} + +/** @brief Checks to be done: + * - Each alias of reference key can be found in the input key + */ +static int check_input_and_ref_key_alias_match(FabricKey& input_key, const FabricKey& ref_key) { + size_t num_errors = 0; + size_t num_keys_checked = 0; + float progress = 0.; + VTR_LOG("Checking key alias matching between reference key and input keys...\n"); + for (FabricKeyId key_id : ref_key.keys()) { + /* Note that this is slow. May consider to build a map first */ + std::string curr_alias = ref_key.key_alias(key_id); + std::vector input_found_keys = input_key.find_key_by_alias(curr_alias); + progress = static_cast(num_keys_checked) / static_cast(ref_key.num_keys()) * 100.0; + VTR_LOG("[%lu\%] Checking key alias '%s'\r", progress, curr_alias.c_str()); + if (input_found_keys.empty()) { + VTR_LOG_ERROR("\nInvalid alias '%s' in the reference key (id='%lu'), which does not exist in the input key!\n", curr_alias.c_str(), size_t(key_id)); + num_errors++; + } + if (input_found_keys.size() > 1) { + VTR_LOG_ERROR("\nInvalid alias '%s' in the input key (id='%lu'), which have been found %lu times!\n", curr_alias.c_str(), size_t(key_id)); + num_errors++; + } + num_keys_checked++; + } + return num_errors ? CMD_EXEC_FATAL_ERROR : CMD_EXEC_SUCCESS; +} + +/** @brief Checks to be done: + * - Each alias of input key can be found in the reference key + * - Update input key with pair of name and value which matches the alias from the reference key + */ +static int update_input_key(FabricKey& input_key, const FabricKey& ref_key) { + size_t num_errors = 0; + size_t num_keys_checked = 0; + float progress = 0.; + VTR_LOG("Pairing key alias between reference key and input keys...\n"); + for (FabricKeyId key_id : input_key.keys()) { + /* Note that this is slow. May consider to build a map first */ + std::string curr_alias = input_key.key_alias(key_id); + std::vector ref_found_keys = ref_key.find_key_by_alias(curr_alias); + progress = static_cast(num_keys_checked) / static_cast(input_key.num_keys()) * 100.0; + VTR_LOG("[%lu\%] Pairing key alias '%s'\r", progress, curr_alias.c_str()); + if (ref_found_keys.empty()) { + VTR_LOG_ERROR("\nInvalid alias '%s' in the input key (id='%lu'), which does not exist in the reference key!\n", curr_alias.c_str(), size_t(key_id)); + num_errors++; + } + if (ref_found_keys.size() > 1) { + VTR_LOG_ERROR("\nInvalid alias '%s' in the reference key (id='%lu'), which have been found %lu times!\n", curr_alias.c_str(), size_t(key_id)); + num_errors++; + } + /* Now we have a key, get the name and value, and update input key */ + input_key.set_key_name(key_id, ref_key.key_name(ref_found_keys[0])); + input_key.set_key_value(key_id, ref_key.key_value(ref_found_keys[0])); + VTR_LOG("[%lu\%] Pairing key alias '%s' -> ('%s', %lu)\r", progress, curr_alias.c_str(), input_key.key_name(key_id).c_str(), input_key.key_value(key_id)); + num_keys_checked++; + } + return num_errors ? CMD_EXEC_FATAL_ERROR : CMD_EXEC_SUCCESS; +} + +/** @brief Checks to be done: + * - Number of configuration regions match + * - Number of keys match + * - Each alias can be found in the reference key + */ +static int check_and_update_input_key(FabricKey& input_key, const FabricKey& ref_key) { + int status = CMD_EXEC_SUCCESS; + status = check_input_key(input_key, ref_key); + if (status != CMD_EXEC_SUCCESS) { + return CMD_EXEC_FATAL_ERROR; + } + return update_input_key(input_key, ref_key); +} + + +int main(int argc, const char** argv) { + /* Create a new command and Initialize the options available in the user interface */ + openfpga::Command cmd("fabric_key_assistant"); + openfpga::CommandOptionId opt_ref = cmd.add_option("reference", true, "Specify the reference fabric key file"); + cmd.set_option_require_value(opt_ref, OPT_STRING); + openfpga::CommandOptionId opt_input = cmd.add_option("input", true, "Specify the hand-crafted fabric key file"); + cmd.set_option_require_value(opt_input, OPT_STRING); + openfpga::CommandOptionId opt_output = cmd.add_option("output", true, "Specify the final fabric key file to be outputted"); + cmd.set_option_require_value(opt_output, OPT_STRING); + openfpga::CommandOptionId opt_help = cmd.add_option("help", true, "Show help desk"); + + /* Parse the option, to avoid issues, we use the command name to replace the + * argv[0] */ + std::vector cmd_opts = format_argv(cmd.name(), argc, argv); + + CommandContext cmd_ctx(cmd); + if (false == parse_command(cmd_opts, cmd, cmd_ctx)) { + /* Echo the command */ + print_command_options(cmd); + } else { + /* Let user to confirm selected options */ + print_command_context(cmd, cmd_ctx); + } + + /* Parse the fabric key from an XML file */ + VTR_LOG("Read the reference fabric key from an XML file: %s.\n", cmd_ctx.option_value(cmd, opt_ref).c_str()); + openfpga::FabricKey ref_key = openfpga::read_xml_fabric_key(cmd_ctx.option_value(cmd, opt_ref).c_str()); + + VTR_LOG("Read the hand-crafted fabric key from an XML file: %s.\n", cmd_ctx.option_value(cmd, opt_input).c_str()); + openfpga::FabricKey input_key = openfpga::read_xml_fabric_key(cmd_ctx.option_value(cmd, opt_input).c_str()); + + /* Check the input key */ + if (check_and_update_input_key(input_key, ref_key)) { + return openfpga::CMD_EXEC_FATAL_ERROR; + } + + VTR_LOG("Write the final fabric key to an XML file: %s.\n", cmd_ctx.option_value(cmd, opt_output).c_str()); + return openfpga::write_xml_fabric_key(cmd_ctx.option_value(cmd, opt_output).c_str(), input_key); +} From cfaae55bda85b84073f78fcc9c5f7a53b53b8fc6 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 26 Aug 2023 13:19:18 -0700 Subject: [PATCH 372/391] [lib] debugged fabric key assistant --- libs/libfabrickey/src/base/fabric_key.cpp | 18 +- libs/libfabrickey/src/base/fabric_key.h | 5 +- .../src/utils/check_fabric_key.cpp | 41 ++-- .../libfabrickey/src/utils/check_fabric_key.h | 3 +- .../test/fabric_key_assistant.cpp | 202 +++++++++++------- 5 files changed, 172 insertions(+), 97 deletions(-) diff --git a/libs/libfabrickey/src/base/fabric_key.cpp b/libs/libfabrickey/src/base/fabric_key.cpp index 12b92846b..a6558a258 100644 --- a/libs/libfabrickey/src/base/fabric_key.cpp +++ b/libs/libfabrickey/src/base/fabric_key.cpp @@ -55,18 +55,18 @@ std::vector FabricKey::sub_keys( /************************************************************************ * Public Accessors : Basic data query ***********************************************************************/ -size_t FabricKey::num_regions() const { - return region_ids_.size(); -} +size_t FabricKey::num_regions() const { return region_ids_.size(); } -size_t FabricKey::num_keys() const { - return key_ids_.size(); -} +size_t FabricKey::num_keys() const { return key_ids_.size(); } -std::vector find_key_by_alias(const std::string& alias) const { - /* Throw warning on empty alias which may cause unexpected results: whole key is dumped! */ +std::vector FabricKey::find_key_by_alias( + const std::string& alias) const { + /* Throw warning on empty alias which may cause unexpected results: whole key + * is dumped! */ if (alias.empty()) { - VTR_LOG_WARN("Empty alias is given! This may cause unexpected results, i.e., a whole data base is dumped!\n"); + VTR_LOG_WARN( + "Empty alias is given! This may cause unexpected results, i.e., a whole " + "data base is dumped!\n"); } size_t num_found_keys = 0; for (FabricKeyId key_id : key_ids_) { diff --git a/libs/libfabrickey/src/base/fabric_key.h b/libs/libfabrickey/src/base/fabric_key.h index 5ef9eb56c..3982cb8b8 100644 --- a/libs/libfabrickey/src/base/fabric_key.h +++ b/libs/libfabrickey/src/base/fabric_key.h @@ -88,7 +88,10 @@ class FabricKey { /* Access the coordinate of a key */ vtr::Point key_coordinate(const FabricKeyId& key_id) const; - /** @brief Find valid key ids for a given alias. Note that you should NOT send an empty alias which may cause a complete list of key ids to be returned (extremely inefficent and NOT useful). Suggest to check if the existing fabric key contains valid alias for each key before calling this API!!! */ + /** @brief Find valid key ids for a given alias. Note that you should NOT send + * an empty alias which may cause a complete list of key ids to be returned + * (extremely inefficent and NOT useful). Suggest to check if the existing + * fabric key contains valid alias for each key before calling this API!!! */ std::vector find_key_by_alias(const std::string& alias) const; /* Check if there are any keys */ diff --git a/libs/libfabrickey/src/utils/check_fabric_key.cpp b/libs/libfabrickey/src/utils/check_fabric_key.cpp index 30b9df07c..1e8964a33 100644 --- a/libs/libfabrickey/src/utils/check_fabric_key.cpp +++ b/libs/libfabrickey/src/utils/check_fabric_key.cpp @@ -17,7 +17,7 @@ namespace openfpga { * - Each alias should NOT be empty * - Each alias should be defined only once! */ -int check_fabric_key_alias(FabricKey& input_key, const bool& verbose) { +int check_fabric_key_alias(const FabricKey& input_key, const bool& verbose) { /* Check each key now */ size_t num_errors = 0; float progress = 0.; @@ -27,10 +27,14 @@ int check_fabric_key_alias(FabricKey& input_key, const bool& verbose) { for (FabricKeyId key_id : input_key.keys()) { /* Note that this is slow. May consider to build a map first */ std::string curr_alias = input_key.key_alias(key_id); - progress = static_cast(num_keys_checked) / static_cast(input_key.num_keys()) * 100.0; - VTR_LOGV(verbose, "[%lu\%] Checking key alias '%s'\r", progress, curr_alias.c_str()); + progress = static_cast(num_keys_checked) / + static_cast(input_key.num_keys()) * 100.0; + VTR_LOGV(verbose, "[%lu%] Checking key alias '%s'\r", size_t(progress), + curr_alias.c_str()); if (curr_alias.empty()) { - VTR_LOG_ERROR("Empty key alias (id='%lu') found in keys which is invalid!\n", size_t(key_id)); + VTR_LOG_ERROR( + "Empty key alias (id='%lu') found in keys which is invalid!\n", + size_t(key_id)); num_errors++; } auto result = alias_count.find(curr_alias); @@ -44,12 +48,16 @@ int check_fabric_key_alias(FabricKey& input_key, const bool& verbose) { for (const auto& kv : alias_count) { if (kv.second > 1) { std::string key_id_str; - std::vector found_keys = input_key.find_key_by_alias(curr_alias); + std::vector found_keys = + input_key.find_key_by_alias(kv.first); for (FabricKeyId found_key_id : found_keys) { key_id_str += std::to_string(size_t(found_key_id)) + ","; } - found_keys.pop(); /* Remove last comma */ - VTR_LOG_ERROR("Duplicated key alias '%s' found %lu times in keys (ids: %s), which is invalid!\n", kv.first.c_str(), kv.second, key_id_str.c_str()); + key_id_str.pop_back(); /* Remove last comma */ + VTR_LOG_ERROR( + "Duplicated key alias '%s' found %lu times in keys (ids: %s), which is " + "invalid!\n", + kv.first.c_str(), kv.second, key_id_str.c_str()); num_errors++; } } @@ -61,7 +69,8 @@ int check_fabric_key_alias(FabricKey& input_key, const bool& verbose) { * - Each name should not be empty * - Each value should be larger than zero ! */ -int check_fabric_key_names_and_values(FabricKey& input_key, const bool& verbose) { +int check_fabric_key_names_and_values(const FabricKey& input_key, + const bool& verbose) { /* Check each key now */ size_t num_errors = 0; float progress = 0.; @@ -72,10 +81,14 @@ int check_fabric_key_names_and_values(FabricKey& input_key, const bool& verbose) /* Note that this is slow. May consider to build a map first */ std::string curr_name = input_key.key_name(key_id); size_t curr_value = input_key.key_value(key_id); - progress = static_cast(num_keys_checked) / static_cast(input_key.num_keys()) * 100.0; - VTR_LOGV(verbose, "[%lu\%] Checking key names and values '(%s, %lu)'\r", progress, curr_name.c_str(), curr_value); + progress = static_cast(num_keys_checked) / + static_cast(input_key.num_keys()) * 100.0; + VTR_LOGV(verbose, "[%lu%] Checking key names and values '(%s, %lu)'\r", + size_t(progress), curr_name.c_str(), curr_value); if (curr_name.empty()) { - VTR_LOG_ERROR("Empty key name (id='%lu') found in keys which is invalid!\n", size_t(key_id)); + VTR_LOG_ERROR( + "Empty key name (id='%lu') found in keys which is invalid!\n", + size_t(key_id)); num_errors++; } auto result = key_value_count[curr_name].find(curr_value); @@ -89,7 +102,10 @@ int check_fabric_key_names_and_values(FabricKey& input_key, const bool& verbose) for (const auto& key_name_kv : key_value_count) { for (const auto& key_value_kv : key_name_kv.second) { if (key_value_kv.second > 1) { - VTR_LOG_ERROR("Duplicated key name and value pair (%s, %lu) found %lu times in keys, which is invalid!\n", key_name_kv.first.c_str(), key_value_kv.first, key_value_kv.second); + VTR_LOG_ERROR( + "Duplicated key name and value pair (%s, %lu) found %lu times in " + "keys, which is invalid!\n", + key_name_kv.first.c_str(), key_value_kv.first, key_value_kv.second); num_errors++; } } @@ -98,5 +114,4 @@ int check_fabric_key_names_and_values(FabricKey& input_key, const bool& verbose) return num_errors; } - } /* end namespace openfpga */ diff --git a/libs/libfabrickey/src/utils/check_fabric_key.h b/libs/libfabrickey/src/utils/check_fabric_key.h index 4849a99d8..63f3f31db 100644 --- a/libs/libfabrickey/src/utils/check_fabric_key.h +++ b/libs/libfabrickey/src/utils/check_fabric_key.h @@ -15,7 +15,8 @@ namespace openfpga { int check_fabric_key_alias(const FabricKey& input_key, const bool& verbose); -int check_fabric_key_names_values(const FabricKey& input_key, const bool& verbose); +int check_fabric_key_names_and_values(const FabricKey& input_key, + const bool& verbose); } /* end namespace openfpga */ diff --git a/libs/libfabrickey/test/fabric_key_assistant.cpp b/libs/libfabrickey/test/fabric_key_assistant.cpp index 77df8dc47..fc69aa583 100644 --- a/libs/libfabrickey/test/fabric_key_assistant.cpp +++ b/libs/libfabrickey/test/fabric_key_assistant.cpp @@ -8,15 +8,17 @@ #include "vtr_log.h" /* Headers from fabric key */ -#include "command_exit_codes.h" +#include "check_fabric_key.h" #include "command_echo.h" +#include "command_exit_codes.h" #include "command_parser.h" #include "read_xml_fabric_key.h" #include "write_xml_fabric_key.h" -#include "check_fabric_key.h" -/** @brief Initialize the options from command-line inputs and organize in the format that is ready for parsing */ -static std::vector format_argv(const std::string& cmd_name, int argc, const char** argv) { +/** @brief Initialize the options from command-line inputs and organize in the + * format that is ready for parsing */ +static std::vector format_argv(const std::string& cmd_name, + int argc, const char** argv) { std::vector cmd_opts; cmd_opts.push_back(cmd_name); for (int iarg = 1; iarg < argc; ++iarg) { @@ -25,92 +27,133 @@ static std::vector format_argv(const std::string& cmd_name, int arg return cmd_opts; } -/** @brief Checks to be done: - * - Number of configuration regions match - * - Number of keys match - */ -static int check_input_key(FabricKey& input_key, const FabricKey& ref_key) { - if (ref_key.num_regions() != input_key.num_regions()) { - VTR_LOG_ERROR("Different number of configuration regions between reference key (='%lu') and input key ('=%lu')!\n", ref_key.num_regions(), input_key.num_regions()); - return CMD_EXEC_FATAL_ERROR; - } - if (ref_key.num_keys() != input_key.num_keys()) { - VTR_LOG_ERROR("Different number of keys between reference key (='%lu') and input key ('=%lu')!\n", ref_key.num_keys(), input_key.num_keys()); - return CMD_EXEC_FATAL_ERROR; - } - size_t num_errors = 0; - size_t curr_num_err = 0; - VTR_LOG("Checking key alias in reference key...\n"); - curr_num_err = check_fabric_key_alias(ref_key, true); - VTR_LOG("Checking key alias in reference key... %s\n", curr_num_err ? "[Fail]" : "[Pass]"); - VTR_LOG("Checking key names and values in reference key...\n"); - curr_num_err = check_fabric_key_names_and_values(ref_key, true); - num_errors += curr_num_err; - VTR_LOG("Checking key names and valus in reference key... %s\n", curr_num_err ? "[Fail]" : "[Pass]"); - VTR_LOG("Checking key alias in input key...\n"); - curr_num_err = check_fabric_key_alias(input_key, true); - num_errors += curr_num_err; - VTR_LOG("Checking key alias in input key... %s\n", curr_num_err ? "[Fail]" : "[Pass]"); - return num_errors ? CMD_EXEC_FATAL_ERROR : CMD_EXEC_SUCCESS; -} - /** @brief Checks to be done: * - Each alias of reference key can be found in the input key */ -static int check_input_and_ref_key_alias_match(FabricKey& input_key, const FabricKey& ref_key) { +static int check_input_and_ref_key_alias_match( + const openfpga::FabricKey& input_key, const openfpga::FabricKey& ref_key) { size_t num_errors = 0; size_t num_keys_checked = 0; float progress = 0.; - VTR_LOG("Checking key alias matching between reference key and input keys...\n"); - for (FabricKeyId key_id : ref_key.keys()) { + VTR_LOG( + "Checking key alias matching between reference key and input keys...\n"); + for (openfpga::FabricKeyId key_id : ref_key.keys()) { /* Note that this is slow. May consider to build a map first */ std::string curr_alias = ref_key.key_alias(key_id); - std::vector input_found_keys = input_key.find_key_by_alias(curr_alias); - progress = static_cast(num_keys_checked) / static_cast(ref_key.num_keys()) * 100.0; - VTR_LOG("[%lu\%] Checking key alias '%s'\r", progress, curr_alias.c_str()); + std::vector input_found_keys = + input_key.find_key_by_alias(curr_alias); + progress = static_cast(num_keys_checked) / + static_cast(ref_key.num_keys()) * 100.0; + VTR_LOG("[%lu%] Checking key alias '%s'\r", size_t(progress), + curr_alias.c_str()); if (input_found_keys.empty()) { - VTR_LOG_ERROR("\nInvalid alias '%s' in the reference key (id='%lu'), which does not exist in the input key!\n", curr_alias.c_str(), size_t(key_id)); + VTR_LOG_ERROR( + "\nInvalid alias '%s' in the reference key (id='%lu'), which does not " + "exist in the input key!\n", + curr_alias.c_str(), size_t(key_id)); num_errors++; } if (input_found_keys.size() > 1) { - VTR_LOG_ERROR("\nInvalid alias '%s' in the input key (id='%lu'), which have been found %lu times!\n", curr_alias.c_str(), size_t(key_id)); + VTR_LOG_ERROR( + "\nInvalid alias '%s' in the input key (id='%lu'), which have been " + "found %lu times!\n", + curr_alias.c_str(), size_t(key_id)); num_errors++; } num_keys_checked++; } - return num_errors ? CMD_EXEC_FATAL_ERROR : CMD_EXEC_SUCCESS; + VTR_LOG( + "Checking key alias matching between reference key and input keys... %s\n", + num_errors ? "[Fail]" : "[Pass]"); + return num_errors ? openfpga::CMD_EXEC_FATAL_ERROR + : openfpga::CMD_EXEC_SUCCESS; +} + +/** @brief Checks to be done: + * - Number of configuration regions match + * - Number of keys match + */ +static int check_input_key(const openfpga::FabricKey& input_key, + const openfpga::FabricKey& ref_key) { + if (ref_key.num_regions() != input_key.num_regions()) { + VTR_LOG_ERROR( + "Different number of configuration regions between reference key " + "(='%lu') and input key ('=%lu')!\n", + ref_key.num_regions(), input_key.num_regions()); + return openfpga::CMD_EXEC_FATAL_ERROR; + } + if (ref_key.num_keys() != input_key.num_keys()) { + VTR_LOG_ERROR( + "Different number of keys between reference key (='%lu') and input key " + "('=%lu')!\n", + ref_key.num_keys(), input_key.num_keys()); + return openfpga::CMD_EXEC_FATAL_ERROR; + } + size_t num_errors = 0; + size_t curr_num_err = 0; + VTR_LOG("Checking key alias in reference key...\n"); + curr_num_err = openfpga::check_fabric_key_alias(ref_key, true); + VTR_LOG("Checking key alias in reference key... %s\n", + curr_num_err ? "[Fail]" : "[Pass]"); + VTR_LOG("Checking key names and values in reference key...\n"); + curr_num_err = openfpga::check_fabric_key_names_and_values(ref_key, true); + num_errors += curr_num_err; + VTR_LOG("Checking key names and valus in reference key... %s\n", + curr_num_err ? "[Fail]" : "[Pass]"); + VTR_LOG("Checking key alias in input key...\n"); + curr_num_err = openfpga::check_fabric_key_alias(input_key, true); + num_errors += curr_num_err; + VTR_LOG("Checking key alias in input key... %s\n", + curr_num_err ? "[Fail]" : "[Pass]"); + num_errors += check_input_and_ref_key_alias_match(input_key, ref_key); + return num_errors ? openfpga::CMD_EXEC_FATAL_ERROR + : openfpga::CMD_EXEC_SUCCESS; } /** @brief Checks to be done: * - Each alias of input key can be found in the reference key - * - Update input key with pair of name and value which matches the alias from the reference key + * - Update input key with pair of name and value which matches the alias from + * the reference key */ -static int update_input_key(FabricKey& input_key, const FabricKey& ref_key) { +static int update_input_key(openfpga::FabricKey& input_key, + const openfpga::FabricKey& ref_key) { size_t num_errors = 0; size_t num_keys_checked = 0; float progress = 0.; VTR_LOG("Pairing key alias between reference key and input keys...\n"); - for (FabricKeyId key_id : input_key.keys()) { + for (openfpga::FabricKeyId key_id : input_key.keys()) { /* Note that this is slow. May consider to build a map first */ std::string curr_alias = input_key.key_alias(key_id); - std::vector ref_found_keys = ref_key.find_key_by_alias(curr_alias); - progress = static_cast(num_keys_checked) / static_cast(input_key.num_keys()) * 100.0; - VTR_LOG("[%lu\%] Pairing key alias '%s'\r", progress, curr_alias.c_str()); + std::vector ref_found_keys = + ref_key.find_key_by_alias(curr_alias); + progress = static_cast(num_keys_checked) / + static_cast(input_key.num_keys()) * 100.0; + VTR_LOG("[%lu%] Pairing key alias '%s'\r", size_t(progress), + curr_alias.c_str()); if (ref_found_keys.empty()) { - VTR_LOG_ERROR("\nInvalid alias '%s' in the input key (id='%lu'), which does not exist in the reference key!\n", curr_alias.c_str(), size_t(key_id)); + VTR_LOG_ERROR( + "\nInvalid alias '%s' in the input key (id='%lu'), which does not " + "exist in the reference key!\n", + curr_alias.c_str(), size_t(key_id)); num_errors++; } if (ref_found_keys.size() > 1) { - VTR_LOG_ERROR("\nInvalid alias '%s' in the reference key (id='%lu'), which have been found %lu times!\n", curr_alias.c_str(), size_t(key_id)); + VTR_LOG_ERROR( + "\nInvalid alias '%s' in the reference key (id='%lu'), which have been " + "found %lu times!\n", + curr_alias.c_str(), size_t(key_id)); num_errors++; } /* Now we have a key, get the name and value, and update input key */ input_key.set_key_name(key_id, ref_key.key_name(ref_found_keys[0])); input_key.set_key_value(key_id, ref_key.key_value(ref_found_keys[0])); - VTR_LOG("[%lu\%] Pairing key alias '%s' -> ('%s', %lu)\r", progress, curr_alias.c_str(), input_key.key_name(key_id).c_str(), input_key.key_value(key_id)); + VTR_LOG("[%lu%] Pairing key alias '%s' -> ('%s', %lu)\r", size_t(progress), + curr_alias.c_str(), input_key.key_name(key_id).c_str(), + input_key.key_value(key_id)); num_keys_checked++; } - return num_errors ? CMD_EXEC_FATAL_ERROR : CMD_EXEC_SUCCESS; + return num_errors ? openfpga::CMD_EXEC_FATAL_ERROR + : openfpga::CMD_EXEC_SUCCESS; } /** @brief Checks to be done: @@ -118,52 +161,65 @@ static int update_input_key(FabricKey& input_key, const FabricKey& ref_key) { * - Number of keys match * - Each alias can be found in the reference key */ -static int check_and_update_input_key(FabricKey& input_key, const FabricKey& ref_key) { - int status = CMD_EXEC_SUCCESS; +static int check_and_update_input_key(openfpga::FabricKey& input_key, + const openfpga::FabricKey& ref_key) { + int status = openfpga::CMD_EXEC_SUCCESS; status = check_input_key(input_key, ref_key); - if (status != CMD_EXEC_SUCCESS) { - return CMD_EXEC_FATAL_ERROR; + if (status != openfpga::CMD_EXEC_SUCCESS) { + return openfpga::CMD_EXEC_FATAL_ERROR; } return update_input_key(input_key, ref_key); } - int main(int argc, const char** argv) { - /* Create a new command and Initialize the options available in the user interface */ + /* Create a new command and Initialize the options available in the user + * interface */ openfpga::Command cmd("fabric_key_assistant"); - openfpga::CommandOptionId opt_ref = cmd.add_option("reference", true, "Specify the reference fabric key file"); - cmd.set_option_require_value(opt_ref, OPT_STRING); - openfpga::CommandOptionId opt_input = cmd.add_option("input", true, "Specify the hand-crafted fabric key file"); - cmd.set_option_require_value(opt_input, OPT_STRING); - openfpga::CommandOptionId opt_output = cmd.add_option("output", true, "Specify the final fabric key file to be outputted"); - cmd.set_option_require_value(opt_output, OPT_STRING); - openfpga::CommandOptionId opt_help = cmd.add_option("help", true, "Show help desk"); + openfpga::CommandOptionId opt_ref = + cmd.add_option("reference", true, "Specify the reference fabric key file"); + cmd.set_option_require_value(opt_ref, openfpga::OPT_STRING); + openfpga::CommandOptionId opt_input = + cmd.add_option("input", true, "Specify the hand-crafted fabric key file"); + cmd.set_option_require_value(opt_input, openfpga::OPT_STRING); + openfpga::CommandOptionId opt_output = cmd.add_option( + "output", true, "Specify the final fabric key file to be outputted"); + cmd.set_option_require_value(opt_output, openfpga::OPT_STRING); + openfpga::CommandOptionId opt_help = + cmd.add_option("help", false, "Show help desk"); /* Parse the option, to avoid issues, we use the command name to replace the * argv[0] */ std::vector cmd_opts = format_argv(cmd.name(), argc, argv); - CommandContext cmd_ctx(cmd); - if (false == parse_command(cmd_opts, cmd, cmd_ctx)) { + openfpga::CommandContext cmd_ctx(cmd); + if (false == parse_command(cmd_opts, cmd, cmd_ctx) || + cmd_ctx.option_enable(cmd, opt_help)) { /* Echo the command */ print_command_options(cmd); + return openfpga::CMD_EXEC_FATAL_ERROR; } else { /* Let user to confirm selected options */ print_command_context(cmd, cmd_ctx); } /* Parse the fabric key from an XML file */ - VTR_LOG("Read the reference fabric key from an XML file: %s.\n", cmd_ctx.option_value(cmd, opt_ref).c_str()); - openfpga::FabricKey ref_key = openfpga::read_xml_fabric_key(cmd_ctx.option_value(cmd, opt_ref).c_str()); + VTR_LOG("Read the reference fabric key from an XML file: %s.\n", + cmd_ctx.option_value(cmd, opt_ref).c_str()); + openfpga::FabricKey ref_key = + openfpga::read_xml_fabric_key(cmd_ctx.option_value(cmd, opt_ref).c_str()); - VTR_LOG("Read the hand-crafted fabric key from an XML file: %s.\n", cmd_ctx.option_value(cmd, opt_input).c_str()); - openfpga::FabricKey input_key = openfpga::read_xml_fabric_key(cmd_ctx.option_value(cmd, opt_input).c_str()); + VTR_LOG("Read the hand-crafted fabric key from an XML file: %s.\n", + cmd_ctx.option_value(cmd, opt_input).c_str()); + openfpga::FabricKey input_key = + openfpga::read_xml_fabric_key(cmd_ctx.option_value(cmd, opt_input).c_str()); /* Check the input key */ if (check_and_update_input_key(input_key, ref_key)) { return openfpga::CMD_EXEC_FATAL_ERROR; } - VTR_LOG("Write the final fabric key to an XML file: %s.\n", cmd_ctx.option_value(cmd, opt_output).c_str()); - return openfpga::write_xml_fabric_key(cmd_ctx.option_value(cmd, opt_output).c_str(), input_key); + VTR_LOG("Write the final fabric key to an XML file: %s.\n", + cmd_ctx.option_value(cmd, opt_output).c_str()); + return openfpga::write_xml_fabric_key( + cmd_ctx.option_value(cmd, opt_output).c_str(), input_key); } From 97619fc5458b56a035231740475e2a9bee7bb45c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 26 Aug 2023 18:07:08 -0700 Subject: [PATCH 373/391] [lib] add verbose output option to fabric key assistant --- .../test/fabric_key_assistant.cpp | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/libs/libfabrickey/test/fabric_key_assistant.cpp b/libs/libfabrickey/test/fabric_key_assistant.cpp index fc69aa583..54831081e 100644 --- a/libs/libfabrickey/test/fabric_key_assistant.cpp +++ b/libs/libfabrickey/test/fabric_key_assistant.cpp @@ -31,7 +31,7 @@ static std::vector format_argv(const std::string& cmd_name, * - Each alias of reference key can be found in the input key */ static int check_input_and_ref_key_alias_match( - const openfpga::FabricKey& input_key, const openfpga::FabricKey& ref_key) { + const openfpga::FabricKey& input_key, const openfpga::FabricKey& ref_key, const bool& verbose) { size_t num_errors = 0; size_t num_keys_checked = 0; float progress = 0.; @@ -44,7 +44,7 @@ static int check_input_and_ref_key_alias_match( input_key.find_key_by_alias(curr_alias); progress = static_cast(num_keys_checked) / static_cast(ref_key.num_keys()) * 100.0; - VTR_LOG("[%lu%] Checking key alias '%s'\r", size_t(progress), + VTR_LOGV(verbose, "[%lu%] Checking key alias '%s'\r", size_t(progress), curr_alias.c_str()); if (input_found_keys.empty()) { VTR_LOG_ERROR( @@ -74,7 +74,8 @@ static int check_input_and_ref_key_alias_match( * - Number of keys match */ static int check_input_key(const openfpga::FabricKey& input_key, - const openfpga::FabricKey& ref_key) { + const openfpga::FabricKey& ref_key, + const bool& verbose) { if (ref_key.num_regions() != input_key.num_regions()) { VTR_LOG_ERROR( "Different number of configuration regions between reference key " @@ -92,20 +93,20 @@ static int check_input_key(const openfpga::FabricKey& input_key, size_t num_errors = 0; size_t curr_num_err = 0; VTR_LOG("Checking key alias in reference key...\n"); - curr_num_err = openfpga::check_fabric_key_alias(ref_key, true); + curr_num_err = openfpga::check_fabric_key_alias(ref_key, verbose); VTR_LOG("Checking key alias in reference key... %s\n", curr_num_err ? "[Fail]" : "[Pass]"); VTR_LOG("Checking key names and values in reference key...\n"); - curr_num_err = openfpga::check_fabric_key_names_and_values(ref_key, true); + curr_num_err = openfpga::check_fabric_key_names_and_values(ref_key, verbose); num_errors += curr_num_err; VTR_LOG("Checking key names and valus in reference key... %s\n", curr_num_err ? "[Fail]" : "[Pass]"); VTR_LOG("Checking key alias in input key...\n"); - curr_num_err = openfpga::check_fabric_key_alias(input_key, true); + curr_num_err = openfpga::check_fabric_key_alias(input_key, verbose); num_errors += curr_num_err; VTR_LOG("Checking key alias in input key... %s\n", curr_num_err ? "[Fail]" : "[Pass]"); - num_errors += check_input_and_ref_key_alias_match(input_key, ref_key); + num_errors += check_input_and_ref_key_alias_match(input_key, ref_key, verbose); return num_errors ? openfpga::CMD_EXEC_FATAL_ERROR : openfpga::CMD_EXEC_SUCCESS; } @@ -116,7 +117,8 @@ static int check_input_key(const openfpga::FabricKey& input_key, * the reference key */ static int update_input_key(openfpga::FabricKey& input_key, - const openfpga::FabricKey& ref_key) { + const openfpga::FabricKey& ref_key, + const bool& verbose) { size_t num_errors = 0; size_t num_keys_checked = 0; float progress = 0.; @@ -128,7 +130,7 @@ static int update_input_key(openfpga::FabricKey& input_key, ref_key.find_key_by_alias(curr_alias); progress = static_cast(num_keys_checked) / static_cast(input_key.num_keys()) * 100.0; - VTR_LOG("[%lu%] Pairing key alias '%s'\r", size_t(progress), + VTR_LOGV(verbose, "[%lu%] Pairing key alias '%s'\r", size_t(progress), curr_alias.c_str()); if (ref_found_keys.empty()) { VTR_LOG_ERROR( @@ -147,7 +149,7 @@ static int update_input_key(openfpga::FabricKey& input_key, /* Now we have a key, get the name and value, and update input key */ input_key.set_key_name(key_id, ref_key.key_name(ref_found_keys[0])); input_key.set_key_value(key_id, ref_key.key_value(ref_found_keys[0])); - VTR_LOG("[%lu%] Pairing key alias '%s' -> ('%s', %lu)\r", size_t(progress), + VTR_LOGV(verbose, "[%lu%] Pairing key alias '%s' -> ('%s', %lu)\r", size_t(progress), curr_alias.c_str(), input_key.key_name(key_id).c_str(), input_key.key_value(key_id)); num_keys_checked++; @@ -162,13 +164,14 @@ static int update_input_key(openfpga::FabricKey& input_key, * - Each alias can be found in the reference key */ static int check_and_update_input_key(openfpga::FabricKey& input_key, - const openfpga::FabricKey& ref_key) { + const openfpga::FabricKey& ref_key, + const bool& verbose) { int status = openfpga::CMD_EXEC_SUCCESS; - status = check_input_key(input_key, ref_key); + status = check_input_key(input_key, ref_key, verbose); if (status != openfpga::CMD_EXEC_SUCCESS) { return openfpga::CMD_EXEC_FATAL_ERROR; } - return update_input_key(input_key, ref_key); + return update_input_key(input_key, ref_key, verbose); } int main(int argc, const char** argv) { @@ -184,6 +187,8 @@ int main(int argc, const char** argv) { openfpga::CommandOptionId opt_output = cmd.add_option( "output", true, "Specify the final fabric key file to be outputted"); cmd.set_option_require_value(opt_output, openfpga::OPT_STRING); + openfpga::CommandOptionId opt_verbose = + cmd.add_option("verbose", false, "Show verbose outputs"); openfpga::CommandOptionId opt_help = cmd.add_option("help", false, "Show help desk"); @@ -214,7 +219,7 @@ int main(int argc, const char** argv) { openfpga::read_xml_fabric_key(cmd_ctx.option_value(cmd, opt_input).c_str()); /* Check the input key */ - if (check_and_update_input_key(input_key, ref_key)) { + if (check_and_update_input_key(input_key, ref_key, cmd_ctx.option_enable(cmd, opt_verbose))) { return openfpga::CMD_EXEC_FATAL_ERROR; } From ac5873bac290d96546d9c0526879022e5458d21d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 26 Aug 2023 18:12:25 -0700 Subject: [PATCH 374/391] [lib] fixed some bugs in message show --- libs/libfabrickey/test/fabric_key_assistant.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/libfabrickey/test/fabric_key_assistant.cpp b/libs/libfabrickey/test/fabric_key_assistant.cpp index 54831081e..380a730a5 100644 --- a/libs/libfabrickey/test/fabric_key_assistant.cpp +++ b/libs/libfabrickey/test/fabric_key_assistant.cpp @@ -57,7 +57,7 @@ static int check_input_and_ref_key_alias_match( VTR_LOG_ERROR( "\nInvalid alias '%s' in the input key (id='%lu'), which have been " "found %lu times!\n", - curr_alias.c_str(), size_t(key_id)); + curr_alias.c_str(), size_t(key_id), input_found_keys.size()); num_errors++; } num_keys_checked++; From 3273728bc3f2762fe7d76fa5c6c341ed63264320 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 26 Aug 2023 18:15:30 -0700 Subject: [PATCH 375/391] [lib] code format --- .../test/fabric_key_assistant.cpp | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/libs/libfabrickey/test/fabric_key_assistant.cpp b/libs/libfabrickey/test/fabric_key_assistant.cpp index 380a730a5..c0befce57 100644 --- a/libs/libfabrickey/test/fabric_key_assistant.cpp +++ b/libs/libfabrickey/test/fabric_key_assistant.cpp @@ -31,7 +31,8 @@ static std::vector format_argv(const std::string& cmd_name, * - Each alias of reference key can be found in the input key */ static int check_input_and_ref_key_alias_match( - const openfpga::FabricKey& input_key, const openfpga::FabricKey& ref_key, const bool& verbose) { + const openfpga::FabricKey& input_key, const openfpga::FabricKey& ref_key, + const bool& verbose) { size_t num_errors = 0; size_t num_keys_checked = 0; float progress = 0.; @@ -45,17 +46,17 @@ static int check_input_and_ref_key_alias_match( progress = static_cast(num_keys_checked) / static_cast(ref_key.num_keys()) * 100.0; VTR_LOGV(verbose, "[%lu%] Checking key alias '%s'\r", size_t(progress), - curr_alias.c_str()); + curr_alias.c_str()); if (input_found_keys.empty()) { VTR_LOG_ERROR( - "\nInvalid alias '%s' in the reference key (id='%lu'), which does not " + "Invalid alias '%s' in the reference key (id='%lu'), which does not " "exist in the input key!\n", curr_alias.c_str(), size_t(key_id)); num_errors++; } if (input_found_keys.size() > 1) { VTR_LOG_ERROR( - "\nInvalid alias '%s' in the input key (id='%lu'), which have been " + "Invalid alias '%s' in the input key (id='%lu'), which have been " "found %lu times!\n", curr_alias.c_str(), size_t(key_id), input_found_keys.size()); num_errors++; @@ -106,7 +107,8 @@ static int check_input_key(const openfpga::FabricKey& input_key, num_errors += curr_num_err; VTR_LOG("Checking key alias in input key... %s\n", curr_num_err ? "[Fail]" : "[Pass]"); - num_errors += check_input_and_ref_key_alias_match(input_key, ref_key, verbose); + num_errors += + check_input_and_ref_key_alias_match(input_key, ref_key, verbose); return num_errors ? openfpga::CMD_EXEC_FATAL_ERROR : openfpga::CMD_EXEC_SUCCESS; } @@ -131,17 +133,17 @@ static int update_input_key(openfpga::FabricKey& input_key, progress = static_cast(num_keys_checked) / static_cast(input_key.num_keys()) * 100.0; VTR_LOGV(verbose, "[%lu%] Pairing key alias '%s'\r", size_t(progress), - curr_alias.c_str()); + curr_alias.c_str()); if (ref_found_keys.empty()) { VTR_LOG_ERROR( - "\nInvalid alias '%s' in the input key (id='%lu'), which does not " + "Invalid alias '%s' in the input key (id='%lu'), which does not " "exist in the reference key!\n", curr_alias.c_str(), size_t(key_id)); num_errors++; } if (ref_found_keys.size() > 1) { VTR_LOG_ERROR( - "\nInvalid alias '%s' in the reference key (id='%lu'), which have been " + "Invalid alias '%s' in the reference key (id='%lu'), which have been " "found %lu times!\n", curr_alias.c_str(), size_t(key_id)); num_errors++; @@ -149,9 +151,9 @@ static int update_input_key(openfpga::FabricKey& input_key, /* Now we have a key, get the name and value, and update input key */ input_key.set_key_name(key_id, ref_key.key_name(ref_found_keys[0])); input_key.set_key_value(key_id, ref_key.key_value(ref_found_keys[0])); - VTR_LOGV(verbose, "[%lu%] Pairing key alias '%s' -> ('%s', %lu)\r", size_t(progress), - curr_alias.c_str(), input_key.key_name(key_id).c_str(), - input_key.key_value(key_id)); + VTR_LOGV(verbose, "[%lu%] Pairing key alias '%s' -> ('%s', %lu)\r", + size_t(progress), curr_alias.c_str(), + input_key.key_name(key_id).c_str(), input_key.key_value(key_id)); num_keys_checked++; } return num_errors ? openfpga::CMD_EXEC_FATAL_ERROR @@ -219,7 +221,8 @@ int main(int argc, const char** argv) { openfpga::read_xml_fabric_key(cmd_ctx.option_value(cmd, opt_input).c_str()); /* Check the input key */ - if (check_and_update_input_key(input_key, ref_key, cmd_ctx.option_enable(cmd, opt_verbose))) { + if (check_and_update_input_key(input_key, ref_key, + cmd_ctx.option_enable(cmd, opt_verbose))) { return openfpga::CMD_EXEC_FATAL_ERROR; } From f53e05f1507d909797306db6a3e4ce177aa94ed1 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 26 Aug 2023 18:46:43 -0700 Subject: [PATCH 376/391] [doc] add utility tool: fabric key assistant --- docs/source/manual/index.rst | 2 + .../manual/utilities/fabric_key_assistant.rst | 50 +++++++++++++++++++ docs/source/manual/utilities/index.rst | 13 +++++ 3 files changed, 65 insertions(+) create mode 100644 docs/source/manual/utilities/fabric_key_assistant.rst create mode 100644 docs/source/manual/utilities/index.rst diff --git a/docs/source/manual/index.rst b/docs/source/manual/index.rst index a00dce30a..fd4f4df56 100644 --- a/docs/source/manual/index.rst +++ b/docs/source/manual/index.rst @@ -18,3 +18,5 @@ file_formats/index + utilities/index + diff --git a/docs/source/manual/utilities/fabric_key_assistant.rst b/docs/source/manual/utilities/fabric_key_assistant.rst new file mode 100644 index 000000000..9bd525b91 --- /dev/null +++ b/docs/source/manual/utilities/fabric_key_assistant.rst @@ -0,0 +1,50 @@ +.. _utility_fabric_key_assistant: + +Fabric Key Assistant +-------------------- + +Fabric Key Assistant is a tool to help users to craft fabric key files. +Note that crafting a fabric key is not an easy task for engineers, as its complexity grows exponentially with FPGA sizes. +This tool is developed to assist engineers when finalizing fabric key files. +It can apply sanity checks on hand-crafted fabric key files, helping engineers to correct and debug. +The tool includes the following options: + +.. option:: --reference + + Specifiy a reference fabric key file, which has been already validated by OpenFPGA. For example, the reference fabric key can be a file which is written by OpenFPGA as a default key. The reference fabric key file is treated as the baseline, on which the input fabric key file will be compared to. + + .. note:: The reference fabric key should contain all the syntax, e.g., ``name``, ``value`` and ``alias``. + +.. option:: --input + + Specify the input fabric key file, which is typically hand-crafted by users. Sanity checks will be applied to the input fabric key file by comparing the reference. + + .. note:: The input fabric key should contain only the syntax ``alias``. + +.. option:: --output + + Specify the output fabric key file, which is an updated version of the input fabric key file. Difference from the input file, the output file contains ``name`` and ``value``, which is added by linking the ``alias`` from input file to reference file. For example, the reference fabric key includes a key: + +.. code-block:: xml + + + + while the input fabric key includes a key: + +.. code-block:: xml + + + + the resulting output fabric key file includes a key: + +.. code-block:: xml + + + +.. option:: --verbose + + To enable verbose output + +.. option:: --help + + Show help desk diff --git a/docs/source/manual/utilities/index.rst b/docs/source/manual/utilities/index.rst new file mode 100644 index 000000000..a51774314 --- /dev/null +++ b/docs/source/manual/utilities/index.rst @@ -0,0 +1,13 @@ +Utilities +--------- + +OpenFPGA contains a number of utility tools to help users to craft files. + + +.. _utilities: + Utility Tools + +.. toctree:: + :maxdepth: 2 + + fabric_key_assistant From 3bc5c3b3b781ba2a886dfe314a33c9800de986f2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 26 Aug 2023 18:56:11 -0700 Subject: [PATCH 377/391] [doc] add links --- docs/source/manual/utilities/fabric_key_assistant.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/source/manual/utilities/fabric_key_assistant.rst b/docs/source/manual/utilities/fabric_key_assistant.rst index 9bd525b91..4625366d2 100644 --- a/docs/source/manual/utilities/fabric_key_assistant.rst +++ b/docs/source/manual/utilities/fabric_key_assistant.rst @@ -3,10 +3,13 @@ Fabric Key Assistant -------------------- -Fabric Key Assistant is a tool to help users to craft fabric key files. +Fabric Key Assistant is a tool to help users to craft fabric key files (see details in :ref:`file_formats_fabric_key`). Note that crafting a fabric key is not an easy task for engineers, as its complexity grows exponentially with FPGA sizes. This tool is developed to assist engineers when finalizing fabric key files. It can apply sanity checks on hand-crafted fabric key files, helping engineers to correct and debug. + +The tool can be found at ``/build/libs/libfabrickey/test/fabric_key_assistant`` + The tool includes the following options: .. option:: --reference @@ -29,13 +32,13 @@ The tool includes the following options: - while the input fabric key includes a key: +while the input fabric key includes a key: .. code-block:: xml - the resulting output fabric key file includes a key: +the resulting output fabric key file includes a key: .. code-block:: xml From c42988ad7b45b286a81defc5b1239bf5c694f249 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 27 Aug 2023 04:43:02 +0000 Subject: [PATCH 378/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 9ef47cba0..325d99f30 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1467 +1.2.1477 From 5b4f4e86ef38c326652f9e502517eabfb997433e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 06:57:01 +0000 Subject: [PATCH 379/391] Bump vtr-verilog-to-routing from `3273c29` to `254d38f` Bumps [vtr-verilog-to-routing](https://github.com/verilog-to-routing/vtr-verilog-to-routing) from `3273c29` to `254d38f`. - [Release notes](https://github.com/verilog-to-routing/vtr-verilog-to-routing/releases) - [Commits](https://github.com/verilog-to-routing/vtr-verilog-to-routing/compare/3273c292ea75010409c325ec573d95ebec19ae67...254d38faa02884c9b48d6a82e18fd54d9b567d44) --- updated-dependencies: - dependency-name: vtr-verilog-to-routing dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- vtr-verilog-to-routing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtr-verilog-to-routing b/vtr-verilog-to-routing index 3273c292e..254d38faa 160000 --- a/vtr-verilog-to-routing +++ b/vtr-verilog-to-routing @@ -1 +1 @@ -Subproject commit 3273c292ea75010409c325ec573d95ebec19ae67 +Subproject commit 254d38faa02884c9b48d6a82e18fd54d9b567d44 From 8cbb0d7cbe299f149b1e0b500702f3aebf129a88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 06:57:03 +0000 Subject: [PATCH 380/391] Bump yosys from `6405bba` to `2f901a8` Bumps [yosys](https://github.com/YosysHQ/yosys) from `6405bba` to `2f901a8`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/6405bbab1e8afc587febf41339aaf8b514c8f202...2f901a82979d05893ff24997968a84f9fa477199) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 6405bbab1..2f901a829 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 6405bbab1e8afc587febf41339aaf8b514c8f202 +Subproject commit 2f901a82979d05893ff24997968a84f9fa477199 From 1d4c151168270b87fec1a85f166fab5735cd82c0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Aug 2023 00:02:24 +0000 Subject: [PATCH 381/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 325d99f30..902756b7b 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1477 +1.2.1483 From faaa52014f3c6faecfc2ce5c5e130b5f3624bc7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Aug 2023 06:14:26 +0000 Subject: [PATCH 382/391] Bump yosys from `2f901a8` to `572ad34` Bumps [yosys](https://github.com/YosysHQ/yosys) from `2f901a8` to `572ad34`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/2f901a82979d05893ff24997968a84f9fa477199...572ad341b7190a4596e99edf6b81f5a1c29e9afb) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 2f901a829..572ad341b 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 2f901a82979d05893ff24997968a84f9fa477199 +Subproject commit 572ad341b7190a4596e99edf6b81f5a1c29e9afb From 4b066d7b9439097e2c52202f04e7c0e3b9b3b339 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 06:48:20 +0000 Subject: [PATCH 383/391] Bump yosys-plugins from `17519a6` to `5661b7d` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `17519a6` to `5661b7d`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/17519a6ac96b649112a331364f59b416fe2d6874...5661b7d46606f2c7b966614c70373fa07ef7235e) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index 17519a6ac..5661b7d46 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit 17519a6ac96b649112a331364f59b416fe2d6874 +Subproject commit 5661b7d46606f2c7b966614c70373fa07ef7235e From 4affc27defaf9eb1c765cd2d2504aff446c8dfe5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 06:48:22 +0000 Subject: [PATCH 384/391] Bump yosys from `572ad34` to `b739213` Bumps [yosys](https://github.com/YosysHQ/yosys) from `572ad34` to `b739213`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/572ad341b7190a4596e99edf6b81f5a1c29e9afb...b739213d9f00b409ef7b9a9e1834d659ea8008d9) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 572ad341b..b739213d9 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 572ad341b7190a4596e99edf6b81f5a1c29e9afb +Subproject commit b739213d9f00b409ef7b9a9e1834d659ea8008d9 From 5bf5a373d71b8ce6f5af42741a2fe06db5c71ba4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 30 Aug 2023 11:31:52 -0700 Subject: [PATCH 385/391] Update .readthedocs.yml by removing deprecated syntax See the logs from readthedocs build: --- .readthedocs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 793cafce9..2b28d0df4 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -21,6 +21,5 @@ formats: all # Optionally set the version of Python and requirements required to build your docs python: - system_packages: true install: - requirements: docs/requirements.txt From 5a04e693dcf065f4505d4db3f3380521ddbd26ec Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Aug 2023 03:03:17 +0000 Subject: [PATCH 386/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 902756b7b..95eb78193 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1483 +1.2.1489 From 2ba96fc9066c6201a94e2362e9c91f496cb79591 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Aug 2023 06:56:09 +0000 Subject: [PATCH 387/391] Bump yosys-plugins from `5661b7d` to `128ed9c` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `5661b7d` to `128ed9c`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/5661b7d46606f2c7b966614c70373fa07ef7235e...128ed9ce9cafc1a95b26f29a98c76c1e520451a6) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index 5661b7d46..128ed9ce9 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit 5661b7d46606f2c7b966614c70373fa07ef7235e +Subproject commit 128ed9ce9cafc1a95b26f29a98c76c1e520451a6 From cd23f4c17e8925d989a2e6db89adf7bcb17f0af7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 00:02:26 +0000 Subject: [PATCH 388/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 95eb78193..8c685f500 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1489 +1.2.1499 From 5bec44f3d94910f24509355308afa0d344368f53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 06:52:44 +0000 Subject: [PATCH 389/391] Bump yosys-plugins from `128ed9c` to `834794d` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `128ed9c` to `834794d`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/128ed9ce9cafc1a95b26f29a98c76c1e520451a6...834794d592bf2f31e7125f9ef103d76110e63281) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index 128ed9ce9..834794d59 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit 128ed9ce9cafc1a95b26f29a98c76c1e520451a6 +Subproject commit 834794d592bf2f31e7125f9ef103d76110e63281 From 0ccd71d00a2b503ec643146790c8151035850876 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 06:52:46 +0000 Subject: [PATCH 390/391] Bump yosys from `b739213` to `73cb497` Bumps [yosys](https://github.com/YosysHQ/yosys) from `b739213` to `73cb497`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/b739213d9f00b409ef7b9a9e1834d659ea8008d9...73cb4977b2e493b840b23419919a63be7c83e6df) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index b739213d9..73cb4977b 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit b739213d9f00b409ef7b9a9e1834d659ea8008d9 +Subproject commit 73cb4977b2e493b840b23419919a63be7c83e6df From b2706dadbb59e8dcdc5175570fcb739330b78494 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 00:02:24 +0000 Subject: [PATCH 391/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 8c685f500..7f274dc5f 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1499 +1.2.1505

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 295/391] [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 296/391] [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 297/391] [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 */ From 86524ec125965ae14686d0b69d0dfefdfaa4cf84 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 8 Aug 2023 01:20:49 +0000 Subject: [PATCH 298/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index f511befb1..1a81f7e3a 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1301 +1.2.1364 From 61212951303f4533b268c1077e1504930edc5999 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Aug 2023 06:04:08 +0000 Subject: [PATCH 299/391] Bump yosys from `e0ba07a` to `389b8d0` Bumps [yosys](https://github.com/YosysHQ/yosys) from `e0ba07a` to `389b8d0`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/e0ba07aed3118502e623139048a24a0cd433d85f...389b8d0f94a24c30e5b9352866f0999b14312b23) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index e0ba07aed..389b8d0f9 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit e0ba07aed3118502e623139048a24a0cd433d85f +Subproject commit 389b8d0f94a24c30e5b9352866f0999b14312b23 From cc73771667c2779079b64933c0244097cd931790 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 8 Aug 2023 10:34:57 -0700 Subject: [PATCH 300/391] [lib] update vtr --- vtr-verilog-to-routing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtr-verilog-to-routing b/vtr-verilog-to-routing index 0963c2047..5bea5da82 160000 --- a/vtr-verilog-to-routing +++ b/vtr-verilog-to-routing @@ -1 +1 @@ -Subproject commit 0963c20472e178d6562e40d90727870342035f67 +Subproject commit 5bea5da827600c06990be740b580213eebae6263 From 867da98d3f9281cac94c1b4a9b70a24b0a6755a0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 8 Aug 2023 16:28:19 -0700 Subject: [PATCH 301/391] [core] update to use latest api from vpr upstream --- CMakeLists.txt | 4 +- openfpga/src/annotation/annotate_rr_graph.cpp | 33 +++++----- .../annotate_simulation_setting.cpp | 4 +- .../src/annotation/append_clock_rr_graph.cpp | 63 ++++++++++--------- .../annotation/openfpga_annotate_routing.cpp | 7 +-- .../annotation/vpr_placement_annotation.cpp | 2 +- openfpga/src/base/openfpga_pb_pin_fixup.cpp | 23 ++++--- .../fabric/build_fabric_io_location_map.cpp | 22 ++++--- openfpga/src/fabric/build_fabric_tile.cpp | 14 +++-- .../src/fabric/build_routing_module_utils.cpp | 6 +- openfpga/src/fabric/build_tile_modules.cpp | 37 ++++++----- openfpga/src/fabric/build_top_module.cpp | 6 +- ...top_module_child_fine_grained_instance.cpp | 54 +++++++++------- ...d_top_module_child_fine_grained_instance.h | 1 + .../build_top_module_child_tile_instance.cpp | 40 +++++++----- .../build_top_module_child_tile_instance.h | 1 + .../fabric/build_top_module_connection.cpp | 44 ++++++++----- .../src/fabric/build_top_module_connection.h | 2 + .../src/fabric/build_top_module_directs.cpp | 14 +++-- .../src/fabric/build_top_module_directs.h | 1 + .../src/fabric/build_top_module_memory.cpp | 16 +++-- openfpga/src/fabric/build_top_module_memory.h | 1 + .../src/fabric/build_top_module_utils.cpp | 4 +- .../fpga_bitstream/build_device_bitstream.cpp | 2 +- .../fpga_bitstream/build_grid_bitstream.cpp | 25 ++++---- .../src/fpga_bitstream/build_grid_bitstream.h | 3 +- .../src/fpga_sdc/analysis_sdc_grid_writer.cpp | 9 +-- .../src/tile_direct/build_tile_direct.cpp | 45 +++++++------ .../utils/openfpga_physical_tile_utils.cpp | 4 +- openfpga/src/vpr_wrapper/vpr_main.cpp | 32 +++------- 30 files changed, 288 insertions(+), 231 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b21400b2f..100351184 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,9 +109,9 @@ endif() include(CheckCXXCompilerFlag) # -# We require c++14 support +# We require c++17 support # -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) #No compiler specific extensions diff --git a/openfpga/src/annotation/annotate_rr_graph.cpp b/openfpga/src/annotation/annotate_rr_graph.cpp index 84a994577..65cbbe01f 100644 --- a/openfpga/src/annotation/annotate_rr_graph.cpp +++ b/openfpga/src/annotation/annotate_rr_graph.cpp @@ -22,6 +22,7 @@ namespace openfpga { /* Build a RRChan Object with the given channel type and coorindators */ static RRChan build_one_rr_chan(const DeviceContext& vpr_device_ctx, const t_rr_type& chan_type, + const size_t& layer, vtr::Point& chan_coord) { std::vector chan_rr_nodes; @@ -32,7 +33,7 @@ static RRChan build_one_rr_chan(const DeviceContext& vpr_device_ctx, /* Collect rr_nodes for this channel */ chan_rr_nodes = find_rr_graph_chan_nodes( - vpr_device_ctx.rr_graph, chan_coord.x(), chan_coord.y(), chan_type); + vpr_device_ctx.rr_graph, layer, chan_coord.x(), chan_coord.y(), chan_type); /* Fill the rr_chan */ for (const RRNodeId& chan_rr_node : chan_rr_nodes) { rr_chan.add_node(vpr_device_ctx.rr_graph, chan_rr_node, @@ -96,6 +97,7 @@ static RRChan build_one_rr_chan(const DeviceContext& vpr_device_ctx, */ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, const vtr::Point& gsb_range, + const size_t& layer, const vtr::Point& gsb_coord, const bool& include_clock) { /* Create an object to return */ @@ -133,7 +135,7 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, /* Routing channels*/ /* Side: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ /* Create a rr_chan object and check if it is unique in the graph */ - rr_chan = build_one_rr_chan(vpr_device_ctx, CHANY, coordinate); + rr_chan = build_one_rr_chan(vpr_device_ctx, CHANY, layer, coordinate); chan_dir_to_port_dir_mapping[0] = OUT_PORT; /* INC_DIRECTION => OUT_PORT */ chan_dir_to_port_dir_mapping[1] = @@ -147,11 +149,11 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, opin_grid_side[1] = LEFT; /* Include Grid[x][y+1] RIGHT side outputs pins */ temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes( - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x(), + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x(), gsb_coord.y() + 1, OPIN, opin_grid_side[0]); /* Include Grid[x+1][y+1] Left side output pins */ temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes( - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x() + 1, + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x() + 1, gsb_coord.y() + 1, OPIN, opin_grid_side[1]); break; @@ -165,7 +167,7 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, /* Side: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ /* Collect rr_nodes for Tracks for top: chany[x][y+1] */ /* Create a rr_chan object and check if it is unique in the graph */ - rr_chan = build_one_rr_chan(vpr_device_ctx, CHANX, coordinate); + rr_chan = build_one_rr_chan(vpr_device_ctx, CHANX, layer, coordinate); chan_dir_to_port_dir_mapping[0] = OUT_PORT; /* INC_DIRECTION => OUT_PORT */ chan_dir_to_port_dir_mapping[1] = @@ -180,11 +182,11 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, /* include Grid[x+1][y+1] Bottom side output pins */ temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes( - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x() + 1, + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x() + 1, gsb_coord.y() + 1, OPIN, opin_grid_side[0]); /* include Grid[x+1][y] Top side output pins */ temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes( - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x() + 1, + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x() + 1, gsb_coord.y(), OPIN, opin_grid_side[1]); break; case BOTTOM: /* BOTTOM = 2*/ @@ -197,7 +199,7 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, /* Side: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ /* Collect rr_nodes for Tracks for bottom: chany[x][y] */ /* Create a rr_chan object and check if it is unique in the graph */ - rr_chan = build_one_rr_chan(vpr_device_ctx, CHANY, coordinate); + rr_chan = build_one_rr_chan(vpr_device_ctx, CHANY, layer, coordinate); chan_dir_to_port_dir_mapping[0] = IN_PORT; /* INC_DIRECTION => IN_PORT */ chan_dir_to_port_dir_mapping[1] = @@ -211,11 +213,11 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, opin_grid_side[1] = RIGHT; /* include Grid[x+1][y] Left side output pins */ temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes( - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x() + 1, + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x() + 1, gsb_coord.y(), OPIN, opin_grid_side[0]); /* include Grid[x][y] Right side output pins */ temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes( - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x(), + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x(), gsb_coord.y(), OPIN, opin_grid_side[1]); break; case LEFT: /* LEFT = 3 */ @@ -228,7 +230,7 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, /* Side: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ /* Collect rr_nodes for Tracks for left: chanx[x][y] */ /* Create a rr_chan object and check if it is unique in the graph */ - rr_chan = build_one_rr_chan(vpr_device_ctx, CHANX, coordinate); + rr_chan = build_one_rr_chan(vpr_device_ctx, CHANX, layer, coordinate); chan_dir_to_port_dir_mapping[0] = IN_PORT; /* INC_DIRECTION => IN_PORT */ chan_dir_to_port_dir_mapping[1] = @@ -241,11 +243,11 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, opin_grid_side[1] = TOP; /* include Grid[x][y+1] Bottom side outputs pins */ temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes( - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x(), + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x(), gsb_coord.y() + 1, OPIN, opin_grid_side[0]); /* include Grid[x][y] Top side output pins */ temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes( - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x(), + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x(), gsb_coord.y(), OPIN, opin_grid_side[1]); break; default: @@ -370,7 +372,7 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, } /* Collect IPIN rr_nodes*/ temp_ipin_rr_nodes = - find_rr_graph_grid_nodes(vpr_device_ctx.rr_graph, vpr_device_ctx.grid, ix, + find_rr_graph_grid_nodes(vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, ix, iy, IPIN, ipin_rr_node_grid_side, include_clock); /* Fill the ipin nodes of RRGSB */ for (const RRNodeId& inode : temp_ipin_rr_nodes) { @@ -422,6 +424,7 @@ void annotate_device_rr_gsb(const DeviceContext& vpr_device_ctx, gsb_range.x(), gsb_range.y()); size_t gsb_cnt = 0; + size_t layer = 0; /* For each switch block, determine the size of array */ for (size_t ix = 0; ix < gsb_range.x(); ++ix) { for (size_t iy = 0; iy < gsb_range.y(); ++iy) { @@ -433,7 +436,7 @@ void annotate_device_rr_gsb(const DeviceContext& vpr_device_ctx, build_rr_gsb(vpr_device_ctx, vtr::Point(vpr_device_ctx.grid.width() - 2, vpr_device_ctx.grid.height() - 2), - vtr::Point(ix, iy), include_clock); + layer, vtr::Point(ix, iy), include_clock); /* Add to device_rr_gsb */ vtr::Point gsb_coordinate = rr_gsb.get_sb_coordinate(); diff --git a/openfpga/src/annotation/annotate_simulation_setting.cpp b/openfpga/src/annotation/annotate_simulation_setting.cpp index 3c3c7b3ee..8f4014427 100644 --- a/openfpga/src/annotation/annotate_simulation_setting.cpp +++ b/openfpga/src/annotation/annotate_simulation_setting.cpp @@ -16,7 +16,7 @@ #include "AnalysisDelayCalculator.h" #include "annotate_simulation_setting.h" #include "net_delay.h" -#include "timing_info.h" +#include "concrete_timing_info.h" /* begin namespace openfpga */ namespace openfpga { @@ -212,7 +212,7 @@ int annotate_simulation_setting( make_net_pins_matrix((const Netlist<>&)cluster_ctx.clb_nlist); /* Load the net delays */ load_net_delay_from_routing((const Netlist<>&)cluster_ctx.clb_nlist, - net_delay, false); + net_delay); /* Do final timing analysis */ auto analysis_delay_calc = std::make_shared( diff --git a/openfpga/src/annotation/append_clock_rr_graph.cpp b/openfpga/src/annotation/append_clock_rr_graph.cpp index 96fe420b3..0f9e1ecbd 100644 --- a/openfpga/src/annotation/append_clock_rr_graph.cpp +++ b/openfpga/src/annotation/append_clock_rr_graph.cpp @@ -44,6 +44,7 @@ static size_t estimate_clock_rr_graph_num_chan_nodes( * Note that switch blocks do not require any new nodes but new edges *******************************************************************/ static size_t estimate_clock_rr_graph_num_nodes(const DeviceGrid& grids, + const size_t& layer, const bool& through_channel, const ClockNetwork& clk_ntwk) { size_t num_nodes = 0; @@ -54,7 +55,7 @@ static size_t estimate_clock_rr_graph_num_nodes(const DeviceGrid& grids, /* Bypass if the routing channel does not exist when through channels are * not allowed */ if ((false == through_channel) && - (false == is_chanx_exist(grids, chanx_coord))) { + (false == is_chanx_exist(grids, layer, chanx_coord))) { continue; } /* Estimate the routing tracks required by clock routing only */ @@ -68,7 +69,7 @@ static size_t estimate_clock_rr_graph_num_nodes(const DeviceGrid& grids, /* Bypass if the routing channel does not exist when through channel are * not allowed */ if ((false == through_channel) && - (false == is_chany_exist(grids, chany_coord))) { + (false == is_chany_exist(grids, layer, chany_coord))) { continue; } /* Estimate the routing tracks required by clock routing only */ @@ -87,11 +88,12 @@ static size_t estimate_clock_rr_graph_num_nodes(const DeviceGrid& grids, static void add_rr_graph_block_clock_nodes( RRGraphBuilder& rr_graph_builder, RRClockSpatialLookup& clk_rr_lookup, const RRGraphView& rr_graph_view, const ClockNetwork& clk_ntwk, + const size_t& layer, const vtr::Point chan_coord, const t_rr_type& chan_type, const int& cost_index_offset, const bool& verbose) { size_t orig_chan_width = rr_graph_view.node_lookup() - .find_channel_nodes(chan_coord.x(), chan_coord.y(), chan_type) + .find_channel_nodes(layer, chan_coord.x(), chan_coord.y(), chan_type) .size(); size_t curr_node_ptc = orig_chan_width; @@ -115,9 +117,10 @@ static void add_rr_graph_block_clock_nodes( for (size_t ipin = 0; ipin < num_pins / 2; ++ipin) { for (auto node_dir : {Direction::INC, Direction::DEC}) { RRNodeId clk_node = rr_graph_builder.create_node( - chan_coord.x(), chan_coord.y(), chan_type, curr_node_ptc); + layer, chan_coord.x(), chan_coord.y(), chan_type, curr_node_ptc); rr_graph_builder.set_node_direction(clk_node, node_dir); rr_graph_builder.set_node_capacity(clk_node, 1); + rr_graph_builder.set_node_layer(clk_node, layer); /* set cost_index using segment id */ rr_graph_builder.set_node_cost_index( clk_node, RRIndexedDataId(cost_index_offset + @@ -149,6 +152,7 @@ static void add_rr_graph_clock_nodes(RRGraphBuilder& rr_graph_builder, RRClockSpatialLookup& clk_rr_lookup, const RRGraphView& rr_graph_view, const DeviceGrid& grids, + const size_t& layer, const bool& through_channel, const ClockNetwork& clk_ntwk, const bool& verbose) { @@ -164,11 +168,11 @@ static void add_rr_graph_clock_nodes(RRGraphBuilder& rr_graph_builder, /* Bypass if the routing channel does not exist when through channels are * not allowed */ if ((false == through_channel) && - (false == is_chanx_exist(grids, chanx_coord))) { + (false == is_chanx_exist(grids, layer, chanx_coord))) { continue; } add_rr_graph_block_clock_nodes(rr_graph_builder, clk_rr_lookup, - rr_graph_view, clk_ntwk, chanx_coord, + rr_graph_view, clk_ntwk, layer, chanx_coord, CHANX, CHANX_COST_INDEX_START, verbose); VTR_ASSERT(rr_graph_view.valid_node( clk_rr_lookup.find_node(1, 0, ClockTreeId(0), ClockLevelId(0), @@ -186,11 +190,11 @@ static void add_rr_graph_clock_nodes(RRGraphBuilder& rr_graph_builder, /* Bypass if the routing channel does not exist when through channel are * not allowed */ if ((false == through_channel) && - (false == is_chany_exist(grids, chany_coord))) { + (false == is_chany_exist(grids, layer, chany_coord))) { continue; } add_rr_graph_block_clock_nodes( - rr_graph_builder, clk_rr_lookup, rr_graph_view, clk_ntwk, chany_coord, + rr_graph_builder, clk_rr_lookup, rr_graph_view, clk_ntwk, layer, chany_coord, CHANY, CHANX_COST_INDEX_START + rr_graph_view.num_rr_segments(), verbose); VTR_ASSERT(rr_graph_view.valid_node( @@ -392,11 +396,12 @@ static std::vector find_clock_track2track_node( *******************************************************************/ static void try_find_and_add_clock_track2ipin_node( std::vector& des_nodes, const DeviceGrid& grids, - const RRGraphView& rr_graph_view, const vtr::Point& grid_coord, + const RRGraphView& rr_graph_view, const size_t& layer, + const vtr::Point& grid_coord, const e_side& pin_side, const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree, const ClockTreePinId& clk_pin) { t_physical_tile_type_ptr grid_type = - grids.get_physical_type(grid_coord.x(), grid_coord.y()); + grids.get_physical_type(t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer)); for (std::string tap_pin_name : clk_ntwk.tree_flatten_taps(clk_tree, clk_pin)) { /* tap pin name could be 'io[5:5].a2f[0]' */ @@ -405,7 +410,7 @@ static void try_find_and_add_clock_track2ipin_node( continue; } RRNodeId des_node = rr_graph_view.node_lookup().find_node( - grid_coord.x(), grid_coord.y(), IPIN, grid_pin_idx, pin_side); + layer, grid_coord.x(), grid_coord.y(), IPIN, grid_pin_idx, pin_side); if (rr_graph_view.valid_node(des_node)) { des_nodes.push_back(des_node); } @@ -440,7 +445,7 @@ static void try_find_and_add_clock_track2ipin_node( *******************************************************************/ static std::vector find_clock_track2ipin_node( const DeviceGrid& grids, const RRGraphView& rr_graph_view, - const t_rr_type& chan_type, const vtr::Point& chan_coord, + const t_rr_type& chan_type, const size_t& layer, const vtr::Point& chan_coord, const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree, const ClockTreePinId& clk_pin) { std::vector des_nodes; @@ -449,26 +454,26 @@ static std::vector find_clock_track2ipin_node( /* Get the clock IPINs at the BOTTOM side of adjacent grids [x][y+1] */ vtr::Point bot_grid_coord(chan_coord.x(), chan_coord.y() + 1); try_find_and_add_clock_track2ipin_node(des_nodes, grids, rr_graph_view, - bot_grid_coord, BOTTOM, clk_ntwk, + layer, bot_grid_coord, BOTTOM, clk_ntwk, clk_tree, clk_pin); /* Get the clock IPINs at the TOP side of adjacent grids [x][y] */ vtr::Point top_grid_coord(chan_coord.x(), chan_coord.y()); try_find_and_add_clock_track2ipin_node(des_nodes, grids, rr_graph_view, - top_grid_coord, TOP, clk_ntwk, + layer, top_grid_coord, TOP, clk_ntwk, clk_tree, clk_pin); } else { VTR_ASSERT(chan_type == CHANY); /* Get the clock IPINs at the LEFT side of adjacent grids [x][y+1] */ vtr::Point left_grid_coord(chan_coord.x() + 1, chan_coord.y()); try_find_and_add_clock_track2ipin_node(des_nodes, grids, rr_graph_view, - left_grid_coord, LEFT, clk_ntwk, + layer, left_grid_coord, LEFT, clk_ntwk, clk_tree, clk_pin); /* Get the clock IPINs at the RIGHT side of adjacent grids [x][y] */ vtr::Point right_grid_coord(chan_coord.x(), chan_coord.y()); try_find_and_add_clock_track2ipin_node(des_nodes, grids, rr_graph_view, - right_grid_coord, RIGHT, clk_ntwk, + layer, right_grid_coord, RIGHT, clk_ntwk, clk_tree, clk_pin); } @@ -481,7 +486,9 @@ static std::vector find_clock_track2ipin_node( static void add_rr_graph_block_clock_edges( RRGraphBuilder& rr_graph_builder, size_t& num_edges_to_create, const RRClockSpatialLookup& clk_rr_lookup, const RRGraphView& rr_graph_view, - const DeviceGrid& grids, const ClockNetwork& clk_ntwk, + const DeviceGrid& grids, + const size_t& layer, + const ClockNetwork& clk_ntwk, const vtr::Point& chan_coord, const t_rr_type& chan_type, const bool& verbose) { size_t edge_count = 0; @@ -524,7 +531,7 @@ static void add_rr_graph_block_clock_edges( /* Create edges */ VTR_ASSERT(rr_graph_view.valid_node(des_node)); rr_graph_builder.create_edge(src_node, des_node, - clk_ntwk.default_switch()); + clk_ntwk.default_switch(), false); edge_count++; } VTR_LOGV(verbose, "\tWill add %lu edges to other clock nodes\n", @@ -535,12 +542,12 @@ static void add_rr_graph_block_clock_edges( if (clk_ntwk.is_last_level(itree, ilvl)) { size_t curr_edge_count = edge_count; for (RRNodeId des_node : find_clock_track2ipin_node( - grids, rr_graph_view, chan_type, chan_coord, clk_ntwk, itree, + grids, rr_graph_view, chan_type, layer, chan_coord, clk_ntwk, itree, ClockTreePinId(ipin))) { /* Create edges */ VTR_ASSERT(rr_graph_view.valid_node(des_node)); rr_graph_builder.create_edge(src_node, des_node, - clk_ntwk.default_switch()); + clk_ntwk.default_switch(), false); edge_count++; } VTR_LOGV(verbose, "\tWill add %lu edges to other IPIN\n", @@ -579,7 +586,7 @@ static void add_rr_graph_block_clock_edges( static void add_rr_graph_clock_edges( RRGraphBuilder& rr_graph_builder, size_t& num_edges_to_create, const RRClockSpatialLookup& clk_rr_lookup, const RRGraphView& rr_graph_view, - const DeviceGrid& grids, const bool& through_channel, + const DeviceGrid& grids, const size_t& layer, const bool& through_channel, const ClockNetwork& clk_ntwk, const bool& verbose) { /* Add edges which is driven by X-direction clock routing tracks */ for (size_t iy = 0; iy < grids.height() - 1; ++iy) { @@ -588,11 +595,11 @@ static void add_rr_graph_clock_edges( /* Bypass if the routing channel does not exist when through channels are * not allowed */ if ((false == through_channel) && - (false == is_chanx_exist(grids, chanx_coord))) { + (false == is_chanx_exist(grids, layer, chanx_coord))) { continue; } add_rr_graph_block_clock_edges(rr_graph_builder, num_edges_to_create, - clk_rr_lookup, rr_graph_view, grids, + clk_rr_lookup, rr_graph_view, grids, layer, clk_ntwk, chanx_coord, CHANX, verbose); } } @@ -604,11 +611,11 @@ static void add_rr_graph_clock_edges( /* Bypass if the routing channel does not exist when through channel are * not allowed */ if ((false == through_channel) && - (false == is_chany_exist(grids, chany_coord))) { + (false == is_chany_exist(grids, layer, chany_coord))) { continue; } add_rr_graph_block_clock_edges(rr_graph_builder, num_edges_to_create, - clk_rr_lookup, rr_graph_view, grids, + clk_rr_lookup, rr_graph_view, grids, layer, clk_ntwk, chany_coord, CHANY, verbose); } } @@ -647,7 +654,7 @@ int append_clock_rr_graph(DeviceContext& vpr_device_ctx, /* Estimate the number of nodes and pre-allocate */ size_t orig_num_nodes = vpr_device_ctx.rr_graph.num_nodes(); size_t num_clock_nodes = estimate_clock_rr_graph_num_nodes( - vpr_device_ctx.grid, vpr_device_ctx.arch->through_channel, clk_ntwk); + vpr_device_ctx.grid, 0, vpr_device_ctx.arch->through_channel, clk_ntwk); vpr_device_ctx.rr_graph_builder.unlock_storage(); vpr_device_ctx.rr_graph_builder.reserve_nodes(num_clock_nodes + orig_num_nodes); @@ -658,7 +665,7 @@ int append_clock_rr_graph(DeviceContext& vpr_device_ctx, /* Add clock nodes */ add_rr_graph_clock_nodes(vpr_device_ctx.rr_graph_builder, clk_rr_lookup, - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, 0, vpr_device_ctx.arch->through_channel, clk_ntwk, verbose); VTR_LOGV(verbose, @@ -673,7 +680,7 @@ int append_clock_rr_graph(DeviceContext& vpr_device_ctx, add_rr_graph_clock_edges( vpr_device_ctx.rr_graph_builder, num_clock_edges, static_cast(clk_rr_lookup), - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, 0, vpr_device_ctx.arch->through_channel, clk_ntwk, verbose); VTR_LOGV(verbose, "Added %lu clock edges to routing " diff --git a/openfpga/src/annotation/openfpga_annotate_routing.cpp b/openfpga/src/annotation/openfpga_annotate_routing.cpp index 6c5327e04..e4aa8e88d 100644 --- a/openfpga/src/annotation/openfpga_annotate_routing.cpp +++ b/openfpga/src/annotation/openfpga_annotate_routing.cpp @@ -2,9 +2,8 @@ * This file includes functions that are used to annotate routing results * from VPR to OpenFPGA *******************************************************************/ -/* Headers from vtrutil library */ #include "openfpga_annotate_routing.h" - +#include "old_traceback.h" #include "annotate_routing.h" #include "vtr_assert.h" #include "vtr_log.h" @@ -134,13 +133,13 @@ void annotate_rr_node_previous_nodes( /* Cache Previous nodes */ RRNodeId prev_node = RRNodeId::INVALID(); - t_trace* tptr = routing_ctx.trace[net_id].head; + t_trace* tptr = TracebackCompat::traceback_from_route_tree(routing_ctx.route_trees[net_id].value()); while (tptr != nullptr) { RRNodeId rr_node = RRNodeId(tptr->index); /* Find the right previous node */ prev_node = find_previous_node_from_routing_traces( - device_ctx.rr_graph, routing_ctx.trace[net_id].head, prev_node, + device_ctx.rr_graph, TracebackCompat::traceback_from_route_tree(routing_ctx.route_trees[net_id].value()), prev_node, rr_node); /* Only update mapped nodes */ diff --git a/openfpga/src/annotation/vpr_placement_annotation.cpp b/openfpga/src/annotation/vpr_placement_annotation.cpp index efdcb0ea6..0f24b05fd 100644 --- a/openfpga/src/annotation/vpr_placement_annotation.cpp +++ b/openfpga/src/annotation/vpr_placement_annotation.cpp @@ -32,7 +32,7 @@ void VprPlacementAnnotation::init_mapped_blocks(const DeviceGrid& grids) { for (size_t x = 0; x < grids.width(); ++x) { for (size_t y = 0; y < grids.height(); ++y) { /* Deposit invalid ids and we will fill later */ - blocks_[x][y].resize(grids.get_physical_type(x, y)->capacity, + blocks_[x][y].resize(grids.get_physical_type(t_physical_tile_loc(x, y, 0))->capacity, ClusterBlockId::INVALID()); } } diff --git a/openfpga/src/base/openfpga_pb_pin_fixup.cpp b/openfpga/src/base/openfpga_pb_pin_fixup.cpp index 952a495d1..b57616f78 100644 --- a/openfpga/src/base/openfpga_pb_pin_fixup.cpp +++ b/openfpga/src/base/openfpga_pb_pin_fixup.cpp @@ -35,12 +35,13 @@ static void update_cluster_pin_with_post_routing_results( const DeviceContext& device_ctx, const ClusteringContext& clustering_ctx, const VprRoutingAnnotation& vpr_routing_annotation, VprClusteringAnnotation& vpr_clustering_annotation, + const size_t& layer, const vtr::Point& grid_coord, const ClusterBlockId& blk_id, const e_side& border_side, const size_t& z, const bool& verbose) { /* Handle each pin */ auto logical_block = clustering_ctx.clb_nlist.block_type(blk_id); auto physical_tile = - device_ctx.grid.get_physical_type(grid_coord.x(), grid_coord.y()); + device_ctx.grid.get_physical_type(t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer)); for (int j = 0; j < logical_block->pb_type->num_pins; j++) { /* Get the ptc num for the pin in rr_graph, we need t consider the z offset @@ -86,7 +87,7 @@ static void update_cluster_pin_with_post_routing_results( /* Find the net mapped to this pin in routing results */ const RRNodeId& rr_node = device_ctx.rr_graph.node_lookup().find_node( - grid_coord.x(), grid_coord.y(), rr_node_type, physical_pin, pin_side); + layer, grid_coord.x(), grid_coord.y(), rr_node_type, physical_pin, pin_side); if (false == device_ctx.rr_graph.valid_node(rr_node)) { continue; } @@ -192,16 +193,18 @@ void update_pb_pin_with_post_routing_results( * post-routing clustering result sync-up */ vpr_clustering_annotation.clear_net_remapping(); + size_t layer = 0; /* Update the core logic (center blocks of the FPGA) */ for (size_t x = 1; x < device_ctx.grid.width() - 1; ++x) { for (size_t y = 1; y < device_ctx.grid.height() - 1; ++y) { + t_physical_tile_type_ptr phy_tile = device_ctx.grid.get_physical_type(t_physical_tile_loc(x, y, layer)); /* Bypass the EMPTY tiles */ - if (true == is_empty_type(device_ctx.grid.get_physical_type(x, y))) { + if (true == is_empty_type(phy_tile)) { continue; } /* Get the mapped blocks to this grid */ - for (const ClusterBlockId& cluster_blk_id : - placement_ctx.grid_blocks[x][y].blocks) { + for (int isubtile = 0; isubtile < phy_tile->capacity; ++isubtile) { + ClusterBlockId cluster_blk_id = placement_ctx.grid_blocks.block_at_location({(int)x, (int)y, (int)isubtile, (int)layer}); /* Skip invalid ids */ if (ClusterBlockId::INVALID() == cluster_blk_id) { continue; @@ -211,7 +214,7 @@ void update_pb_pin_with_post_routing_results( vtr::Point grid_coord(x, y); update_cluster_pin_with_post_routing_results( device_ctx, clustering_ctx, vpr_routing_annotation, - vpr_clustering_annotation, grid_coord, cluster_blk_id, NUM_SIDES, + vpr_clustering_annotation, layer, grid_coord, cluster_blk_id, NUM_SIDES, placement_ctx.block_locs[cluster_blk_id].loc.sub_tile, verbose); } } @@ -224,14 +227,14 @@ void update_pb_pin_with_post_routing_results( for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coord : io_coordinates[io_side]) { t_physical_tile_type_ptr phy_tile_type = - device_ctx.grid.get_physical_type(io_coord.x(), io_coord.y()); + device_ctx.grid.get_physical_type(t_physical_tile_loc(io_coord.x(), io_coord.y(), layer)); /* Bypass EMPTY grid */ if (true == is_empty_type(phy_tile_type)) { continue; } /* Get the mapped blocks to this grid */ - for (const ClusterBlockId& cluster_blk_id : - placement_ctx.grid_blocks[io_coord.x()][io_coord.y()].blocks) { + for (int isubtile = 0; isubtile < phy_tile_type->capacity; ++isubtile) { + ClusterBlockId cluster_blk_id = placement_ctx.grid_blocks.block_at_location({(int)io_coord.x(), (int)io_coord.y(), (int)isubtile, (int)layer}); /* Skip invalid ids */ if (ClusterBlockId::INVALID() == cluster_blk_id) { continue; @@ -239,7 +242,7 @@ void update_pb_pin_with_post_routing_results( /* Update on I/O grid */ update_cluster_pin_with_post_routing_results( device_ctx, clustering_ctx, vpr_routing_annotation, - vpr_clustering_annotation, io_coord, cluster_blk_id, io_side, + vpr_clustering_annotation, layer, io_coord, cluster_blk_id, io_side, placement_ctx.block_locs[cluster_blk_id].loc.sub_tile, verbose); } } diff --git a/openfpga/src/fabric/build_fabric_io_location_map.cpp b/openfpga/src/fabric/build_fabric_io_location_map.cpp index c1f31f999..75b8d37ed 100644 --- a/openfpga/src/fabric/build_fabric_io_location_map.cpp +++ b/openfpga/src/fabric/build_fabric_io_location_map.cpp @@ -32,7 +32,7 @@ namespace openfpga { *(x, y, z) coordinate to the actual indices *******************************************************************/ static IoLocationMap build_fabric_fine_grained_io_location_map( - const ModuleManager& module_manager, const DeviceGrid& grids) { + const ModuleManager& module_manager, const DeviceGrid& grids, const size_t& layer) { vtr::ScopedStartFinishTimer timer( "Create I/O location mapping for top module"); @@ -50,8 +50,9 @@ static IoLocationMap build_fabric_fine_grained_io_location_map( ModuleId child = module_manager.io_children(top_module)[ichild]; vtr::Point coord = module_manager.io_child_coordinates(top_module)[ichild]; + t_physical_tile_loc phy_tile_loc(coord.x(), coord.y(), layer); t_physical_tile_type_ptr phy_tile_type = - grids.get_physical_type(coord.x(), coord.y()); + grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY grid */ if (true == is_empty_type(phy_tile_type)) { @@ -59,8 +60,8 @@ static IoLocationMap build_fabric_fine_grained_io_location_map( } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(coord.x(), coord.y())) || - (0 < grids.get_height_offset(coord.x(), coord.y()))) { + if ((0 < grids.get_width_offset(phy_tile_loc)) || + (0 < grids.get_height_offset(phy_tile_loc))) { continue; } @@ -153,7 +154,7 @@ static IoLocationMap build_fabric_fine_grained_io_location_map( *(x, y, z) coordinate to the actual indices *******************************************************************/ static IoLocationMap build_fabric_tiled_io_location_map( - const ModuleManager& module_manager, const DeviceGrid& grids) { + const ModuleManager& module_manager, const DeviceGrid& grids, const size_t& layer) { vtr::ScopedStartFinishTimer timer( "Create I/O location mapping for top module"); @@ -171,8 +172,9 @@ static IoLocationMap build_fabric_tiled_io_location_map( ModuleId child = module_manager.io_children(top_module)[ichild]; vtr::Point coord = module_manager.io_child_coordinates(top_module)[ichild]; + t_physical_tile_loc phy_tile_loc(coord.x(), coord.y(), layer); t_physical_tile_type_ptr phy_tile_type = - grids.get_physical_type(coord.x(), coord.y()); + grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY grid */ if (true == is_empty_type(phy_tile_type)) { @@ -180,8 +182,8 @@ static IoLocationMap build_fabric_tiled_io_location_map( } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(coord.x(), coord.y())) || - (0 < grids.get_height_offset(coord.x(), coord.y()))) { + if ((0 < grids.get_width_offset(phy_tile_loc)) || + (0 < grids.get_height_offset(phy_tile_loc))) { continue; } @@ -280,9 +282,9 @@ IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, const DeviceGrid& grids, const bool& tiled_fabric) { if (tiled_fabric) { - return build_fabric_tiled_io_location_map(module_manager, grids); + return build_fabric_tiled_io_location_map(module_manager, grids, 0); } - return build_fabric_fine_grained_io_location_map(module_manager, grids); + return build_fabric_fine_grained_io_location_map(module_manager, grids, 0); } } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_fabric_tile.cpp b/openfpga/src/fabric/build_fabric_tile.cpp index c2dd79648..b1cc7c80e 100644 --- a/openfpga/src/fabric/build_fabric_tile.cpp +++ b/openfpga/src/fabric/build_fabric_tile.cpp @@ -29,6 +29,7 @@ namespace openfpga { *******************************************************************/ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, const DeviceGrid& grids, + const size_t& layer, const DeviceRRGSB& device_rr_gsb, const bool& verbose) { int status_code = CMD_EXEC_SUCCESS; @@ -36,7 +37,8 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, /* Walk through all the device rr_gsb and create tile one by one */ for (size_t ix = 0; ix < grids.width(); ++ix) { for (size_t iy = 0; iy < grids.height(); ++iy) { - t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(ix, iy); + t_physical_tile_loc tile_loc(ix, iy, layer); + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(tile_loc); bool skip_add_pb = false; vtr::Point curr_tile_coord(ix, iy); vtr::Point curr_gsb_coord(ix, iy - 1); @@ -56,15 +58,15 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, "programmable block\n", curr_tile_coord.x(), curr_tile_coord.y()); curr_tile_id = fabric_tile.create_tile(curr_tile_coord); - } else if ((0 < grids.get_width_offset(ix, iy)) || - (0 < grids.get_height_offset(ix, iy))) { + } else if ((0 < grids.get_width_offset(tile_loc)) || + (0 < grids.get_height_offset(tile_loc))) { /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ /* Find the root of this grid, the instance id should be valid. * We just copy it here */ vtr::Point root_tile_coord( - ix - grids.get_width_offset(ix, iy), - iy - grids.get_height_offset(ix, iy)); + ix - grids.get_width_offset(tile_loc), + iy - grids.get_height_offset(tile_loc)); skip_add_pb = true; VTR_LOGV(verbose, "Tile[%lu][%lu] contains a heterogeneous block which is " @@ -146,7 +148,7 @@ int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config, /* Depending on the selected style, follow different approaches */ if (tile_config.style() == TileConfig::e_style::TOP_LEFT) { - status_code = build_fabric_tile_style_top_left(fabric_tile, grids, + status_code = build_fabric_tile_style_top_left(fabric_tile, grids, 0, device_rr_gsb, verbose); } else { /* Error out for styles that are not supported yet! */ diff --git a/openfpga/src/fabric/build_routing_module_utils.cpp b/openfpga/src/fabric/build_routing_module_utils.cpp index 64a1472ea..cf7585b9e 100644 --- a/openfpga/src/fabric/build_routing_module_utils.cpp +++ b/openfpga/src/fabric/build_routing_module_utils.cpp @@ -77,7 +77,7 @@ std::string generate_sb_module_grid_port_name( int pin_id = rr_graph.node_pin_num(rr_node); e_side pin_side = get_rr_graph_single_node_side(rr_graph, rr_node); t_physical_tile_type_ptr physical_tile = vpr_device_grid.get_physical_type( - rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node)); + t_physical_tile_loc(rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node), rr_graph.node_layer(rr_node))); int pin_width_offset = physical_tile->pin_width_offset[pin_id]; int pin_height_offset = physical_tile->pin_height_offset[pin_id]; BasicPort pin_info = @@ -110,8 +110,8 @@ std::string generate_cb_module_grid_port_name( /* Collect the attributes of the rr_node required to generate the port name */ int pin_id = rr_graph.node_pin_num(rr_node); e_side pin_side = get_rr_graph_single_node_side(rr_graph, rr_node); - t_physical_tile_type_ptr physical_tile = vpr_device_grid.get_physical_type( - rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node)); + t_physical_tile_type_ptr physical_tile = vpr_device_grid.get_physical_type(t_physical_tile_loc( + rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node), rr_graph.node_layer(rr_node))); int pin_width_offset = physical_tile->pin_width_offset[pin_id]; int pin_height_offset = physical_tile->pin_height_offset[pin_id]; BasicPort pin_info = diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 88858cb0c..1589b7162 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -60,7 +60,8 @@ namespace openfpga { *******************************************************************/ static int build_tile_module_port_and_nets_between_sb_and_pb( ModuleManager& module_manager, const ModuleId& tile_module, - const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const DeviceGrid& grids, const size_t& layer, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, const std::vector& pb_instances, @@ -125,7 +126,7 @@ static int build_tile_module_port_and_nets_between_sb_and_pb( rr_gsb.get_opin_node(side_manager.get_side(), inode)); t_physical_tile_type_ptr grid_type_descriptor = - grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); size_t src_grid_pin_width = grid_type_descriptor->pin_width_offset[src_grid_pin_index]; size_t src_grid_pin_height = @@ -287,7 +288,8 @@ static int build_tile_module_port_and_nets_between_sb_and_pb( *******************************************************************/ static int build_tile_module_port_and_nets_between_cb_and_pb( ModuleManager& module_manager, const ModuleId& tile_module, - const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const DeviceGrid& grids, const size_t& layer, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph, const RRGSB& rr_gsb, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, const t_rr_type& cb_type, @@ -372,7 +374,7 @@ static int build_tile_module_port_and_nets_between_cb_and_pb( size_t sink_grid_pin_index = rr_graph.node_pin_num(instance_ipin_node); t_physical_tile_type_ptr grid_type_descriptor = - grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); size_t sink_grid_pin_width = grid_type_descriptor->pin_width_offset[sink_grid_pin_index]; size_t sink_grid_pin_height = @@ -991,14 +993,15 @@ static int build_tile_module_ports_from_cb( ********************************************************************/ static int build_tile_port_and_nets_from_pb( ModuleManager& module_manager, const ModuleId& tile_module, - const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const DeviceGrid& grids, const size_t& layer, + const VprDeviceAnnotation& vpr_device_annotation, const RRGraphView& rr_graph, const vtr::Point& pb_coord, const std::vector& pb_instances, const FabricTile& fabric_tile, const FabricTileId& curr_fabric_tile_id, const size_t& ipb, const bool& frame_view, const bool& verbose) { size_t pb_instance = pb_instances[ipb]; t_physical_tile_type_ptr phy_tile = - grids.get_physical_type(pb_coord.x(), pb_coord.y()); + grids.get_physical_type(t_physical_tile_loc(pb_coord.x(), pb_coord.y(), layer)); /* Empty type does not require a module */ if (is_empty_type(phy_tile)) { return CMD_EXEC_SUCCESS; @@ -1120,7 +1123,7 @@ static int build_tile_port_and_nets_from_pb( size_t num_fanout_in_tile = module_manager.module_net_sinks(tile_module, curr_net).size(); RRNodeId rr_node = rr_graph.node_lookup().find_node( - pb_coord.x() + iwidth, pb_coord.y() + iheight, OPIN, ipin, + layer, pb_coord.x() + iwidth, pb_coord.y() + iheight, OPIN, ipin, side); size_t num_fanout_required = rr_graph.node_out_edges(rr_node).size(); @@ -1179,7 +1182,8 @@ static int build_tile_port_and_nets_from_pb( *******************************************************************/ static int build_tile_module_ports_and_nets( ModuleManager& module_manager, const ModuleId& tile_module, - const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const DeviceGrid& grids, const size_t& layer, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph_view, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, const std::vector& pb_instances, @@ -1196,7 +1200,7 @@ static int build_tile_module_ports_and_nets( fabric_tile.sb_coordinates(fabric_tile_id)[isb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); status_code = build_tile_module_port_and_nets_between_sb_and_pb( - module_manager, tile_module, grids, vpr_device_annotation, device_rr_gsb, + module_manager, tile_module, grids, layer, vpr_device_annotation, device_rr_gsb, rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, pb_instances, sb_instances, isb, true, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { @@ -1213,7 +1217,7 @@ static int build_tile_module_ports_and_nets( fabric_tile.cb_coordinates(fabric_tile_id, cb_type)[icb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(cb_coord); status_code = build_tile_module_port_and_nets_between_cb_and_pb( - module_manager, tile_module, grids, vpr_device_annotation, + module_manager, tile_module, grids, layer, vpr_device_annotation, device_rr_gsb, rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, cb_type, pb_instances, cb_instances, icb, true, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { @@ -1244,7 +1248,7 @@ static int build_tile_module_ports_and_nets( vtr::Point pb_coord = fabric_tile.pb_coordinates(fabric_tile_id)[ipb]; status_code = build_tile_port_and_nets_from_pb( - module_manager, tile_module, grids, vpr_device_annotation, rr_graph_view, + module_manager, tile_module, grids, layer, vpr_device_annotation, rr_graph_view, pb_coord, pb_instances, fabric_tile, fabric_tile_id, ipb, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { @@ -1285,7 +1289,8 @@ static int build_tile_module_ports_and_nets( static int build_tile_module( ModuleManager& module_manager, DecoderLibrary& decoder_lib, const FabricTile& fabric_tile, const FabricTileId& fabric_tile_id, - const DeviceGrid& grids, const VprDeviceAnnotation& vpr_device_annotation, + const DeviceGrid& grids, const size_t& layer, + const VprDeviceAnnotation& vpr_device_annotation, const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph_view, const CircuitLibrary& circuit_lib, const CircuitModelId& sram_model, const e_config_protocol_type& sram_orgz_type, const bool& frame_view, @@ -1304,7 +1309,7 @@ static int build_tile_module( for (vtr::Point grid_coord : fabric_tile.pb_coordinates(fabric_tile_id)) { t_physical_tile_type_ptr phy_tile = - grids.get_physical_type(grid_coord.x(), grid_coord.y()); + grids.get_physical_type(t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer)); VTR_LOGV(verbose, "Try to find pb at [%lu][%lu]\n", grid_coord.x(), grid_coord.y()); /* Empty type does not require a module */ @@ -1433,7 +1438,7 @@ static int build_tile_module( /* Add module nets and ports */ status_code = build_tile_module_ports_and_nets( - module_manager, tile_module, grids, vpr_device_annotation, device_rr_gsb, + module_manager, tile_module, grids, layer, vpr_device_annotation, device_rr_gsb, rr_graph_view, fabric_tile, fabric_tile_id, pb_instances, cb_instances, sb_instances, frame_view, verbose); @@ -1512,10 +1517,12 @@ int build_tile_modules(ModuleManager& module_manager, int status_code = CMD_EXEC_SUCCESS; + size_t layer = 0; + /* Build a module for each unique tile */ for (FabricTileId fabric_tile_id : fabric_tile.unique_tiles()) { status_code = build_tile_module( - module_manager, decoder_lib, fabric_tile, fabric_tile_id, grids, + module_manager, decoder_lib, fabric_tile, fabric_tile_id, grids, layer, vpr_device_annotation, device_rr_gsb, rr_graph_view, circuit_lib, sram_model, sram_orgz_type, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index e8e5066a1..a81eb86f2 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -71,10 +71,12 @@ int build_top_module( /* Label module usage */ module_manager.set_module_usage(top_module, ModuleManager::MODULE_TOP); + size_t layer = 0; + if (fabric_tile.empty()) { status = build_top_module_fine_grained_child_instances( module_manager, top_module, blwl_sr_banks, circuit_lib, clk_ntwk, - rr_clock_lookup, vpr_device_annotation, grids, tile_annotation, rr_graph, + rr_clock_lookup, vpr_device_annotation, grids, layer, 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, group_config_block); @@ -82,7 +84,7 @@ int build_top_module( /* 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, + rr_clock_lookup, vpr_device_annotation, grids, layer, tile_annotation, rr_graph, device_rr_gsb, tile_direct, arch_direct, fabric_tile, config_protocol, sram_model, fabric_key, group_config_block, frame_view, verbose); } 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 78e2565d0..4f44f6a41 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 @@ -95,7 +95,7 @@ static size_t add_top_module_grid_instance( *******************************************************************/ static vtr::Matrix add_top_module_grid_instances( ModuleManager& module_manager, const ModuleId& top_module, - const DeviceGrid& grids) { + const DeviceGrid& grids, const size_t& layer) { vtr::ScopedStartFinishTimer timer("Add grid instances to top module"); /* Reserve an array for the instance ids */ @@ -109,23 +109,24 @@ static vtr::Matrix add_top_module_grid_instances( for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { + t_physical_tile_loc phy_tile_loc(io_coordinate.x(), io_coordinate.y(), layer); t_physical_tile_type_ptr phy_tile_type = - grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); + grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY grid */ if (true == is_empty_type(phy_tile_type)) { continue; } /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || - (0 < grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { + if ((0 < grids.get_width_offset(phy_tile_loc)) || + (0 < grids.get_height_offset(phy_tile_loc))) { /* Find the root of this grid, the instance id should be valid. * We just copy it here */ vtr::Point root_grid_coord( io_coordinate.x() - - grids.get_width_offset(io_coordinate.x(), io_coordinate.y()), + grids.get_width_offset(phy_tile_loc), io_coordinate.y() - - grids.get_height_offset(io_coordinate.x(), io_coordinate.y())); + grids.get_height_offset(phy_tile_loc)); VTR_ASSERT(size_t(-1) != grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = @@ -148,20 +149,21 @@ static vtr::Matrix add_top_module_grid_instances( */ for (size_t ix = 1; ix < grids.width() - 1; ++ix) { for (size_t iy = 1; iy < grids.height() - 1; ++iy) { - t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(ix, iy); + t_physical_tile_loc phy_tile_loc(ix, iy, layer); + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY grid */ if (true == is_empty_type(phy_tile_type)) { continue; } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(ix, iy)) || - (0 < grids.get_height_offset(ix, iy))) { + if ((0 < grids.get_width_offset(phy_tile_loc)) || + (0 < grids.get_height_offset(phy_tile_loc))) { /* Find the root of this grid, the instance id should be valid. * We just copy it here */ vtr::Point root_grid_coord( - ix - grids.get_width_offset(ix, iy), - iy - grids.get_height_offset(ix, iy)); + ix - grids.get_width_offset(phy_tile_loc), + iy - grids.get_height_offset(phy_tile_loc)); VTR_ASSERT(size_t(-1) != grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); grid_instance_ids[ix][iy] = @@ -319,22 +321,24 @@ static vtr::Matrix add_top_module_connection_block_instances( *******************************************************************/ static void add_top_module_io_children( ModuleManager& module_manager, const ModuleId& top_module, - const DeviceGrid& grids, const vtr::Matrix& grid_instance_ids) { + const DeviceGrid& grids, const size_t& layer, + const vtr::Matrix& grid_instance_ids) { /* Create the coordinate range for the perimeter I/Os of FPGA fabric */ std::map>> io_coordinates = generate_perimeter_grid_coordinates(grids); for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coord : io_coordinates[io_side]) { + t_physical_tile_loc phy_tile_loc(io_coord.x(), io_coord.y(), layer); t_physical_tile_type_ptr grid_type = - grids.get_physical_type(io_coord.x(), io_coord.y()); + grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY grid */ if (true == is_empty_type(grid_type)) { continue; } /* Skip width, height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(io_coord.x(), io_coord.y())) || - (0 < grids.get_height_offset(io_coord.x(), io_coord.y()))) { + if ((0 < grids.get_width_offset(phy_tile_loc)) || + (0 < grids.get_height_offset(phy_tile_loc))) { continue; } /* Find the module name for this type of grid */ @@ -395,15 +399,16 @@ static void add_top_module_io_children( /* Now walk through the coordinates */ for (vtr::Point coord : coords) { + t_physical_tile_loc phy_tile_loc(coord.x(), coord.y(), layer); t_physical_tile_type_ptr grid_type = - grids.get_physical_type(coord.x(), coord.y()); + grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY grid */ if (true == is_empty_type(grid_type)) { continue; } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(coord.x(), coord.y())) || - (0 < grids.get_height_offset(coord.x(), coord.y()))) { + if ((0 < grids.get_width_offset(phy_tile_loc)) || + (0 < grids.get_height_offset(phy_tile_loc))) { continue; } /* Find the module name for this type of grid */ @@ -431,6 +436,7 @@ int build_top_module_fine_grained_child_instances( const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, const ArchDirect& arch_direct, const ConfigProtocol& config_protocol, @@ -443,7 +449,7 @@ int build_top_module_fine_grained_child_instances( /* Add sub modules, which are grid, SB and CBX/CBY modules as instances */ /* Add all the grids across the fabric */ vtr::Matrix grid_instance_ids = - add_top_module_grid_instances(module_manager, top_module, grids); + add_top_module_grid_instances(module_manager, top_module, grids, layer); /* Add all the SBs across the fabric */ vtr::Matrix sb_instance_ids = add_top_module_switch_block_instances( module_manager, top_module, device_rr_gsb, compact_routing_hierarchy); @@ -457,7 +463,7 @@ int build_top_module_fine_grained_child_instances( /* Update I/O children list */ add_top_module_io_children(module_manager, top_module, grids, - grid_instance_ids); + layer, grid_instance_ids); /* Add nets when we need a complete fabric modeling, * which is required by downstream functions @@ -468,19 +474,19 @@ int build_top_module_fine_grained_child_instances( /* Add module nets to connect the sub modules */ add_top_module_nets_connect_grids_and_gsbs( - module_manager, top_module, vpr_device_annotation, grids, + module_manager, top_module, vpr_device_annotation, grids, layer, grid_instance_ids, rr_graph, device_rr_gsb, sb_instance_ids, cb_instance_ids, compact_routing_hierarchy, duplicate_grid_pin); /* Add inter-CLB direct connections */ add_top_module_nets_tile_direct_connections( module_manager, top_module, circuit_lib, vpr_device_annotation, grids, - grid_instance_ids, tile_direct, arch_direct); + layer, grid_instance_ids, tile_direct, arch_direct); } /* Add global ports from grid ports that are defined as global in tile * annotation */ status = add_top_module_global_ports_from_grid_modules( - module_manager, top_module, tile_annotation, vpr_device_annotation, grids, + module_manager, top_module, tile_annotation, vpr_device_annotation, grids, layer, rr_graph, device_rr_gsb, cb_instance_ids, grid_instance_ids, clk_ntwk, rr_clock_lookup); if (CMD_EXEC_FATAL_ERROR == status) { @@ -500,7 +506,7 @@ int build_top_module_fine_grained_child_instances( if (true == fabric_key.empty()) { organize_top_module_memory_modules( module_manager, top_module, circuit_lib, config_protocol, sram_model, - grids, grid_instance_ids, device_rr_gsb, sb_instance_ids, cb_instance_ids, + grids, layer, grid_instance_ids, device_rr_gsb, sb_instance_ids, cb_instance_ids, compact_routing_hierarchy); } else { VTR_ASSERT_SAFE(false == fabric_key.empty()); 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 41628ac79..648583830 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 @@ -38,6 +38,7 @@ int build_top_module_fine_grained_child_instances( const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, const ArchDirect& arch_direct, const ConfigProtocol& config_protocol, 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 664be1ceb..06c73bd6c 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1272,6 +1272,7 @@ static int build_top_module_global_net_for_given_tile_module( const TileGlobalPortId& tile_global_port, const BasicPort& tile_port_to_connect, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Point& grid_coordinate, const e_side& border_side, const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile) { /* Get the tile module and instance */ @@ -1298,7 +1299,7 @@ static int build_top_module_global_net_for_given_tile_module( unique_fabric_tile_id)[pb_idx_in_curr_fabric_tile]; t_physical_tile_type_ptr physical_tile = - grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); /* Find the module name for this type of grid */ std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); std::string grid_instance_name = @@ -1419,6 +1420,7 @@ static int build_top_module_global_net_from_tile_modules( const ModulePortId& top_module_port, const TileAnnotation& tile_annotation, const TileGlobalPortId& tile_global_port, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile) { int status = CMD_EXEC_SUCCESS; @@ -1475,15 +1477,16 @@ static int build_top_module_global_net_from_tile_modules( /* Spot the port from child modules from core grids */ for (size_t ix = start_coord.x(); ix < end_coord.x(); ++ix) { for (size_t iy = start_coord.y(); iy < end_coord.y(); ++iy) { + t_physical_tile_loc phy_tile_loc(ix, iy, layer); t_physical_tile_type_ptr phy_tile_type = - grids.get_physical_type(ix, iy); + grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY tiles */ if (true == is_empty_type(phy_tile_type)) { continue; } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(ix, iy)) || - (0 < grids.get_height_offset(ix, iy))) { + if ((0 < grids.get_width_offset(phy_tile_loc)) || + (0 < grids.get_height_offset(phy_tile_loc))) { continue; } @@ -1495,7 +1498,7 @@ static int build_top_module_global_net_from_tile_modules( /* Create nets and finish connection build-up */ status = build_top_module_global_net_for_given_tile_module( module_manager, top_module, top_module_port, tile_annotation, - tile_global_port, tile_port, vpr_device_annotation, grids, + tile_global_port, tile_port, vpr_device_annotation, grids, layer, vtr::Point(ix, iy), NUM_SIDES, tile_instance_ids, fabric_tile); if (CMD_EXEC_FATAL_ERROR == status) { @@ -1507,8 +1510,9 @@ static int build_top_module_global_net_from_tile_modules( /* Walk through all the grids on the perimeter, which are I/O grids */ for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { + t_physical_tile_loc phy_tile_loc(io_coordinate.x(), io_coordinate.y(), layer); t_physical_tile_type_ptr phy_tile_type = - grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); + grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY grid */ if (true == is_empty_type(phy_tile_type)) { continue; @@ -1516,9 +1520,9 @@ static int build_top_module_global_net_from_tile_modules( /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ if ((0 < - grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || + grids.get_width_offset(phy_tile_loc)) || (0 < - grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { + grids.get_height_offset(phy_tile_loc))) { continue; } @@ -1543,7 +1547,7 @@ static int build_top_module_global_net_from_tile_modules( /* Create nets and finish connection build-up */ status = build_top_module_global_net_for_given_tile_module( module_manager, top_module, top_module_port, tile_annotation, - tile_global_port, tile_port, vpr_device_annotation, grids, + tile_global_port, tile_port, vpr_device_annotation, grids, layer, io_coordinate, io_side, tile_instance_ids, fabric_tile); if (CMD_EXEC_FATAL_ERROR == status) { return status; @@ -1563,6 +1567,7 @@ static int add_top_module_global_ports_from_tile_modules( ModuleManager& module_manager, const ModuleId& top_module, const TileAnnotation& tile_annotation, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile, const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup) { @@ -1621,7 +1626,7 @@ static int add_top_module_global_ports_from_tile_modules( } else { status = build_top_module_global_net_from_tile_modules( module_manager, top_module, top_module_port, tile_annotation, - tile_global_port, vpr_device_annotation, grids, tile_instance_ids, + tile_global_port, vpr_device_annotation, grids, layer, tile_instance_ids, fabric_tile); } if (status == CMD_EXEC_FATAL_ERROR) { @@ -1647,6 +1652,7 @@ static void add_module_nets_connect_tile_direct_connection( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile, const TileDirect& tile_direct, const TileDirectId& tile_direct_id, const ArchDirect& arch_direct) { @@ -1739,8 +1745,9 @@ static void add_module_nets_connect_tile_direct_connection( e_side src_pin_grid_side = tile_direct.from_tile_side(tile_direct_id); size_t src_tile_pin = tile_direct.from_tile_pin(tile_direct_id); + t_physical_tile_loc src_tile_loc(src_clb_coord.x(), src_clb_coord.y(), layer); t_physical_tile_type_ptr src_grid_type_descriptor = - grids.get_physical_type(src_clb_coord.x(), src_clb_coord.y()); + grids.get_physical_type(src_tile_loc); size_t src_pin_width = src_grid_type_descriptor->pin_width_offset[src_tile_pin]; size_t src_pin_height = @@ -1775,8 +1782,9 @@ static void add_module_nets_connect_tile_direct_connection( e_side sink_pin_grid_side = tile_direct.to_tile_side(tile_direct_id); size_t sink_tile_pin = tile_direct.to_tile_pin(tile_direct_id); + t_physical_tile_loc des_tile_loc(des_clb_coord.x(), des_clb_coord.y(), layer); t_physical_tile_type_ptr sink_grid_type_descriptor = - grids.get_physical_type(des_clb_coord.x(), des_clb_coord.y()); + grids.get_physical_type(des_tile_loc); size_t sink_pin_width = sink_grid_type_descriptor->pin_width_offset[src_tile_pin]; size_t sink_pin_height = @@ -1838,6 +1846,7 @@ static void add_top_module_nets_connect_tile_direct_connections( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile, const TileDirect& tile_direct, const ArchDirect& arch_direct) { vtr::ScopedStartFinishTimer timer( @@ -1846,7 +1855,7 @@ static void add_top_module_nets_connect_tile_direct_connections( for (const TileDirectId& tile_direct_id : tile_direct.directs()) { add_module_nets_connect_tile_direct_connection( module_manager, top_module, circuit_lib, vpr_device_annotation, grids, - tile_instance_ids, fabric_tile, tile_direct, tile_direct_id, arch_direct); + layer, tile_instance_ids, fabric_tile, tile_direct, tile_direct_id, arch_direct); } } @@ -1860,6 +1869,7 @@ int build_top_module_tile_child_instances( const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, const ArchDirect& arch_direct, const FabricTile& fabric_tile, @@ -1892,7 +1902,7 @@ int build_top_module_tile_child_instances( /* TODO: Inter-tile direct connections */ add_top_module_nets_connect_tile_direct_connections( module_manager, top_module, circuit_lib, vpr_device_annotation, grids, - tile_instance_ids, fabric_tile, tile_direct, arch_direct); + layer, tile_instance_ids, fabric_tile, tile_direct, arch_direct); } /* Add global ports from tile modules: how to connect to clock architecture @@ -1900,7 +1910,7 @@ int build_top_module_tile_child_instances( */ status = add_top_module_global_ports_from_tile_modules( module_manager, top_module, tile_annotation, vpr_device_annotation, grids, - rr_graph, device_rr_gsb, tile_instance_ids, fabric_tile, clk_ntwk, + layer, rr_graph, device_rr_gsb, tile_instance_ids, fabric_tile, clk_ntwk, rr_clock_lookup); 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 5adc39c75..e35ad3623 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.h +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.h @@ -38,6 +38,7 @@ int build_top_module_tile_child_instances( const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, const ArchDirect& arch_direct, const FabricTile& fabric_tile, diff --git a/openfpga/src/fabric/build_top_module_connection.cpp b/openfpga/src/fabric/build_top_module_connection.cpp index de4402c51..a0afbf79c 100644 --- a/openfpga/src/fabric/build_top_module_connection.cpp +++ b/openfpga/src/fabric/build_top_module_connection.cpp @@ -64,6 +64,7 @@ namespace openfpga { static void add_top_module_nets_connect_grids_and_sb( ModuleManager& module_manager, const ModuleId& top_module, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const RRGSB& rr_gsb, const vtr::Matrix& sb_instance_ids, @@ -127,7 +128,7 @@ static void add_top_module_nets_connect_grids_and_sb( rr_gsb.get_opin_node(side_manager.get_side(), inode)); t_physical_tile_type_ptr grid_type_descriptor = - grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); size_t src_grid_pin_width = grid_type_descriptor->pin_width_offset[src_grid_pin_index]; size_t src_grid_pin_height = @@ -227,6 +228,7 @@ static void add_top_module_nets_connect_grids_and_sb( static void add_top_module_nets_connect_grids_and_sb_with_duplicated_pins( ModuleManager& module_manager, const ModuleId& top_module, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const RRGSB& rr_gsb, const vtr::Matrix& sb_instance_ids, @@ -300,8 +302,9 @@ static void add_top_module_nets_connect_grids_and_sb_with_duplicated_pins( size_t src_grid_pin_index = rr_graph.node_pin_num( rr_gsb.get_opin_node(side_manager.get_side(), inode)); + t_physical_tile_loc phy_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer); t_physical_tile_type_ptr grid_type_descriptor = - grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + grids.get_physical_type(phy_tile_loc); size_t src_grid_pin_width = grid_type_descriptor->pin_width_offset[src_grid_pin_index]; size_t src_grid_pin_height = @@ -437,6 +440,7 @@ static void add_top_module_nets_connect_grids_and_sb_with_duplicated_pins( static void add_top_module_nets_connect_grids_and_cb( ModuleManager& module_manager, const ModuleId& top_module, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const RRGSB& rr_gsb, const t_rr_type& cb_type, const vtr::Matrix& cb_instance_ids, @@ -520,7 +524,7 @@ static void add_top_module_nets_connect_grids_and_cb( size_t sink_grid_pin_index = rr_graph.node_pin_num(instance_ipin_node); t_physical_tile_type_ptr grid_type_descriptor = - grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); size_t sink_grid_pin_width = grid_type_descriptor->pin_width_offset[sink_grid_pin_index]; size_t sink_grid_pin_height = @@ -802,6 +806,7 @@ static void add_top_module_nets_connect_sb_and_cb( void add_top_module_nets_connect_grids_and_gsbs( ModuleManager& module_manager, const ModuleId& top_module, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, @@ -819,24 +824,24 @@ void add_top_module_nets_connect_grids_and_gsbs( if (false == duplicate_grid_pin) { add_top_module_nets_connect_grids_and_sb( module_manager, top_module, vpr_device_annotation, grids, - grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, sb_instance_ids, + layer, grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, sb_instance_ids, compact_routing_hierarchy); } else { VTR_ASSERT_SAFE(true == duplicate_grid_pin); add_top_module_nets_connect_grids_and_sb_with_duplicated_pins( module_manager, top_module, vpr_device_annotation, grids, - grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, sb_instance_ids, + layer, grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, sb_instance_ids, compact_routing_hierarchy); } add_top_module_nets_connect_grids_and_cb( module_manager, top_module, vpr_device_annotation, grids, - grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, CHANX, + layer, grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, CHANX, cb_instance_ids.at(CHANX), compact_routing_hierarchy); add_top_module_nets_connect_grids_and_cb( module_manager, top_module, vpr_device_annotation, grids, - grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, CHANY, + layer, grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, CHANY, cb_instance_ids.at(CHANY), compact_routing_hierarchy); add_top_module_nets_connect_sb_and_cb( @@ -856,10 +861,11 @@ static int build_top_module_global_net_for_given_grid_module( const TileGlobalPortId& tile_global_port, const BasicPort& tile_port_to_connect, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Point& grid_coordinate, const e_side& border_side, const vtr::Matrix& grid_instance_ids) { t_physical_tile_type_ptr physical_tile = - grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); /* Find the module name for this type of grid */ std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); std::string grid_module_name = generate_grid_block_module_name( @@ -982,6 +988,7 @@ static int build_top_module_global_net_from_grid_modules( const ModulePortId& top_module_port, const TileAnnotation& tile_annotation, const TileGlobalPortId& tile_global_port, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& grid_instance_ids) { int status = CMD_EXEC_SUCCESS; @@ -1038,15 +1045,16 @@ static int build_top_module_global_net_from_grid_modules( /* Spot the port from child modules from core grids */ for (size_t ix = start_coord.x(); ix < end_coord.x(); ++ix) { for (size_t iy = start_coord.y(); iy < end_coord.y(); ++iy) { + t_physical_tile_loc tile_loc(ix, iy, layer); t_physical_tile_type_ptr phy_tile_type = - grids.get_physical_type(ix, iy); + grids.get_physical_type(tile_loc); /* Bypass EMPTY tiles */ if (true == is_empty_type(phy_tile_type)) { continue; } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(ix, iy)) || - (0 < grids.get_height_offset(ix, iy))) { + if ((0 < grids.get_width_offset(tile_loc)) || + (0 < grids.get_height_offset(tile_loc))) { continue; } @@ -1059,7 +1067,7 @@ static int build_top_module_global_net_from_grid_modules( status = build_top_module_global_net_for_given_grid_module( module_manager, top_module, top_module_port, tile_annotation, tile_global_port, tile_port, vpr_device_annotation, grids, - vtr::Point(ix, iy), NUM_SIDES, grid_instance_ids); + layer, vtr::Point(ix, iy), NUM_SIDES, grid_instance_ids); if (CMD_EXEC_FATAL_ERROR == status) { return status; } @@ -1069,8 +1077,9 @@ static int build_top_module_global_net_from_grid_modules( /* Walk through all the grids on the perimeter, which are I/O grids */ for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { + t_physical_tile_loc tile_loc(io_coordinate.x(), io_coordinate.y(), layer); t_physical_tile_type_ptr phy_tile_type = - grids.get_physical_type(io_coordinate.x(), io_coordinate.y()); + grids.get_physical_type(tile_loc); /* Bypass EMPTY grid */ if (true == is_empty_type(phy_tile_type)) { continue; @@ -1078,9 +1087,9 @@ static int build_top_module_global_net_from_grid_modules( /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ if ((0 < - grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || + grids.get_width_offset(tile_loc)) || (0 < - grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { + grids.get_height_offset(tile_loc))) { continue; } @@ -1106,7 +1115,7 @@ static int build_top_module_global_net_from_grid_modules( status = build_top_module_global_net_for_given_grid_module( module_manager, top_module, top_module_port, tile_annotation, tile_global_port, tile_port, vpr_device_annotation, grids, - io_coordinate, io_side, grid_instance_ids); + layer, io_coordinate, io_side, grid_instance_ids); if (CMD_EXEC_FATAL_ERROR == status) { return status; } @@ -1203,6 +1212,7 @@ int add_top_module_global_ports_from_grid_modules( ModuleManager& module_manager, const ModuleId& top_module, const TileAnnotation& tile_annotation, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const std::map>& cb_instance_ids, const vtr::Matrix& grid_instance_ids, const ClockNetwork& clk_ntwk, @@ -1262,7 +1272,7 @@ int add_top_module_global_ports_from_grid_modules( } else { status = build_top_module_global_net_from_grid_modules( module_manager, top_module, top_module_port, tile_annotation, - tile_global_port, vpr_device_annotation, grids, grid_instance_ids); + tile_global_port, vpr_device_annotation, grids, layer, grid_instance_ids); } if (status == CMD_EXEC_FATAL_ERROR) { return status; diff --git a/openfpga/src/fabric/build_top_module_connection.h b/openfpga/src/fabric/build_top_module_connection.h index a155d6a7d..80659e2bd 100644 --- a/openfpga/src/fabric/build_top_module_connection.h +++ b/openfpga/src/fabric/build_top_module_connection.h @@ -28,6 +28,7 @@ namespace openfpga { void add_top_module_nets_connect_grids_and_gsbs( ModuleManager& module_manager, const ModuleId& top_module, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, @@ -37,6 +38,7 @@ int add_top_module_global_ports_from_grid_modules( ModuleManager& module_manager, const ModuleId& top_module, const TileAnnotation& tile_annotation, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, const std::map>& cb_instance_ids, const vtr::Matrix& grid_instance_ids, const ClockNetwork& clk_ntwk, diff --git a/openfpga/src/fabric/build_top_module_directs.cpp b/openfpga/src/fabric/build_top_module_directs.cpp index 6af69cdb2..20c43e419 100644 --- a/openfpga/src/fabric/build_top_module_directs.cpp +++ b/openfpga/src/fabric/build_top_module_directs.cpp @@ -39,6 +39,7 @@ static void add_module_nets_tile_direct_connection( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, const TileDirect& tile_direct, const TileDirectId& tile_direct_id, const ArchDirect& arch_direct) { vtr::Point device_size(grids.width(), grids.height()); @@ -46,8 +47,9 @@ static void add_module_nets_tile_direct_connection( /* Find the module name of source clb */ vtr::Point src_clb_coord = tile_direct.from_tile_coordinate(tile_direct_id); + t_physical_tile_loc src_grid_loc(src_clb_coord.x(), src_clb_coord.y(), layer); t_physical_tile_type_ptr src_grid_type = - grids.get_physical_type(src_clb_coord.x(), src_clb_coord.y()); + grids.get_physical_type(src_grid_loc); e_side src_grid_border_side = find_grid_border_side(device_size, src_clb_coord); std::string src_module_name_prefix(GRID_MODULE_NAME_PREFIX); @@ -63,8 +65,9 @@ static void add_module_nets_tile_direct_connection( /* Find the module name of sink clb */ vtr::Point des_clb_coord = tile_direct.to_tile_coordinate(tile_direct_id); + t_physical_tile_loc sink_grid_loc(des_clb_coord.x(), des_clb_coord.y(), layer); t_physical_tile_type_ptr sink_grid_type = - grids.get_physical_type(des_clb_coord.x(), des_clb_coord.y()); + grids.get_physical_type(sink_grid_loc); e_side sink_grid_border_side = find_grid_border_side(device_size, des_clb_coord); std::string sink_module_name_prefix(GRID_MODULE_NAME_PREFIX); @@ -114,7 +117,7 @@ static void add_module_nets_tile_direct_connection( size_t src_tile_pin = tile_direct.from_tile_pin(tile_direct_id); t_physical_tile_type_ptr src_grid_type_descriptor = - grids.get_physical_type(src_clb_coord.x(), src_clb_coord.y()); + grids.get_physical_type(src_grid_loc); size_t src_pin_width = src_grid_type_descriptor->pin_width_offset[src_tile_pin]; size_t src_pin_height = @@ -148,7 +151,7 @@ static void add_module_nets_tile_direct_connection( size_t sink_tile_pin = tile_direct.to_tile_pin(tile_direct_id); t_physical_tile_type_ptr sink_grid_type_descriptor = - grids.get_physical_type(des_clb_coord.x(), des_clb_coord.y()); + grids.get_physical_type(sink_grid_loc); size_t sink_pin_width = sink_grid_type_descriptor->pin_width_offset[src_tile_pin]; size_t sink_pin_height = @@ -209,6 +212,7 @@ void add_top_module_nets_tile_direct_connections( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, const TileDirect& tile_direct, const ArchDirect& arch_direct) { vtr::ScopedStartFinishTimer timer( @@ -217,7 +221,7 @@ void add_top_module_nets_tile_direct_connections( for (const TileDirectId& tile_direct_id : tile_direct.directs()) { add_module_nets_tile_direct_connection( module_manager, top_module, circuit_lib, vpr_device_annotation, grids, - grid_instance_ids, tile_direct, tile_direct_id, arch_direct); + layer, grid_instance_ids, tile_direct, tile_direct_id, arch_direct); } } diff --git a/openfpga/src/fabric/build_top_module_directs.h b/openfpga/src/fabric/build_top_module_directs.h index 4de48efb7..c9c13d66b 100644 --- a/openfpga/src/fabric/build_top_module_directs.h +++ b/openfpga/src/fabric/build_top_module_directs.h @@ -26,6 +26,7 @@ void add_top_module_nets_tile_direct_connections( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, const TileDirect& tile_direct, const ArchDirect& arch_direct); diff --git a/openfpga/src/fabric/build_top_module_memory.cpp b/openfpga/src/fabric/build_top_module_memory.cpp index 4e0b45c98..447ecd604 100644 --- a/openfpga/src/fabric/build_top_module_memory.cpp +++ b/openfpga/src/fabric/build_top_module_memory.cpp @@ -136,7 +136,9 @@ static void organize_top_module_tile_memory_modules( const vtr::Matrix& grid_instance_ids, const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, - const bool& compact_routing_hierarchy, const vtr::Point& tile_coord, + const bool& compact_routing_hierarchy, + const size_t& layer, + const vtr::Point& tile_coord, const e_side& tile_border_side) { vtr::Point gsb_coord_range = device_rr_gsb.get_gsb_range(); @@ -191,8 +193,9 @@ static void organize_top_module_tile_memory_modules( } /* Find the module name for this type of grid */ + t_physical_tile_loc phy_tile_loc(tile_coord.x(), tile_coord.y(), layer); t_physical_tile_type_ptr grid_type = - grids.get_physical_type(tile_coord.x(), tile_coord.y()); + grids.get_physical_type(phy_tile_loc); /* Skip EMPTY Grid */ if (true == is_empty_type(grid_type)) { @@ -200,8 +203,8 @@ static void organize_top_module_tile_memory_modules( } /* Skip width > 1 or height > 1 Grid, which should already been processed when * offset=0 */ - if ((0 < grids.get_width_offset(tile_coord.x(), tile_coord.y())) || - (0 < grids.get_height_offset(tile_coord.x(), tile_coord.y()))) { + if ((0 < grids.get_width_offset(phy_tile_loc)) || + (0 < grids.get_height_offset(phy_tile_loc))) { return; } @@ -437,6 +440,7 @@ void organize_top_module_memory_modules( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, const CircuitModelId& sram_model, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, @@ -497,7 +501,7 @@ void organize_top_module_memory_modules( organize_top_module_tile_memory_modules( module_manager, top_module, circuit_lib, config_protocol.type(), sram_model, grids, grid_instance_ids, device_rr_gsb, sb_instance_ids, - cb_instance_ids, compact_routing_hierarchy, io_coord, io_side); + cb_instance_ids, compact_routing_hierarchy, layer, io_coord, io_side); } } @@ -525,7 +529,7 @@ void organize_top_module_memory_modules( organize_top_module_tile_memory_modules( module_manager, top_module, circuit_lib, config_protocol.type(), sram_model, grids, grid_instance_ids, device_rr_gsb, sb_instance_ids, - cb_instance_ids, compact_routing_hierarchy, core_coord, NUM_SIDES); + cb_instance_ids, compact_routing_hierarchy, layer, core_coord, NUM_SIDES); } /* Split memory modules into different regions */ diff --git a/openfpga/src/fabric/build_top_module_memory.h b/openfpga/src/fabric/build_top_module_memory.h index a452588ec..69df431c5 100644 --- a/openfpga/src/fabric/build_top_module_memory.h +++ b/openfpga/src/fabric/build_top_module_memory.h @@ -32,6 +32,7 @@ void organize_top_module_memory_modules( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, const CircuitModelId& sram_model, const DeviceGrid& grids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, diff --git a/openfpga/src/fabric/build_top_module_utils.cpp b/openfpga/src/fabric/build_top_module_utils.cpp index 1fe1bd158..d3c7ac175 100644 --- a/openfpga/src/fabric/build_top_module_utils.cpp +++ b/openfpga/src/fabric/build_top_module_utils.cpp @@ -30,7 +30,7 @@ std::string generate_grid_block_module_name_in_top_module( vtr::Point device_size(grids.width(), grids.height()); e_side border_side = find_grid_border_side(device_size, grid_coord); t_physical_tile_type_ptr phy_tile_type = - grids.get_physical_type(grid_coord.x(), grid_coord.y()); + grids.get_physical_type(t_physical_tile_loc(grid_coord.x(), grid_coord.y(), 0)); return generate_grid_block_module_name( prefix, std::string(phy_tile_type->name), is_io_type(phy_tile_type), @@ -52,7 +52,7 @@ std::string generate_grid_module_port_name_in_top_module( const VprDeviceAnnotation& vpr_device_annotation, const RRGraphView& rr_graph, const RRNodeId& inode) { t_physical_tile_type_ptr grid_type_descriptor = - grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), 0)); size_t sink_grid_pin_width = grid_type_descriptor->pin_width_offset[sink_grid_pin_index]; size_t sink_grid_pin_height = diff --git a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp index d4a15986f..69e723f05 100644 --- a/openfpga/src/fpga_bitstream/build_device_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_device_bitstream.cpp @@ -209,7 +209,7 @@ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx, build_grid_bitstream(bitstream_manager, top_block, openfpga_ctx.module_graph(), openfpga_ctx.fabric_tile(), openfpga_ctx.arch().circuit_lib, openfpga_ctx.mux_lib(), - vpr_ctx.device().grid, vpr_ctx.atom(), + vpr_ctx.device().grid, 0, vpr_ctx.atom(), openfpga_ctx.vpr_device_annotation(), openfpga_ctx.vpr_clustering_annotation(), openfpga_ctx.vpr_placement_annotation(), diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index 80342a20d..7952b108a 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -824,11 +824,12 @@ static void build_physical_block_bitstream( const VprClusteringAnnotation& cluster_annotation, const VprPlacementAnnotation& place_annotation, const VprBitstreamAnnotation& bitstream_annotation, const DeviceGrid& grids, + const size_t& layer, 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()); + grids.get_physical_type(t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer)); std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); /* Early exit if this parent module has no configurable child modules */ @@ -950,7 +951,8 @@ void build_grid_bitstream( BitstreamManager& bitstream_manager, const ConfigBlockId& top_block, const ModuleManager& module_manager, const FabricTile& fabric_tile, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, - const DeviceGrid& grids, const AtomContext& atom_ctx, + const DeviceGrid& grids, const size_t& layer, + const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, const VprClusteringAnnotation& cluster_annotation, const VprPlacementAnnotation& place_annotation, @@ -960,13 +962,14 @@ void build_grid_bitstream( /* Generate bitstream for the core logic block one by one */ for (size_t ix = 1; ix < grids.width() - 1; ++ix) { for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + t_physical_tile_loc phy_tile_loc(ix, iy, layer); /* Bypass EMPTY grid */ - if (true == is_empty_type(grids.get_physical_type(ix, iy))) { + if (true == is_empty_type(grids.get_physical_type(phy_tile_loc))) { continue; } /* Skip width > 1 or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(ix, iy)) || - (0 < grids.get_height_offset(ix, iy))) { + if ((0 < grids.get_width_offset(phy_tile_loc)) || + (0 < grids.get_height_offset(phy_tile_loc))) { continue; } /* Add a grid module to top_module*/ @@ -993,7 +996,7 @@ 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, layer, grid_coord, NUM_SIDES, verbose); } } @@ -1008,14 +1011,14 @@ void build_grid_bitstream( /* Add instances of I/O grids to top_module */ for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { + t_physical_tile_loc phy_tile_loc(io_coordinate.x(), io_coordinate.y(), layer); /* Bypass EMPTY grid */ - if (true == is_empty_type(grids.get_physical_type(io_coordinate.x(), - io_coordinate.y()))) { + if (true == is_empty_type(grids.get_physical_type(phy_tile_loc))) { continue; } /* Skip height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < grids.get_width_offset(io_coordinate.x(), io_coordinate.y())) || - (0 < grids.get_height_offset(io_coordinate.x(), io_coordinate.y()))) { + if ((0 < grids.get_width_offset(phy_tile_loc)) || + (0 < grids.get_height_offset(phy_tile_loc))) { continue; } /* TODO: If the fabric tile is not empty, find the tile module and create @@ -1040,7 +1043,7 @@ 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, layer, io_coordinate, io_side, verbose); } } diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.h b/openfpga/src/fpga_bitstream/build_grid_bitstream.h index e6b882fcd..3a5e38131 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.h +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.h @@ -29,7 +29,8 @@ void build_grid_bitstream( BitstreamManager& bitstream_manager, const ConfigBlockId& top_block, const ModuleManager& module_manager, const FabricTile& fabric_tile, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, - const DeviceGrid& grids, const AtomContext& atom_ctx, + const DeviceGrid& grids, const size_t& layer, + const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, const VprClusteringAnnotation& cluster_annotation, const VprPlacementAnnotation& place_annotation, diff --git a/openfpga/src/fpga_sdc/analysis_sdc_grid_writer.cpp b/openfpga/src/fpga_sdc/analysis_sdc_grid_writer.cpp index 94bda6b5b..59546d3a0 100644 --- a/openfpga/src/fpga_sdc/analysis_sdc_grid_writer.cpp +++ b/openfpga/src/fpga_sdc/analysis_sdc_grid_writer.cpp @@ -602,16 +602,17 @@ static void print_analysis_sdc_disable_unused_grid( const ModuleManager& module_manager, const e_side& border_side) { /* Validate file stream */ valid_file_stream(fp); - + + t_physical_tile_loc phy_tile_loc(grid_coordinate.x(), grid_coordinate.y(), 0); t_physical_tile_type_ptr grid_type = - grids.get_physical_type(grid_coordinate.x(), grid_coordinate.y()); + grids.get_physical_type(phy_tile_loc); /* Bypass conditions for grids : * 1. EMPTY type, which is by nature unused * 2. Offset > 0, which has already been processed when offset = 0 */ if ((true == is_empty_type(grid_type)) || - (0 < grids.get_width_offset(grid_coordinate.x(), grid_coordinate.y())) || - (0 < grids.get_height_offset(grid_coordinate.x(), grid_coordinate.y()))) { + (0 < grids.get_width_offset(phy_tile_loc)) || + (0 < grids.get_height_offset(phy_tile_loc))) { return; } diff --git a/openfpga/src/tile_direct/build_tile_direct.cpp b/openfpga/src/tile_direct/build_tile_direct.cpp index e2c46cf12..3d6aaeb79 100644 --- a/openfpga/src/tile_direct/build_tile_direct.cpp +++ b/openfpga/src/tile_direct/build_tile_direct.cpp @@ -168,7 +168,7 @@ static vtr::Point find_grid_coordinate_given_type( continue; } if (wanted_grid_type_name == - std::string(grids.get_physical_type(coord.x(), coord.y())->name)) { + std::string(grids.get_physical_type(t_physical_tile_loc(coord.x(), coord.y(), 0))->name)) { return coord; } } @@ -401,8 +401,9 @@ static void build_inner_column_row_tile_direct( /* Walk through the device fabric and find the grid that fit the source */ for (size_t x = 0; x < device_ctx.grid.width(); ++x) { for (size_t y = 0; y < device_ctx.grid.height(); ++y) { + t_physical_tile_loc from_phy_tile_loc(x, y, 0); t_physical_tile_type_ptr from_phy_tile_type = - device_ctx.grid.get_physical_type(x, y); + device_ctx.grid.get_physical_type(from_phy_tile_loc); /* Bypass empty grid */ if (true == is_empty_type(from_phy_tile_type)) { continue; @@ -420,8 +421,8 @@ static void build_inner_column_row_tile_direct( for (const e_side& from_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ std::vector from_pins = find_physical_tile_pin_id( - from_phy_tile_type, device_ctx.grid.get_width_offset(x, y), - device_ctx.grid.get_height_offset(x, y), from_tile_port, from_side); + from_phy_tile_type, device_ctx.grid.get_width_offset(from_phy_tile_loc), + device_ctx.grid.get_height_offset(from_phy_tile_loc), from_tile_port, from_side); /* If nothing found, we can continue */ if (0 == from_pins.size()) { continue; @@ -437,9 +438,9 @@ static void build_inner_column_row_tile_direct( continue; } + t_physical_tile_loc to_phy_tile_loc(to_grid_coord.x(), to_grid_coord.y(), 0); t_physical_tile_type_ptr to_phy_tile_type = - device_ctx.grid.get_physical_type(to_grid_coord.x(), - to_grid_coord.y()); + device_ctx.grid.get_physical_type(to_phy_tile_loc); /* Bypass the grid that does not fit the from_tile name */ if (to_tile_name != std::string(to_phy_tile_type->name)) { continue; @@ -454,9 +455,9 @@ static void build_inner_column_row_tile_direct( std::vector to_pins = find_physical_tile_pin_id(to_phy_tile_type, device_ctx.grid.get_width_offset( - to_grid_coord.x(), to_grid_coord.y()), + to_phy_tile_loc), device_ctx.grid.get_height_offset( - to_grid_coord.x(), to_grid_coord.y()), + to_phy_tile_loc), to_tile_port, to_side); /* If nothing found, we can continue */ if (0 == to_pins.size()) { @@ -594,13 +595,14 @@ static void build_inter_column_row_tile_direct( */ for (const e_side& from_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ + t_physical_tile_loc from_phy_tile_loc(from_grid_coord.x(), from_grid_coord.y(), 0); std::vector from_pins = find_physical_tile_pin_id(device_ctx.grid.get_physical_type( - from_grid_coord.x(), from_grid_coord.y()), + from_phy_tile_loc), device_ctx.grid.get_width_offset( - from_grid_coord.x(), from_grid_coord.y()), + from_phy_tile_loc), device_ctx.grid.get_height_offset( - from_grid_coord.x(), from_grid_coord.y()), + from_phy_tile_loc), from_tile_port, from_side); /* If nothing found, we can continue */ if (0 == from_pins.size()) { @@ -625,13 +627,14 @@ static void build_inter_column_row_tile_direct( */ for (const e_side& to_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ + t_physical_tile_loc to_phy_tile_loc(to_grid_coord.x(), to_grid_coord.y(), 0); std::vector to_pins = find_physical_tile_pin_id(device_ctx.grid.get_physical_type( - to_grid_coord.x(), to_grid_coord.y()), + to_phy_tile_loc), device_ctx.grid.get_width_offset( - to_grid_coord.x(), to_grid_coord.y()), + to_phy_tile_loc), device_ctx.grid.get_height_offset( - to_grid_coord.x(), to_grid_coord.y()), + to_phy_tile_loc), to_tile_port, to_side); /* If nothing found, we can continue */ if (0 == to_pins.size()) { @@ -707,13 +710,14 @@ static void build_inter_column_row_tile_direct( */ for (const e_side& from_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ + t_physical_tile_loc from_phy_tile_loc(from_grid_coord.x(), from_grid_coord.y(), 0); std::vector from_pins = find_physical_tile_pin_id(device_ctx.grid.get_physical_type( - from_grid_coord.x(), from_grid_coord.y()), + from_phy_tile_loc), device_ctx.grid.get_width_offset( - from_grid_coord.x(), from_grid_coord.y()), + from_phy_tile_loc), device_ctx.grid.get_height_offset( - from_grid_coord.x(), from_grid_coord.y()), + from_phy_tile_loc), from_tile_port, from_side); /* If nothing found, we can continue */ if (0 == from_pins.size()) { @@ -738,13 +742,14 @@ static void build_inter_column_row_tile_direct( */ for (const e_side& to_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ + t_physical_tile_loc to_phy_tile_loc(to_grid_coord.x(), to_grid_coord.y(), 0); std::vector to_pins = find_physical_tile_pin_id(device_ctx.grid.get_physical_type( - to_grid_coord.x(), to_grid_coord.y()), + to_phy_tile_loc), device_ctx.grid.get_width_offset( - to_grid_coord.x(), to_grid_coord.y()), + to_phy_tile_loc), device_ctx.grid.get_height_offset( - to_grid_coord.x(), to_grid_coord.y()), + to_phy_tile_loc), to_tile_port, to_side); /* If nothing found, we can continue */ if (0 == to_pins.size()) { diff --git a/openfpga/src/utils/openfpga_physical_tile_utils.cpp b/openfpga/src/utils/openfpga_physical_tile_utils.cpp index 2e599eb1b..8091081fc 100644 --- a/openfpga/src/utils/openfpga_physical_tile_utils.cpp +++ b/openfpga/src/utils/openfpga_physical_tile_utils.cpp @@ -89,7 +89,7 @@ std::set find_physical_io_tile_located_sides( for (size_t ix = 1; ix < grids.width() - 1; ++ix) { for (size_t iy = 1; iy < grids.height() - 1; ++iy) { /* If located in center, we add a NUM_SIDES and finish */ - if (physical_tile == grids.get_physical_type(ix, iy)) { + if (physical_tile == grids.get_physical_type(t_physical_tile_loc(ix, iy, 0))) { io_sides.insert(NUM_SIDES); center_io = true; break; @@ -109,7 +109,7 @@ std::set find_physical_io_tile_located_sides( for (const vtr::Point& io_coordinate : io_coordinates[fpga_side]) { /* If located in center, we add a NUM_SIDES and finish */ if (physical_tile == - grids.get_physical_type(io_coordinate.x(), io_coordinate.y())) { + grids.get_physical_type(t_physical_tile_loc(io_coordinate.x(), io_coordinate.y(), 0))) { io_sides.insert(fpga_side); break; } diff --git a/openfpga/src/vpr_wrapper/vpr_main.cpp b/openfpga/src/vpr_wrapper/vpr_main.cpp index e9b5d563a..940fc0644 100644 --- a/openfpga/src/vpr_wrapper/vpr_main.cpp +++ b/openfpga/src/vpr_wrapper/vpr_main.cpp @@ -118,20 +118,15 @@ static int vpr_standalone(int argc, char** argv) { /* Read options, architecture, and circuit netlist */ vpr_init(argc, const_cast(argv), &Options, &vpr_setup, &Arch); - const Netlist<>& net_list = - vpr_setup.RouterOpts.flat_routing - ? (const Netlist<>&)g_vpr_ctx.atom().nlist - : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; - if (Options.show_version) { - vpr_free_all(net_list, Arch, vpr_setup); + vpr_free_all(Arch, vpr_setup); return SUCCESS_EXIT_CODE; } bool flow_succeeded = vpr_flow(vpr_setup, Arch); if (!flow_succeeded) { VTR_LOG("VPR failed to implement circuit\n"); - vpr_free_all(net_list, Arch, vpr_setup); + vpr_free_all(Arch, vpr_setup); return UNIMPLEMENTABLE_EXIT_CODE; } @@ -139,43 +134,30 @@ static int vpr_standalone(int argc, char** argv) { print_timing_stats("Flow", timing_ctx.stats); /* free data structures */ - vpr_free_all(net_list, Arch, vpr_setup); + vpr_free_all(Arch, vpr_setup); VTR_LOG("VPR succeeded\n"); } catch (const tatum::Error& tatum_error) { VTR_LOG_ERROR("%s\n", format_tatum_error(tatum_error).c_str()); - const Netlist<>& net_list = - vpr_setup.RouterOpts.flat_routing - ? (const Netlist<>&)g_vpr_ctx.atom().nlist - : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; - vpr_free_all(net_list, Arch, vpr_setup); + vpr_free_all(Arch, vpr_setup); return ERROR_EXIT_CODE; } catch (const VprError& vpr_error) { vpr_print_error(vpr_error); - const Netlist<>& net_list = - vpr_setup.RouterOpts.flat_routing - ? (const Netlist<>&)g_vpr_ctx.atom().nlist - : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; - if (vpr_error.type() == VPR_ERROR_INTERRUPTED) { - vpr_free_all(net_list, Arch, vpr_setup); + vpr_free_all(Arch, vpr_setup); return INTERRUPTED_EXIT_CODE; } else { - vpr_free_all(net_list, Arch, vpr_setup); + vpr_free_all(Arch, vpr_setup); return ERROR_EXIT_CODE; } } catch (const vtr::VtrError& vtr_error) { VTR_LOG_ERROR("%s:%d %s\n", vtr_error.filename_c_str(), vtr_error.line(), vtr_error.what()); - const Netlist<>& net_list = - vpr_setup.RouterOpts.flat_routing - ? (const Netlist<>&)g_vpr_ctx.atom().nlist - : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; - vpr_free_all(net_list, Arch, vpr_setup); + vpr_free_all(Arch, vpr_setup); return ERROR_EXIT_CODE; } From 94d80a9b7c226b45ba20970724fac8d042e62c1a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 8 Aug 2023 16:28:56 -0700 Subject: [PATCH 302/391] [core] code format --- openfpga/src/annotation/annotate_rr_graph.cpp | 25 +++-- .../annotate_simulation_setting.cpp | 2 +- .../src/annotation/append_clock_rr_graph.cpp | 67 +++++++------- .../annotation/openfpga_annotate_routing.cpp | 12 ++- .../annotation/vpr_placement_annotation.cpp | 5 +- openfpga/src/base/openfpga_pb_pin_fixup.cpp | 33 ++++--- .../fabric/build_fabric_io_location_map.cpp | 6 +- openfpga/src/fabric/build_fabric_tile.cpp | 3 +- .../src/fabric/build_routing_module_utils.cpp | 12 ++- openfpga/src/fabric/build_tile_modules.cpp | 47 +++++----- openfpga/src/fabric/build_top_module.cpp | 15 +-- ...top_module_child_fine_grained_instance.cpp | 43 +++++---- ...d_top_module_child_fine_grained_instance.h | 14 +-- .../build_top_module_child_tile_instance.cpp | 60 ++++++------ .../build_top_module_child_tile_instance.h | 13 ++- .../fabric/build_top_module_connection.cpp | 91 +++++++++---------- .../src/fabric/build_top_module_connection.h | 10 +- .../src/fabric/build_top_module_directs.cpp | 14 +-- .../src/fabric/build_top_module_directs.h | 5 +- .../src/fabric/build_top_module_memory.cpp | 12 +-- openfpga/src/fabric/build_top_module_memory.h | 3 +- .../src/fabric/build_top_module_utils.cpp | 8 +- .../fpga_bitstream/build_grid_bitstream.cpp | 23 +++-- .../src/fpga_bitstream/build_grid_bitstream.h | 3 +- .../src/fpga_sdc/analysis_sdc_grid_writer.cpp | 5 +- .../src/tile_direct/build_tile_direct.cpp | 88 +++++++++--------- .../utils/openfpga_physical_tile_utils.cpp | 7 +- 27 files changed, 309 insertions(+), 317 deletions(-) diff --git a/openfpga/src/annotation/annotate_rr_graph.cpp b/openfpga/src/annotation/annotate_rr_graph.cpp index 65cbbe01f..b58dbfd8d 100644 --- a/openfpga/src/annotation/annotate_rr_graph.cpp +++ b/openfpga/src/annotation/annotate_rr_graph.cpp @@ -21,8 +21,7 @@ namespace openfpga { /* Build a RRChan Object with the given channel type and coorindators */ static RRChan build_one_rr_chan(const DeviceContext& vpr_device_ctx, - const t_rr_type& chan_type, - const size_t& layer, + const t_rr_type& chan_type, const size_t& layer, vtr::Point& chan_coord) { std::vector chan_rr_nodes; @@ -153,8 +152,8 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, gsb_coord.y() + 1, OPIN, opin_grid_side[0]); /* Include Grid[x+1][y+1] Left side output pins */ temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes( - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x() + 1, - gsb_coord.y() + 1, OPIN, opin_grid_side[1]); + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, + gsb_coord.x() + 1, gsb_coord.y() + 1, OPIN, opin_grid_side[1]); break; case RIGHT: /* RIGHT = 1 */ @@ -182,12 +181,12 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, /* include Grid[x+1][y+1] Bottom side output pins */ temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes( - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x() + 1, - gsb_coord.y() + 1, OPIN, opin_grid_side[0]); + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, + gsb_coord.x() + 1, gsb_coord.y() + 1, OPIN, opin_grid_side[0]); /* include Grid[x+1][y] Top side output pins */ temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes( - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x() + 1, - gsb_coord.y(), OPIN, opin_grid_side[1]); + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, + gsb_coord.x() + 1, gsb_coord.y(), OPIN, opin_grid_side[1]); break; case BOTTOM: /* BOTTOM = 2*/ /* For the border, we should take special care */ @@ -213,8 +212,8 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, opin_grid_side[1] = RIGHT; /* include Grid[x+1][y] Left side output pins */ temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes( - vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x() + 1, - gsb_coord.y(), OPIN, opin_grid_side[0]); + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, + gsb_coord.x() + 1, gsb_coord.y(), OPIN, opin_grid_side[0]); /* include Grid[x][y] Right side output pins */ temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes( vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x(), @@ -371,9 +370,9 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx, continue; } /* Collect IPIN rr_nodes*/ - temp_ipin_rr_nodes = - find_rr_graph_grid_nodes(vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, ix, - iy, IPIN, ipin_rr_node_grid_side, include_clock); + temp_ipin_rr_nodes = find_rr_graph_grid_nodes( + vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, ix, iy, IPIN, + ipin_rr_node_grid_side, include_clock); /* Fill the ipin nodes of RRGSB */ for (const RRNodeId& inode : temp_ipin_rr_nodes) { /* Skip those has no configurable outgoing, they should NOT appear in the diff --git a/openfpga/src/annotation/annotate_simulation_setting.cpp b/openfpga/src/annotation/annotate_simulation_setting.cpp index 8f4014427..40056f51c 100644 --- a/openfpga/src/annotation/annotate_simulation_setting.cpp +++ b/openfpga/src/annotation/annotate_simulation_setting.cpp @@ -15,8 +15,8 @@ /* Headers from vpr library */ #include "AnalysisDelayCalculator.h" #include "annotate_simulation_setting.h" -#include "net_delay.h" #include "concrete_timing_info.h" +#include "net_delay.h" /* begin namespace openfpga */ namespace openfpga { diff --git a/openfpga/src/annotation/append_clock_rr_graph.cpp b/openfpga/src/annotation/append_clock_rr_graph.cpp index 0f9e1ecbd..a52a86ed2 100644 --- a/openfpga/src/annotation/append_clock_rr_graph.cpp +++ b/openfpga/src/annotation/append_clock_rr_graph.cpp @@ -88,9 +88,9 @@ static size_t estimate_clock_rr_graph_num_nodes(const DeviceGrid& grids, static void add_rr_graph_block_clock_nodes( RRGraphBuilder& rr_graph_builder, RRClockSpatialLookup& clk_rr_lookup, const RRGraphView& rr_graph_view, const ClockNetwork& clk_ntwk, - const size_t& layer, - const vtr::Point chan_coord, const t_rr_type& chan_type, - const int& cost_index_offset, const bool& verbose) { + const size_t& layer, const vtr::Point chan_coord, + const t_rr_type& chan_type, const int& cost_index_offset, + const bool& verbose) { size_t orig_chan_width = rr_graph_view.node_lookup() .find_channel_nodes(layer, chan_coord.x(), chan_coord.y(), chan_type) @@ -148,14 +148,11 @@ static void add_rr_graph_block_clock_nodes( * Add clock nodes one by one to the routing resource graph. * Assign node-level attributes properly and register in dedicated lookup *******************************************************************/ -static void add_rr_graph_clock_nodes(RRGraphBuilder& rr_graph_builder, - RRClockSpatialLookup& clk_rr_lookup, - const RRGraphView& rr_graph_view, - const DeviceGrid& grids, - const size_t& layer, - const bool& through_channel, - const ClockNetwork& clk_ntwk, - const bool& verbose) { +static void add_rr_graph_clock_nodes( + RRGraphBuilder& rr_graph_builder, RRClockSpatialLookup& clk_rr_lookup, + const RRGraphView& rr_graph_view, const DeviceGrid& grids, + const size_t& layer, const bool& through_channel, + const ClockNetwork& clk_ntwk, const bool& verbose) { /* Pre-allocate memory: Must do otherwise data will be messed up! */ clk_rr_lookup.reserve_nodes(grids.width(), grids.height(), clk_ntwk.num_trees(), clk_ntwk.max_tree_depth(), @@ -171,9 +168,9 @@ static void add_rr_graph_clock_nodes(RRGraphBuilder& rr_graph_builder, (false == is_chanx_exist(grids, layer, chanx_coord))) { continue; } - add_rr_graph_block_clock_nodes(rr_graph_builder, clk_rr_lookup, - rr_graph_view, clk_ntwk, layer, chanx_coord, - CHANX, CHANX_COST_INDEX_START, verbose); + add_rr_graph_block_clock_nodes( + rr_graph_builder, clk_rr_lookup, rr_graph_view, clk_ntwk, layer, + chanx_coord, CHANX, CHANX_COST_INDEX_START, verbose); VTR_ASSERT(rr_graph_view.valid_node( clk_rr_lookup.find_node(1, 0, ClockTreeId(0), ClockLevelId(0), ClockTreePinId(0), Direction::INC))); @@ -194,9 +191,9 @@ static void add_rr_graph_clock_nodes(RRGraphBuilder& rr_graph_builder, continue; } add_rr_graph_block_clock_nodes( - rr_graph_builder, clk_rr_lookup, rr_graph_view, clk_ntwk, layer, chany_coord, - CHANY, CHANX_COST_INDEX_START + rr_graph_view.num_rr_segments(), - verbose); + rr_graph_builder, clk_rr_lookup, rr_graph_view, clk_ntwk, layer, + chany_coord, CHANY, + CHANX_COST_INDEX_START + rr_graph_view.num_rr_segments(), verbose); VTR_ASSERT(rr_graph_view.valid_node( clk_rr_lookup.find_node(1, 0, ClockTreeId(0), ClockLevelId(0), ClockTreePinId(0), Direction::INC))); @@ -397,11 +394,11 @@ static std::vector find_clock_track2track_node( static void try_find_and_add_clock_track2ipin_node( std::vector& des_nodes, const DeviceGrid& grids, const RRGraphView& rr_graph_view, const size_t& layer, - const vtr::Point& grid_coord, - const e_side& pin_side, const ClockNetwork& clk_ntwk, - const ClockTreeId& clk_tree, const ClockTreePinId& clk_pin) { - t_physical_tile_type_ptr grid_type = - grids.get_physical_type(t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer)); + const vtr::Point& grid_coord, const e_side& pin_side, + const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree, + const ClockTreePinId& clk_pin) { + t_physical_tile_type_ptr grid_type = grids.get_physical_type( + t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer)); for (std::string tap_pin_name : clk_ntwk.tree_flatten_taps(clk_tree, clk_pin)) { /* tap pin name could be 'io[5:5].a2f[0]' */ @@ -445,17 +442,17 @@ static void try_find_and_add_clock_track2ipin_node( *******************************************************************/ static std::vector find_clock_track2ipin_node( const DeviceGrid& grids, const RRGraphView& rr_graph_view, - const t_rr_type& chan_type, const size_t& layer, const vtr::Point& chan_coord, - const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree, - const ClockTreePinId& clk_pin) { + const t_rr_type& chan_type, const size_t& layer, + const vtr::Point& chan_coord, const ClockNetwork& clk_ntwk, + const ClockTreeId& clk_tree, const ClockTreePinId& clk_pin) { std::vector des_nodes; if (chan_type == CHANX) { /* Get the clock IPINs at the BOTTOM side of adjacent grids [x][y+1] */ vtr::Point bot_grid_coord(chan_coord.x(), chan_coord.y() + 1); try_find_and_add_clock_track2ipin_node(des_nodes, grids, rr_graph_view, - layer, bot_grid_coord, BOTTOM, clk_ntwk, - clk_tree, clk_pin); + layer, bot_grid_coord, BOTTOM, + clk_ntwk, clk_tree, clk_pin); /* Get the clock IPINs at the TOP side of adjacent grids [x][y] */ vtr::Point top_grid_coord(chan_coord.x(), chan_coord.y()); @@ -467,14 +464,14 @@ static std::vector find_clock_track2ipin_node( /* Get the clock IPINs at the LEFT side of adjacent grids [x][y+1] */ vtr::Point left_grid_coord(chan_coord.x() + 1, chan_coord.y()); try_find_and_add_clock_track2ipin_node(des_nodes, grids, rr_graph_view, - layer, left_grid_coord, LEFT, clk_ntwk, - clk_tree, clk_pin); + layer, left_grid_coord, LEFT, + clk_ntwk, clk_tree, clk_pin); /* Get the clock IPINs at the RIGHT side of adjacent grids [x][y] */ vtr::Point right_grid_coord(chan_coord.x(), chan_coord.y()); try_find_and_add_clock_track2ipin_node(des_nodes, grids, rr_graph_view, - layer, right_grid_coord, RIGHT, clk_ntwk, - clk_tree, clk_pin); + layer, right_grid_coord, RIGHT, + clk_ntwk, clk_tree, clk_pin); } return des_nodes; @@ -486,9 +483,7 @@ static std::vector find_clock_track2ipin_node( static void add_rr_graph_block_clock_edges( RRGraphBuilder& rr_graph_builder, size_t& num_edges_to_create, const RRClockSpatialLookup& clk_rr_lookup, const RRGraphView& rr_graph_view, - const DeviceGrid& grids, - const size_t& layer, - const ClockNetwork& clk_ntwk, + const DeviceGrid& grids, const size_t& layer, const ClockNetwork& clk_ntwk, const vtr::Point& chan_coord, const t_rr_type& chan_type, const bool& verbose) { size_t edge_count = 0; @@ -542,8 +537,8 @@ static void add_rr_graph_block_clock_edges( if (clk_ntwk.is_last_level(itree, ilvl)) { size_t curr_edge_count = edge_count; for (RRNodeId des_node : find_clock_track2ipin_node( - grids, rr_graph_view, chan_type, layer, chan_coord, clk_ntwk, itree, - ClockTreePinId(ipin))) { + grids, rr_graph_view, chan_type, layer, chan_coord, clk_ntwk, + itree, ClockTreePinId(ipin))) { /* Create edges */ VTR_ASSERT(rr_graph_view.valid_node(des_node)); rr_graph_builder.create_edge(src_node, des_node, diff --git a/openfpga/src/annotation/openfpga_annotate_routing.cpp b/openfpga/src/annotation/openfpga_annotate_routing.cpp index e4aa8e88d..f95b2ea37 100644 --- a/openfpga/src/annotation/openfpga_annotate_routing.cpp +++ b/openfpga/src/annotation/openfpga_annotate_routing.cpp @@ -3,8 +3,9 @@ * from VPR to OpenFPGA *******************************************************************/ #include "openfpga_annotate_routing.h" -#include "old_traceback.h" + #include "annotate_routing.h" +#include "old_traceback.h" #include "vtr_assert.h" #include "vtr_log.h" @@ -133,14 +134,17 @@ void annotate_rr_node_previous_nodes( /* Cache Previous nodes */ RRNodeId prev_node = RRNodeId::INVALID(); - t_trace* tptr = TracebackCompat::traceback_from_route_tree(routing_ctx.route_trees[net_id].value()); + t_trace* tptr = TracebackCompat::traceback_from_route_tree( + routing_ctx.route_trees[net_id].value()); while (tptr != nullptr) { RRNodeId rr_node = RRNodeId(tptr->index); /* Find the right previous node */ prev_node = find_previous_node_from_routing_traces( - device_ctx.rr_graph, TracebackCompat::traceback_from_route_tree(routing_ctx.route_trees[net_id].value()), prev_node, - rr_node); + device_ctx.rr_graph, + TracebackCompat::traceback_from_route_tree( + routing_ctx.route_trees[net_id].value()), + prev_node, rr_node); /* Only update mapped nodes */ if (prev_node) { diff --git a/openfpga/src/annotation/vpr_placement_annotation.cpp b/openfpga/src/annotation/vpr_placement_annotation.cpp index 0f24b05fd..4967732b0 100644 --- a/openfpga/src/annotation/vpr_placement_annotation.cpp +++ b/openfpga/src/annotation/vpr_placement_annotation.cpp @@ -32,8 +32,9 @@ void VprPlacementAnnotation::init_mapped_blocks(const DeviceGrid& grids) { for (size_t x = 0; x < grids.width(); ++x) { for (size_t y = 0; y < grids.height(); ++y) { /* Deposit invalid ids and we will fill later */ - blocks_[x][y].resize(grids.get_physical_type(t_physical_tile_loc(x, y, 0))->capacity, - ClusterBlockId::INVALID()); + blocks_[x][y].resize( + grids.get_physical_type(t_physical_tile_loc(x, y, 0))->capacity, + ClusterBlockId::INVALID()); } } } diff --git a/openfpga/src/base/openfpga_pb_pin_fixup.cpp b/openfpga/src/base/openfpga_pb_pin_fixup.cpp index b57616f78..e17a22183 100644 --- a/openfpga/src/base/openfpga_pb_pin_fixup.cpp +++ b/openfpga/src/base/openfpga_pb_pin_fixup.cpp @@ -34,14 +34,13 @@ namespace openfpga { static void update_cluster_pin_with_post_routing_results( const DeviceContext& device_ctx, const ClusteringContext& clustering_ctx, const VprRoutingAnnotation& vpr_routing_annotation, - VprClusteringAnnotation& vpr_clustering_annotation, - const size_t& layer, + VprClusteringAnnotation& vpr_clustering_annotation, const size_t& layer, const vtr::Point& grid_coord, const ClusterBlockId& blk_id, const e_side& border_side, const size_t& z, const bool& verbose) { /* Handle each pin */ auto logical_block = clustering_ctx.clb_nlist.block_type(blk_id); - auto physical_tile = - device_ctx.grid.get_physical_type(t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer)); + auto physical_tile = device_ctx.grid.get_physical_type( + t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer)); for (int j = 0; j < logical_block->pb_type->num_pins; j++) { /* Get the ptc num for the pin in rr_graph, we need t consider the z offset @@ -87,7 +86,8 @@ static void update_cluster_pin_with_post_routing_results( /* Find the net mapped to this pin in routing results */ const RRNodeId& rr_node = device_ctx.rr_graph.node_lookup().find_node( - layer, grid_coord.x(), grid_coord.y(), rr_node_type, physical_pin, pin_side); + layer, grid_coord.x(), grid_coord.y(), rr_node_type, physical_pin, + pin_side); if (false == device_ctx.rr_graph.valid_node(rr_node)) { continue; } @@ -197,14 +197,17 @@ void update_pb_pin_with_post_routing_results( /* Update the core logic (center blocks of the FPGA) */ for (size_t x = 1; x < device_ctx.grid.width() - 1; ++x) { for (size_t y = 1; y < device_ctx.grid.height() - 1; ++y) { - t_physical_tile_type_ptr phy_tile = device_ctx.grid.get_physical_type(t_physical_tile_loc(x, y, layer)); + t_physical_tile_type_ptr phy_tile = + device_ctx.grid.get_physical_type(t_physical_tile_loc(x, y, layer)); /* Bypass the EMPTY tiles */ if (true == is_empty_type(phy_tile)) { continue; } /* Get the mapped blocks to this grid */ - for (int isubtile = 0; isubtile < phy_tile->capacity; ++isubtile) { - ClusterBlockId cluster_blk_id = placement_ctx.grid_blocks.block_at_location({(int)x, (int)y, (int)isubtile, (int)layer}); + for (int isubtile = 0; isubtile < phy_tile->capacity; ++isubtile) { + ClusterBlockId cluster_blk_id = + placement_ctx.grid_blocks.block_at_location( + {(int)x, (int)y, (int)isubtile, (int)layer}); /* Skip invalid ids */ if (ClusterBlockId::INVALID() == cluster_blk_id) { continue; @@ -214,8 +217,9 @@ void update_pb_pin_with_post_routing_results( vtr::Point grid_coord(x, y); update_cluster_pin_with_post_routing_results( device_ctx, clustering_ctx, vpr_routing_annotation, - vpr_clustering_annotation, layer, grid_coord, cluster_blk_id, NUM_SIDES, - placement_ctx.block_locs[cluster_blk_id].loc.sub_tile, verbose); + vpr_clustering_annotation, layer, grid_coord, cluster_blk_id, + NUM_SIDES, placement_ctx.block_locs[cluster_blk_id].loc.sub_tile, + verbose); } } } @@ -227,14 +231,17 @@ void update_pb_pin_with_post_routing_results( for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coord : io_coordinates[io_side]) { t_physical_tile_type_ptr phy_tile_type = - device_ctx.grid.get_physical_type(t_physical_tile_loc(io_coord.x(), io_coord.y(), layer)); + device_ctx.grid.get_physical_type( + t_physical_tile_loc(io_coord.x(), io_coord.y(), layer)); /* Bypass EMPTY grid */ if (true == is_empty_type(phy_tile_type)) { continue; } /* Get the mapped blocks to this grid */ - for (int isubtile = 0; isubtile < phy_tile_type->capacity; ++isubtile) { - ClusterBlockId cluster_blk_id = placement_ctx.grid_blocks.block_at_location({(int)io_coord.x(), (int)io_coord.y(), (int)isubtile, (int)layer}); + for (int isubtile = 0; isubtile < phy_tile_type->capacity; ++isubtile) { + ClusterBlockId cluster_blk_id = + placement_ctx.grid_blocks.block_at_location( + {(int)io_coord.x(), (int)io_coord.y(), (int)isubtile, (int)layer}); /* Skip invalid ids */ if (ClusterBlockId::INVALID() == cluster_blk_id) { continue; diff --git a/openfpga/src/fabric/build_fabric_io_location_map.cpp b/openfpga/src/fabric/build_fabric_io_location_map.cpp index 75b8d37ed..88097f976 100644 --- a/openfpga/src/fabric/build_fabric_io_location_map.cpp +++ b/openfpga/src/fabric/build_fabric_io_location_map.cpp @@ -32,7 +32,8 @@ namespace openfpga { *(x, y, z) coordinate to the actual indices *******************************************************************/ static IoLocationMap build_fabric_fine_grained_io_location_map( - const ModuleManager& module_manager, const DeviceGrid& grids, const size_t& layer) { + const ModuleManager& module_manager, const DeviceGrid& grids, + const size_t& layer) { vtr::ScopedStartFinishTimer timer( "Create I/O location mapping for top module"); @@ -154,7 +155,8 @@ static IoLocationMap build_fabric_fine_grained_io_location_map( *(x, y, z) coordinate to the actual indices *******************************************************************/ static IoLocationMap build_fabric_tiled_io_location_map( - const ModuleManager& module_manager, const DeviceGrid& grids, const size_t& layer) { + const ModuleManager& module_manager, const DeviceGrid& grids, + const size_t& layer) { vtr::ScopedStartFinishTimer timer( "Create I/O location mapping for top module"); diff --git a/openfpga/src/fabric/build_fabric_tile.cpp b/openfpga/src/fabric/build_fabric_tile.cpp index b1cc7c80e..465cf7cc9 100644 --- a/openfpga/src/fabric/build_fabric_tile.cpp +++ b/openfpga/src/fabric/build_fabric_tile.cpp @@ -38,7 +38,8 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile, for (size_t ix = 0; ix < grids.width(); ++ix) { for (size_t iy = 0; iy < grids.height(); ++iy) { t_physical_tile_loc tile_loc(ix, iy, layer); - t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(tile_loc); + t_physical_tile_type_ptr phy_tile_type = + grids.get_physical_type(tile_loc); bool skip_add_pb = false; vtr::Point curr_tile_coord(ix, iy); vtr::Point curr_gsb_coord(ix, iy - 1); diff --git a/openfpga/src/fabric/build_routing_module_utils.cpp b/openfpga/src/fabric/build_routing_module_utils.cpp index cf7585b9e..e1f3db9ad 100644 --- a/openfpga/src/fabric/build_routing_module_utils.cpp +++ b/openfpga/src/fabric/build_routing_module_utils.cpp @@ -76,8 +76,10 @@ std::string generate_sb_module_grid_port_name( /* Collect the attributes of the rr_node required to generate the port name */ int pin_id = rr_graph.node_pin_num(rr_node); e_side pin_side = get_rr_graph_single_node_side(rr_graph, rr_node); - t_physical_tile_type_ptr physical_tile = vpr_device_grid.get_physical_type( - t_physical_tile_loc(rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node), rr_graph.node_layer(rr_node))); + t_physical_tile_type_ptr physical_tile = + vpr_device_grid.get_physical_type(t_physical_tile_loc( + rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node), + rr_graph.node_layer(rr_node))); int pin_width_offset = physical_tile->pin_width_offset[pin_id]; int pin_height_offset = physical_tile->pin_height_offset[pin_id]; BasicPort pin_info = @@ -110,8 +112,10 @@ std::string generate_cb_module_grid_port_name( /* Collect the attributes of the rr_node required to generate the port name */ int pin_id = rr_graph.node_pin_num(rr_node); e_side pin_side = get_rr_graph_single_node_side(rr_graph, rr_node); - t_physical_tile_type_ptr physical_tile = vpr_device_grid.get_physical_type(t_physical_tile_loc( - rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node), rr_graph.node_layer(rr_node))); + t_physical_tile_type_ptr physical_tile = + vpr_device_grid.get_physical_type(t_physical_tile_loc( + rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node), + rr_graph.node_layer(rr_node))); int pin_width_offset = physical_tile->pin_width_offset[pin_id]; int pin_height_offset = physical_tile->pin_height_offset[pin_id]; BasicPort pin_info = diff --git a/openfpga/src/fabric/build_tile_modules.cpp b/openfpga/src/fabric/build_tile_modules.cpp index 1589b7162..621621fc1 100644 --- a/openfpga/src/fabric/build_tile_modules.cpp +++ b/openfpga/src/fabric/build_tile_modules.cpp @@ -125,8 +125,8 @@ static int build_tile_module_port_and_nets_between_sb_and_pb( size_t src_grid_pin_index = rr_graph.node_pin_num( rr_gsb.get_opin_node(side_manager.get_side(), inode)); - t_physical_tile_type_ptr grid_type_descriptor = - grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); + t_physical_tile_type_ptr grid_type_descriptor = grids.get_physical_type( + t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); size_t src_grid_pin_width = grid_type_descriptor->pin_width_offset[src_grid_pin_index]; size_t src_grid_pin_height = @@ -373,8 +373,8 @@ static int build_tile_module_port_and_nets_between_cb_and_pb( VTR_ASSERT(true == module_manager.valid_module_id(sink_grid_module)); size_t sink_grid_pin_index = rr_graph.node_pin_num(instance_ipin_node); - t_physical_tile_type_ptr grid_type_descriptor = - grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); + t_physical_tile_type_ptr grid_type_descriptor = grids.get_physical_type( + t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); size_t sink_grid_pin_width = grid_type_descriptor->pin_width_offset[sink_grid_pin_index]; size_t sink_grid_pin_height = @@ -994,14 +994,13 @@ static int build_tile_module_ports_from_cb( static int build_tile_port_and_nets_from_pb( ModuleManager& module_manager, const ModuleId& tile_module, const DeviceGrid& grids, const size_t& layer, - const VprDeviceAnnotation& vpr_device_annotation, - const RRGraphView& rr_graph, const vtr::Point& pb_coord, - const std::vector& pb_instances, const FabricTile& fabric_tile, - const FabricTileId& curr_fabric_tile_id, const size_t& ipb, - const bool& frame_view, const bool& verbose) { + const VprDeviceAnnotation& vpr_device_annotation, const RRGraphView& rr_graph, + const vtr::Point& pb_coord, const std::vector& pb_instances, + const FabricTile& fabric_tile, const FabricTileId& curr_fabric_tile_id, + const size_t& ipb, const bool& frame_view, const bool& verbose) { size_t pb_instance = pb_instances[ipb]; - t_physical_tile_type_ptr phy_tile = - grids.get_physical_type(t_physical_tile_loc(pb_coord.x(), pb_coord.y(), layer)); + t_physical_tile_type_ptr phy_tile = grids.get_physical_type( + t_physical_tile_loc(pb_coord.x(), pb_coord.y(), layer)); /* Empty type does not require a module */ if (is_empty_type(phy_tile)) { return CMD_EXEC_SUCCESS; @@ -1123,8 +1122,8 @@ static int build_tile_port_and_nets_from_pb( size_t num_fanout_in_tile = module_manager.module_net_sinks(tile_module, curr_net).size(); RRNodeId rr_node = rr_graph.node_lookup().find_node( - layer, pb_coord.x() + iwidth, pb_coord.y() + iheight, OPIN, ipin, - side); + layer, pb_coord.x() + iwidth, pb_coord.y() + iheight, OPIN, + ipin, side); size_t num_fanout_required = rr_graph.node_out_edges(rr_node).size(); if (num_fanout_in_tile == num_fanout_required) { @@ -1200,9 +1199,9 @@ static int build_tile_module_ports_and_nets( fabric_tile.sb_coordinates(fabric_tile_id)[isb]; const RRGSB& rr_gsb = device_rr_gsb.get_gsb(sb_coord); status_code = build_tile_module_port_and_nets_between_sb_and_pb( - module_manager, tile_module, grids, layer, vpr_device_annotation, device_rr_gsb, - rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, pb_instances, - sb_instances, isb, true, frame_view, verbose); + module_manager, tile_module, grids, layer, vpr_device_annotation, + device_rr_gsb, rr_graph_view, rr_gsb, fabric_tile, fabric_tile_id, + pb_instances, sb_instances, isb, true, frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -1248,9 +1247,9 @@ static int build_tile_module_ports_and_nets( vtr::Point pb_coord = fabric_tile.pb_coordinates(fabric_tile_id)[ipb]; status_code = build_tile_port_and_nets_from_pb( - module_manager, tile_module, grids, layer, vpr_device_annotation, rr_graph_view, - pb_coord, pb_instances, fabric_tile, fabric_tile_id, ipb, frame_view, - verbose); + module_manager, tile_module, grids, layer, vpr_device_annotation, + rr_graph_view, pb_coord, pb_instances, fabric_tile, fabric_tile_id, ipb, + frame_view, verbose); if (status_code != CMD_EXEC_SUCCESS) { return CMD_EXEC_FATAL_ERROR; } @@ -1308,8 +1307,8 @@ static int build_tile_module( pb_instances; /* Keep tracking the instance id of each pb */ for (vtr::Point grid_coord : fabric_tile.pb_coordinates(fabric_tile_id)) { - t_physical_tile_type_ptr phy_tile = - grids.get_physical_type(t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer)); + t_physical_tile_type_ptr phy_tile = grids.get_physical_type( + t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer)); VTR_LOGV(verbose, "Try to find pb at [%lu][%lu]\n", grid_coord.x(), grid_coord.y()); /* Empty type does not require a module */ @@ -1438,9 +1437,9 @@ static int build_tile_module( /* Add module nets and ports */ status_code = build_tile_module_ports_and_nets( - module_manager, tile_module, grids, layer, vpr_device_annotation, device_rr_gsb, - rr_graph_view, fabric_tile, fabric_tile_id, pb_instances, cb_instances, - sb_instances, frame_view, verbose); + module_manager, tile_module, grids, layer, vpr_device_annotation, + device_rr_gsb, rr_graph_view, fabric_tile, fabric_tile_id, pb_instances, + cb_instances, sb_instances, frame_view, verbose); /* Add global ports to the pb_module: * This is a much easier job after adding sub modules (instances), diff --git a/openfpga/src/fabric/build_top_module.cpp b/openfpga/src/fabric/build_top_module.cpp index a81eb86f2..5d668f2f2 100644 --- a/openfpga/src/fabric/build_top_module.cpp +++ b/openfpga/src/fabric/build_top_module.cpp @@ -76,17 +76,18 @@ int build_top_module( if (fabric_tile.empty()) { status = build_top_module_fine_grained_child_instances( module_manager, top_module, blwl_sr_banks, circuit_lib, clk_ntwk, - rr_clock_lookup, vpr_device_annotation, grids, layer, 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, - group_config_block); + rr_clock_lookup, vpr_device_annotation, grids, layer, 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, 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, layer, tile_annotation, rr_graph, - device_rr_gsb, tile_direct, arch_direct, fabric_tile, config_protocol, - sram_model, fabric_key, group_config_block, frame_view, verbose); + rr_clock_lookup, vpr_device_annotation, grids, layer, tile_annotation, + rr_graph, device_rr_gsb, tile_direct, arch_direct, fabric_tile, + config_protocol, sram_model, fabric_key, group_config_block, frame_view, + verbose); } if (status != CMD_EXEC_SUCCESS) { 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 4f44f6a41..97152ebb4 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 @@ -109,7 +109,8 @@ static vtr::Matrix add_top_module_grid_instances( for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { - t_physical_tile_loc phy_tile_loc(io_coordinate.x(), io_coordinate.y(), layer); + t_physical_tile_loc phy_tile_loc(io_coordinate.x(), io_coordinate.y(), + layer); t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY grid */ @@ -123,10 +124,8 @@ static vtr::Matrix add_top_module_grid_instances( * We just copy it here */ vtr::Point root_grid_coord( - io_coordinate.x() - - grids.get_width_offset(phy_tile_loc), - io_coordinate.y() - - grids.get_height_offset(phy_tile_loc)); + io_coordinate.x() - grids.get_width_offset(phy_tile_loc), + io_coordinate.y() - grids.get_height_offset(phy_tile_loc)); VTR_ASSERT(size_t(-1) != grid_instance_ids[root_grid_coord.x()][root_grid_coord.y()]); grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = @@ -150,7 +149,8 @@ static vtr::Matrix add_top_module_grid_instances( for (size_t ix = 1; ix < grids.width() - 1; ++ix) { for (size_t iy = 1; iy < grids.height() - 1; ++iy) { t_physical_tile_loc phy_tile_loc(ix, iy, layer); - t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(phy_tile_loc); + t_physical_tile_type_ptr phy_tile_type = + grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY grid */ if (true == is_empty_type(phy_tile_type)) { continue; @@ -400,8 +400,7 @@ static void add_top_module_io_children( /* Now walk through the coordinates */ for (vtr::Point coord : coords) { t_physical_tile_loc phy_tile_loc(coord.x(), coord.y(), layer); - t_physical_tile_type_ptr grid_type = - grids.get_physical_type(phy_tile_loc); + t_physical_tile_type_ptr grid_type = grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY grid */ if (true == is_empty_type(grid_type)) { continue; @@ -436,13 +435,13 @@ int build_top_module_fine_grained_child_instances( const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, - const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, - 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 bool& group_config_block) { + const size_t& layer, const TileAnnotation& tile_annotation, + const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, + const TileDirect& tile_direct, 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 bool& group_config_block) { int status = CMD_EXEC_SUCCESS; std::map> cb_instance_ids; @@ -462,8 +461,8 @@ int build_top_module_fine_grained_child_instances( compact_routing_hierarchy); /* Update I/O children list */ - add_top_module_io_children(module_manager, top_module, grids, - layer, grid_instance_ids); + add_top_module_io_children(module_manager, top_module, grids, layer, + grid_instance_ids); /* Add nets when we need a complete fabric modeling, * which is required by downstream functions @@ -486,9 +485,9 @@ int build_top_module_fine_grained_child_instances( /* Add global ports from grid ports that are defined as global in tile * annotation */ status = add_top_module_global_ports_from_grid_modules( - module_manager, top_module, tile_annotation, vpr_device_annotation, grids, layer, - rr_graph, device_rr_gsb, cb_instance_ids, grid_instance_ids, clk_ntwk, - rr_clock_lookup); + module_manager, top_module, tile_annotation, vpr_device_annotation, grids, + layer, rr_graph, device_rr_gsb, cb_instance_ids, grid_instance_ids, + clk_ntwk, rr_clock_lookup); if (CMD_EXEC_FATAL_ERROR == status) { return status; } @@ -506,8 +505,8 @@ int build_top_module_fine_grained_child_instances( if (true == fabric_key.empty()) { organize_top_module_memory_modules( module_manager, top_module, circuit_lib, config_protocol, sram_model, - grids, layer, grid_instance_ids, device_rr_gsb, sb_instance_ids, cb_instance_ids, - compact_routing_hierarchy); + grids, layer, grid_instance_ids, device_rr_gsb, sb_instance_ids, + cb_instance_ids, compact_routing_hierarchy); } else { VTR_ASSERT_SAFE(false == fabric_key.empty()); /* Throw a fatal error when the fabric key has a mismatch in region 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 648583830..f54e07c24 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 @@ -38,13 +38,13 @@ int build_top_module_fine_grained_child_instances( const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, - const DeviceRRGSB& device_rr_gsb, const TileDirect& tile_direct, - 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 bool& group_config_block); + const size_t& layer, const TileAnnotation& tile_annotation, + const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, + const TileDirect& tile_direct, 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 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 06c73bd6c..9b190c646 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.cpp +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.cpp @@ -1272,9 +1272,9 @@ static int build_top_module_global_net_for_given_tile_module( const TileGlobalPortId& tile_global_port, const BasicPort& tile_port_to_connect, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Point& grid_coordinate, const e_side& border_side, - const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile) { + const size_t& layer, const vtr::Point& grid_coordinate, + const e_side& border_side, const vtr::Matrix& tile_instance_ids, + const FabricTile& fabric_tile) { /* Get the tile module and instance */ FabricTileId curr_fabric_tile_id = fabric_tile.find_tile_by_pb_coordinate(grid_coordinate); @@ -1298,8 +1298,8 @@ static int build_top_module_global_net_for_given_tile_module( fabric_tile.pb_coordinates( unique_fabric_tile_id)[pb_idx_in_curr_fabric_tile]; - t_physical_tile_type_ptr physical_tile = - grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); + t_physical_tile_type_ptr physical_tile = grids.get_physical_type( + t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); /* Find the module name for this type of grid */ std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); std::string grid_instance_name = @@ -1420,8 +1420,8 @@ static int build_top_module_global_net_from_tile_modules( const ModulePortId& top_module_port, const TileAnnotation& tile_annotation, const TileGlobalPortId& tile_global_port, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile) { + const size_t& layer, const vtr::Matrix& tile_instance_ids, + const FabricTile& fabric_tile) { int status = CMD_EXEC_SUCCESS; std::map>> io_coordinates = @@ -1510,7 +1510,8 @@ static int build_top_module_global_net_from_tile_modules( /* Walk through all the grids on the perimeter, which are I/O grids */ for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { - t_physical_tile_loc phy_tile_loc(io_coordinate.x(), io_coordinate.y(), layer); + t_physical_tile_loc phy_tile_loc(io_coordinate.x(), io_coordinate.y(), + layer); t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(phy_tile_loc); /* Bypass EMPTY grid */ @@ -1519,10 +1520,8 @@ static int build_top_module_global_net_from_tile_modules( } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < - grids.get_width_offset(phy_tile_loc)) || - (0 < - grids.get_height_offset(phy_tile_loc))) { + if ((0 < grids.get_width_offset(phy_tile_loc)) || + (0 < grids.get_height_offset(phy_tile_loc))) { continue; } @@ -1567,8 +1566,8 @@ static int add_top_module_global_ports_from_tile_modules( ModuleManager& module_manager, const ModuleId& top_module, const TileAnnotation& tile_annotation, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, + const size_t& layer, const RRGraphView& rr_graph, + const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile, const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup) { int status = CMD_EXEC_SUCCESS; @@ -1626,8 +1625,8 @@ static int add_top_module_global_ports_from_tile_modules( } else { status = build_top_module_global_net_from_tile_modules( module_manager, top_module, top_module_port, tile_annotation, - tile_global_port, vpr_device_annotation, grids, layer, tile_instance_ids, - fabric_tile); + tile_global_port, vpr_device_annotation, grids, layer, + tile_instance_ids, fabric_tile); } if (status == CMD_EXEC_FATAL_ERROR) { return status; @@ -1652,10 +1651,9 @@ static void add_module_nets_connect_tile_direct_connection( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile, - const TileDirect& tile_direct, const TileDirectId& tile_direct_id, - const ArchDirect& arch_direct) { + const size_t& layer, const vtr::Matrix& tile_instance_ids, + const FabricTile& fabric_tile, const TileDirect& tile_direct, + const TileDirectId& tile_direct_id, const ArchDirect& arch_direct) { vtr::Point device_size(grids.width(), grids.height()); std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); @@ -1846,16 +1844,17 @@ static void add_top_module_nets_connect_tile_direct_connections( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& tile_instance_ids, const FabricTile& fabric_tile, - const TileDirect& tile_direct, const ArchDirect& arch_direct) { + const size_t& layer, const vtr::Matrix& tile_instance_ids, + const FabricTile& fabric_tile, const TileDirect& tile_direct, + const ArchDirect& arch_direct) { vtr::ScopedStartFinishTimer timer( "Add module nets for inter-tile connections"); for (const TileDirectId& tile_direct_id : tile_direct.directs()) { add_module_nets_connect_tile_direct_connection( module_manager, top_module, circuit_lib, vpr_device_annotation, grids, - layer, tile_instance_ids, fabric_tile, tile_direct, tile_direct_id, arch_direct); + layer, tile_instance_ids, fabric_tile, tile_direct, tile_direct_id, + arch_direct); } } @@ -1869,13 +1868,12 @@ int build_top_module_tile_child_instances( const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, - 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& group_config_block, - const bool& frame_view, const bool& verbose) { + const size_t& layer, const TileAnnotation& tile_annotation, + const RRGraphView& rr_graph, 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& 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, 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 e35ad3623..57d3604e4 100644 --- a/openfpga/src/fabric/build_top_module_child_tile_instance.h +++ b/openfpga/src/fabric/build_top_module_child_tile_instance.h @@ -38,13 +38,12 @@ int build_top_module_tile_child_instances( const CircuitLibrary& circuit_lib, const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const TileAnnotation& tile_annotation, const RRGraphView& rr_graph, - 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& group_config_block, - const bool& frame_view, const bool& verbose); + const size_t& layer, const TileAnnotation& tile_annotation, + const RRGraphView& rr_graph, 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& group_config_block, const bool& frame_view, const bool& verbose); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_top_module_connection.cpp b/openfpga/src/fabric/build_top_module_connection.cpp index a0afbf79c..c2d029d92 100644 --- a/openfpga/src/fabric/build_top_module_connection.cpp +++ b/openfpga/src/fabric/build_top_module_connection.cpp @@ -64,10 +64,9 @@ namespace openfpga { static void add_top_module_nets_connect_grids_and_sb( ModuleManager& module_manager, const ModuleId& top_module, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& grid_instance_ids, const RRGraphView& rr_graph, - const DeviceRRGSB& device_rr_gsb, const RRGSB& rr_gsb, - const vtr::Matrix& sb_instance_ids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, + const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, + const RRGSB& rr_gsb, const vtr::Matrix& sb_instance_ids, const bool& compact_routing_hierarchy) { /* Skip those Switch blocks that do not exist */ if (false == rr_gsb.is_sb_exist()) { @@ -127,8 +126,8 @@ static void add_top_module_nets_connect_grids_and_sb( size_t src_grid_pin_index = rr_graph.node_pin_num( rr_gsb.get_opin_node(side_manager.get_side(), inode)); - t_physical_tile_type_ptr grid_type_descriptor = - grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); + t_physical_tile_type_ptr grid_type_descriptor = grids.get_physical_type( + t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); size_t src_grid_pin_width = grid_type_descriptor->pin_width_offset[src_grid_pin_index]; size_t src_grid_pin_height = @@ -228,10 +227,9 @@ static void add_top_module_nets_connect_grids_and_sb( static void add_top_module_nets_connect_grids_and_sb_with_duplicated_pins( ModuleManager& module_manager, const ModuleId& top_module, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& grid_instance_ids, const RRGraphView& rr_graph, - const DeviceRRGSB& device_rr_gsb, const RRGSB& rr_gsb, - const vtr::Matrix& sb_instance_ids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, + const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, + const RRGSB& rr_gsb, const vtr::Matrix& sb_instance_ids, const bool& compact_routing_hierarchy) { /* Skip those Switch blocks that do not exist */ if (false == rr_gsb.is_sb_exist()) { @@ -302,7 +300,8 @@ static void add_top_module_nets_connect_grids_and_sb_with_duplicated_pins( size_t src_grid_pin_index = rr_graph.node_pin_num( rr_gsb.get_opin_node(side_manager.get_side(), inode)); - t_physical_tile_loc phy_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer); + t_physical_tile_loc phy_tile_loc(grid_coordinate.x(), grid_coordinate.y(), + layer); t_physical_tile_type_ptr grid_type_descriptor = grids.get_physical_type(phy_tile_loc); size_t src_grid_pin_width = @@ -440,10 +439,10 @@ static void add_top_module_nets_connect_grids_and_sb_with_duplicated_pins( static void add_top_module_nets_connect_grids_and_cb( ModuleManager& module_manager, const ModuleId& top_module, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& grid_instance_ids, const RRGraphView& rr_graph, - const DeviceRRGSB& device_rr_gsb, const RRGSB& rr_gsb, - const t_rr_type& cb_type, const vtr::Matrix& cb_instance_ids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, + const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, + const RRGSB& rr_gsb, const t_rr_type& cb_type, + const vtr::Matrix& cb_instance_ids, const bool& compact_routing_hierarchy) { /* We could have two different coordinators, one is the instance, the other is * the module */ @@ -523,8 +522,8 @@ static void add_top_module_nets_connect_grids_and_cb( grid_instance_ids[grid_coordinate.x()][grid_coordinate.y()]; size_t sink_grid_pin_index = rr_graph.node_pin_num(instance_ipin_node); - t_physical_tile_type_ptr grid_type_descriptor = - grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); + t_physical_tile_type_ptr grid_type_descriptor = grids.get_physical_type( + t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); size_t sink_grid_pin_width = grid_type_descriptor->pin_width_offset[sink_grid_pin_index]; size_t sink_grid_pin_height = @@ -806,9 +805,9 @@ static void add_top_module_nets_connect_sb_and_cb( void add_top_module_nets_connect_grids_and_gsbs( ModuleManager& module_manager, const ModuleId& top_module, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& grid_instance_ids, const RRGraphView& rr_graph, - const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& sb_instance_ids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, + const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, + const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, const bool& compact_routing_hierarchy, const bool& duplicate_grid_pin) { vtr::ScopedStartFinishTimer timer("Add module nets between grids and GSBs"); @@ -823,25 +822,25 @@ void add_top_module_nets_connect_grids_and_gsbs( /* Connect the grid pins of the GSB to adjacent grids */ if (false == duplicate_grid_pin) { add_top_module_nets_connect_grids_and_sb( - module_manager, top_module, vpr_device_annotation, grids, - layer, grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, sb_instance_ids, + module_manager, top_module, vpr_device_annotation, grids, layer, + grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, sb_instance_ids, compact_routing_hierarchy); } else { VTR_ASSERT_SAFE(true == duplicate_grid_pin); add_top_module_nets_connect_grids_and_sb_with_duplicated_pins( - module_manager, top_module, vpr_device_annotation, grids, - layer, grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, sb_instance_ids, + module_manager, top_module, vpr_device_annotation, grids, layer, + grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, sb_instance_ids, compact_routing_hierarchy); } add_top_module_nets_connect_grids_and_cb( - module_manager, top_module, vpr_device_annotation, grids, - layer, grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, CHANX, + module_manager, top_module, vpr_device_annotation, grids, layer, + grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, CHANX, cb_instance_ids.at(CHANX), compact_routing_hierarchy); add_top_module_nets_connect_grids_and_cb( - module_manager, top_module, vpr_device_annotation, grids, - layer, grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, CHANY, + module_manager, top_module, vpr_device_annotation, grids, layer, + grid_instance_ids, rr_graph, device_rr_gsb, rr_gsb, CHANY, cb_instance_ids.at(CHANY), compact_routing_hierarchy); add_top_module_nets_connect_sb_and_cb( @@ -861,11 +860,10 @@ static int build_top_module_global_net_for_given_grid_module( const TileGlobalPortId& tile_global_port, const BasicPort& tile_port_to_connect, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Point& grid_coordinate, const e_side& border_side, - const vtr::Matrix& grid_instance_ids) { - t_physical_tile_type_ptr physical_tile = - grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); + const size_t& layer, const vtr::Point& grid_coordinate, + const e_side& border_side, const vtr::Matrix& grid_instance_ids) { + t_physical_tile_type_ptr physical_tile = grids.get_physical_type( + t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); /* Find the module name for this type of grid */ std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); std::string grid_module_name = generate_grid_block_module_name( @@ -988,8 +986,7 @@ static int build_top_module_global_net_from_grid_modules( const ModulePortId& top_module_port, const TileAnnotation& tile_annotation, const TileGlobalPortId& tile_global_port, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& grid_instance_ids) { + const size_t& layer, const vtr::Matrix& grid_instance_ids) { int status = CMD_EXEC_SUCCESS; std::map>> io_coordinates = @@ -1066,8 +1063,8 @@ static int build_top_module_global_net_from_grid_modules( /* Create nets and finish connection build-up */ status = build_top_module_global_net_for_given_grid_module( module_manager, top_module, top_module_port, tile_annotation, - tile_global_port, tile_port, vpr_device_annotation, grids, - layer, vtr::Point(ix, iy), NUM_SIDES, grid_instance_ids); + tile_global_port, tile_port, vpr_device_annotation, grids, layer, + vtr::Point(ix, iy), NUM_SIDES, grid_instance_ids); if (CMD_EXEC_FATAL_ERROR == status) { return status; } @@ -1077,7 +1074,8 @@ static int build_top_module_global_net_from_grid_modules( /* Walk through all the grids on the perimeter, which are I/O grids */ for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { - t_physical_tile_loc tile_loc(io_coordinate.x(), io_coordinate.y(), layer); + t_physical_tile_loc tile_loc(io_coordinate.x(), io_coordinate.y(), + layer); t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(tile_loc); /* Bypass EMPTY grid */ @@ -1086,10 +1084,8 @@ static int build_top_module_global_net_from_grid_modules( } /* Skip width or height > 1 tiles (mostly heterogeneous blocks) */ - if ((0 < - grids.get_width_offset(tile_loc)) || - (0 < - grids.get_height_offset(tile_loc))) { + if ((0 < grids.get_width_offset(tile_loc)) || + (0 < grids.get_height_offset(tile_loc))) { continue; } @@ -1114,8 +1110,8 @@ static int build_top_module_global_net_from_grid_modules( /* Create nets and finish connection build-up */ status = build_top_module_global_net_for_given_grid_module( module_manager, top_module, top_module_port, tile_annotation, - tile_global_port, tile_port, vpr_device_annotation, grids, - layer, io_coordinate, io_side, grid_instance_ids); + tile_global_port, tile_port, vpr_device_annotation, grids, layer, + io_coordinate, io_side, grid_instance_ids); if (CMD_EXEC_FATAL_ERROR == status) { return status; } @@ -1212,8 +1208,8 @@ int add_top_module_global_ports_from_grid_modules( ModuleManager& module_manager, const ModuleId& top_module, const TileAnnotation& tile_annotation, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, + const size_t& layer, const RRGraphView& rr_graph, + const DeviceRRGSB& device_rr_gsb, const std::map>& cb_instance_ids, const vtr::Matrix& grid_instance_ids, const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup) { @@ -1272,7 +1268,8 @@ int add_top_module_global_ports_from_grid_modules( } else { status = build_top_module_global_net_from_grid_modules( module_manager, top_module, top_module_port, tile_annotation, - tile_global_port, vpr_device_annotation, grids, layer, grid_instance_ids); + tile_global_port, vpr_device_annotation, grids, layer, + grid_instance_ids); } if (status == CMD_EXEC_FATAL_ERROR) { return status; diff --git a/openfpga/src/fabric/build_top_module_connection.h b/openfpga/src/fabric/build_top_module_connection.h index 80659e2bd..d4c18e7a7 100644 --- a/openfpga/src/fabric/build_top_module_connection.h +++ b/openfpga/src/fabric/build_top_module_connection.h @@ -28,9 +28,9 @@ namespace openfpga { void add_top_module_nets_connect_grids_and_gsbs( ModuleManager& module_manager, const ModuleId& top_module, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& grid_instance_ids, const RRGraphView& rr_graph, - const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& sb_instance_ids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, + const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, + const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, const bool& compact_routing_hierarchy, const bool& duplicate_grid_pin); @@ -38,8 +38,8 @@ int add_top_module_global_ports_from_grid_modules( ModuleManager& module_manager, const ModuleId& top_module, const TileAnnotation& tile_annotation, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const RRGraphView& rr_graph, const DeviceRRGSB& device_rr_gsb, + const size_t& layer, const RRGraphView& rr_graph, + const DeviceRRGSB& device_rr_gsb, const std::map>& cb_instance_ids, const vtr::Matrix& grid_instance_ids, const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup); diff --git a/openfpga/src/fabric/build_top_module_directs.cpp b/openfpga/src/fabric/build_top_module_directs.cpp index 20c43e419..205c54702 100644 --- a/openfpga/src/fabric/build_top_module_directs.cpp +++ b/openfpga/src/fabric/build_top_module_directs.cpp @@ -39,9 +39,9 @@ static void add_module_nets_tile_direct_connection( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& grid_instance_ids, const TileDirect& tile_direct, - const TileDirectId& tile_direct_id, const ArchDirect& arch_direct) { + const size_t& layer, const vtr::Matrix& grid_instance_ids, + const TileDirect& tile_direct, const TileDirectId& tile_direct_id, + const ArchDirect& arch_direct) { vtr::Point device_size(grids.width(), grids.height()); /* Find the module name of source clb */ @@ -65,7 +65,8 @@ static void add_module_nets_tile_direct_connection( /* Find the module name of sink clb */ vtr::Point des_clb_coord = tile_direct.to_tile_coordinate(tile_direct_id); - t_physical_tile_loc sink_grid_loc(des_clb_coord.x(), des_clb_coord.y(), layer); + t_physical_tile_loc sink_grid_loc(des_clb_coord.x(), des_clb_coord.y(), + layer); t_physical_tile_type_ptr sink_grid_type = grids.get_physical_type(sink_grid_loc); e_side sink_grid_border_side = @@ -212,9 +213,8 @@ void add_top_module_nets_tile_direct_connections( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& grid_instance_ids, const TileDirect& tile_direct, - const ArchDirect& arch_direct) { + const size_t& layer, const vtr::Matrix& grid_instance_ids, + const TileDirect& tile_direct, const ArchDirect& arch_direct) { vtr::ScopedStartFinishTimer timer( "Add module nets for inter-tile connections"); diff --git a/openfpga/src/fabric/build_top_module_directs.h b/openfpga/src/fabric/build_top_module_directs.h index c9c13d66b..7e85661dc 100644 --- a/openfpga/src/fabric/build_top_module_directs.h +++ b/openfpga/src/fabric/build_top_module_directs.h @@ -26,9 +26,8 @@ void add_top_module_nets_tile_direct_connections( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& grid_instance_ids, const TileDirect& tile_direct, - const ArchDirect& arch_direct); + const size_t& layer, const vtr::Matrix& grid_instance_ids, + const TileDirect& tile_direct, const ArchDirect& arch_direct); } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_top_module_memory.cpp b/openfpga/src/fabric/build_top_module_memory.cpp index 447ecd604..094a5ea05 100644 --- a/openfpga/src/fabric/build_top_module_memory.cpp +++ b/openfpga/src/fabric/build_top_module_memory.cpp @@ -136,10 +136,8 @@ static void organize_top_module_tile_memory_modules( const vtr::Matrix& grid_instance_ids, const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, - const bool& compact_routing_hierarchy, - const size_t& layer, - const vtr::Point& tile_coord, - const e_side& tile_border_side) { + const bool& compact_routing_hierarchy, const size_t& layer, + const vtr::Point& tile_coord, const e_side& tile_border_side) { vtr::Point gsb_coord_range = device_rr_gsb.get_gsb_range(); vtr::Point gsb_coord(tile_coord.x(), tile_coord.y() - 1); @@ -194,8 +192,7 @@ static void organize_top_module_tile_memory_modules( /* Find the module name for this type of grid */ t_physical_tile_loc phy_tile_loc(tile_coord.x(), tile_coord.y(), layer); - t_physical_tile_type_ptr grid_type = - grids.get_physical_type(phy_tile_loc); + t_physical_tile_type_ptr grid_type = grids.get_physical_type(phy_tile_loc); /* Skip EMPTY Grid */ if (true == is_empty_type(grid_type)) { @@ -440,8 +437,7 @@ void organize_top_module_memory_modules( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, const CircuitModelId& sram_model, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& grid_instance_ids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, const bool& compact_routing_hierarchy) { diff --git a/openfpga/src/fabric/build_top_module_memory.h b/openfpga/src/fabric/build_top_module_memory.h index 69df431c5..3f53e3a42 100644 --- a/openfpga/src/fabric/build_top_module_memory.h +++ b/openfpga/src/fabric/build_top_module_memory.h @@ -32,8 +32,7 @@ void organize_top_module_memory_modules( ModuleManager& module_manager, const ModuleId& top_module, const CircuitLibrary& circuit_lib, const ConfigProtocol& config_protocol, const CircuitModelId& sram_model, const DeviceGrid& grids, - const size_t& layer, - const vtr::Matrix& grid_instance_ids, + const size_t& layer, const vtr::Matrix& grid_instance_ids, const DeviceRRGSB& device_rr_gsb, const vtr::Matrix& sb_instance_ids, const std::map>& cb_instance_ids, const bool& compact_routing_hierarchy); diff --git a/openfpga/src/fabric/build_top_module_utils.cpp b/openfpga/src/fabric/build_top_module_utils.cpp index d3c7ac175..ebf52eb77 100644 --- a/openfpga/src/fabric/build_top_module_utils.cpp +++ b/openfpga/src/fabric/build_top_module_utils.cpp @@ -29,8 +29,8 @@ std::string generate_grid_block_module_name_in_top_module( /* Determine if the grid locates at the border */ vtr::Point device_size(grids.width(), grids.height()); e_side border_side = find_grid_border_side(device_size, grid_coord); - t_physical_tile_type_ptr phy_tile_type = - grids.get_physical_type(t_physical_tile_loc(grid_coord.x(), grid_coord.y(), 0)); + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type( + t_physical_tile_loc(grid_coord.x(), grid_coord.y(), 0)); return generate_grid_block_module_name( prefix, std::string(phy_tile_type->name), is_io_type(phy_tile_type), @@ -51,8 +51,8 @@ std::string generate_grid_module_port_name_in_top_module( const size_t& sink_grid_pin_index, const VprDeviceAnnotation& vpr_device_annotation, const RRGraphView& rr_graph, const RRNodeId& inode) { - t_physical_tile_type_ptr grid_type_descriptor = - grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), 0)); + t_physical_tile_type_ptr grid_type_descriptor = grids.get_physical_type( + t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), 0)); size_t sink_grid_pin_width = grid_type_descriptor->pin_width_offset[sink_grid_pin_index]; size_t sink_grid_pin_height = diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp index 7952b108a..df3f070aa 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.cpp @@ -824,12 +824,11 @@ static void build_physical_block_bitstream( const VprClusteringAnnotation& cluster_annotation, const VprPlacementAnnotation& place_annotation, const VprBitstreamAnnotation& bitstream_annotation, const DeviceGrid& grids, - const size_t& layer, - const vtr::Point& grid_coord, const e_side& border_side, - const bool& verbose) { + const size_t& layer, 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(t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer)); + t_physical_tile_type_ptr grid_type = grids.get_physical_type( + t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer)); std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); /* Early exit if this parent module has no configurable child modules */ @@ -951,8 +950,7 @@ void build_grid_bitstream( BitstreamManager& bitstream_manager, const ConfigBlockId& top_block, const ModuleManager& module_manager, const FabricTile& fabric_tile, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, - const DeviceGrid& grids, const size_t& layer, - const AtomContext& atom_ctx, + const DeviceGrid& grids, const size_t& layer, const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, const VprClusteringAnnotation& cluster_annotation, const VprPlacementAnnotation& place_annotation, @@ -996,8 +994,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, layer, grid_coord, NUM_SIDES, - verbose); + place_annotation, bitstream_annotation, grids, layer, grid_coord, + NUM_SIDES, verbose); } } VTR_LOGV(verbose, "Done\n"); @@ -1011,7 +1009,8 @@ void build_grid_bitstream( /* Add instances of I/O grids to top_module */ for (const e_side& io_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[io_side]) { - t_physical_tile_loc phy_tile_loc(io_coordinate.x(), io_coordinate.y(), layer); + t_physical_tile_loc phy_tile_loc(io_coordinate.x(), io_coordinate.y(), + layer); /* Bypass EMPTY grid */ if (true == is_empty_type(grids.get_physical_type(phy_tile_loc))) { continue; @@ -1043,8 +1042,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, layer, io_coordinate, io_side, - verbose); + place_annotation, bitstream_annotation, grids, layer, io_coordinate, + io_side, verbose); } } VTR_LOGV(verbose, "Done\n"); diff --git a/openfpga/src/fpga_bitstream/build_grid_bitstream.h b/openfpga/src/fpga_bitstream/build_grid_bitstream.h index 3a5e38131..a3f4f2603 100644 --- a/openfpga/src/fpga_bitstream/build_grid_bitstream.h +++ b/openfpga/src/fpga_bitstream/build_grid_bitstream.h @@ -29,8 +29,7 @@ void build_grid_bitstream( BitstreamManager& bitstream_manager, const ConfigBlockId& top_block, const ModuleManager& module_manager, const FabricTile& fabric_tile, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, - const DeviceGrid& grids, const size_t& layer, - const AtomContext& atom_ctx, + const DeviceGrid& grids, const size_t& layer, const AtomContext& atom_ctx, const VprDeviceAnnotation& device_annotation, const VprClusteringAnnotation& cluster_annotation, const VprPlacementAnnotation& place_annotation, diff --git a/openfpga/src/fpga_sdc/analysis_sdc_grid_writer.cpp b/openfpga/src/fpga_sdc/analysis_sdc_grid_writer.cpp index 59546d3a0..2de04a881 100644 --- a/openfpga/src/fpga_sdc/analysis_sdc_grid_writer.cpp +++ b/openfpga/src/fpga_sdc/analysis_sdc_grid_writer.cpp @@ -602,10 +602,9 @@ static void print_analysis_sdc_disable_unused_grid( const ModuleManager& module_manager, const e_side& border_side) { /* Validate file stream */ valid_file_stream(fp); - + t_physical_tile_loc phy_tile_loc(grid_coordinate.x(), grid_coordinate.y(), 0); - t_physical_tile_type_ptr grid_type = - grids.get_physical_type(phy_tile_loc); + t_physical_tile_type_ptr grid_type = grids.get_physical_type(phy_tile_loc); /* Bypass conditions for grids : * 1. EMPTY type, which is by nature unused * 2. Offset > 0, which has already been processed when offset = 0 diff --git a/openfpga/src/tile_direct/build_tile_direct.cpp b/openfpga/src/tile_direct/build_tile_direct.cpp index 3d6aaeb79..73b5fd77a 100644 --- a/openfpga/src/tile_direct/build_tile_direct.cpp +++ b/openfpga/src/tile_direct/build_tile_direct.cpp @@ -168,7 +168,9 @@ static vtr::Point find_grid_coordinate_given_type( continue; } if (wanted_grid_type_name == - std::string(grids.get_physical_type(t_physical_tile_loc(coord.x(), coord.y(), 0))->name)) { + std::string( + grids.get_physical_type(t_physical_tile_loc(coord.x(), coord.y(), 0)) + ->name)) { return coord; } } @@ -421,8 +423,10 @@ static void build_inner_column_row_tile_direct( for (const e_side& from_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ std::vector from_pins = find_physical_tile_pin_id( - from_phy_tile_type, device_ctx.grid.get_width_offset(from_phy_tile_loc), - device_ctx.grid.get_height_offset(from_phy_tile_loc), from_tile_port, from_side); + from_phy_tile_type, + device_ctx.grid.get_width_offset(from_phy_tile_loc), + device_ctx.grid.get_height_offset(from_phy_tile_loc), from_tile_port, + from_side); /* If nothing found, we can continue */ if (0 == from_pins.size()) { continue; @@ -438,7 +442,8 @@ static void build_inner_column_row_tile_direct( continue; } - t_physical_tile_loc to_phy_tile_loc(to_grid_coord.x(), to_grid_coord.y(), 0); + t_physical_tile_loc to_phy_tile_loc(to_grid_coord.x(), + to_grid_coord.y(), 0); t_physical_tile_type_ptr to_phy_tile_type = device_ctx.grid.get_physical_type(to_phy_tile_loc); /* Bypass the grid that does not fit the from_tile name */ @@ -452,13 +457,10 @@ static void build_inner_column_row_tile_direct( */ for (const e_side& to_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ - std::vector to_pins = - find_physical_tile_pin_id(to_phy_tile_type, - device_ctx.grid.get_width_offset( - to_phy_tile_loc), - device_ctx.grid.get_height_offset( - to_phy_tile_loc), - to_tile_port, to_side); + std::vector to_pins = find_physical_tile_pin_id( + to_phy_tile_type, device_ctx.grid.get_width_offset(to_phy_tile_loc), + device_ctx.grid.get_height_offset(to_phy_tile_loc), to_tile_port, + to_side); /* If nothing found, we can continue */ if (0 == to_pins.size()) { continue; @@ -595,15 +597,13 @@ static void build_inter_column_row_tile_direct( */ for (const e_side& from_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ - t_physical_tile_loc from_phy_tile_loc(from_grid_coord.x(), from_grid_coord.y(), 0); - std::vector from_pins = - find_physical_tile_pin_id(device_ctx.grid.get_physical_type( - from_phy_tile_loc), - device_ctx.grid.get_width_offset( - from_phy_tile_loc), - device_ctx.grid.get_height_offset( - from_phy_tile_loc), - from_tile_port, from_side); + t_physical_tile_loc from_phy_tile_loc(from_grid_coord.x(), + from_grid_coord.y(), 0); + std::vector from_pins = find_physical_tile_pin_id( + device_ctx.grid.get_physical_type(from_phy_tile_loc), + device_ctx.grid.get_width_offset(from_phy_tile_loc), + device_ctx.grid.get_height_offset(from_phy_tile_loc), from_tile_port, + from_side); /* If nothing found, we can continue */ if (0 == from_pins.size()) { continue; @@ -627,15 +627,13 @@ static void build_inter_column_row_tile_direct( */ for (const e_side& to_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ - t_physical_tile_loc to_phy_tile_loc(to_grid_coord.x(), to_grid_coord.y(), 0); - std::vector to_pins = - find_physical_tile_pin_id(device_ctx.grid.get_physical_type( - to_phy_tile_loc), - device_ctx.grid.get_width_offset( - to_phy_tile_loc), - device_ctx.grid.get_height_offset( - to_phy_tile_loc), - to_tile_port, to_side); + t_physical_tile_loc to_phy_tile_loc(to_grid_coord.x(), + to_grid_coord.y(), 0); + std::vector to_pins = find_physical_tile_pin_id( + device_ctx.grid.get_physical_type(to_phy_tile_loc), + device_ctx.grid.get_width_offset(to_phy_tile_loc), + device_ctx.grid.get_height_offset(to_phy_tile_loc), to_tile_port, + to_side); /* If nothing found, we can continue */ if (0 == to_pins.size()) { continue; @@ -710,15 +708,13 @@ static void build_inter_column_row_tile_direct( */ for (const e_side& from_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ - t_physical_tile_loc from_phy_tile_loc(from_grid_coord.x(), from_grid_coord.y(), 0); - std::vector from_pins = - find_physical_tile_pin_id(device_ctx.grid.get_physical_type( - from_phy_tile_loc), - device_ctx.grid.get_width_offset( - from_phy_tile_loc), - device_ctx.grid.get_height_offset( - from_phy_tile_loc), - from_tile_port, from_side); + t_physical_tile_loc from_phy_tile_loc(from_grid_coord.x(), + from_grid_coord.y(), 0); + std::vector from_pins = find_physical_tile_pin_id( + device_ctx.grid.get_physical_type(from_phy_tile_loc), + device_ctx.grid.get_width_offset(from_phy_tile_loc), + device_ctx.grid.get_height_offset(from_phy_tile_loc), from_tile_port, + from_side); /* If nothing found, we can continue */ if (0 == from_pins.size()) { continue; @@ -742,15 +738,13 @@ static void build_inter_column_row_tile_direct( */ for (const e_side& to_side : {TOP, RIGHT, BOTTOM, LEFT}) { /* Try to find the pin in this tile */ - t_physical_tile_loc to_phy_tile_loc(to_grid_coord.x(), to_grid_coord.y(), 0); - std::vector to_pins = - find_physical_tile_pin_id(device_ctx.grid.get_physical_type( - to_phy_tile_loc), - device_ctx.grid.get_width_offset( - to_phy_tile_loc), - device_ctx.grid.get_height_offset( - to_phy_tile_loc), - to_tile_port, to_side); + t_physical_tile_loc to_phy_tile_loc(to_grid_coord.x(), + to_grid_coord.y(), 0); + std::vector to_pins = find_physical_tile_pin_id( + device_ctx.grid.get_physical_type(to_phy_tile_loc), + device_ctx.grid.get_width_offset(to_phy_tile_loc), + device_ctx.grid.get_height_offset(to_phy_tile_loc), to_tile_port, + to_side); /* If nothing found, we can continue */ if (0 == to_pins.size()) { continue; diff --git a/openfpga/src/utils/openfpga_physical_tile_utils.cpp b/openfpga/src/utils/openfpga_physical_tile_utils.cpp index 8091081fc..1f539ffcf 100644 --- a/openfpga/src/utils/openfpga_physical_tile_utils.cpp +++ b/openfpga/src/utils/openfpga_physical_tile_utils.cpp @@ -89,7 +89,8 @@ std::set find_physical_io_tile_located_sides( for (size_t ix = 1; ix < grids.width() - 1; ++ix) { for (size_t iy = 1; iy < grids.height() - 1; ++iy) { /* If located in center, we add a NUM_SIDES and finish */ - if (physical_tile == grids.get_physical_type(t_physical_tile_loc(ix, iy, 0))) { + if (physical_tile == + grids.get_physical_type(t_physical_tile_loc(ix, iy, 0))) { io_sides.insert(NUM_SIDES); center_io = true; break; @@ -108,8 +109,8 @@ std::set find_physical_io_tile_located_sides( for (const e_side& fpga_side : FPGA_SIDES_CLOCKWISE) { for (const vtr::Point& io_coordinate : io_coordinates[fpga_side]) { /* If located in center, we add a NUM_SIDES and finish */ - if (physical_tile == - grids.get_physical_type(t_physical_tile_loc(io_coordinate.x(), io_coordinate.y(), 0))) { + if (physical_tile == grids.get_physical_type(t_physical_tile_loc( + io_coordinate.x(), io_coordinate.y(), 0))) { io_sides.insert(fpga_side); break; } From ff6fa1e90c83c225196d81ebb65d0eebed03b4e0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 8 Aug 2023 22:41:43 -0700 Subject: [PATCH 303/391] [core] fix memory leak --- openfpga/src/annotation/openfpga_annotate_routing.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openfpga/src/annotation/openfpga_annotate_routing.cpp b/openfpga/src/annotation/openfpga_annotate_routing.cpp index f95b2ea37..450f3413a 100644 --- a/openfpga/src/annotation/openfpga_annotate_routing.cpp +++ b/openfpga/src/annotation/openfpga_annotate_routing.cpp @@ -136,6 +136,7 @@ void annotate_rr_node_previous_nodes( t_trace* tptr = TracebackCompat::traceback_from_route_tree( routing_ctx.route_trees[net_id].value()); + t_trace* head = tptr; while (tptr != nullptr) { RRNodeId rr_node = RRNodeId(tptr->index); @@ -159,6 +160,7 @@ void annotate_rr_node_previous_nodes( /* Move on to the next */ tptr = tptr->next; } + free_traceback(head); } VTR_LOG("Done with %d nodes mapping\n", counter); From 1c8c4fedbbba8b608f4b518e143725466edd1458 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 8 Aug 2023 23:01:52 -0700 Subject: [PATCH 304/391] [core] fix memory leak --- openfpga/src/annotation/openfpga_annotate_routing.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openfpga/src/annotation/openfpga_annotate_routing.cpp b/openfpga/src/annotation/openfpga_annotate_routing.cpp index 450f3413a..f5299c904 100644 --- a/openfpga/src/annotation/openfpga_annotate_routing.cpp +++ b/openfpga/src/annotation/openfpga_annotate_routing.cpp @@ -143,8 +143,7 @@ void annotate_rr_node_previous_nodes( /* Find the right previous node */ prev_node = find_previous_node_from_routing_traces( device_ctx.rr_graph, - TracebackCompat::traceback_from_route_tree( - routing_ctx.route_trees[net_id].value()), + head, prev_node, rr_node); /* Only update mapped nodes */ From 788e3c17a931016c47f2ffe55562a67638dd79ed Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 8 Aug 2023 23:02:20 -0700 Subject: [PATCH 305/391] [core] format --- openfpga/src/annotation/openfpga_annotate_routing.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openfpga/src/annotation/openfpga_annotate_routing.cpp b/openfpga/src/annotation/openfpga_annotate_routing.cpp index f5299c904..780b1d99d 100644 --- a/openfpga/src/annotation/openfpga_annotate_routing.cpp +++ b/openfpga/src/annotation/openfpga_annotate_routing.cpp @@ -142,9 +142,7 @@ void annotate_rr_node_previous_nodes( /* Find the right previous node */ prev_node = find_previous_node_from_routing_traces( - device_ctx.rr_graph, - head, - prev_node, rr_node); + device_ctx.rr_graph, head, prev_node, rr_node); /* Only update mapped nodes */ if (prev_node) { From 07b8547cffdc1a9b3caab698b038a9648ad5c38b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Aug 2023 06:07:12 +0000 Subject: [PATCH 306/391] Bump yosys-plugins from `7303812` to `f8daf6d` Bumps [yosys-plugins](https://github.com/SymbiFlow/yosys-symbiflow-plugins) from `7303812` to `f8daf6d`. - [Commits](https://github.com/SymbiFlow/yosys-symbiflow-plugins/compare/73038124b0a2943fe9d591c43f46292bcbf82105...f8daf6da2ccaac239fd07aaf4207bdb30087c9e7) --- updated-dependencies: - dependency-name: yosys-plugins dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys-plugins b/yosys-plugins index 73038124b..f8daf6da2 160000 --- a/yosys-plugins +++ b/yosys-plugins @@ -1 +1 @@ -Subproject commit 73038124b0a2943fe9d591c43f46292bcbf82105 +Subproject commit f8daf6da2ccaac239fd07aaf4207bdb30087c9e7 From 387d7a6250f73f2df47fc37796874263914cc478 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Aug 2023 00:02:34 +0000 Subject: [PATCH 307/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 1a81f7e3a..2540d7f6d 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1364 +1.2.1377 From e80cb9b6f1db3cdc10c100fdf6ebf2aba83c5020 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Aug 2023 06:12:09 +0000 Subject: [PATCH 308/391] Bump yosys from `389b8d0` to `2829cd9` Bumps [yosys](https://github.com/YosysHQ/yosys) from `389b8d0` to `2829cd9`. - [Release notes](https://github.com/YosysHQ/yosys/releases) - [Commits](https://github.com/YosysHQ/yosys/compare/389b8d0f94a24c30e5b9352866f0999b14312b23...2829cd9caa5729b3b7e2393d3428e265cd038ea6) --- updated-dependencies: - dependency-name: yosys dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- yosys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yosys b/yosys index 389b8d0f9..2829cd9ca 160000 --- a/yosys +++ b/yosys @@ -1 +1 @@ -Subproject commit 389b8d0f94a24c30e5b9352866f0999b14312b23 +Subproject commit 2829cd9caa5729b3b7e2393d3428e265cd038ea6 From 6c0df8da201a1792336ab69069b2b8fcdb172a42 Mon Sep 17 00:00:00 2001 From: chungshien Date: Fri, 11 Aug 2023 07:59:53 -0700 Subject: [PATCH 309/391] Address follow up from PR 1259 --- .../src/fpga_bitstream/write_xml_fabric_bitstream.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp index a6f09a1a2..06f0c079e 100644 --- a/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp @@ -132,16 +132,16 @@ static int write_fabric_config_bit_to_xml_file( VTR_ASSERT((fabric_size_t)(wl_addr.size()) == lengths.wl); } fp << "\n"; /* Word line address */ write_tab_to_file(fp, xml_hierarchy_depth + 1); fp << "\n"; } else { /* Bit line address */ From aabbd330b302de4ee7452d4fe60dd27f62202fac Mon Sep 17 00:00:00 2001 From: chungshien Date: Fri, 11 Aug 2023 08:06:57 -0700 Subject: [PATCH 310/391] Address follow up from PR 1259 (1) --- .../write_text_fabric_bitstream.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp index 89a52faf8..4cde144d5 100644 --- a/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_text_fabric_bitstream.cpp @@ -358,6 +358,23 @@ static int fast_write_memory_bank_flatten_fabric_bitstream_to_text_file( // (1 << (bl & 7)) - This is to find Bit index of the BL // within that Byte index // When we '&' both, we can know if that BL is set or unset + /* + ----------------------------------------------------------------- + | bit (bl) | Byte index (bl >> 3) | Bit index (1 << (bl & 7)) | + |---------------------------------------------------------------- + | 0 | 0 | b0000_0001 (or 0x01) | + | 1 | 0 | b0000_0010 (or 0x02) | + | 2 | 0 | b0000_0100 (or 0x04) | + | 3 | 0 | b0000_1000 (or 0x08) | + | 4 | 0 | b0001_0000 (or 0x10) | + | 5 | 0 | b0010_0000 (or 0x20) | + | 6 | 0 | b0100_0000 (or 0x40) | + | 7 | 0 | b1000_0000 (or 0x80) | + | 8 | 1 | b0000_0001 (or 0x01) | + | ... | ... | ... | + ------------------------------------------------------------------ + Each BL can be uniquely represented by bit slice in byte array + */ for (size_t bl = 0; bl < lengths.bl; bl++) { if (mask[bl >> 3] & (1 << (bl & 7))) { if (data[bl >> 3] & (1 << (bl & 7))) { From 57dafbc904e39b410a7b6f97787cd946bc9e831a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 11 Aug 2023 11:53:49 -0700 Subject: [PATCH 311/391] [lib] update vtr --- vtr-verilog-to-routing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtr-verilog-to-routing b/vtr-verilog-to-routing index 5bea5da82..c92a92fe3 160000 --- a/vtr-verilog-to-routing +++ b/vtr-verilog-to-routing @@ -1 +1 @@ -Subproject commit 5bea5da827600c06990be740b580213eebae6263 +Subproject commit c92a92fe36c68f9c595157a7703d6632cc170ee8 From 6d449c66a7a0a091cb700dc2035e5c8ae6c7d8f9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 11 Aug 2023 12:19:44 -0700 Subject: [PATCH 312/391] [lib] update vtr --- vtr-verilog-to-routing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtr-verilog-to-routing b/vtr-verilog-to-routing index c92a92fe3..8f4c5567f 160000 --- a/vtr-verilog-to-routing +++ b/vtr-verilog-to-routing @@ -1 +1 @@ -Subproject commit c92a92fe36c68f9c595157a7703d6632cc170ee8 +Subproject commit 8f4c5567f4ff92db0659d54dd1eb6435d3b7a9f6 From dc0eec8b8135e92d7b0f6a298070a615d858532d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 11 Aug 2023 12:49:38 -0700 Subject: [PATCH 313/391] [test] added a new test to validate L shapre --- .../config/task.conf | 49 + .../config/tile_config.xml | 1 + ...in_dsp8_nonLR_caravel_io_skywater130nm.xml | 933 ++++++++++++++++++ 3 files changed, 983 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile_Lshape/config/task.conf create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile_Lshape/config/tile_config.xml create mode 100644 openfpga_flow/vpr_arch/k4_frac_N8_tileableL_reset_softadder_register_scan_chain_dsp8_nonLR_caravel_io_skywater130nm.xml diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile_Lshape/config/task.conf b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile_Lshape/config/task.conf new file mode 100644 index 000000000..8967adc33 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile_Lshape/config/task.conf @@ -0,0 +1,49 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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=3x2L +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_tileableL_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 + +[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 + +[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_Lshape/config/tile_config.xml b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_hetero_fabric_tile_Lshape/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_Lshape/config/tile_config.xml @@ -0,0 +1 @@ + diff --git a/openfpga_flow/vpr_arch/k4_frac_N8_tileableL_reset_softadder_register_scan_chain_dsp8_nonLR_caravel_io_skywater130nm.xml b/openfpga_flow/vpr_arch/k4_frac_N8_tileableL_reset_softadder_register_scan_chain_dsp8_nonLR_caravel_io_skywater130nm.xml new file mode 100644 index 000000000..ec63d7779 --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_frac_N8_tileableL_reset_softadder_register_scan_chain_dsp8_nonLR_caravel_io_skywater130nm.xml @@ -0,0 +1,933 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io_top.outpad io_top.inpad + + + + + + + + + + + + + + io_right.outpad io_right.inpad + + + + + + + + + + + + + + io_bottom.outpad io_bottom.inpad + + + + + + + + + + + + + + io_left.outpad io_left.inpad + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + clb.clk clb.reset + clb.reg_in clb.sc_in clb.cin clb.O[7:0] clb.I0 clb.I0i clb.I1 clb.I1i clb.I2 clb.I2i clb.I3 clb.I3i + clb.O[15:8] clb.I4 clb.I4i clb.I5 clb.I5i clb.I6 clb.I6i clb.I7 clb.I7i + clb.reg_out clb.sc_out clb.cout + + + + + + + + + + + + + + + + + + mult_8.a[0:2] mult_8.b[0:2] mult_8.out[0:5] + mult_8.a[3:5] mult_8.b[3:5] mult_8.out[6:10] + mult_8.a[6:7] mult_8.b[6:7] mult_8.out[11:15] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 + 1 + + + + 1 1 1 + 1 1 + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 253d5fa26c7811a3e58d9a813998ad1ec882a724 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 11 Aug 2023 13:05:46 -0700 Subject: [PATCH 314/391] [core] a new test to validate the L shape in homo geneous fpga --- ...ock_full_testbench_example_script.openfpga | 2 +- .../config/task.conf | 38 ++ .../config/task.conf | 2 + .../vpr_arch/k4_N4_tileableL_40nm.xml | 334 ++++++++++++++++++ 4 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_Lshape_full_testbench/config/task.conf create mode 100644 openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.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 5b60dc626..74ec7b206 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 @@ -1,6 +1,6 @@ # Run VPR for the 'and' design #--write_rr_graph example_rr_graph.xml -vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --device ${OPENFPGA_VPR_DEVICE} --route_chan_width ${OPENFPGA_VPR_ROUTE_CHAN_WIDTH} --clock_modeling ideal # Read OpenFPGA architecture definition read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} diff --git a/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_Lshape_full_testbench/config/task.conf b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_Lshape_full_testbench/config/task.conf new file mode 100644 index 000000000..3b77cc65e --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_Lshape_full_testbench/config/task.conf @@ -0,0 +1,38 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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= +openfpga_add_fpga_core_module= +openfpga_vpr_device=4x4L +openfpga_vpr_route_chan_width=20 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileableL_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_full_testbench/config/task.conf b/openfpga_flow/tasks/basic_tests/group_config_block/group_config_block_homo_full_testbench/config/task.conf index 30732a74d..5e22d1e08 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 @@ -21,6 +21,8 @@ openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_ 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= +openfpga_vpr_device=2x2 +openfpga_vpr_route_chan_width=20 [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_TileOrgzTl_40nm.xml diff --git a/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml new file mode 100644 index 000000000..f1fc5abfe --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_N4_tileableL_40nm.xml @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 16f102f4c1146ddb3a5a1358cbd02f3313dbb127 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 11 Aug 2023 13:07:41 -0700 Subject: [PATCH 315/391] [test] deploy new tests to basic regression tests --- openfpga_flow/regression_test_scripts/basic_reg_test.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index f88ceccc0..f345d8428 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -188,7 +188,11 @@ 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_Lshape_full_testbench $@ run-task basic_tests/group_config_block/group_config_block_homo_fabric_tile $@ +run-task basic_tests/group_config_block/group_config_block_homo_fabric_tile_core_wrapper $@ +run-task basic_tests/group_config_block/group_config_block_hetero_fabric_tile $@ +run-task basic_tests/group_config_block/group_config_block_hetero_fabric_tile_Lshape $@ echo -e "Testing global port definition from tiles"; run-task basic_tests/global_tile_ports/global_tile_clock $@ From b155e660ee468f75f2dd1004c8936292c49af495 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 11 Aug 2023 16:55:35 -0700 Subject: [PATCH 316/391] [test] fixed a bug --- .../group_config_block_homo_fabric_tile/config/task.conf | 2 ++ 1 file changed, 2 insertions(+) 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 283c7b751..0273db97c 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 @@ -21,6 +21,8 @@ openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_ 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= +openfpga_vpr_device=auto +openfpga_vpr_route_chan_width=20 [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_TileOrgzTl_40nm.xml From 6c603f1f62456554c8688e2f607e5b753dcaabb8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 12 Aug 2023 00:56:21 +0000 Subject: [PATCH 317/391] Updated Patch Count --- VERSION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.md b/VERSION.md index 2540d7f6d..b2bca563d 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -1.2.1377 +1.2.1384 From 2f49c25f0997f7a112242a3cb19f59a4842a9108 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Fri, 11 Aug 2023 21:19:06 -0700 Subject: [PATCH 318/391] [test] updated --- .../config/task.conf | 2 ++ 1 file changed, 2 insertions(+) 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 index 81af76bfa..ec54e6ca9 100644 --- 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 @@ -21,6 +21,8 @@ openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_ 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 +openfpga_vpr_device=auto +openfpga_vpr_route_chan_width=20 [ARCHITECTURES] arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_TileOrgzTl_40nm.xml From d9e3392194e32cefe20fdb9eac8f41c88667fd86 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 12 Aug 2023 12:25:38 -0700 Subject: [PATCH 319/391] [doc] add description about new option ``shrink_boundary`` --- .../manual/arch_lang/addon_vpr_syntax.rst | 22 ++++++++++++++---- .../arch_lang/figures/shrink_boundary.png | Bin 0 -> 569309 bytes 2 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 docs/source/manual/arch_lang/figures/shrink_boundary.png diff --git a/docs/source/manual/arch_lang/addon_vpr_syntax.rst b/docs/source/manual/arch_lang/addon_vpr_syntax.rst index c1d0f593f..ef93be938 100644 --- a/docs/source/manual/arch_lang/addon_vpr_syntax.rst +++ b/docs/source/manual/arch_lang/addon_vpr_syntax.rst @@ -59,12 +59,12 @@ Layout .. option:: through_channel="" Allow routing channels to pass through multi-width and multi-height programable blocks. This is mainly used in heterogeneous FPGAs to increase routability, as illustrated in :numref:`fig_thru_channel`. - By default, it is ``off``. + By default, it is ``false``. .. _fig_thru_channel: .. figure:: ./figures/thru_channel.png - :scale: 80% + :width: 100% :alt: Impact of through channel Impact on routing architecture when through channel in multi-width and multi-height programmable blocks: (a) disabled; (b) enabled. @@ -73,12 +73,26 @@ Layout .. warning:: You cannot use ``spread`` pin location for the ``height > 1`` or ``width >1`` tiles when using the tileable routing resource graph!!! Otherwise, it will cause undriven pins in your device!!! +.. option:: shrink_boundary="" + + Remove all the routing wires in empty regions. This is mainly used in non-rectangle FPGAs to avoid redundant routing wires in blank area, as illustrated in :numref:`fig_shrink_boundary`. + By default, it is ``false``. -A quick example to show tileable routing is enabled and through channels are disabled: + .. _fig_shrink_boundary: + + .. figure:: ./figures/shrink_boundary.png + :width: 100% + :alt: Impact of shrink boundary + + Impact on routing architecture when shrink-boundary: (a) disabled; (b) enabled. + + .. warning:: Do NOT enable ``shrink_boundary`` if you are not using the tileable routing resource graph generator! + +A quick example to show tileable routing is enabled, other options, e.g., through channels are disabled: .. code-block:: xml - + Switch Block diff --git a/docs/source/manual/arch_lang/figures/shrink_boundary.png b/docs/source/manual/arch_lang/figures/shrink_boundary.png new file mode 100644 index 0000000000000000000000000000000000000000..acbb4518f1113ff08cb6157ee5c0a8c4b4b50926 GIT binary patch literal 569309 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%0Axpd7~}GWh$o#uvpB7n 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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*~