From 35af0dd676c478b97b106f376a9379552744b7fa Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 27 Jul 2020 16:34:43 -0600 Subject: [PATCH 001/148] streamline fabric bitstream file format --- .../src/bitstream_manager_utils.cpp | 17 +++++++++++ .../src/bitstream_manager_utils.h | 3 ++ .../write_xml_fabric_bitstream.cpp | 28 ++++++++----------- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.cpp b/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.cpp index 1225602ef..8968b8d1c 100644 --- a/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.cpp +++ b/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.cpp @@ -55,4 +55,21 @@ std::vector find_bitstream_manager_top_blocks(const BitstreamMana return top_blocks; } +/******************************************************************** + * Find the index of a configuration bit in the children bits of its parent block + *******************************************************************/ +size_t find_bitstream_manager_config_bit_index_in_parent_block(const BitstreamManager& bitstream_manager, + const ConfigBitId& bit_id) { + size_t curr_index = 0; + for (const ConfigBitId& cand_bit : bitstream_manager.block_bits(bitstream_manager.bit_parent_block(bit_id))) { + if (cand_bit == bit_id) { + break; + } + curr_index++; + } + + return curr_index; +} + + } /* end namespace openfpga */ diff --git a/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.h b/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.h index 4c8b26ad2..328394b01 100644 --- a/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.h +++ b/libopenfpga/libfpgabitstream/src/bitstream_manager_utils.h @@ -19,6 +19,9 @@ std::vector find_bitstream_manager_block_hierarchy(const Bitstrea std::vector find_bitstream_manager_top_blocks(const BitstreamManager& bitstream_manager); +size_t find_bitstream_manager_config_bit_index_in_parent_block(const BitstreamManager& bitstream_manager, + const ConfigBitId& bit_id); + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp index b1d5aeebe..b4d9cbebd 100644 --- a/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp @@ -77,30 +77,26 @@ int write_fabric_config_bit_to_xml_file(std::fstream& fp, } write_tab_to_file(fp, 1); - fp << "\n"; + fp << "\""; /* Output hierarchy of this parent*/ const ConfigBitId& config_bit = fabric_bitstream.config_bit(fabric_bit); const ConfigBlockId& config_block = bitstream_manager.bit_parent_block(config_bit); std::vector block_hierarchy = find_bitstream_manager_block_hierarchy(bitstream_manager, config_block); - write_tab_to_file(fp, 2); - fp << "\n"; - size_t hierarchy_counter = 0; + std::string hie_path; for (const ConfigBlockId& temp_block : block_hierarchy) { - write_tab_to_file(fp, 3); - fp << "\n"; - hierarchy_counter++; + hie_path += bitstream_manager.block_name(temp_block); + hie_path += std::string("."); } - write_tab_to_file(fp, 2); - fp << "\n"; + hie_path += generate_configurable_memory_data_out_name(); + hie_path += std::string("["); + hie_path += std::to_string(find_bitstream_manager_config_bit_index_in_parent_block(bitstream_manager, config_bit)); + hie_path += std::string("]"); + + fp << " path=\"" << hie_path << "\"/>\n"; switch (config_type) { case CONFIG_MEM_STANDALONE: From f773491f871d19bd4d9132520e6fc8c66f01c14b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 27 Jul 2020 16:37:10 -0600 Subject: [PATCH 002/148] update documentation to sync with the new fabric bitstream format --- .../fabric_dependent_bitstream.rst | 35 +++---------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/docs/source/manual/fpga_bitstream/fabric_dependent_bitstream.rst b/docs/source/manual/fpga_bitstream/fabric_dependent_bitstream.rst index 131d0ce57..825760a5e 100644 --- a/docs/source/manual/fpga_bitstream/fabric_dependent_bitstream.rst +++ b/docs/source/manual/fpga_bitstream/fabric_dependent_bitstream.rst @@ -67,26 +67,13 @@ In principle, the file consist a number of XML node ````, each bit contains - ``value``: The configuration bit value. - - ``hierarchy`` represents the location of this block in FPGA fabric. - The hierachy includes the full hierarchy of this block - - - ``instance`` denotes the instance name which you can find in the fabric netlists - - - ``level`` denotes the depth of the block in the hierarchy - - - ``width`` denotes the number of configuration bits under the instance. Typically, only leaf instance has this attribute. +- ``path`` represents the location of this block in FPGA fabric, i.e., the full path in the hierarchy of FPGA fabric. A quick example: .. code-block:: xml - - - - - - - + Other information may depend on the type of configuration procotol. @@ -101,14 +88,7 @@ Other information may depend on the type of configuration procotol. .. code-block:: xml - - - - - - - - + @@ -121,13 +101,6 @@ Other information may depend on the type of configuration procotol. .. code-block:: xml - - - - - - - - + From 5595ee9052f38347a4481076009e7056a713d41d Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 27 Jul 2020 16:53:29 -0600 Subject: [PATCH 003/148] refine the test case for load external arch bitstream --- .../config/task.conf | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/openfpga_flow/tasks/fpga_bitstream/load_external_architecture_bitstream/config/task.conf b/openfpga_flow/tasks/fpga_bitstream/load_external_architecture_bitstream/config/task.conf index 4db289d23..2d7eb177f 100644 --- a/openfpga_flow/tasks/fpga_bitstream/load_external_architecture_bitstream/config/task.conf +++ b/openfpga_flow/tasks/fpga_bitstream/load_external_architecture_bitstream/config/task.conf @@ -13,7 +13,7 @@ power_analysis = true spice_output=false verilog_output=true timeout_each_job = 20*60 -fpga_flow=yosys_vpr +fpga_flow=vpr_blif [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/OpenFPGAShellScripts/load_external_arch_bitstream_example_script.openfpga @@ -25,10 +25,17 @@ openfpga_external_arch_bitstream_file=${PATH:OPENFPGA_PATH}/openfpga_flow/arch_b arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.blif [SYNTHESIS_PARAM] -bench0_top = and2 +bench0_top = or2 +bench0_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.act +######################## +# Use a different verilog as reference here +# This verilog is consistent with the external architecture bitstream generated above +# As such, we can test if the bitstream database is indeed overwritten by another benchmark +# which is different than the one given to VPR +bench0_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v bench0_chan_width = 300 [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] From 9add82148f211a31a349f670b3340f405246ea05 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 27 Jul 2020 17:05:46 -0600 Subject: [PATCH 004/148] refactored CI for split regression tests in terms of OpenFPGA tools --- .travis.yml | 20 ++++++++++++++++++- .travis/basic_reg_test.sh | 9 --------- .travis/fpga_bitstream_reg_test.sh | 16 +++++++++++++++ .travis/fpga_sdc_reg_test.sh | 16 +++++++++++++++ .travis/fpga_spice_reg_test.sh | 16 +++++++++++++++ ...g_reg_test.sh => fpga_verilog_reg_test.sh} | 8 ++++---- 6 files changed, 71 insertions(+), 14 deletions(-) create mode 100755 .travis/fpga_bitstream_reg_test.sh create mode 100755 .travis/fpga_sdc_reg_test.sh create mode 100755 .travis/fpga_spice_reg_test.sh rename .travis/{verilog_reg_test.sh => fpga_verilog_reg_test.sh} (94%) diff --git a/.travis.yml b/.travis.yml index ca370367a..bf9c42724 100644 --- a/.travis.yml +++ b/.travis.yml @@ -96,7 +96,25 @@ jobs: name: "FPGA-Verilog regression tests" script: - source .travis/build.sh - - source .travis/verilog_reg_test.sh + - source .travis/fpga_verilog_reg_test.sh + + - stage: Test + name: "FPGA-Bitstream regression tests" + script: + - source .travis/build.sh + - source .travis/fpga_bitstream_reg_test.sh + + - stage: Test + name: "FPGA-SDC regression tests" + script: + - source .travis/build.sh + - source .travis/fpga_sdc_reg_test.sh + + - stage: Test + name: "FPGA-SPICE regression tests" + script: + - source .travis/build.sh + - source .travis/fpga_spice_reg_test.sh #after_failure: # - .travis/after_failure.sh diff --git a/.travis/basic_reg_test.sh b/.travis/basic_reg_test.sh index b794858d9..b8f6e2efd 100755 --- a/.travis/basic_reg_test.sh +++ b/.travis/basic_reg_test.sh @@ -35,16 +35,7 @@ python3 openfpga_flow/scripts/run_fpga_task.py generate_fabric --debug --show_th echo -e "Testing Verilog testbench generation only"; python3 openfpga_flow/scripts/run_fpga_task.py generate_testbench --debug --show_thread_logs -echo -e "Testing bitstream generation only"; -python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/generate_bitstream --debug --show_thread_logs - echo -e "Testing user-defined simulation settings: clock frequency and number of cycles"; python3 openfpga_flow/scripts/run_fpga_task.py fixed_simulation_settings --debug --show_thread_logs -echo -e "Testing SDC generation with time units"; -python3 openfpga_flow/scripts/run_fpga_task.py sdc_time_unit --debug --show_thread_logs - -echo -e "Testing FPGA-SPICE with netlist generation"; -python3 openfpga_flow/scripts/run_fpga_task.py fpga_spice/generate_spice --debug --show_thread_logs - end_section "OpenFPGA.TaskTun" diff --git a/.travis/fpga_bitstream_reg_test.sh b/.travis/fpga_bitstream_reg_test.sh new file mode 100755 index 000000000..e2787f78f --- /dev/null +++ b/.travis/fpga_bitstream_reg_test.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +start_section "OpenFPGA.TaskTun" "${GREEN}..Running_Regression..${NC}" +cd ${TRAVIS_BUILD_DIR} + +############################################### +# OpenFPGA Shell with VPR8 +############################################## +echo -e "FPGA-Bitstream regression tests"; + +echo -e "Testing bitstream generation only"; +python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/generate_bitstream --debug --show_thread_logs + +end_section "OpenFPGA.TaskTun" diff --git a/.travis/fpga_sdc_reg_test.sh b/.travis/fpga_sdc_reg_test.sh new file mode 100755 index 000000000..984199312 --- /dev/null +++ b/.travis/fpga_sdc_reg_test.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +start_section "OpenFPGA.TaskTun" "${GREEN}..Running_Regression..${NC}" +cd ${TRAVIS_BUILD_DIR} + +############################################### +# OpenFPGA Shell with VPR8 +############################################## +echo -e "FPGA-SDC regression tests"; + +echo -e "Testing SDC generation with time units"; +python3 openfpga_flow/scripts/run_fpga_task.py sdc_time_unit --debug --show_thread_logs + +end_section "OpenFPGA.TaskTun" diff --git a/.travis/fpga_spice_reg_test.sh b/.travis/fpga_spice_reg_test.sh new file mode 100755 index 000000000..8856d3ec8 --- /dev/null +++ b/.travis/fpga_spice_reg_test.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +start_section "OpenFPGA.TaskTun" "${GREEN}..Running_Regression..${NC}" +cd ${TRAVIS_BUILD_DIR} + +############################################### +# OpenFPGA Shell with VPR8 +############################################## +echo -e "FPGA-SPICE regression tests"; + +echo -e "Testing FPGA-SPICE with netlist generation"; +python3 openfpga_flow/scripts/run_fpga_task.py fpga_spice/generate_spice --debug --show_thread_logs + +end_section "OpenFPGA.TaskTun" diff --git a/.travis/verilog_reg_test.sh b/.travis/fpga_verilog_reg_test.sh similarity index 94% rename from .travis/verilog_reg_test.sh rename to .travis/fpga_verilog_reg_test.sh index 18ffe91e5..c9cab90f7 100755 --- a/.travis/verilog_reg_test.sh +++ b/.travis/fpga_verilog_reg_test.sh @@ -8,7 +8,7 @@ cd ${TRAVIS_BUILD_DIR} ############################################### # OpenFPGA Shell with VPR8 ############################################## -echo -e "OpenFPGA Feature Testing for Verilog-to-Verification"; +echo -e "FPGA-Verilog Feature Tests"; echo -e "Testing Verilog generation for LUTs: a single mode LUT6 FPGA using micro benchmarks"; python3 openfpga_flow/scripts/run_fpga_task.py lut_design/single_mode --debug --show_thread_logs @@ -46,13 +46,13 @@ python3 openfpga_flow/scripts/run_fpga_task.py fabric_chain/register_chain --deb echo -e "Testing Verilog generation with scan chain across an FPGA"; python3 openfpga_flow/scripts/run_fpga_task.py fabric_chain/scan_chain --debug --show_thread_logs -echo -e "Testing Verilog generation with routing mutliplexers implemented by tree structure"; +echo -e "Testing Verilog generation with routing multiplexers implemented by tree structure"; python3 openfpga_flow/scripts/run_fpga_task.py mux_design/tree_structure --debug --show_thread_logs -echo -e "Testing Verilog generation with routing mutliplexers implemented by standard cell MUX2"; +echo -e "Testing Verilog generation with routing multiplexers implemented by standard cell MUX2"; python3 openfpga_flow/scripts/run_fpga_task.py mux_design/stdcell_mux2 --debug --show_thread_logs -echo -e "Testing Verilog generation with routing mutliplexers implemented by local encoders"; +echo -e "Testing Verilog generation with routing multiplexers implemented by local encoders"; python3 openfpga_flow/scripts/run_fpga_task.py mux_design/local_encoder --debug --show_thread_logs echo -e "Testing Verilog generation with behavioral description"; From 50cc4dfba3d6a835800538dc0ae65877af42346f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 27 Jul 2020 17:18:59 -0600 Subject: [PATCH 005/148] classify regression test to dedicated categories --- .travis/basic_reg_test.sh | 28 +++++----- .travis/fpga_sdc_reg_test.sh | 2 +- .travis/fpga_verilog_reg_test.sh | 52 +++++++++---------- .../config/task.conf | 0 .../configuration_chain/config/task.conf | 0 .../configuration_frame/config/task.conf | 0 .../fast_configuration_chain/config/task.conf | 0 .../fast_configuration_frame/config/task.conf | 0 .../fast_memory_bank/config/task.conf | 0 .../flatten_memory/config/task.conf | 0 .../memory_bank/config/task.conf | 0 .../generate_fabric/config/task.conf | 0 .../generate_testbench/config/task.conf | 0 .../configuration_chain/config/task.conf | 0 .../configuration_frame/config/task.conf | 0 .../flatten_memory/config/task.conf | 0 .../memory_bank/config/task.conf | 0 .../sdc_time_unit/config/task.conf | 0 .../behavioral_verilog/config/task.conf | 0 .../bram/dpram16k/config/task.conf | 0 .../bram/wide_dpram16k/config/task.conf | 0 .../depopulate_crossbar/config/task.conf | 0 .../duplicated_grid_pin/config/task.conf | 0 .../fabric_chain/adder_chain/config/task.conf | 0 .../register_chain/config/task.conf | 0 .../fabric_chain/scan_chain/config/task.conf | 0 .../generate_random_key/config/task.conf | 0 .../generate_vanilla_key/config/task.conf | 0 .../load_external_key/config/task.conf | 0 .../config/task.conf | 19 +++---- .../flatten_routing/config/task.conf | 0 .../hard_adder/config/task.conf | 0 .../implicit_verilog/config/task.conf | 0 .../io/aib/config/task.conf | 0 .../io/multi_io_capacity/config/task.conf | 0 .../io/reduced_io/config/task.conf | 0 .../lut_design/frac_lut/config/task.conf | 0 .../intermediate_buffer/config/task.conf | 0 .../lut_design/single_mode/config/task.conf | 0 .../mux_design/local_encoder/config/task.conf | 0 .../mux_design/stdcell_mux2/config/task.conf | 0 .../tree_structure/config/task.conf | 0 .../power_gated_inverter/config/task.conf | 0 .../spypad/config/task.conf | 0 .../untileable/config/task.conf | 0 45 files changed, 51 insertions(+), 50 deletions(-) rename openfpga_flow/tasks/{ => basic_tests}/fixed_simulation_settings/config/task.conf (100%) rename openfpga_flow/tasks/{ => basic_tests}/full_testbench/configuration_chain/config/task.conf (100%) rename openfpga_flow/tasks/{ => basic_tests}/full_testbench/configuration_frame/config/task.conf (100%) rename openfpga_flow/tasks/{ => basic_tests}/full_testbench/fast_configuration_chain/config/task.conf (100%) rename openfpga_flow/tasks/{ => basic_tests}/full_testbench/fast_configuration_frame/config/task.conf (100%) rename openfpga_flow/tasks/{ => basic_tests}/full_testbench/fast_memory_bank/config/task.conf (100%) rename openfpga_flow/tasks/{ => basic_tests}/full_testbench/flatten_memory/config/task.conf (100%) rename openfpga_flow/tasks/{ => basic_tests}/full_testbench/memory_bank/config/task.conf (100%) rename openfpga_flow/tasks/{ => basic_tests}/generate_fabric/config/task.conf (100%) rename openfpga_flow/tasks/{ => basic_tests}/generate_testbench/config/task.conf (100%) rename openfpga_flow/tasks/{ => basic_tests}/preconfig_testbench/configuration_chain/config/task.conf (100%) rename openfpga_flow/tasks/{ => basic_tests}/preconfig_testbench/configuration_frame/config/task.conf (100%) rename openfpga_flow/tasks/{ => basic_tests}/preconfig_testbench/flatten_memory/config/task.conf (100%) rename openfpga_flow/tasks/{ => basic_tests}/preconfig_testbench/memory_bank/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_sdc}/sdc_time_unit/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/behavioral_verilog/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/bram/dpram16k/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/bram/wide_dpram16k/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/depopulate_crossbar/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/duplicated_grid_pin/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/fabric_chain/adder_chain/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/fabric_chain/register_chain/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/fabric_chain/scan_chain/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/fabric_key/generate_random_key/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/fabric_key/generate_vanilla_key/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/fabric_key/load_external_key/config/task.conf (100%) rename openfpga_flow/tasks/{ncounter => fpga_verilog/fabric_key/load_external_key_cc_fpga}/config/task.conf (57%) rename openfpga_flow/tasks/{ => fpga_verilog}/flatten_routing/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/hard_adder/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/implicit_verilog/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/io/aib/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/io/multi_io_capacity/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/io/reduced_io/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/lut_design/frac_lut/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/lut_design/intermediate_buffer/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/lut_design/single_mode/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/mux_design/local_encoder/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/mux_design/stdcell_mux2/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/mux_design/tree_structure/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/power_gated_design/power_gated_inverter/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/spypad/config/task.conf (100%) rename openfpga_flow/tasks/{ => fpga_verilog}/untileable/config/task.conf (100%) diff --git a/.travis/basic_reg_test.sh b/.travis/basic_reg_test.sh index b8f6e2efd..ff29f7b71 100755 --- a/.travis/basic_reg_test.sh +++ b/.travis/basic_reg_test.sh @@ -11,31 +11,31 @@ cd ${TRAVIS_BUILD_DIR} echo -e "Basic regression tests"; echo -e "Testing configuration chain of a K4N4 FPGA"; -python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/configuration_chain --debug --show_thread_logs -python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/fast_configuration_chain --debug --show_thread_logs -python3 openfpga_flow/scripts/run_fpga_task.py preconfig_testbench/configuration_chain --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/configuration_chain --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/fast_configuration_chain --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/preconfig_testbench/configuration_chain --debug --show_thread_logs echo -e "Testing fram-based configuration protocol of a K4N4 FPGA"; -python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/configuration_frame --debug --show_thread_logs -python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/fast_configuration_frame --debug --show_thread_logs -python3 openfpga_flow/scripts/run_fpga_task.py preconfig_testbench/configuration_frame --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/configuration_frame --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/fast_configuration_frame --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/preconfig_testbench/configuration_frame --debug --show_thread_logs echo -e "Testing memory bank configuration protocol of a K4N4 FPGA"; -python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/memory_bank --debug --show_thread_logs -python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/fast_memory_bank --debug --show_thread_logs -python3 openfpga_flow/scripts/run_fpga_task.py preconfig_testbench/memory_bank --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/memory_bank --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/fast_memory_bank --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/preconfig_testbench/memory_bank --debug --show_thread_logs echo -e "Testing standalone (flatten memory) configuration protocol of a K4N4 FPGA"; -python3 openfpga_flow/scripts/run_fpga_task.py full_testbench/flatten_memory --debug --show_thread_logs -python3 openfpga_flow/scripts/run_fpga_task.py preconfig_testbench/flatten_memory --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/flatten_memory --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/preconfig_testbench/flatten_memory --debug --show_thread_logs echo -e "Testing fabric Verilog generation only"; -python3 openfpga_flow/scripts/run_fpga_task.py generate_fabric --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/generate_fabric --debug --show_thread_logs echo -e "Testing Verilog testbench generation only"; -python3 openfpga_flow/scripts/run_fpga_task.py generate_testbench --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/generate_testbench --debug --show_thread_logs echo -e "Testing user-defined simulation settings: clock frequency and number of cycles"; -python3 openfpga_flow/scripts/run_fpga_task.py fixed_simulation_settings --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/fixed_simulation_settings --debug --show_thread_logs end_section "OpenFPGA.TaskTun" diff --git a/.travis/fpga_sdc_reg_test.sh b/.travis/fpga_sdc_reg_test.sh index 984199312..70dd34809 100755 --- a/.travis/fpga_sdc_reg_test.sh +++ b/.travis/fpga_sdc_reg_test.sh @@ -11,6 +11,6 @@ cd ${TRAVIS_BUILD_DIR} echo -e "FPGA-SDC regression tests"; echo -e "Testing SDC generation with time units"; -python3 openfpga_flow/scripts/run_fpga_task.py sdc_time_unit --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_sdc/sdc_time_unit --debug --show_thread_logs end_section "OpenFPGA.TaskTun" diff --git a/.travis/fpga_verilog_reg_test.sh b/.travis/fpga_verilog_reg_test.sh index c9cab90f7..7ee1a5095 100755 --- a/.travis/fpga_verilog_reg_test.sh +++ b/.travis/fpga_verilog_reg_test.sh @@ -11,80 +11,80 @@ cd ${TRAVIS_BUILD_DIR} echo -e "FPGA-Verilog Feature Tests"; echo -e "Testing Verilog generation for LUTs: a single mode LUT6 FPGA using micro benchmarks"; -python3 openfpga_flow/scripts/run_fpga_task.py lut_design/single_mode --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/lut_design/single_mode --debug --show_thread_logs echo -e "Testing Verilog generation for LUTs: simple fracturable LUT6 "; -python3 openfpga_flow/scripts/run_fpga_task.py lut_design/frac_lut --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/lut_design/frac_lut --debug --show_thread_logs echo -e "Testing Verilog generation for LUTs: LUT6 with intermediate buffers"; -python3 openfpga_flow/scripts/run_fpga_task.py lut_design/intermediate_buffer --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/lut_design/intermediate_buffer --debug --show_thread_logs echo -e "Testing Verilog generation with VPR's untileable routing architecture "; -python3 openfpga_flow/scripts/run_fpga_task.py untileable --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/untileable --debug --show_thread_logs echo -e "Testing Verilog generation with hard adder chain in CLBs "; -python3 openfpga_flow/scripts/run_fpga_task.py hard_adder --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/hard_adder --debug --show_thread_logs echo -e "Testing Verilog generation with 16k block RAMs "; -python3 openfpga_flow/scripts/run_fpga_task.py bram/dpram16k --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/bram/dpram16k --debug --show_thread_logs echo -e "Testing Verilog generation with 16k block RAMs spanning two columns "; -python3 openfpga_flow/scripts/run_fpga_task.py bram/wide_dpram16k --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/bram/wide_dpram16k --debug --show_thread_logs echo -e "Testing Verilog generation with different I/O capacities on each side of an FPGA "; -python3 openfpga_flow/scripts/run_fpga_task.py io/multi_io_capacity --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/io/multi_io_capacity --debug --show_thread_logs echo -e "Testing Verilog generation with I/Os only on left and right sides of an FPGA "; -python3 openfpga_flow/scripts/run_fpga_task.py io/reduced_io --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/io/reduced_io --debug --show_thread_logs echo -e "Testing Verilog generation with adder chain across an FPGA"; -python3 openfpga_flow/scripts/run_fpga_task.py fabric_chain/adder_chain --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fabric_chain/adder_chain --debug --show_thread_logs echo -e "Testing Verilog generation with shift register chain across an FPGA"; -python3 openfpga_flow/scripts/run_fpga_task.py fabric_chain/register_chain --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fabric_chain/register_chain --debug --show_thread_logs echo -e "Testing Verilog generation with scan chain across an FPGA"; -python3 openfpga_flow/scripts/run_fpga_task.py fabric_chain/scan_chain --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fabric_chain/scan_chain --debug --show_thread_logs echo -e "Testing Verilog generation with routing multiplexers implemented by tree structure"; -python3 openfpga_flow/scripts/run_fpga_task.py mux_design/tree_structure --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/tree_structure --debug --show_thread_logs echo -e "Testing Verilog generation with routing multiplexers implemented by standard cell MUX2"; -python3 openfpga_flow/scripts/run_fpga_task.py mux_design/stdcell_mux2 --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/stdcell_mux2 --debug --show_thread_logs echo -e "Testing Verilog generation with routing multiplexers implemented by local encoders"; -python3 openfpga_flow/scripts/run_fpga_task.py mux_design/local_encoder --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/local_encoder --debug --show_thread_logs echo -e "Testing Verilog generation with behavioral description"; -python3 openfpga_flow/scripts/run_fpga_task.py behavioral_verilog --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/behavioral_verilog --debug --show_thread_logs echo -e "Testing implicit Verilog generation"; -python3 openfpga_flow/scripts/run_fpga_task.py implicit_verilog --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/implicit_verilog --debug --show_thread_logs echo -e "Testing Verilog generation with flatten routing modules"; -python3 openfpga_flow/scripts/run_fpga_task.py flatten_routing --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/flatten_routing --debug --show_thread_logs echo -e "Testing Verilog generation with duplicated grid output pins"; -python3 openfpga_flow/scripts/run_fpga_task.py duplicated_grid_pin --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/duplicated_grid_pin --debug --show_thread_logs echo -e "Testing Verilog generation with spy output pads"; -python3 openfpga_flow/scripts/run_fpga_task.py spypad --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/spypad --debug --show_thread_logs echo -e "Testing Secured FPGA fabrics"; -python3 openfpga_flow/scripts/run_fpga_task.py fabric_key/generate_vanilla_key --debug --show_thread_logs -python3 openfpga_flow/scripts/run_fpga_task.py fabric_key/generate_random_key --debug --show_thread_logs -python3 openfpga_flow/scripts/run_fpga_task.py fabric_key/load_external_key --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fabric_key/generate_vanilla_key --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fabric_key/generate_random_key --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fabric_key/load_external_key --debug --show_thread_logs echo -e "Testing Power-gating designs"; -python3 openfpga_flow/scripts/run_fpga_task.py power_gated_design/power_gated_inverter --show_thread_logs --debug +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/power_gated_design/power_gated_inverter --show_thread_logs --debug echo -e "Testing Depopulated crossbar in local routing"; -python3 openfpga_flow/scripts/run_fpga_task.py depopulate_crossbar --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/depopulate_crossbar --debug --show_thread_logs # Verify MCNC big20 benchmark suite with ModelSim # Please make sure you have ModelSim installed in the environment # Otherwise, it will fail -#python3 openfpga_flow/scripts/run_fpga_task.py mcnc_big20 --debug --show_thread_logs --maxthreads 20 +#python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mcnc_big20 --debug --show_thread_logs --maxthreads 20 #python3 openfpga_flow/scripts/run_modelsim.py mcnc_big20 --run_sim end_section "OpenFPGA.TaskTun" diff --git a/openfpga_flow/tasks/fixed_simulation_settings/config/task.conf b/openfpga_flow/tasks/basic_tests/fixed_simulation_settings/config/task.conf similarity index 100% rename from openfpga_flow/tasks/fixed_simulation_settings/config/task.conf rename to openfpga_flow/tasks/basic_tests/fixed_simulation_settings/config/task.conf diff --git a/openfpga_flow/tasks/full_testbench/configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf similarity index 100% rename from openfpga_flow/tasks/full_testbench/configuration_chain/config/task.conf rename to openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf diff --git a/openfpga_flow/tasks/full_testbench/configuration_frame/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame/config/task.conf similarity index 100% rename from openfpga_flow/tasks/full_testbench/configuration_frame/config/task.conf rename to openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame/config/task.conf diff --git a/openfpga_flow/tasks/full_testbench/fast_configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain/config/task.conf similarity index 100% rename from openfpga_flow/tasks/full_testbench/fast_configuration_chain/config/task.conf rename to openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain/config/task.conf diff --git a/openfpga_flow/tasks/full_testbench/fast_configuration_frame/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_frame/config/task.conf similarity index 100% rename from openfpga_flow/tasks/full_testbench/fast_configuration_frame/config/task.conf rename to openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_frame/config/task.conf diff --git a/openfpga_flow/tasks/full_testbench/fast_memory_bank/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fast_memory_bank/config/task.conf similarity index 100% rename from openfpga_flow/tasks/full_testbench/fast_memory_bank/config/task.conf rename to openfpga_flow/tasks/basic_tests/full_testbench/fast_memory_bank/config/task.conf diff --git a/openfpga_flow/tasks/full_testbench/flatten_memory/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/flatten_memory/config/task.conf similarity index 100% rename from openfpga_flow/tasks/full_testbench/flatten_memory/config/task.conf rename to openfpga_flow/tasks/basic_tests/full_testbench/flatten_memory/config/task.conf diff --git a/openfpga_flow/tasks/full_testbench/memory_bank/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank/config/task.conf similarity index 100% rename from openfpga_flow/tasks/full_testbench/memory_bank/config/task.conf rename to openfpga_flow/tasks/basic_tests/full_testbench/memory_bank/config/task.conf diff --git a/openfpga_flow/tasks/generate_fabric/config/task.conf b/openfpga_flow/tasks/basic_tests/generate_fabric/config/task.conf similarity index 100% rename from openfpga_flow/tasks/generate_fabric/config/task.conf rename to openfpga_flow/tasks/basic_tests/generate_fabric/config/task.conf diff --git a/openfpga_flow/tasks/generate_testbench/config/task.conf b/openfpga_flow/tasks/basic_tests/generate_testbench/config/task.conf similarity index 100% rename from openfpga_flow/tasks/generate_testbench/config/task.conf rename to openfpga_flow/tasks/basic_tests/generate_testbench/config/task.conf diff --git a/openfpga_flow/tasks/preconfig_testbench/configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/configuration_chain/config/task.conf similarity index 100% rename from openfpga_flow/tasks/preconfig_testbench/configuration_chain/config/task.conf rename to openfpga_flow/tasks/basic_tests/preconfig_testbench/configuration_chain/config/task.conf diff --git a/openfpga_flow/tasks/preconfig_testbench/configuration_frame/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/configuration_frame/config/task.conf similarity index 100% rename from openfpga_flow/tasks/preconfig_testbench/configuration_frame/config/task.conf rename to openfpga_flow/tasks/basic_tests/preconfig_testbench/configuration_frame/config/task.conf diff --git a/openfpga_flow/tasks/preconfig_testbench/flatten_memory/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/flatten_memory/config/task.conf similarity index 100% rename from openfpga_flow/tasks/preconfig_testbench/flatten_memory/config/task.conf rename to openfpga_flow/tasks/basic_tests/preconfig_testbench/flatten_memory/config/task.conf diff --git a/openfpga_flow/tasks/preconfig_testbench/memory_bank/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/memory_bank/config/task.conf similarity index 100% rename from openfpga_flow/tasks/preconfig_testbench/memory_bank/config/task.conf rename to openfpga_flow/tasks/basic_tests/preconfig_testbench/memory_bank/config/task.conf diff --git a/openfpga_flow/tasks/sdc_time_unit/config/task.conf b/openfpga_flow/tasks/fpga_sdc/sdc_time_unit/config/task.conf similarity index 100% rename from openfpga_flow/tasks/sdc_time_unit/config/task.conf rename to openfpga_flow/tasks/fpga_sdc/sdc_time_unit/config/task.conf diff --git a/openfpga_flow/tasks/behavioral_verilog/config/task.conf b/openfpga_flow/tasks/fpga_verilog/behavioral_verilog/config/task.conf similarity index 100% rename from openfpga_flow/tasks/behavioral_verilog/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/behavioral_verilog/config/task.conf diff --git a/openfpga_flow/tasks/bram/dpram16k/config/task.conf b/openfpga_flow/tasks/fpga_verilog/bram/dpram16k/config/task.conf similarity index 100% rename from openfpga_flow/tasks/bram/dpram16k/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/bram/dpram16k/config/task.conf diff --git a/openfpga_flow/tasks/bram/wide_dpram16k/config/task.conf b/openfpga_flow/tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf similarity index 100% rename from openfpga_flow/tasks/bram/wide_dpram16k/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/bram/wide_dpram16k/config/task.conf diff --git a/openfpga_flow/tasks/depopulate_crossbar/config/task.conf b/openfpga_flow/tasks/fpga_verilog/depopulate_crossbar/config/task.conf similarity index 100% rename from openfpga_flow/tasks/depopulate_crossbar/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/depopulate_crossbar/config/task.conf diff --git a/openfpga_flow/tasks/duplicated_grid_pin/config/task.conf b/openfpga_flow/tasks/fpga_verilog/duplicated_grid_pin/config/task.conf similarity index 100% rename from openfpga_flow/tasks/duplicated_grid_pin/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/duplicated_grid_pin/config/task.conf diff --git a/openfpga_flow/tasks/fabric_chain/adder_chain/config/task.conf b/openfpga_flow/tasks/fpga_verilog/fabric_chain/adder_chain/config/task.conf similarity index 100% rename from openfpga_flow/tasks/fabric_chain/adder_chain/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/fabric_chain/adder_chain/config/task.conf diff --git a/openfpga_flow/tasks/fabric_chain/register_chain/config/task.conf b/openfpga_flow/tasks/fpga_verilog/fabric_chain/register_chain/config/task.conf similarity index 100% rename from openfpga_flow/tasks/fabric_chain/register_chain/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/fabric_chain/register_chain/config/task.conf diff --git a/openfpga_flow/tasks/fabric_chain/scan_chain/config/task.conf b/openfpga_flow/tasks/fpga_verilog/fabric_chain/scan_chain/config/task.conf similarity index 100% rename from openfpga_flow/tasks/fabric_chain/scan_chain/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/fabric_chain/scan_chain/config/task.conf diff --git a/openfpga_flow/tasks/fabric_key/generate_random_key/config/task.conf b/openfpga_flow/tasks/fpga_verilog/fabric_key/generate_random_key/config/task.conf similarity index 100% rename from openfpga_flow/tasks/fabric_key/generate_random_key/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/fabric_key/generate_random_key/config/task.conf diff --git a/openfpga_flow/tasks/fabric_key/generate_vanilla_key/config/task.conf b/openfpga_flow/tasks/fpga_verilog/fabric_key/generate_vanilla_key/config/task.conf similarity index 100% rename from openfpga_flow/tasks/fabric_key/generate_vanilla_key/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/fabric_key/generate_vanilla_key/config/task.conf diff --git a/openfpga_flow/tasks/fabric_key/load_external_key/config/task.conf b/openfpga_flow/tasks/fpga_verilog/fabric_key/load_external_key/config/task.conf similarity index 100% rename from openfpga_flow/tasks/fabric_key/load_external_key/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/fabric_key/load_external_key/config/task.conf diff --git a/openfpga_flow/tasks/ncounter/config/task.conf b/openfpga_flow/tasks/fpga_verilog/fabric_key/load_external_key_cc_fpga/config/task.conf similarity index 57% rename from openfpga_flow/tasks/ncounter/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/fabric_key/load_external_key_cc_fpga/config/task.conf index 75af20185..5931a4a13 100644 --- a/openfpga_flow/tasks/ncounter/config/task.conf +++ b/openfpga_flow/tasks/fpga_verilog/fabric_key/load_external_key_cc_fpga/config/task.conf @@ -16,22 +16,23 @@ timeout_each_job = 20*60 fpga_flow=vpr_blif [OpenFPGA_SHELL] -openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/OpenFPGAShellScripts/mcnc_example_script.openfpga -openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_N10_40nm_openfpga.xml -external_fabric_key_file= +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/OpenFPGAShellScripts/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_key.xml [ARCHITECTURES] -arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_N10_tileable_40nm.xml +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml [BENCHMARKS] -bench0=/var/tmp/AA_SC/ncounter_task/Ncounter.blif +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.blif [SYNTHESIS_PARAM] -bench0_top = Ncounter -bench0_act = /var/tmp/AA_SC/ncounter_task/Ncounter.act -bench0_verilog = /var/tmp/AA_SC/ncounter_task/Ncounter_output_verilog.v +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= +#vpr_fpga_verilog_formal_verification_top_netlist= diff --git a/openfpga_flow/tasks/flatten_routing/config/task.conf b/openfpga_flow/tasks/fpga_verilog/flatten_routing/config/task.conf similarity index 100% rename from openfpga_flow/tasks/flatten_routing/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/flatten_routing/config/task.conf diff --git a/openfpga_flow/tasks/hard_adder/config/task.conf b/openfpga_flow/tasks/fpga_verilog/hard_adder/config/task.conf similarity index 100% rename from openfpga_flow/tasks/hard_adder/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/hard_adder/config/task.conf diff --git a/openfpga_flow/tasks/implicit_verilog/config/task.conf b/openfpga_flow/tasks/fpga_verilog/implicit_verilog/config/task.conf similarity index 100% rename from openfpga_flow/tasks/implicit_verilog/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/implicit_verilog/config/task.conf diff --git a/openfpga_flow/tasks/io/aib/config/task.conf b/openfpga_flow/tasks/fpga_verilog/io/aib/config/task.conf similarity index 100% rename from openfpga_flow/tasks/io/aib/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/io/aib/config/task.conf diff --git a/openfpga_flow/tasks/io/multi_io_capacity/config/task.conf b/openfpga_flow/tasks/fpga_verilog/io/multi_io_capacity/config/task.conf similarity index 100% rename from openfpga_flow/tasks/io/multi_io_capacity/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/io/multi_io_capacity/config/task.conf diff --git a/openfpga_flow/tasks/io/reduced_io/config/task.conf b/openfpga_flow/tasks/fpga_verilog/io/reduced_io/config/task.conf similarity index 100% rename from openfpga_flow/tasks/io/reduced_io/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/io/reduced_io/config/task.conf diff --git a/openfpga_flow/tasks/lut_design/frac_lut/config/task.conf b/openfpga_flow/tasks/fpga_verilog/lut_design/frac_lut/config/task.conf similarity index 100% rename from openfpga_flow/tasks/lut_design/frac_lut/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/lut_design/frac_lut/config/task.conf diff --git a/openfpga_flow/tasks/lut_design/intermediate_buffer/config/task.conf b/openfpga_flow/tasks/fpga_verilog/lut_design/intermediate_buffer/config/task.conf similarity index 100% rename from openfpga_flow/tasks/lut_design/intermediate_buffer/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/lut_design/intermediate_buffer/config/task.conf diff --git a/openfpga_flow/tasks/lut_design/single_mode/config/task.conf b/openfpga_flow/tasks/fpga_verilog/lut_design/single_mode/config/task.conf similarity index 100% rename from openfpga_flow/tasks/lut_design/single_mode/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/lut_design/single_mode/config/task.conf diff --git a/openfpga_flow/tasks/mux_design/local_encoder/config/task.conf b/openfpga_flow/tasks/fpga_verilog/mux_design/local_encoder/config/task.conf similarity index 100% rename from openfpga_flow/tasks/mux_design/local_encoder/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/mux_design/local_encoder/config/task.conf diff --git a/openfpga_flow/tasks/mux_design/stdcell_mux2/config/task.conf b/openfpga_flow/tasks/fpga_verilog/mux_design/stdcell_mux2/config/task.conf similarity index 100% rename from openfpga_flow/tasks/mux_design/stdcell_mux2/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/mux_design/stdcell_mux2/config/task.conf diff --git a/openfpga_flow/tasks/mux_design/tree_structure/config/task.conf b/openfpga_flow/tasks/fpga_verilog/mux_design/tree_structure/config/task.conf similarity index 100% rename from openfpga_flow/tasks/mux_design/tree_structure/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/mux_design/tree_structure/config/task.conf diff --git a/openfpga_flow/tasks/power_gated_design/power_gated_inverter/config/task.conf b/openfpga_flow/tasks/fpga_verilog/power_gated_design/power_gated_inverter/config/task.conf similarity index 100% rename from openfpga_flow/tasks/power_gated_design/power_gated_inverter/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/power_gated_design/power_gated_inverter/config/task.conf diff --git a/openfpga_flow/tasks/spypad/config/task.conf b/openfpga_flow/tasks/fpga_verilog/spypad/config/task.conf similarity index 100% rename from openfpga_flow/tasks/spypad/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/spypad/config/task.conf diff --git a/openfpga_flow/tasks/untileable/config/task.conf b/openfpga_flow/tasks/fpga_verilog/untileable/config/task.conf similarity index 100% rename from openfpga_flow/tasks/untileable/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/untileable/config/task.conf From 9a7364c6e611c18079057bd61815808b21727211 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 27 Jul 2020 19:22:36 -0600 Subject: [PATCH 006/148] bug fix in fabric bitstream XML syntax --- openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp index b4d9cbebd..32d5a209c 100644 --- a/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/write_xml_fabric_bitstream.cpp @@ -96,7 +96,7 @@ int write_fabric_config_bit_to_xml_file(std::fstream& fp, hie_path += std::to_string(find_bitstream_manager_config_bit_index_in_parent_block(bitstream_manager, config_bit)); hie_path += std::string("]"); - fp << " path=\"" << hie_path << "\"/>\n"; + fp << " path=\"" << hie_path << "\">\n"; switch (config_type) { case CONFIG_MEM_STANDALONE: From 5d83abb2cf3b0b00a48c26d961a990f1682d13cb Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 27 Jul 2020 19:37:05 -0600 Subject: [PATCH 007/148] bug fix in read architecture bitstream and regression tests --- openfpga/src/base/openfpga_bitstream.cpp | 2 +- .../micro_benchmark/or2/or2_load_bitstream.blif | 12 ++++++++++++ .../config/task.conf | 6 ++++-- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 openfpga_flow/benchmarks/micro_benchmark/or2/or2_load_bitstream.blif diff --git a/openfpga/src/base/openfpga_bitstream.cpp b/openfpga/src/base/openfpga_bitstream.cpp index 138ee2032..c32e9ae87 100644 --- a/openfpga/src/base/openfpga_bitstream.cpp +++ b/openfpga/src/base/openfpga_bitstream.cpp @@ -38,7 +38,7 @@ int fpga_bitstream(OpenfpgaContext& openfpga_ctx, CommandOptionId opt_read_file = cmd.option("read_file"); if (true == cmd_context.option_enable(cmd, opt_read_file)) { - openfpga_ctx.mutable_bitstream_manager() = read_xml_architecture_bitstream(cmd_context.option_value(cmd, opt_write_file).c_str()); + openfpga_ctx.mutable_bitstream_manager() = read_xml_architecture_bitstream(cmd_context.option_value(cmd, opt_read_file).c_str()); } else { openfpga_ctx.mutable_bitstream_manager() = build_device_bitstream(g_vpr_ctx, openfpga_ctx, diff --git a/openfpga_flow/benchmarks/micro_benchmark/or2/or2_load_bitstream.blif b/openfpga_flow/benchmarks/micro_benchmark/or2/or2_load_bitstream.blif new file mode 100644 index 000000000..fc79555cb --- /dev/null +++ b/openfpga_flow/benchmarks/micro_benchmark/or2/or2_load_bitstream.blif @@ -0,0 +1,12 @@ +# This BLIF is created to test the feature of +# loading external bitstream files. +# Therefore, its module name is and2 rather than or2 +# Please do NOT use this file is regular regression tests +.model and2 +.inputs a b +.outputs c + +.names a b c +00 0 + +.end diff --git a/openfpga_flow/tasks/fpga_bitstream/load_external_architecture_bitstream/config/task.conf b/openfpga_flow/tasks/fpga_bitstream/load_external_architecture_bitstream/config/task.conf index 2d7eb177f..d36f074ae 100644 --- a/openfpga_flow/tasks/fpga_bitstream/load_external_architecture_bitstream/config/task.conf +++ b/openfpga_flow/tasks/fpga_bitstream/load_external_architecture_bitstream/config/task.conf @@ -25,10 +25,12 @@ openfpga_external_arch_bitstream_file=${PATH:OPENFPGA_PATH}/openfpga_flow/arch_b arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml [BENCHMARKS] -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.blif +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2_load_bitstream.blif [SYNTHESIS_PARAM] -bench0_top = or2 +# We use a special BLIF file whose top module name is and2 +# in order to be consistent with the architecture bistream design name +bench0_top = and2 bench0_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.act ######################## # Use a different verilog as reference here From 9809db01c52b0e8e60bec7331714cfd97cbe0bed Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 27 Jul 2020 19:39:14 -0600 Subject: [PATCH 008/148] deploy load_arch_bitstream test case to CI --- .travis/fpga_bitstream_reg_test.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis/fpga_bitstream_reg_test.sh b/.travis/fpga_bitstream_reg_test.sh index e2787f78f..90044ed8e 100755 --- a/.travis/fpga_bitstream_reg_test.sh +++ b/.travis/fpga_bitstream_reg_test.sh @@ -13,4 +13,7 @@ echo -e "FPGA-Bitstream regression tests"; echo -e "Testing bitstream generation only"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/generate_bitstream --debug --show_thread_logs +echo -e "Testing loading architecture bitstream from an external file"; +python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/load_external_architecture_bitstream --debug --show_thread_logs + end_section "OpenFPGA.TaskTun" From a1568075596c3e945fb30d8a1ae3084abe651a2e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 27 Jul 2020 19:47:43 -0600 Subject: [PATCH 009/148] enrich basic regression tests to cover more critical microbenchmarks --- .../full_testbench/configuration_chain/config/task.conf | 8 ++++++++ .../full_testbench/configuration_frame/config/task.conf | 8 ++++++++ .../fast_configuration_chain/config/task.conf | 8 ++++++++ .../fast_configuration_frame/config/task.conf | 8 ++++++++ .../full_testbench/fast_memory_bank/config/task.conf | 8 ++++++++ .../full_testbench/flatten_memory/config/task.conf | 8 ++++++++ .../full_testbench/memory_bank/config/task.conf | 8 ++++++++ .../configuration_chain/config/task.conf | 8 ++++++++ .../configuration_frame/config/task.conf | 8 ++++++++ .../preconfig_testbench/flatten_memory/config/task.conf | 8 ++++++++ .../preconfig_testbench/memory_bank/config/task.conf | 8 ++++++++ 11 files changed, 88 insertions(+) diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf index cb1eb9a5c..f2786dee3 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain/config/task.conf @@ -25,10 +25,18 @@ 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] 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/configuration_frame/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame/config/task.conf index 04342ba43..ded20701d 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_frame/config/task.conf @@ -25,10 +25,18 @@ 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] 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/fast_configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain/config/task.conf index d0ab2bad6..0d98afa11 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_chain/config/task.conf @@ -26,10 +26,18 @@ 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] 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/fast_configuration_frame/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_frame/config/task.conf index 5c1eed83a..3626d3e85 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_frame/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fast_configuration_frame/config/task.conf @@ -25,10 +25,18 @@ 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] 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/fast_memory_bank/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/fast_memory_bank/config/task.conf index bfa88b66e..e669edf34 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/fast_memory_bank/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/fast_memory_bank/config/task.conf @@ -25,10 +25,18 @@ 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] 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/flatten_memory/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/flatten_memory/config/task.conf index 00b210d8f..74cba9434 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/flatten_memory/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/flatten_memory/config/task.conf @@ -25,10 +25,18 @@ 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] 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/memory_bank/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank/config/task.conf index 6e48c7da7..5da25a81e 100644 --- a/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/full_testbench/memory_bank/config/task.conf @@ -25,10 +25,18 @@ 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] 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/configuration_chain/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/configuration_chain/config/task.conf index e6c0caea0..fd40bb331 100644 --- a/openfpga_flow/tasks/basic_tests/preconfig_testbench/configuration_chain/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/preconfig_testbench/configuration_chain/config/task.conf @@ -25,11 +25,19 @@ 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] 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= diff --git a/openfpga_flow/tasks/basic_tests/preconfig_testbench/configuration_frame/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/configuration_frame/config/task.conf index ecf61f537..8a82fbb57 100644 --- a/openfpga_flow/tasks/basic_tests/preconfig_testbench/configuration_frame/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/preconfig_testbench/configuration_frame/config/task.conf @@ -25,11 +25,19 @@ 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] 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= diff --git a/openfpga_flow/tasks/basic_tests/preconfig_testbench/flatten_memory/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/flatten_memory/config/task.conf index 5ce4abf20..513ac66d4 100644 --- a/openfpga_flow/tasks/basic_tests/preconfig_testbench/flatten_memory/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/preconfig_testbench/flatten_memory/config/task.conf @@ -25,11 +25,19 @@ 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] 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= diff --git a/openfpga_flow/tasks/basic_tests/preconfig_testbench/memory_bank/config/task.conf b/openfpga_flow/tasks/basic_tests/preconfig_testbench/memory_bank/config/task.conf index a99aa4e24..a8cfe8b50 100644 --- a/openfpga_flow/tasks/basic_tests/preconfig_testbench/memory_bank/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/preconfig_testbench/memory_bank/config/task.conf @@ -25,11 +25,19 @@ 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] 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 534c609e17dce1d07539b9e7729e444d7fd79814 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 28 Jul 2020 11:51:50 -0600 Subject: [PATCH 010/148] add fixed layouts to a flagship architecture to test bitstream generation runtime --- ...adder_register_scan_chain_depop50_40nm.xml | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_register_scan_chain_depop50_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_register_scan_chain_depop50_40nm.xml index 174649d93..35079cc2d 100755 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_register_scan_chain_depop50_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_register_scan_chain_depop50_40nm.xml @@ -143,18 +143,34 @@ --> - - - + + + + + + + + + + + + + + + + From f33422d4d730d08a020b57e6c181f93b15593461 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 28 Jul 2020 12:38:42 -0600 Subject: [PATCH 011/148] add regression test to track runtime on big fpga devices using practical benchmarks --- .travis/fpga_bitstream_reg_test.sh | 2 +- ...tstream_fix_device_example_script.openfpga | 51 +++++++++++++++++++ .../RISC_posedge_clk/{ => rtl}/ALU.v | 0 .../RISC_posedge_clk/{ => rtl}/Controller.v | 0 .../RISC_posedge_clk/{ => rtl}/IR.v | 0 .../RISC_posedge_clk/{ => rtl}/Memory.v | 0 .../RISC_posedge_clk/{ => rtl}/Mux_31.v | 0 .../RISC_posedge_clk/{ => rtl}/Mux_51.v | 0 .../RISC_posedge_clk/{ => rtl}/PC.v | 0 .../{ => rtl}/RISC_core_mem_top.v | 0 .../{ => rtl}/RISC_core_top.v | 0 .../RISC_posedge_clk/{ => rtl}/Reg_1bit.v | 0 .../RISC_posedge_clk/{ => rtl}/Reg_8bit.v | 0 .../{ => testbench}/RISC_testbench.v | 0 .../micro_benchmark/SAPone/{ => rtl}/ACC.v | 0 .../micro_benchmark/SAPone/{ => rtl}/ADDSUB.v | 0 .../SAPone/{ => rtl}/BRegister.v | 0 .../SAPone/{ => rtl}/Controller.v | 0 .../micro_benchmark/SAPone/{ => rtl}/IR.v | 0 .../micro_benchmark/SAPone/{ => rtl}/MAR.v | 0 .../SAPone/{ => rtl}/OutputRegister.v | 0 .../micro_benchmark/SAPone/{ => rtl}/PC.v | 0 .../micro_benchmark/SAPone/{ => rtl}/ROM.v | 0 .../micro_benchmark/SAPone/{ => rtl}/SAPone.v | 0 .../SAPone/{ => testbench}/testSAPone.v | 0 .../device_48x48/config/task.conf | 35 +++++++++++++ .../device_96x96/config/task.conf | 35 +++++++++++++ .../{ => device_auto}/config/task.conf | 0 28 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 openfpga_flow/OpenFPGAShellScripts/generate_bitstream_fix_device_example_script.openfpga rename openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/{ => rtl}/ALU.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/{ => rtl}/Controller.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/{ => rtl}/IR.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/{ => rtl}/Memory.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/{ => rtl}/Mux_31.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/{ => rtl}/Mux_51.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/{ => rtl}/PC.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/{ => rtl}/RISC_core_mem_top.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/{ => rtl}/RISC_core_top.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/{ => rtl}/Reg_1bit.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/{ => rtl}/Reg_8bit.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/{ => testbench}/RISC_testbench.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/SAPone/{ => rtl}/ACC.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/SAPone/{ => rtl}/ADDSUB.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/SAPone/{ => rtl}/BRegister.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/SAPone/{ => rtl}/Controller.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/SAPone/{ => rtl}/IR.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/SAPone/{ => rtl}/MAR.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/SAPone/{ => rtl}/OutputRegister.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/SAPone/{ => rtl}/PC.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/SAPone/{ => rtl}/ROM.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/SAPone/{ => rtl}/SAPone.v (100%) rename openfpga_flow/benchmarks/micro_benchmark/SAPone/{ => testbench}/testSAPone.v (100%) create mode 100644 openfpga_flow/tasks/fpga_bitstream/generate_bitstream/device_48x48/config/task.conf create mode 100644 openfpga_flow/tasks/fpga_bitstream/generate_bitstream/device_96x96/config/task.conf rename openfpga_flow/tasks/fpga_bitstream/generate_bitstream/{ => device_auto}/config/task.conf (100%) diff --git a/.travis/fpga_bitstream_reg_test.sh b/.travis/fpga_bitstream_reg_test.sh index 90044ed8e..dcce57081 100755 --- a/.travis/fpga_bitstream_reg_test.sh +++ b/.travis/fpga_bitstream_reg_test.sh @@ -11,7 +11,7 @@ cd ${TRAVIS_BUILD_DIR} echo -e "FPGA-Bitstream regression tests"; echo -e "Testing bitstream generation only"; -python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/generate_bitstream --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/generate_bitstream/device_auto --debug --show_thread_logs echo -e "Testing loading architecture bitstream from an external file"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/load_external_architecture_bitstream --debug --show_thread_logs diff --git a/openfpga_flow/OpenFPGAShellScripts/generate_bitstream_fix_device_example_script.openfpga b/openfpga_flow/OpenFPGAShellScripts/generate_bitstream_fix_device_example_script.openfpga new file mode 100644 index 000000000..9558a5944 --- /dev/null +++ b/openfpga_flow/OpenFPGAShellScripts/generate_bitstream_fix_device_example_script.openfpga @@ -0,0 +1,51 @@ +# Run VPR for the 'and' design +#--write_rr_graph example_rr_graph.xml +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route --route_chan_width ${OPENFPGA_VPR_ROUTE_CHAN_WIDTH} --device ${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 clustering nets based on routing results +pb_pin_fixup --verbose + +# 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 +# - Skip outputting 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 diff --git a/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/ALU.v b/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/ALU.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/ALU.v rename to openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/ALU.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/Controller.v b/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/Controller.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/Controller.v rename to openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/Controller.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/IR.v b/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/IR.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/IR.v rename to openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/IR.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/Memory.v b/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/Memory.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/Memory.v rename to openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/Memory.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/Mux_31.v b/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/Mux_31.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/Mux_31.v rename to openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/Mux_31.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/Mux_51.v b/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/Mux_51.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/Mux_51.v rename to openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/Mux_51.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/PC.v b/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/PC.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/PC.v rename to openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/PC.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/RISC_core_mem_top.v b/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/RISC_core_mem_top.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/RISC_core_mem_top.v rename to openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/RISC_core_mem_top.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/RISC_core_top.v b/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/RISC_core_top.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/RISC_core_top.v rename to openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/RISC_core_top.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/Reg_1bit.v b/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/Reg_1bit.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/Reg_1bit.v rename to openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/Reg_1bit.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/Reg_8bit.v b/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/Reg_8bit.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/Reg_8bit.v rename to openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/Reg_8bit.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/RISC_testbench.v b/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/testbench/RISC_testbench.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/RISC_testbench.v rename to openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/testbench/RISC_testbench.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/SAPone/ACC.v b/openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/ACC.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/SAPone/ACC.v rename to openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/ACC.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/SAPone/ADDSUB.v b/openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/ADDSUB.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/SAPone/ADDSUB.v rename to openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/ADDSUB.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/SAPone/BRegister.v b/openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/BRegister.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/SAPone/BRegister.v rename to openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/BRegister.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/SAPone/Controller.v b/openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/Controller.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/SAPone/Controller.v rename to openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/Controller.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/SAPone/IR.v b/openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/IR.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/SAPone/IR.v rename to openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/IR.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/SAPone/MAR.v b/openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/MAR.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/SAPone/MAR.v rename to openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/MAR.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/SAPone/OutputRegister.v b/openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/OutputRegister.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/SAPone/OutputRegister.v rename to openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/OutputRegister.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/SAPone/PC.v b/openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/PC.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/SAPone/PC.v rename to openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/PC.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/SAPone/ROM.v b/openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/ROM.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/SAPone/ROM.v rename to openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/ROM.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/SAPone/SAPone.v b/openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/SAPone.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/SAPone/SAPone.v rename to openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/SAPone.v diff --git a/openfpga_flow/benchmarks/micro_benchmark/SAPone/testSAPone.v b/openfpga_flow/benchmarks/micro_benchmark/SAPone/testbench/testSAPone.v similarity index 100% rename from openfpga_flow/benchmarks/micro_benchmark/SAPone/testSAPone.v rename to openfpga_flow/benchmarks/micro_benchmark/SAPone/testbench/testSAPone.v diff --git a/openfpga_flow/tasks/fpga_bitstream/generate_bitstream/device_48x48/config/task.conf b/openfpga_flow/tasks/fpga_bitstream/generate_bitstream/device_48x48/config/task.conf new file mode 100644 index 000000000..911239184 --- /dev/null +++ b/openfpga_flow/tasks/fpga_bitstream/generate_bitstream/device_48x48/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 +# Runtime of this bitstream generation should not exceed 3 minutes as a QoR requirement +timeout_each_job = 3*60 +fpga_flow=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/OpenFPGAShellScripts/generate_bitstream_fix_device_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_40nm_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_route_chan_width=50 +openfpga_vpr_device_layout=48x48 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_register_scan_chain_depop50_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/SAPone/rtl/* + +[SYNTHESIS_PARAM] +bench0_top = SAPone + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] diff --git a/openfpga_flow/tasks/fpga_bitstream/generate_bitstream/device_96x96/config/task.conf b/openfpga_flow/tasks/fpga_bitstream/generate_bitstream/device_96x96/config/task.conf new file mode 100644 index 000000000..97dcc465b --- /dev/null +++ b/openfpga_flow/tasks/fpga_bitstream/generate_bitstream/device_96x96/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 +# Runtime of this bitstream generation should not exceed 6 minutes as a QoR requirement +timeout_each_job = 6*60 +fpga_flow=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/OpenFPGAShellScripts/generate_bitstream_fix_device_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_40nm_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_route_chan_width=100 +openfpga_vpr_device_layout=96x96 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_register_scan_chain_depop50_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/RISC_posedge_clk/rtl/*.v + +[SYNTHESIS_PARAM] +bench0_top = RISC_core_top + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] diff --git a/openfpga_flow/tasks/fpga_bitstream/generate_bitstream/config/task.conf b/openfpga_flow/tasks/fpga_bitstream/generate_bitstream/device_auto/config/task.conf similarity index 100% rename from openfpga_flow/tasks/fpga_bitstream/generate_bitstream/config/task.conf rename to openfpga_flow/tasks/fpga_bitstream/generate_bitstream/device_auto/config/task.conf From 1e53d79c570b35d16b0a8ca093d37cd89dcbff5e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 28 Jul 2020 12:41:58 -0600 Subject: [PATCH 012/148] deploy large bitstream regression tests to CI --- .travis/fpga_bitstream_reg_test.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.travis/fpga_bitstream_reg_test.sh b/.travis/fpga_bitstream_reg_test.sh index dcce57081..8b9b6c0f4 100755 --- a/.travis/fpga_bitstream_reg_test.sh +++ b/.travis/fpga_bitstream_reg_test.sh @@ -10,9 +10,16 @@ cd ${TRAVIS_BUILD_DIR} ############################################## echo -e "FPGA-Bitstream regression tests"; -echo -e "Testing bitstream generation only"; +echo -e "Testing bitstream generation for an auto-sized device"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/generate_bitstream/device_auto --debug --show_thread_logs + +echo -e "Testing bitstream generation for an 48x48 FPGA device"; +python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/generate_bitstream/device_48x48 --debug --show_thread_logs + +echo -e "Testing bitstream generation for an 96x96 FPGA device"; +python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/generate_bitstream/device_96x96 --debug --show_thread_logs + echo -e "Testing loading architecture bitstream from an external file"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_bitstream/load_external_architecture_bitstream --debug --show_thread_logs From cadf29022ebea8dcbb97d659ba133a95d5aeb6a0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 28 Jul 2020 13:44:06 -0600 Subject: [PATCH 013/148] add README to explain the organization of regression tests --- openfpga_flow/tasks/README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 openfpga_flow/tasks/README.md diff --git a/openfpga_flow/tasks/README.md b/openfpga_flow/tasks/README.md new file mode 100644 index 000000000..930a2d4dd --- /dev/null +++ b/openfpga_flow/tasks/README.md @@ -0,0 +1,23 @@ +# Regression tests for OpenFPGA +The regression tests are grouped in category of OpenFPGA tools as well as integrated flows. +The principle is that each OpenFPGA tool should have a set of regression tests. + +- compilation\_verfication: a quicktest after compilation + +- Basic regression tests should focus on fundamental flow integration, such as + + - Yosys + VPR + OpenFPGA for a Verilog-to-Verification flow-run + +- FPGA-Verilog regression tests should focus on testing fabric correctness, such as + + - VPR + OpenFPGA integration for a BLIF-to-Verification flow-run + + +- FPGA-Bitstream regression tests should focus on testing bitstream correctness and runtime on large devices and benchmark suites + +- FPGA-SDC regression test should focus on SDC file generation and necessary syntax check + +- FPGA-SPICE regression test should focus on SPICE netlist generation / compilation and SPICE simulations qwith QoR checks. + + +Please keep this README up-to-date on the OpenFPGA tools From 1ca28298687c4f74c697f7cecf188de45706651a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 17 Aug 2020 13:54:26 -0600 Subject: [PATCH 014/148] update readme for vpr architecture naming --- openfpga_flow/vpr_arch/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openfpga_flow/vpr_arch/README.md b/openfpga_flow/vpr_arch/README.md index e56ccca4b..aad2c1421 100644 --- a/openfpga_flow/vpr_arch/README.md +++ b/openfpga_flow/vpr_arch/README.md @@ -1,17 +1,17 @@ # Naming convention for VPR architecture files Please reveal the following architecture features in the names to help quickly spot architecture files. -- k: Look-Up Table (LUT) size of FPGA. If you have fracturable LUTs or multiple LUT circuits, this should be largest input size. -- frac: If fracturable LUT is used or not. +- k\_: Look-Up Table (LUT) size of FPGA. If you have fracturable LUTs or multiple LUT circuits, this should be largest input size. The keyword 'frac' is to specify if fracturable LUT is used or not. - N: Number of logic elements for a CLB. If you have multiple CLB architectures, this should be largest number. - tileable: If the routing architecture is tileable or not. - adder\_chain: If hard adder/carry chain is used inside CLBs - register\_chain: If shift register chain is used inside CLBs - scan\_chain: If scan chain testing infrastructure is used inside CLBs -- \_mem: If block RAM (BRAM) is used or not. If used, the memory size should be clarified here. The keyword wide is to specify if the BRAM spanns more than 1 column. +- \_\_mem: If block RAM (BRAM) is used or not. If used, the memory size should be clarified here. The keyword 'wide' is to specify if the BRAM spans more than 1 column. The keyword 'frac' is to specify if the BRAM is fracturable to operate in different modes. +- \_\_dsp: If Digital Signal Processor (DSP) is used or not. If used, the input size should be clarified here. The keyword 'wide' is to specify if the DSP spans more than 1 column. The keyword 'frac' is to specify if the DSP is fracturable to operate in different modes. - aib: If the Advanced Interface Bus (AIB) is used in place of some I/Os. - 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 -- : The technology node which the delay numbers are extracted from. +- : The technology node which the delay numbers are extracted from. Other features are used in naming should be listed here. From cfd035bf8f3a0c65634c02ffeff4373ef8012656 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 17 Aug 2020 14:33:51 -0600 Subject: [PATCH 015/148] update tutorials about the verilog-to-verification --- docs/source/tutorials/design_flow/index.rst | 4 ++-- .../design_flow/{verilog_to_gds2.rst => verilog2gds2.rst} | 0 .../{blif_to_verification.rst => verilog2verification.rst} | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) rename docs/source/tutorials/design_flow/{verilog_to_gds2.rst => verilog2gds2.rst} (100%) rename docs/source/tutorials/design_flow/{blif_to_verification.rst => verilog2verification.rst} (98%) diff --git a/docs/source/tutorials/design_flow/index.rst b/docs/source/tutorials/design_flow/index.rst index e854a08c6..82a9ed3e4 100644 --- a/docs/source/tutorials/design_flow/index.rst +++ b/docs/source/tutorials/design_flow/index.rst @@ -7,6 +7,6 @@ Design Flows .. toctree:: :maxdepth: 2 - blif_to_verification + verilog2verification - verilog_to_gds2 + verilog2gds2 diff --git a/docs/source/tutorials/design_flow/verilog_to_gds2.rst b/docs/source/tutorials/design_flow/verilog2gds2.rst similarity index 100% rename from docs/source/tutorials/design_flow/verilog_to_gds2.rst rename to docs/source/tutorials/design_flow/verilog2gds2.rst diff --git a/docs/source/tutorials/design_flow/blif_to_verification.rst b/docs/source/tutorials/design_flow/verilog2verification.rst similarity index 98% rename from docs/source/tutorials/design_flow/blif_to_verification.rst rename to docs/source/tutorials/design_flow/verilog2verification.rst index 26a0b87cf..505aa39c7 100644 --- a/docs/source/tutorials/design_flow/blif_to_verification.rst +++ b/docs/source/tutorials/design_flow/verilog2verification.rst @@ -1,7 +1,7 @@ -.. _from_blif_to_verification: +.. _from_verilog_to_verification: -From BLIF to Verification -------------------------- +From Verilog to Verification +---------------------------- This tutorial will show an example how to - generate Verilog netlists for a FPGA fabric From f833e0ec66df540ea108108dfb9e1e17b34e453b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 17 Aug 2020 17:49:51 -0600 Subject: [PATCH 016/148] add a flagship architecture using fracturable memory and dsp --- ...n_frac_mem32K_frac_dsp36_40nm_openfpga.xml | 451 +++++ ...dder_chain_frac_mem32K_frac_dsp36_40nm.xml | 1578 +++++++++++++++++ 2 files changed, 2029 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml create mode 100644 openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml new file mode 100644 index 000000000..d47e396ee --- /dev/null +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml @@ -0,0 +1,451 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml new file mode 100644 index 000000000..6acc37ba2 --- /dev/null +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml @@ -0,0 +1,1578 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + + + + + + + + + + + + + + + + + + + clb.clk + clb.cin + clb.O[9:0] clb.I[19:0] + clb.cout clb.O[19:10] clb.I[39:20] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 195e-12 + 195e-12 + 195e-12 + 195e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2712c354a9ea80e194932c53341a54c221d480ec Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 18 Aug 2020 12:38:56 -0600 Subject: [PATCH 017/148] now physical pb_port binding support multiple ports --- .../src/pb_type_annotation.cpp | 63 ++++++------ .../libarchopenfpga/src/pb_type_annotation.h | 13 ++- .../src/read_xml_pb_type_annotation.cpp | 38 +++++++- .../src/write_xml_pb_type_annotation.cpp | 21 +++- .../libopenfpgautil/src/openfpga_port.cpp | 10 ++ .../libopenfpgautil/src/openfpga_port.h | 1 + openfpga/src/annotation/annotate_pb_graph.cpp | 58 +++++------ openfpga/src/annotation/annotate_pb_types.cpp | 50 +++++----- .../annotation/check_pb_type_annotation.cpp | 2 +- .../src/annotation/vpr_device_annotation.cpp | 97 +++++++++++-------- .../src/annotation/vpr_device_annotation.h | 30 +++--- 11 files changed, 239 insertions(+), 144 deletions(-) diff --git a/libopenfpga/libarchopenfpga/src/pb_type_annotation.cpp b/libopenfpga/libarchopenfpga/src/pb_type_annotation.cpp index b38796c9c..133ee000c 100644 --- a/libopenfpga/libarchopenfpga/src/pb_type_annotation.cpp +++ b/libopenfpga/libarchopenfpga/src/pb_type_annotation.cpp @@ -1,6 +1,7 @@ /************************************************************************ * Member functions for class PbTypeAnnotation ***********************************************************************/ +#include #include "vtr_log.h" #include "vtr_assert.h" #include "pb_type_annotation.h" @@ -85,24 +86,15 @@ std::vector PbTypeAnnotation::port_names() const { return keys; } -BasicPort PbTypeAnnotation::physical_pb_type_port(const std::string& port_name) const { - std::map::const_iterator it = operating_pb_type_ports_.find(port_name); +std::map PbTypeAnnotation::physical_pb_type_port(const std::string& port_name) const { + std::map>::const_iterator it = operating_pb_type_ports_.find(port_name); if (it == operating_pb_type_ports_.end()) { /* Return an empty port */ - return BasicPort(); + return std::map(); } return operating_pb_type_ports_.at(port_name); } -int PbTypeAnnotation::physical_pin_rotate_offset(const std::string& port_name) const { - std::map::const_iterator it = physical_pin_rotate_offsets_.find(port_name); - if (it == physical_pin_rotate_offsets_.end()) { - /* Return a zero offset which is default */ - return 0; - } - return physical_pin_rotate_offsets_.at(port_name); -} - std::vector PbTypeAnnotation::interconnect_names() const { std::vector keys; for (auto const& element : interconnect_circuit_model_names_) { @@ -177,27 +169,42 @@ void PbTypeAnnotation::set_physical_pb_type_index_offset(const int& value) { void PbTypeAnnotation::add_pb_type_port_pair(const std::string& operating_pb_port_name, const BasicPort& physical_pb_port) { /* Give a warning if the operating_pb_port_name already exist */ - std::map::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); - /* Give a warning if the interconnection name already exist */ - if (it != operating_pb_type_ports_.end()) { - VTR_LOG_WARN("Redefine operating pb type port '%s' with physical pb type port '%s'\n", - operating_pb_port_name.c_str(), physical_pb_port.get_name().c_str()); - } + std::map>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); - operating_pb_type_ports_[operating_pb_port_name] = physical_pb_port; -} - -void PbTypeAnnotation::set_physical_pin_rotate_offset(const std::string& operating_pb_port_name, - const int& physical_pin_rotate_offset) { - std::map::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); - /* Give a warning if the interconnection name already exist */ + /* If not exist, initialize and set a default value */ if (it == operating_pb_type_ports_.end()) { - VTR_LOG_ERROR("Operating pb type port '%s' does not exist! Ignore physical pin rotate offset '%d'\n", - operating_pb_port_name.c_str(), physical_pin_rotate_offset); + operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = 0; + /* We can return early */ return; } - physical_pin_rotate_offsets_[operating_pb_port_name] = physical_pin_rotate_offset; + /* If the physical port is not in the list, we create one and set a default value */ + if (0 == operating_pb_type_ports_[operating_pb_port_name].count(physical_pb_port)) { + operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = 0; + } +} + +void PbTypeAnnotation::set_physical_pin_rotate_offset(const std::string& operating_pb_port_name, + const BasicPort& physical_pb_port, + const int& physical_pin_rotate_offset) { + std::map>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); + + if (it == operating_pb_type_ports_.end()) { + VTR_LOG_ERROR("The operating pb_type port '%s' is not valid!\n", + operating_pb_port_name.c_str()); + exit(1); + } + + if (operating_pb_type_ports_[operating_pb_port_name].end() == operating_pb_type_ports_[operating_pb_port_name].find(physical_pb_port)) { + VTR_LOG_ERROR("The physical pb_type port '%s[%lu:%lu]' definition for operating pb_type port '%s' is not valid!\n", + physical_pb_port.get_name().c_str(), + physical_pb_port.get_lsb(), + physical_pb_port.get_msb(), + operating_pb_port_name.c_str()); + exit(1); + } + + operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = physical_pin_rotate_offset; } void PbTypeAnnotation::add_interconnect_circuit_model_pair(const std::string& interc_name, diff --git a/libopenfpga/libarchopenfpga/src/pb_type_annotation.h b/libopenfpga/libarchopenfpga/src/pb_type_annotation.h index e80acc3dc..4e1d4e8a2 100644 --- a/libopenfpga/libarchopenfpga/src/pb_type_annotation.h +++ b/libopenfpga/libarchopenfpga/src/pb_type_annotation.h @@ -48,8 +48,7 @@ class PbTypeAnnotation { float physical_pb_type_index_factor() const; int physical_pb_type_index_offset() const; std::vector port_names() const; - BasicPort physical_pb_type_port(const std::string& port_name) const; - int physical_pin_rotate_offset(const std::string& port_name) const; + std::map physical_pb_type_port(const std::string& port_name) const; std::vector interconnect_names() const; std::string interconnect_circuit_model_name(const std::string& interc_name) const; public: /* Public mutators */ @@ -68,6 +67,7 @@ class PbTypeAnnotation { void add_pb_type_port_pair(const std::string& operating_pb_port_name, const BasicPort& physical_pb_port); void set_physical_pin_rotate_offset(const std::string& operating_pb_port_name, + const BasicPort& physical_pb_port, const int& physical_pin_rotate_offset); void add_interconnect_circuit_model_pair(const std::string& interc_name, const std::string& circuit_model_name); @@ -133,10 +133,9 @@ class PbTypeAnnotation { */ int physical_pb_type_index_offset_; - /* Link from the pins under an operating pb_type to physical pb_type */ - std::map operating_pb_type_ports_; - - /* The offset aims to align the pin indices for port of pb_type + /* Link from the pins under an operating pb_type to pairs of + * its physical pb_type and its pin rotating offset + * The offset aims to align the pin indices for port of pb_type * between operating and physical modes, especially when an operating * mode contains multiple pb_type (num_pb>1) that are linked to * the same physical pb_type. @@ -148,7 +147,7 @@ class PbTypeAnnotation { * operating pb_type adder[0].pin[0] with a full path clb.fle[arith].adder[0] * to physical pb_type adder[0].pin[1] with a full path clb.fle[physical].adder[0] */ - std::map physical_pin_rotate_offsets_; + std::map> operating_pb_type_ports_; /* Link between the interconnects under this pb_type and circuit model names */ std::map interconnect_circuit_model_names_; diff --git a/libopenfpga/libarchopenfpga/src/read_xml_pb_type_annotation.cpp b/libopenfpga/libarchopenfpga/src/read_xml_pb_type_annotation.cpp index 743e23741..a4d25e1cf 100644 --- a/libopenfpga/libarchopenfpga/src/read_xml_pb_type_annotation.cpp +++ b/libopenfpga/libarchopenfpga/src/read_xml_pb_type_annotation.cpp @@ -55,12 +55,40 @@ void read_xml_pb_port_annotation(pugi::xml_node& xml_port, const std::string& name_attr = get_attribute(xml_port, "name", loc_data).as_string(); const std::string& physical_mode_port_attr = get_attribute(xml_port, "physical_mode_port", loc_data).as_string(); - /* Parse the mode port using openfpga port parser */ - openfpga::PortParser port_parser(physical_mode_port_attr); - pb_type_annotation.add_pb_type_port_pair(name_attr, port_parser.port()); + /* Split the physical mode port attributes with space */ + openfpga::StringToken port_tokenizer(physical_mode_port_attr); + const std::vector physical_mode_ports = port_tokenizer.split(); - /* We have an optional attribute: physical_mode_pin_rotate_offset */ - pb_type_annotation.set_physical_pin_rotate_offset(name_attr, get_attribute(xml_port, "physical_mode_pin_rotate_offset", loc_data, pugiutil::ReqOpt::OPTIONAL).as_int(0)); + /* Parse the mode port using openfpga port parser */ + for (const auto& physical_mode_port : physical_mode_ports) { + openfpga::PortParser port_parser(physical_mode_port); + pb_type_annotation.add_pb_type_port_pair(name_attr, port_parser.port()); + } + + /* We have an optional attribute: physical_mode_pin_rotate_offset + * Split based on the number of physical pb_type ports that have been defined + */ + const std::string& physical_pin_rotate_offset_attr = get_attribute(xml_port, "physical_mode_pin_rotate_offset", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string(); + + if (false == physical_pin_rotate_offset_attr.empty()) { + /* Split the physical mode port attributes with space */ + openfpga::StringToken offset_tokenizer(physical_pin_rotate_offset_attr); + const std::vector rotate_offsets = offset_tokenizer.split(); + + /* Error out if the offset does not match the port definition */ + if (physical_mode_ports.size() != rotate_offsets.size()) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_port), + "Defined %lu physical mode ports but only %lu physical pin rotate offset are defined! Expect size matching.\n", + physical_mode_ports.size(), rotate_offsets.size()); + } + + for (size_t iport = 0; iport < physical_mode_ports.size(); ++iport) { + openfpga::PortParser port_parser(physical_mode_ports[iport]); + pb_type_annotation.set_physical_pin_rotate_offset(name_attr, + port_parser.port(), + std::stoi(rotate_offsets[iport])); + } + } } /******************************************************************** diff --git a/libopenfpga/libarchopenfpga/src/write_xml_pb_type_annotation.cpp b/libopenfpga/libarchopenfpga/src/write_xml_pb_type_annotation.cpp index 531e117f1..b013e5801 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_pb_type_annotation.cpp +++ b/libopenfpga/libarchopenfpga/src/write_xml_pb_type_annotation.cpp @@ -116,8 +116,25 @@ void write_xml_pb_port_annotation(std::fstream& fp, fp << "\t\t\t" << "" << "\n"; } diff --git a/libopenfpga/libopenfpgautil/src/openfpga_port.cpp b/libopenfpga/libopenfpgautil/src/openfpga_port.cpp index 7e7cc4007..023af7618 100644 --- a/libopenfpga/libopenfpgautil/src/openfpga_port.cpp +++ b/libopenfpga/libopenfpgautil/src/openfpga_port.cpp @@ -124,6 +124,16 @@ bool BasicPort::operator== (const BasicPort& portA) const { return false; } +bool BasicPort::operator< (const BasicPort& portA) const { + if ( (0 == this->get_name().compare(portA.get_name())) + && (this->get_lsb() < portA.get_lsb()) + && (this->get_msb() < portA.get_msb()) ) { + return true; + } + return false; +} + + /************************************************************************ * Mutators ***********************************************************************/ diff --git a/libopenfpga/libopenfpgautil/src/openfpga_port.h b/libopenfpga/libopenfpgautil/src/openfpga_port.h index d870e94fc..ca1afb4a5 100644 --- a/libopenfpga/libopenfpgautil/src/openfpga_port.h +++ b/libopenfpga/libopenfpgautil/src/openfpga_port.h @@ -21,6 +21,7 @@ class BasicPort { BasicPort(const BasicPort& basic_port); /* Copy constructor */ public: /* Overloaded operators */ bool operator== (const BasicPort& portA) const; + bool operator< (const BasicPort& portA) const; public: /* Accessors */ size_t get_width() const; /* get the port width */ size_t get_msb() const; /* get the LSB */ diff --git a/openfpga/src/annotation/annotate_pb_graph.cpp b/openfpga/src/annotation/annotate_pb_graph.cpp index 081acf900..93a154222 100644 --- a/openfpga/src/annotation/annotate_pb_graph.cpp +++ b/openfpga/src/annotation/annotate_pb_graph.cpp @@ -306,35 +306,37 @@ bool try_match_pb_graph_pin(t_pb_graph_pin* operating_pb_graph_pin, t_pb_graph_pin* physical_pb_graph_pin, const VprDeviceAnnotation& vpr_device_annotation) { /* If the parent ports of the two pins are not paired, fail */ - if (physical_pb_graph_pin->port != vpr_device_annotation.physical_pb_port(operating_pb_graph_pin->port)) { - return false; - } - /* Check the pin number of physical pb_graph_pin matches the pin number of - * operating pb_graph_pin plus a rotation offset - * operating port physical port - * LSB port_range.lsb() pin_number pin_number MSB - * | | | - * Operating port | | +------ | - * | |<----offset--->| - * Physical port | + + + - * - * Note: - * - accumulated offset is NOT the pin rotate offset specified by users - * It is an aggregation of the offset during pin pairing - * Each time, we manage to pair two pins, the accumulated offset will be incremented - * by the pin rotate offset value - * The accumulated offset will be reset to 0 when it exceeds the msb() of the physical port - */ - int acc_offset = vpr_device_annotation.physical_pb_pin_offset(operating_pb_graph_pin->port); - const BasicPort& physical_port_range = vpr_device_annotation.physical_pb_port_range(operating_pb_graph_pin->port); - if (physical_pb_graph_pin->pin_number != operating_pb_graph_pin->pin_number - + (int)physical_port_range.get_lsb() - + acc_offset) { - return false; - } + for (t_port* candidate_port : vpr_device_annotation.physical_pb_port(operating_pb_graph_pin->port)) { + if (physical_pb_graph_pin->port != candidate_port) { + return false; + } + /* Check the pin number of physical pb_graph_pin matches the pin number of + * operating pb_graph_pin plus a rotation offset + * operating port physical port + * LSB port_range.lsb() pin_number pin_number MSB + * | | | + * Operating port | | +------ | + * | |<----offset--->| + * Physical port | + + + + * + * Note: + * - accumulated offset is NOT the pin rotate offset specified by users + * It is an aggregation of the offset during pin pairing + * Each time, we manage to pair two pins, the accumulated offset will be incremented + * by the pin rotate offset value + * The accumulated offset will be reset to 0 when it exceeds the msb() of the physical port + */ + int acc_offset = vpr_device_annotation.physical_pb_pin_offset(operating_pb_graph_pin->port, candidate_port); + const BasicPort& physical_port_range = vpr_device_annotation.physical_pb_port_range(operating_pb_graph_pin->port, candidate_port); + if (physical_pb_graph_pin->pin_number != operating_pb_graph_pin->pin_number + + (int)physical_port_range.get_lsb() + + acc_offset) { + return false; + } - /* Reach here, it means all the requirements have been met */ - return true; + /* Reach here, it means all the requirements have been met */ + return true; + } } /******************************************************************** diff --git a/openfpga/src/annotation/annotate_pb_types.cpp b/openfpga/src/annotation/annotate_pb_types.cpp index d8701be78..cb6c9e9b3 100644 --- a/openfpga/src/annotation/annotate_pb_types.cpp +++ b/openfpga/src/annotation/annotate_pb_types.cpp @@ -211,32 +211,38 @@ bool pair_operating_and_physical_pb_types(t_pb_type* operating_pb_type, * if not found, we assume that the physical port is the same as the operating pb_port */ for (t_port* operating_pb_port : pb_type_ports(operating_pb_type)) { - /* Try to find the port in the pb_type_annotation */ - BasicPort expected_physical_pb_port = pb_type_annotation.physical_pb_type_port(std::string(operating_pb_port->name)); - if (true == expected_physical_pb_port.get_name().empty()) { - /* Not found, we reset the port information to be consistent as the operating pb_port */ + std::map expected_physical_pb_ports = pb_type_annotation.physical_pb_type_port(std::string(operating_pb_port->name)); + + /* If not defined in the annotation, set the default pair: + * rotate_offset is 0 by default! + */ + if (true == expected_physical_pb_ports.empty()) { + BasicPort expected_physical_pb_port; expected_physical_pb_port.set_name(std::string(operating_pb_port->name)); expected_physical_pb_port.set_width(operating_pb_port->num_pins); + expected_physical_pb_ports[expected_physical_pb_port] = 0; } - /* Try to find the expected port in the physical pb_type */ - t_port* physical_pb_port = find_pb_type_port(physical_pb_type, expected_physical_pb_port.get_name()); - /* Not found, mapping fails */ - if (nullptr == physical_pb_port) { - return false; + for (const auto& expected_physical_pb_port : expected_physical_pb_ports) { + /* Try to find the expected port in the physical pb_type */ + t_port* physical_pb_port = find_pb_type_port(physical_pb_type, expected_physical_pb_port.first.get_name()); + /* Not found, mapping fails */ + if (nullptr == physical_pb_port) { + return false; + } + /* If the port range does not match, mapping fails */ + if (false == BasicPort(physical_pb_port->name, physical_pb_port->num_pins).contained(expected_physical_pb_port.first)) { + return false; + } + /* Now, port mapping should succeed, we update the vpr_device_annotation + * - port binding + * - port range + * - port pin rotate offset + */ + vpr_device_annotation.add_physical_pb_port(operating_pb_port, physical_pb_port); + vpr_device_annotation.add_physical_pb_port_range(operating_pb_port, physical_pb_port, expected_physical_pb_port.first); + vpr_device_annotation.add_physical_pb_pin_rotate_offset(operating_pb_port, physical_pb_port, expected_physical_pb_port.second); } - /* If the port range does not match, mapping fails */ - if (false == BasicPort(physical_pb_port->name, physical_pb_port->num_pins).contained(expected_physical_pb_port)) { - return false; - } - /* Now, port mapping should succeed, we update the vpr_device_annotation - * - port binding - * - port range - * - port pin rotate offset - */ - vpr_device_annotation.add_physical_pb_port(operating_pb_port, physical_pb_port); - vpr_device_annotation.add_physical_pb_port_range(operating_pb_port, expected_physical_pb_port); - vpr_device_annotation.add_physical_pb_pin_rotate_offset(operating_pb_port, pb_type_annotation.physical_pin_rotate_offset(std::string(operating_pb_port->name))); } /* Now, pb_type mapping should succeed, we update the vpr_device_annotation @@ -365,7 +371,7 @@ bool self_pair_physical_pb_types(t_pb_type* physical_pb_type, for (t_port* physical_pb_port : pb_type_ports(physical_pb_type)) { BasicPort physical_port_range(physical_pb_port->name, physical_pb_port->num_pins); vpr_device_annotation.add_physical_pb_port(physical_pb_port, physical_pb_port); - vpr_device_annotation.add_physical_pb_port_range(physical_pb_port, physical_port_range); + vpr_device_annotation.add_physical_pb_port_range(physical_pb_port, physical_pb_port, physical_port_range); } /* Now, pb_type mapping should succeed, we update the vpr_device_annotation */ diff --git a/openfpga/src/annotation/check_pb_type_annotation.cpp b/openfpga/src/annotation/check_pb_type_annotation.cpp index 5159ec064..d0709c81c 100644 --- a/openfpga/src/annotation/check_pb_type_annotation.cpp +++ b/openfpga/src/annotation/check_pb_type_annotation.cpp @@ -114,7 +114,7 @@ void check_vpr_physical_primitive_pb_type_annotation(t_pb_type* cur_pb_type, /* Now we need to check each port of the pb_type */ for (t_port* pb_port : pb_type_ports(cur_pb_type)) { - if (nullptr == vpr_device_annotation.physical_pb_port(pb_port)) { + if (0 == vpr_device_annotation.physical_pb_port(pb_port).size()) { VTR_LOG_ERROR("Find a port '%s' of pb_type '%s' which has not been mapped to any physical port!\n", pb_port->name, cur_pb_type->name); VTR_LOG_ERROR("Please specify in the OpenFPGA architecture\n"); diff --git a/openfpga/src/annotation/vpr_device_annotation.cpp b/openfpga/src/annotation/vpr_device_annotation.cpp index 0787e0225..71b3baf63 100644 --- a/openfpga/src/annotation/vpr_device_annotation.cpp +++ b/openfpga/src/annotation/vpr_device_annotation.cpp @@ -48,23 +48,28 @@ t_pb_type* VprDeviceAnnotation::physical_pb_type(t_pb_type* pb_type) const { return physical_pb_types_.at(pb_type); } -t_port* VprDeviceAnnotation::physical_pb_port(t_port* pb_port) const { +std::vector VprDeviceAnnotation::physical_pb_port(t_port* pb_port) const { /* Ensure that the pb_type is in the list */ - std::map::const_iterator it = physical_pb_ports_.find(pb_port); + std::map>::const_iterator it = physical_pb_ports_.find(pb_port); if (it == physical_pb_ports_.end()) { - return nullptr; + return std::vector(); } return physical_pb_ports_.at(pb_port); } -BasicPort VprDeviceAnnotation::physical_pb_port_range(t_port* pb_port) const { +BasicPort VprDeviceAnnotation::physical_pb_port_range(t_port* operating_pb_port, + t_port* physical_pb_port) const { /* Ensure that the pb_type is in the list */ - std::map::const_iterator it = physical_pb_port_ranges_.find(pb_port); + std::map>::const_iterator it = physical_pb_port_ranges_.find(operating_pb_port); if (it == physical_pb_port_ranges_.end()) { /* Return an invalid port. As such the port width will be 0, which is an invalid value */ return BasicPort(); } - return physical_pb_port_ranges_.at(pb_port); + if (0 == physical_pb_port_ranges_.at(operating_pb_port).count(physical_pb_port)) { + /* Return an invalid port. As such the port width will be 0, which is an invalid value */ + return BasicPort(); + } + return physical_pb_port_ranges_.at(operating_pb_port).at(physical_pb_port); } CircuitModelId VprDeviceAnnotation::pb_type_circuit_model(t_pb_type* physical_pb_type) const { @@ -185,24 +190,34 @@ int VprDeviceAnnotation::physical_pb_type_index_offset(t_pb_type* pb_type) const return physical_pb_type_index_offsets_.at(pb_type); } -int VprDeviceAnnotation::physical_pb_pin_rotate_offset(t_port* pb_port) const { +int VprDeviceAnnotation::physical_pb_pin_rotate_offset(t_port* operating_pb_port, + t_port* physical_pb_port) const { /* Ensure that the pb_type is in the list */ - std::map::const_iterator it = physical_pb_pin_rotate_offsets_.find(pb_port); + std::map>::const_iterator it = physical_pb_pin_rotate_offsets_.find(operating_pb_port); if (it == physical_pb_pin_rotate_offsets_.end()) { /* Default value is 0 */ return 0; } - return physical_pb_pin_rotate_offsets_.at(pb_port); + if (0 == physical_pb_pin_rotate_offsets_.at(operating_pb_port).count(physical_pb_port)) { + /* Default value is 0 */ + return 0; + } + return physical_pb_pin_rotate_offsets_.at(operating_pb_port).at(physical_pb_port); } -int VprDeviceAnnotation::physical_pb_pin_offset(t_port* pb_port) const { +int VprDeviceAnnotation::physical_pb_pin_offset(t_port* operating_pb_port, + t_port* physical_pb_port) const { /* Ensure that the pb_type is in the list */ - std::map::const_iterator it = physical_pb_pin_offsets_.find(pb_port); + std::map>::const_iterator it = physical_pb_pin_offsets_.find(operating_pb_port); if (it == physical_pb_pin_offsets_.end()) { /* Default value is 0 */ return 0; } - return physical_pb_pin_offsets_.at(pb_port); + if (0 == physical_pb_pin_offsets_.at(operating_pb_port).count(physical_pb_port)) { + /* Default value is 0 */ + return 0; + } + return physical_pb_pin_offsets_.at(operating_pb_port).at(physical_pb_port); } @@ -274,29 +289,28 @@ void VprDeviceAnnotation::add_physical_pb_type(t_pb_type* operating_pb_type, t_p physical_pb_types_[operating_pb_type] = physical_pb_type; } -void VprDeviceAnnotation::add_physical_pb_port(t_port* operating_pb_port, t_port* physical_pb_port) { - /* Warn any override attempt */ - std::map::const_iterator it = physical_pb_ports_.find(operating_pb_port); - if (it != physical_pb_ports_.end()) { - VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port '%s'!\n", - operating_pb_port->name, physical_pb_port->name); - } - - physical_pb_ports_[operating_pb_port] = physical_pb_port; +void VprDeviceAnnotation::add_physical_pb_port(t_port* operating_pb_port, + t_port* physical_pb_port) { + physical_pb_ports_[operating_pb_port].push_back(physical_pb_port); } -void VprDeviceAnnotation::add_physical_pb_port_range(t_port* operating_pb_port, const BasicPort& port_range) { +void VprDeviceAnnotation::add_physical_pb_port_range(t_port* operating_pb_port, + t_port* physical_pb_port, + const BasicPort& port_range) { /* The port range must satify the port width*/ VTR_ASSERT((size_t)operating_pb_port->num_pins == port_range.get_width()); /* Warn any override attempt */ - std::map::const_iterator it = physical_pb_port_ranges_.find(operating_pb_port); - if (it != physical_pb_port_ranges_.end()) { - VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port range '[%ld:%ld]'!\n", - operating_pb_port->name, port_range.get_lsb(), port_range.get_msb()); + std::map>::const_iterator it = physical_pb_port_ranges_.find(operating_pb_port); + if ( (it != physical_pb_port_ranges_.end()) + && (0 < physical_pb_port_ranges_[operating_pb_port].count(physical_pb_port)) ) { + VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port range '%s[%ld:%ld]'!\n", + operating_pb_port->name, + physical_pb_port->name, + port_range.get_lsb(), port_range.get_msb()); } - physical_pb_port_ranges_[operating_pb_port] = port_range; + physical_pb_port_ranges_[operating_pb_port][physical_pb_port] = port_range; } void VprDeviceAnnotation::add_pb_type_circuit_model(t_pb_type* physical_pb_type, const CircuitModelId& circuit_model) { @@ -396,17 +410,20 @@ void VprDeviceAnnotation::add_physical_pb_type_index_offset(t_pb_type* pb_type, physical_pb_type_index_offsets_[pb_type] = offset; } -void VprDeviceAnnotation::add_physical_pb_pin_rotate_offset(t_port* pb_port, const int& offset) { +void VprDeviceAnnotation::add_physical_pb_pin_rotate_offset(t_port* operating_pb_port, + t_port* physical_pb_port, + const int& offset) { /* Warn any override attempt */ - std::map::const_iterator it = physical_pb_pin_rotate_offsets_.find(pb_port); - if (it != physical_pb_pin_rotate_offsets_.end()) { - VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port pin rotate offset '%d'!\n", - pb_port->name, offset); + std::map>::const_iterator it = physical_pb_pin_rotate_offsets_.find(operating_pb_port); + if ( (it != physical_pb_pin_rotate_offsets_.end()) + && (0 < physical_pb_pin_rotate_offsets_[operating_pb_port].count(physical_pb_port)) ) { + VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port '%s' pin rotate offset '%d'!\n", + operating_pb_port->name, offset); } - physical_pb_pin_rotate_offsets_[pb_port] = offset; + physical_pb_pin_rotate_offsets_[operating_pb_port][physical_pb_port] = offset; /* We initialize the accumulated offset to 0 */ - physical_pb_pin_offsets_[pb_port] = 0; + physical_pb_pin_offsets_[operating_pb_port][physical_pb_port] = 0; } void VprDeviceAnnotation::add_physical_pb_graph_pin(const t_pb_graph_pin* operating_pb_graph_pin, @@ -432,17 +449,17 @@ void VprDeviceAnnotation::add_physical_pb_graph_pin(const t_pb_graph_pin* operat * Physical port | + + + * */ - if (0 == physical_pb_pin_rotate_offset(operating_pb_graph_pin->port)) { + if (0 == physical_pb_pin_rotate_offset(operating_pb_graph_pin->port, physical_pb_graph_pin->port)) { return; } - physical_pb_pin_offsets_[operating_pb_graph_pin->port] += physical_pb_pin_rotate_offset(operating_pb_graph_pin->port); + physical_pb_pin_offsets_[operating_pb_graph_pin->port][physical_pb_graph_pin->port] += physical_pb_pin_rotate_offset(operating_pb_graph_pin->port, physical_pb_graph_pin->port); - if ((size_t)physical_pb_port(operating_pb_graph_pin->port)->num_pins - 1 + if ((size_t)physical_pb_graph_pin->port->num_pins - 1 < operating_pb_graph_pin->pin_number - + physical_pb_port_range(operating_pb_graph_pin->port).get_lsb() - + physical_pb_pin_offsets_[operating_pb_graph_pin->port]) { - physical_pb_pin_offsets_[operating_pb_graph_pin->port] = 0; + + physical_pb_port_range(operating_pb_graph_pin->port, physical_pb_graph_pin->port).get_lsb() + + physical_pb_pin_offsets_[operating_pb_graph_pin->port][physical_pb_graph_pin->port]) { + physical_pb_pin_offsets_[operating_pb_graph_pin->port][physical_pb_graph_pin->port] = 0; } } diff --git a/openfpga/src/annotation/vpr_device_annotation.h b/openfpga/src/annotation/vpr_device_annotation.h index 00209df5c..6ac9ab4b5 100644 --- a/openfpga/src/annotation/vpr_device_annotation.h +++ b/openfpga/src/annotation/vpr_device_annotation.h @@ -45,8 +45,9 @@ class VprDeviceAnnotation { bool is_physical_pb_type(t_pb_type* pb_type) const; t_mode* physical_mode(t_pb_type* pb_type) const; t_pb_type* physical_pb_type(t_pb_type* pb_type) const; - t_port* physical_pb_port(t_port* pb_port) const; - BasicPort physical_pb_port_range(t_port* pb_port) const; + std::vector physical_pb_port(t_port* pb_port) const; + BasicPort physical_pb_port_range(t_port* operating_pb_port, + t_port* physical_pb_port) const; CircuitModelId pb_type_circuit_model(t_pb_type* physical_pb_type) const; CircuitModelId interconnect_circuit_model(t_interconnect* pb_interconnect) const; e_interconnect interconnect_physical_type(t_interconnect* pb_interconnect) const; @@ -60,7 +61,8 @@ class VprDeviceAnnotation { float physical_pb_type_index_factor(t_pb_type* pb_type) const; int physical_pb_type_index_offset(t_pb_type* pb_type) const; - int physical_pb_pin_rotate_offset(t_port* pb_port) const; + int physical_pb_pin_rotate_offset(t_port* operating_pb_port, + t_port* physical_pb_port) const; /**This function returns an accumulated offset. Note that the * accumulated offset is NOT the pin rotate offset specified by users @@ -69,7 +71,8 @@ class VprDeviceAnnotation { * by the pin rotate offset value * The accumulated offset will be reset to 0 when it exceeds the msb() of the physical port */ - int physical_pb_pin_offset(t_port* pb_port) const; + int physical_pb_pin_offset(t_port* operating_pb_port, + t_port* physical_pb_port) const; t_pb_graph_pin* physical_pb_graph_pin(const t_pb_graph_pin* pb_graph_pin) const; CircuitModelId rr_switch_circuit_model(const RRSwitchId& rr_switch) const; CircuitModelId rr_segment_circuit_model(const RRSegmentId& rr_segment) const; @@ -78,8 +81,11 @@ class VprDeviceAnnotation { public: /* Public mutators */ void add_pb_type_physical_mode(t_pb_type* pb_type, t_mode* physical_mode); void add_physical_pb_type(t_pb_type* operating_pb_type, t_pb_type* physical_pb_type); - void add_physical_pb_port(t_port* operating_pb_port, t_port* physical_pb_port); - void add_physical_pb_port_range(t_port* operating_pb_port, const BasicPort& port_range); + void add_physical_pb_port(t_port* operating_pb_port, + t_port* physical_pb_port); + void add_physical_pb_port_range(t_port* operating_pb_port, + t_port* physical_pb_port, + const BasicPort& port_range); void add_pb_type_circuit_model(t_pb_type* physical_pb_type, const CircuitModelId& circuit_model); void add_interconnect_circuit_model(t_interconnect* pb_interconnect, const CircuitModelId& circuit_model); void add_interconnect_physical_type(t_interconnect* pb_interconnect, const e_interconnect& physical_type); @@ -90,7 +96,9 @@ class VprDeviceAnnotation { t_pb_graph_node* physical_pb_graph_node); void add_physical_pb_type_index_factor(t_pb_type* pb_type, const float& factor); void add_physical_pb_type_index_offset(t_pb_type* pb_type, const int& offset); - void add_physical_pb_pin_rotate_offset(t_port* pb_port, const int& offset); + void add_physical_pb_pin_rotate_offset(t_port* operating_pb_port, + t_port* physical_pb_port, + const int& offset); void add_physical_pb_graph_pin(const t_pb_graph_pin* operating_pb_graph_pin, t_pb_graph_pin* physical_pb_graph_pin); void add_rr_switch_circuit_model(const RRSwitchId& rr_switch, const CircuitModelId& circuit_model); void add_rr_segment_circuit_model(const RRSegmentId& rr_segment, const CircuitModelId& circuit_model); @@ -139,17 +147,17 @@ class VprDeviceAnnotation { * Note: * - the parent of physical pb_port MUST be a physical pb_type */ - std::map physical_pb_ports_; - std::map physical_pb_pin_rotate_offsets_; + std::map> physical_pb_ports_; + std::map> physical_pb_pin_rotate_offsets_; /* Accumulated offsets for a physical pb_type port, just for internal usage */ - std::map physical_pb_pin_offsets_; + std::map> physical_pb_pin_offsets_; /* Pair a pb_port to its LSB and MSB of a physical pb_port * Note: * - the LSB and MSB MUST be in range of the physical pb_port */ - std::map physical_pb_port_ranges_; + std::map> physical_pb_port_ranges_; /* Pair a pb_port to a circuit port in circuit model * Note: From 53f87f44b47ec19cc06cae2a92ed9d66376df1db Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 18 Aug 2020 12:44:38 -0600 Subject: [PATCH 018/148] update documentation for the multi-port support in physical pb_pin --- docs/source/manual/arch_lang/annotate_vpr_arch.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/manual/arch_lang/annotate_vpr_arch.rst b/docs/source/manual/arch_lang/annotate_vpr_arch.rst index 39e02269c..5a6d77b14 100644 --- a/docs/source/manual/arch_lang/annotate_vpr_arch.rst +++ b/docs/source/manual/arch_lang/annotate_vpr_arch.rst @@ -136,8 +136,12 @@ The ``circuit_model_name`` should match the given name of a ``circuit_model`` de - ``physical_mode_pin="" creates the link of ``port`` of ``pb_type`` between operating and physical modes. This syntax is mandatory for every primitive ``pb_type`` in an operating mode ``pb_type``. It should be a valid ``port`` name of leaf ``pb_type`` in physical mode and the port size should also match. + .. note:: Users can define multiple ports. For example: ``physical_mode_pin="a[0:1] b[2:2]"``. When multiple ports are used, the ``physical_mode_pin_rotate_offset`` should also be adapt. For example: ``physical_mode_pin_rotate_offset="1 0"``) + - ``physical_mode_pin_rotate_offset=""`` aims to align the pin indices for ``port`` of ``pb_type`` between operating and physical modes, especially when an operating mode contains multiple ``pb_type`` (``num_pb``>1) that are linked to the same physical ``pb_type``. When ``physical_mode_pin_rotate_offset`` is larger than zero, the pin index of ``pb_type`` (whose index is large than 1) will be shifted by the given offset. + .. note:: If not defined, the default value of ``physical_mode_pin_rotate_offset`` is set to ``0``. + .. note:: It is highly recommended that only one physical mode is defined for a multi-mode configurable block. Try not to use nested physical mode definition. This will ease the debugging and lead to clean XML description. From 21c7eaa9cff380c45266cf0653961ed1018625c2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 18 Aug 2020 14:06:08 -0600 Subject: [PATCH 019/148] add 36-bit fracturable multiplier Verilog --- openfpga_flow/VerilogNetlists/mult_36x36.v | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 openfpga_flow/VerilogNetlists/mult_36x36.v diff --git a/openfpga_flow/VerilogNetlists/mult_36x36.v b/openfpga_flow/VerilogNetlists/mult_36x36.v new file mode 100644 index 000000000..6fda49dd9 --- /dev/null +++ b/openfpga_flow/VerilogNetlists/mult_36x36.v @@ -0,0 +1,35 @@ +//----------------------------------------------------- +// Design Name : mult_36x36 +// File Name : mult_36x36.v +// Function : A 36-bit multiplier which can operate in fracturable modes: +// 1. four 9-bit multipliers +// 2. two 18-bit multipliers +// 3. one 36-bit multipliers +// Coder : Xifan Tang +//----------------------------------------------------- + +module mult_36x36 ( + input [0:35] a, + input [0:35] b, + output [0:71] out, + input [0:1] mode); + + reg [0:71] out_reg; + + always @(mode, a, b) begin + if (2'b01 == mode) begin + out_reg[0:17] <= a[0:8] * b[0:8]; + out_reg[18:35] <= a[9:17] * b[9:17]; + out_reg[36:53] <= a[18:26] * b[18:26]; + out_reg[54:71] <= a[27:35] * b[27:35]; + end else if (2'b10 == mode) begin + out_reg[0:35] <= a[0:17] * b[0:17]; + out_reg[36:71] <= a[18:35] * b[18:35]; + end else begin + out_reg <= a * b; + end + end + + assign out = out_reg; + +endmodule From 098859fe0671510fcb70e479c5fed75a49ac9e63 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 18 Aug 2020 15:05:51 -0600 Subject: [PATCH 020/148] bug fix in the frac memory & DSP architecture --- ...n_frac_mem32K_frac_dsp36_40nm_openfpga.xml | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml index d47e396ee..328ff7b57 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml @@ -315,7 +315,7 @@ - + @@ -323,7 +323,7 @@ - + @@ -331,7 +331,7 @@ - + @@ -339,7 +339,7 @@ - + @@ -347,7 +347,7 @@ - + @@ -355,7 +355,7 @@ - + @@ -363,18 +363,6 @@ - - - - - - - - - - - - @@ -386,8 +374,20 @@ + + + + + + + + + + + + - + @@ -399,7 +399,7 @@ - + @@ -411,7 +411,7 @@ - + @@ -423,7 +423,7 @@ - + @@ -435,7 +435,7 @@ - + From 3ee4e10aa8b475aa9bb0831f8ec9a30b092dd139 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 18 Aug 2020 17:25:45 -0600 Subject: [PATCH 021/148] bug fix in the frac mem & DSP vpr arch --- ...dder_chain_frac_mem32K_frac_dsp36_40nm.xml | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml index 6acc37ba2..d34f3bba7 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml @@ -939,15 +939,30 @@ - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + From 42b5ea2cb10795c3771c53cf476996a088227bbd Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 18 Aug 2020 20:42:36 -0600 Subject: [PATCH 022/148] bug fix in openfpga arch for frac mem and dsp --- ...n_frac_mem32K_frac_dsp36_40nm_openfpga.xml | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml index 328ff7b57..228271ce7 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml @@ -374,20 +374,8 @@ - - - - - - - - - - - - - + @@ -399,7 +387,7 @@ - + @@ -411,7 +399,7 @@ - + @@ -423,7 +411,7 @@ - + @@ -435,7 +423,7 @@ - + From 743167521a200c11e2ab32c711062c696a047f90 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 18 Aug 2020 21:13:46 -0600 Subject: [PATCH 023/148] add Verilog design for fracturable 32k memory --- openfpga_flow/VerilogNetlists/frac_mem_32k.v | 371 +++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 openfpga_flow/VerilogNetlists/frac_mem_32k.v diff --git a/openfpga_flow/VerilogNetlists/frac_mem_32k.v b/openfpga_flow/VerilogNetlists/frac_mem_32k.v new file mode 100644 index 000000000..2d0b8fd26 --- /dev/null +++ b/openfpga_flow/VerilogNetlists/frac_mem_32k.v @@ -0,0 +1,371 @@ +//----------------------------------------------------- +// Design Name : frac_mem_32k +// File Name : frac_mem_32k.v +// Function : A fracturable BRAM which can operate in 13 modes +// - Single port RAM 512x64 bit +// - Single port RAM 1024x32 bit +// - Single port RAM 2048x16 bit +// - Single port RAM 4096x8 bit +// - Single port RAM 8192x4 bit +// - Single port RAM 16384x2 bit +// - Single port RAM 32768x1 bit +// - Dual port RAM 1024x32 +// - Dual port RAM 2048x16 bit +// - Dual port RAM 4096x8 bit +// - Dual port RAM 8192x4 bit +// - Dual port RAM 16384x2 bit +// - Dual port RAM 32768x1 bit +// Inspired by the dual port ram Verilog from +// https://www.intel.com/content/www/us/en/programmable/support/support-resources/design-examples/design-software/vhdl/vhd-true-dual-port-ram-sclk.html +// Coder : Xifan Tang +//----------------------------------------------------- + +module frac_mem_32k ( + input [0:14] addr_a, + input [0:14] addr_b, + input [0:31] data_a, + input [0:31] data_b, + input we_a, + input we_b, + output reg [0:31] q_a, + output reg [0:31] q_b, + input clk, + input [0:3] mode); + + reg [0:9] ram_a [0:31]; + reg [0:9] ram_b [0:31]; + + always @(posedge clk) begin + // Operating mode: single port RAM 512 x 64 + if (4'b0000 == mode) begin + if (we_a) begin + ram_a[addr_a[0:8]] <= data_a; + ram_b[addr_a[0:8]] <= data_b; + q_a <= data_a; + q_b <= data_b; + end else begin + q_a <= ram_a[addr_a[0:8]]; + q_b <= ram_b[addr_a[0:8]]; + end + // Operating mode: single port RAM 1024 x 32 + end else if (4'b0001 == mode) begin + if (we_a) begin + ram_a[addr_a[0:9]] <= data_a; + end else begin + q_a <= ram_a[addr_a[0:9]]; + end + // Operating mode: single port RAM 2048 x 16 + end else if (4'b0010 == mode) begin + if (we_a) begin + case (addr_a[10:10]) + 1'b0 : ram_a[addr_a[0:9]][0:15] <= data_a[0:15]; + 1'b1 : ram_a[addr_a[0:9]][16:31] <= data_a[0:15]; + endcase + end else begin + case (addr_a[10:10]) + 1'b0 : q_a <= ram_a[addr_a[0:9]][0:15]; + 1'b1 : q_a <= ram_a[addr_a[0:9]][16:31]; + endcase + end + // Operating mode: single port RAM 4096 x 8 + end else if (4'b0011 == mode) begin + if (we_a) begin + case (addr_a[10:11]) + 2'b00 : ram_a[addr_a[0:9]][0:7] <= data_a[0:7]; + 2'b01 : ram_a[addr_a[0:9]][8:15] <= data_a[0:7]; + 2'b10 : ram_a[addr_a[0:9]][16:23] <= data_a[0:7]; + 2'b11 : ram_a[addr_a[0:9]][24:31] <= data_a[0:7]; + endcase + end else begin + case (addr_a[10:11]) + 2'b00 : q_a <= ram_a[addr_a[0:9]][0:7]; + 2'b01 : q_a <= ram_a[addr_a[0:9]][8:15]; + 2'b10 : q_a <= ram_a[addr_a[0:9]][16:23]; + 2'b11 : q_a <= ram_a[addr_a[0:9]][24:31]; + endcase + end + // Operating mode: single port RAM 8192 x 4 + end else if (4'b0100 == mode) begin + if (we_a) begin + case (addr_a[10:12]) + 3'b000 : ram_a[addr_a[0:9]][0:3] <= data_a[0:3]; + 3'b001 : ram_a[addr_a[0:9]][4:7] <= data_a[0:3]; + 3'b010 : ram_a[addr_a[0:9]][8:11] <= data_a[0:3]; + 3'b011 : ram_a[addr_a[0:9]][12:15] <= data_a[0:3]; + 3'b100 : ram_a[addr_a[0:9]][16:19] <= data_a[0:3]; + 3'b101 : ram_a[addr_a[0:9]][20:23] <= data_a[0:3]; + 3'b110 : ram_a[addr_a[0:9]][24:27] <= data_a[0:3]; + 3'b111 : ram_a[addr_a[0:9]][28:31] <= data_a[0:3]; + endcase + end else begin + case (addr_a[10:12]) + 3'b000 : q_a <= ram_a[addr_a[0:9]][0:3]; + 3'b001 : q_a <= ram_a[addr_a[0:9]][4:7]; + 3'b010 : q_a <= ram_a[addr_a[0:9]][8:11]; + 3'b011 : q_a <= ram_a[addr_a[0:9]][12:15]; + 3'b100 : q_a <= ram_a[addr_a[0:9]][16:19]; + 3'b101 : q_a <= ram_a[addr_a[0:9]][20:23]; + 3'b110 : q_a <= ram_a[addr_a[0:9]][24:27]; + 3'b111 : q_a <= ram_a[addr_a[0:9]][28:31]; + endcase + end + // Operating mode: single port RAM 16384 x 2 + end else if (4'b0101 == mode) begin + if (we_a) begin + case (addr_a[10:13]) + 4'b0000 : ram_a[addr_a[0:9]][0:1] <= data_a[0:1]; + 4'b0001 : ram_a[addr_a[0:9]][2:3] <= data_a[0:1]; + 4'b0010 : ram_a[addr_a[0:9]][4:5] <= data_a[0:1]; + 4'b0011 : ram_a[addr_a[0:9]][6:7] <= data_a[0:1]; + 4'b0100 : ram_a[addr_a[0:9]][8:9] <= data_a[0:1]; + 4'b0101 : ram_a[addr_a[0:9]][10:11] <= data_a[0:1]; + 4'b0110 : ram_a[addr_a[0:9]][12:13] <= data_a[0:1]; + 4'b0111 : ram_a[addr_a[0:9]][14:15] <= data_a[0:1]; + 4'b1000 : ram_a[addr_a[0:9]][16:17] <= data_a[0:1]; + 4'b1001 : ram_a[addr_a[0:9]][18:19] <= data_a[0:1]; + 4'b1010 : ram_a[addr_a[0:9]][20:21] <= data_a[0:1]; + 4'b1011 : ram_a[addr_a[0:9]][22:23] <= data_a[0:1]; + 4'b1100 : ram_a[addr_a[0:9]][24:25] <= data_a[0:1]; + 4'b1101 : ram_a[addr_a[0:9]][26:27] <= data_a[0:1]; + 4'b1110 : ram_a[addr_a[0:9]][28:29] <= data_a[0:1]; + 4'b1111 : ram_a[addr_a[0:9]][30:31] <= data_a[0:1]; + endcase + end else begin + case (addr_a[10:13]) + 4'b0000 : q_a <= ram_a[addr_a[0:9]][0:1]; + 4'b0001 : q_a <= ram_a[addr_a[0:9]][2:3]; + 4'b0010 : q_a <= ram_a[addr_a[0:9]][4:5]; + 4'b0011 : q_a <= ram_a[addr_a[0:9]][6:7]; + 4'b0100 : q_a <= ram_a[addr_a[0:9]][8:9]; + 4'b0101 : q_a <= ram_a[addr_a[0:9]][10:11]; + 4'b0110 : q_a <= ram_a[addr_a[0:9]][12:13]; + 4'b0111 : q_a <= ram_a[addr_a[0:9]][14:15]; + 4'b1000 : q_a <= ram_a[addr_a[0:9]][16:17]; + 4'b1001 : q_a <= ram_a[addr_a[0:9]][18:19]; + 4'b1010 : q_a <= ram_a[addr_a[0:9]][20:21]; + 4'b1011 : q_a <= ram_a[addr_a[0:9]][22:23]; + 4'b1100 : q_a <= ram_a[addr_a[0:9]][24:25]; + 4'b1101 : q_a <= ram_a[addr_a[0:9]][26:27]; + 4'b1110 : q_a <= ram_a[addr_a[0:9]][28:29]; + 4'b1111 : q_a <= ram_a[addr_a[0:9]][30:31]; + endcase + end + // Operating mode: single port RAM 32768 x 1 + end else if (4'b0110 == mode) begin + if (we_a) begin + ram_a[addr_a[0:9]][addr_a[10:14]] = data_a[0:0]; + end else begin + q_a <= ram_a[addr_a[0:9]][addr_a[10:14]]; + end + // Operating mode: dual port RAM 1024 x 32 + end else if (4'b0111 == mode) begin + if (we_a) begin + ram_a[addr_a[0:9]] <= data_a; + ram_b[addr_b[0:9]] <= data_b; + q_a <= data_a; + q_b <= data_b; + end else begin + q_a <= ram_a[addr_a[0:9]]; + q_b <= ram_b[addr_b[0:9]]; + end + // Operating mode: dual port RAM 2048 x 16 + end else if (4'b1000 == mode) begin + if (we_a) begin + case (addr_a[10:10]) + 1'b0 : ram_a[addr_a[0:9]][0:15] <= data_a; + 1'b1 : ram_a[addr_a[0:9]][16:31] <= data_a; + endcase + end else begin + case (addr_a[10:10]) + 1'b0 : q_a <= ram_a[addr_a[0:9]][0:15]; + 1'b1 : q_a <= ram_a[addr_a[0:9]][16:31]; + endcase + end + + if (we_b) begin + case (addr_b[10:10]) + 1'b0 : ram_b[addr_b[0:9]][0:15] <= data_b; + 1'b1 : ram_b[addr_b[0:9]][16:31] <= data_b; + endcase + end else begin + case (addr_b[10:10]) + 1'b0 : q_b <= ram_b[addr_b[0:9]][0:15]; + 1'b1 : q_b <= ram_b[addr_b[0:9]][16:31]; + endcase + end + // Operating mode: dual port RAM 4096 x 8 + end else if (4'b1001 == mode) begin + if (we_a) begin + case (addr_a[10:11]) + 2'b00 : ram_a[addr_a[0:9]][0:7] <= data_a[0:7]; + 2'b01 : ram_a[addr_a[0:9]][8:15] <= data_a[0:7]; + 2'b10 : ram_a[addr_a[0:9]][16:23] <= data_a[0:7]; + 2'b11 : ram_a[addr_a[0:9]][24:31] <= data_a[0:7]; + endcase + end else begin + case (addr_a[10:11]) + 2'b00 : q_a <= ram_a[addr_a[0:9]][0:7]; + 2'b01 : q_a <= ram_a[addr_a[0:9]][8:15]; + 2'b10 : q_a <= ram_a[addr_a[0:9]][16:23]; + 2'b11 : q_a <= ram_a[addr_a[0:9]][24:31]; + endcase + end + + if (we_b) begin + case (addr_b[10:11]) + 2'b00 : ram_b[addr_b[0:9]][0:7] <= data_b[0:7]; + 2'b01 : ram_b[addr_b[0:9]][8:15] <= data_b[0:7]; + 2'b10 : ram_b[addr_b[0:9]][16:23] <= data_b[0:7]; + 2'b11 : ram_b[addr_b[0:9]][24:31] <= data_b[0:7]; + endcase + end else begin + case (addr_b[10:11]) + 2'b00 : q_b <= ram_b[addr_b[0:9]][0:7]; + 2'b01 : q_b <= ram_b[addr_b[0:9]][8:15]; + 2'b10 : q_b <= ram_b[addr_b[0:9]][16:23]; + 2'b11 : q_b <= ram_b[addr_b[0:9]][24:31]; + endcase + end + // Operating mode: dual port RAM 8192 x 4 + end else if (4'b1011 == mode) begin + if (we_a) begin + case (addr_a[10:12]) + 3'b000 : ram_a[addr_a[0:9]][0:3] <= data_a[0:3]; + 3'b001 : ram_a[addr_a[0:9]][4:7] <= data_a[0:3]; + 3'b010 : ram_a[addr_a[0:9]][8:11] <= data_a[0:3]; + 3'b011 : ram_a[addr_a[0:9]][12:15] <= data_a[0:3]; + 3'b100 : ram_a[addr_a[0:9]][16:19] <= data_a[0:3]; + 3'b101 : ram_a[addr_a[0:9]][20:23] <= data_a[0:3]; + 3'b110 : ram_a[addr_a[0:9]][24:27] <= data_a[0:3]; + 3'b111 : ram_a[addr_a[0:9]][28:31] <= data_a[0:3]; + endcase + end else begin + case (addr_a[10:12]) + 3'b000 : q_a <= ram_a[addr_a[0:9]][0:3]; + 3'b001 : q_a <= ram_a[addr_a[0:9]][4:7]; + 3'b010 : q_a <= ram_a[addr_a[0:9]][8:11]; + 3'b011 : q_a <= ram_a[addr_a[0:9]][12:15]; + 3'b100 : q_a <= ram_a[addr_a[0:9]][16:19]; + 3'b101 : q_a <= ram_a[addr_a[0:9]][20:23]; + 3'b110 : q_a <= ram_a[addr_a[0:9]][24:27]; + 3'b111 : q_a <= ram_a[addr_a[0:9]][28:31]; + endcase + end + if (we_b) begin + case (addr_b[10:12]) + 3'b000 : ram_b[addr_b[0:9]][0:3] <= data_b[0:3]; + 3'b001 : ram_b[addr_b[0:9]][4:7] <= data_b[0:3]; + 3'b010 : ram_b[addr_b[0:9]][8:11] <= data_b[0:3]; + 3'b011 : ram_b[addr_b[0:9]][12:15] <= data_b[0:3]; + 3'b100 : ram_b[addr_b[0:9]][16:19] <= data_b[0:3]; + 3'b101 : ram_b[addr_b[0:9]][20:23] <= data_b[0:3]; + 3'b110 : ram_b[addr_b[0:9]][24:27] <= data_b[0:3]; + 3'b111 : ram_b[addr_b[0:9]][28:31] <= data_b[0:3]; + endcase + end else begin + case (addr_b[10:12]) + 3'b000 : q_b <= ram_b[addr_b[0:9]][0:3]; + 3'b001 : q_b <= ram_b[addr_b[0:9]][4:7]; + 3'b010 : q_b <= ram_b[addr_b[0:9]][8:11]; + 3'b011 : q_b <= ram_b[addr_b[0:9]][12:15]; + 3'b100 : q_b <= ram_b[addr_b[0:9]][16:19]; + 3'b101 : q_b <= ram_b[addr_b[0:9]][20:23]; + 3'b110 : q_b <= ram_b[addr_b[0:9]][24:27]; + 3'b111 : q_b <= ram_b[addr_b[0:9]][28:31]; + endcase + end + // Operating mode: dual port RAM 16384 x 2 + end else if (4'b1100 == mode) begin + if (we_a) begin + case (addr_a[10:13]) + 4'b0000 : ram_a[addr_a[0:9]][0:1] <= data_a[0:1]; + 4'b0001 : ram_a[addr_a[0:9]][2:3] <= data_a[0:1]; + 4'b0010 : ram_a[addr_a[0:9]][4:5] <= data_a[0:1]; + 4'b0011 : ram_a[addr_a[0:9]][6:7] <= data_a[0:1]; + 4'b0100 : ram_a[addr_a[0:9]][8:9] <= data_a[0:1]; + 4'b0101 : ram_a[addr_a[0:9]][10:11] <= data_a[0:1]; + 4'b0110 : ram_a[addr_a[0:9]][12:13] <= data_a[0:1]; + 4'b0111 : ram_a[addr_a[0:9]][14:15] <= data_a[0:1]; + 4'b1000 : ram_a[addr_a[0:9]][16:17] <= data_a[0:1]; + 4'b1001 : ram_a[addr_a[0:9]][18:19] <= data_a[0:1]; + 4'b1010 : ram_a[addr_a[0:9]][20:21] <= data_a[0:1]; + 4'b1011 : ram_a[addr_a[0:9]][22:23] <= data_a[0:1]; + 4'b1100 : ram_a[addr_a[0:9]][24:25] <= data_a[0:1]; + 4'b1101 : ram_a[addr_a[0:9]][26:27] <= data_a[0:1]; + 4'b1110 : ram_a[addr_a[0:9]][28:29] <= data_a[0:1]; + 4'b1111 : ram_a[addr_a[0:9]][30:31] <= data_a[0:1]; + endcase + end else begin + case (addr_a[10:13]) + 4'b0000 : q_a <= ram_a[addr_a[0:9]][0:1]; + 4'b0001 : q_a <= ram_a[addr_a[0:9]][2:3]; + 4'b0010 : q_a <= ram_a[addr_a[0:9]][4:5]; + 4'b0011 : q_a <= ram_a[addr_a[0:9]][6:7]; + 4'b0100 : q_a <= ram_a[addr_a[0:9]][8:9]; + 4'b0101 : q_a <= ram_a[addr_a[0:9]][10:11]; + 4'b0110 : q_a <= ram_a[addr_a[0:9]][12:13]; + 4'b0111 : q_a <= ram_a[addr_a[0:9]][14:15]; + 4'b1000 : q_a <= ram_a[addr_a[0:9]][16:17]; + 4'b1001 : q_a <= ram_a[addr_a[0:9]][18:19]; + 4'b1010 : q_a <= ram_a[addr_a[0:9]][20:21]; + 4'b1011 : q_a <= ram_a[addr_a[0:9]][22:23]; + 4'b1100 : q_a <= ram_a[addr_a[0:9]][24:25]; + 4'b1101 : q_a <= ram_a[addr_a[0:9]][26:27]; + 4'b1110 : q_a <= ram_a[addr_a[0:9]][28:29]; + 4'b1111 : q_a <= ram_a[addr_a[0:9]][30:31]; + endcase + end + if (we_b) begin + case (addr_b[10:13]) + 4'b0000 : ram_b[addr_b[0:9]][0:1] <= data_b[0:1]; + 4'b0001 : ram_b[addr_b[0:9]][2:3] <= data_b[0:1]; + 4'b0010 : ram_b[addr_b[0:9]][4:5] <= data_b[0:1]; + 4'b0011 : ram_b[addr_b[0:9]][6:7] <= data_b[0:1]; + 4'b0100 : ram_b[addr_b[0:9]][8:9] <= data_b[0:1]; + 4'b0101 : ram_b[addr_b[0:9]][10:11] <= data_b[0:1]; + 4'b0110 : ram_b[addr_b[0:9]][12:13] <= data_b[0:1]; + 4'b0111 : ram_b[addr_b[0:9]][14:15] <= data_b[0:1]; + 4'b1000 : ram_b[addr_b[0:9]][16:17] <= data_b[0:1]; + 4'b1001 : ram_b[addr_b[0:9]][18:19] <= data_b[0:1]; + 4'b1010 : ram_b[addr_b[0:9]][20:21] <= data_b[0:1]; + 4'b1011 : ram_b[addr_b[0:9]][22:23] <= data_b[0:1]; + 4'b1100 : ram_b[addr_b[0:9]][24:25] <= data_b[0:1]; + 4'b1101 : ram_b[addr_b[0:9]][26:27] <= data_b[0:1]; + 4'b1110 : ram_b[addr_b[0:9]][28:29] <= data_b[0:1]; + 4'b1111 : ram_b[addr_b[0:9]][30:31] <= data_b[0:1]; + endcase + end else begin + case (addr_b[10:13]) + 4'b0000 : q_b <= ram_b[addr_b[0:9]][0:1]; + 4'b0001 : q_b <= ram_b[addr_b[0:9]][2:3]; + 4'b0010 : q_b <= ram_b[addr_b[0:9]][4:5]; + 4'b0011 : q_b <= ram_b[addr_b[0:9]][6:7]; + 4'b0100 : q_b <= ram_b[addr_b[0:9]][8:9]; + 4'b0101 : q_b <= ram_b[addr_b[0:9]][10:11]; + 4'b0110 : q_b <= ram_b[addr_b[0:9]][12:13]; + 4'b0111 : q_b <= ram_b[addr_b[0:9]][14:15]; + 4'b1000 : q_b <= ram_b[addr_b[0:9]][16:17]; + 4'b1001 : q_b <= ram_b[addr_b[0:9]][18:19]; + 4'b1010 : q_b <= ram_b[addr_b[0:9]][20:21]; + 4'b1011 : q_b <= ram_b[addr_b[0:9]][22:23]; + 4'b1100 : q_b <= ram_b[addr_b[0:9]][24:25]; + 4'b1101 : q_b <= ram_b[addr_b[0:9]][26:27]; + 4'b1110 : q_b <= ram_b[addr_b[0:9]][28:29]; + 4'b1111 : q_b <= ram_b[addr_b[0:9]][30:31]; + endcase + end + // Operating mode: dual port RAM 32768 x 1 + end else if (4'b1101 == mode) begin + if (we_a) begin + ram_a[addr_a[0:9]][addr_a[10:14]] = data_a[0:0]; + end else begin + q_a <= ram_a[addr_a[0:9]][addr_a[10:14]]; + end + if (we_b) begin + ram_b[addr_b[0:9]][addr_b[10:14]] = data_b[0:0]; + end else begin + q_b <= ram_b[addr_b[0:9]][addr_b[10:14]]; + end + end + end +endmodule From dbd93e429d699d6509687669a425d7bf9a02c230 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 09:43:44 -0600 Subject: [PATCH 024/148] now pro_blif.pl can accept customized clock name --- openfpga_flow/scripts/pro_blif.pl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openfpga_flow/scripts/pro_blif.pl b/openfpga_flow/scripts/pro_blif.pl index 40203e136..f3cb0e99b 100755 --- a/openfpga_flow/scripts/pro_blif.pl +++ b/openfpga_flow/scripts/pro_blif.pl @@ -25,8 +25,9 @@ sub print_usage() print " -i \n"; print " -o \n"; print " Options: (Optional)\n"; - print " -remove_buffers\n"; - print " -add_default_clk\n"; + print " -remove_buffers: remove buffers in the blif\n"; + print " -add_default_clk: add a default clock using the default clock name 'clk', or set by users through the option '-default clk'\n"; + print " -default_clk : set the default clk name to be used by .latch\n"; print " -initial_blif \n"; print "\n"; return 0; @@ -46,6 +47,8 @@ sub opts_read() $frpt = $ARGV[$iargv+1]; } elsif ("-add_default_clk" eq $ARGV[$iargv]) { $add_default_clk = "on"; + } elsif ("-default_clk" eq $ARGV[$iargv]) { + $default_clk_name = $ARGV[$iargv+1]; } elsif ("-initial_blif" eq $ARGV[$iargv]) { $finitial = $ARGV[$iargv+1]; } elsif ("-remove_buffers" eq $ARGV[$iargv]) { @@ -219,7 +222,7 @@ sub scan_blif() my ($line_no) = (0); if (!defined($finitial)) { - $latch_token = "re clk"; + $latch_token = "re $default_clk_name"; } else { my $latch_token_found = 0; my $count = 0; From d7efdf35b62b77a784c68935fdb69e4305222dca Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 11:15:25 -0600 Subject: [PATCH 025/148] add custom pin location to the flagship vpr arch with frac mem and dsp --- ...adder_chain_frac_mem32K_frac_dsp36_40nm.xml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml index d34f3bba7..c56ce6041 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml @@ -223,7 +223,14 @@ - + + + + + + mult_36.a[0:17] mult_36.b[0:17] mult_36.out[0:35] + mult_36.a[18:35] mult_36.b[18:35] mult_36.out[36:71] + @@ -237,7 +244,14 @@ - + + + + + memory.clk + memory.addr1[0:14] memory.data[0:31] memory.we1 memory.out[0:31] + memory.addr2[0:14] memory.data[32:63] memory.we2 memory.out[32:63] + From af1c7c6f29900f2d02aee74d5ff6815808933e7b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 12:18:35 -0600 Subject: [PATCH 026/148] start fixing the bug in thru channels --- .../rr_graph_builder_utils.cpp | 35 +++++++++++++++++-- .../rr_graph_builder_utils.h | 6 ++-- .../tileable_rr_graph_node_builder.cpp | 13 ++++--- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/vpr/src/tileable_rr_graph/rr_graph_builder_utils.cpp b/vpr/src/tileable_rr_graph/rr_graph_builder_utils.cpp index a599c8194..3bf54a6d5 100644 --- a/vpr/src/tileable_rr_graph/rr_graph_builder_utils.cpp +++ b/vpr/src/tileable_rr_graph/rr_graph_builder_utils.cpp @@ -172,7 +172,21 @@ size_t get_grid_num_classes(const t_grid_tile& cur_grid, * When height_offset == height - 1, it means that the grid is at the top side of this multi-width and multi-height block ***********************************************************************/ bool is_chanx_exist(const DeviceGrid& grids, - const vtr::Point& chanx_coord) { + const vtr::Point& chanx_coord, + const bool& through_channel) { + + if ((1 > chanx_coord.x()) || (chanx_coord.x() > grids.width() - 2)) { + return false; + } + + if (chanx_coord.y() > grids.height() - 2) { + return false; + } + + if (true == through_channel) { + return true; + } + return (grids[chanx_coord.x()][chanx_coord.y()].height_offset == grids[chanx_coord.x()][chanx_coord.y()].type->height - 1); } @@ -192,9 +206,26 @@ bool is_chanx_exist(const DeviceGrid& grids, * If the CHANY is in the middle of a multi-width and multi-height grid * it should locate at a grid whose width_offset is lower than the its width defined in physical_tile * When height_offset == height - 1, it means that the grid is at the top side of this multi-width and multi-height block + * + * If through channel is allowed, the chany will always exists + * unless it falls out of the grid array ***********************************************************************/ bool is_chany_exist(const DeviceGrid& grids, - const vtr::Point& chany_coord) { + const vtr::Point& chany_coord, + const bool& through_channel) { + + if (chany_coord.x() > grids.width() - 2) { + return false; + } + + if ((1 > chany_coord.y()) || (chany_coord.y() > grids.height() - 2)) { + return false; + } + + if (true == through_channel) { + return true; + } + return (grids[chany_coord.x()][chany_coord.y()].width_offset == grids[chany_coord.x()][chany_coord.y()].type->width - 1); } diff --git a/vpr/src/tileable_rr_graph/rr_graph_builder_utils.h b/vpr/src/tileable_rr_graph/rr_graph_builder_utils.h index 23e030d9b..6ff78412f 100644 --- a/vpr/src/tileable_rr_graph/rr_graph_builder_utils.h +++ b/vpr/src/tileable_rr_graph/rr_graph_builder_utils.h @@ -40,10 +40,12 @@ size_t get_grid_num_classes(const t_grid_tile& cur_grid, const e_pin_type& pin_type); bool is_chanx_exist(const DeviceGrid& grids, - const vtr::Point& chanx_coord); + const vtr::Point& chanx_coord, + const bool& through_channel=false); bool is_chany_exist(const DeviceGrid& grids, - const vtr::Point& chany_coord); + const vtr::Point& chany_coord, + const bool& through_channel=false); bool is_chanx_right_to_multi_height_grid(const DeviceGrid& grids, const vtr::Point& chanx_coord, diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp b/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp index 753c455ff..4107d4538 100644 --- a/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp @@ -186,7 +186,8 @@ size_t estimate_num_chanx_rr_nodes(const DeviceGrid& grids, vtr::Point chanx_coord(ix, iy); /* Bypass if the routing channel does not exist when through channels are not allowed */ - if (false == is_chanx_exist(grids, chanx_coord)) { + if ( (false == through_channel) + && (false == is_chanx_exist(grids, chanx_coord))) { continue; } @@ -237,11 +238,13 @@ size_t estimate_num_chany_rr_nodes(const DeviceGrid& grids, for (size_t iy = 1; iy < grids.height() - 1; ++iy) { vtr::Point chany_coord(ix, iy); - /* Bypass if the routing channel does not exist when through channels are not allowed */ - if (false == is_chany_exist(grids, chany_coord)) { + /* 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))) { continue; } + bool force_start = false; bool force_end = false; @@ -922,7 +925,9 @@ void load_chany_rr_nodes_basic_info(RRGraph& rr_graph, ChanNodeDetails chany_details = build_unidir_chan_node_details(chan_width, grids.height() - 2, force_start, force_end, segment_infs); - /* Force node_ids from the previous chanx */ + /* Force node_ids from the previous chany + * This will not be applied when the routing channel is cut off (force to start) + */ if (0 < track_node_ids.size()) { /* Rotate should be done based on a typical case of routing tracks. * Tracks on the borders are not regularly started and ended, From f64079641d1ee4954a91572726af06434e795336 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 12:43:58 -0600 Subject: [PATCH 027/148] bug fix in flagship vpr arch with frac mem and dsp --- ...c_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml index 228271ce7..b2fa706af 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml @@ -287,7 +287,7 @@ There are four 9x9 multipliers, each of which occupies part of the input/output of the 36x36 multiplier --> - + @@ -296,7 +296,7 @@ There are two 18x18 multipliers, each of which occupies part of the input/output of the 36x36 multiplier --> - + From 79b6ff3cb08b1c20612bf5c6d6755fb0966ad0b4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 12:44:51 -0600 Subject: [PATCH 028/148] relax checking for device annotation as we support multi-port during physical mode pin mapping --- openfpga/src/annotation/vpr_device_annotation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga/src/annotation/vpr_device_annotation.cpp b/openfpga/src/annotation/vpr_device_annotation.cpp index 71b3baf63..f196f50da 100644 --- a/openfpga/src/annotation/vpr_device_annotation.cpp +++ b/openfpga/src/annotation/vpr_device_annotation.cpp @@ -298,7 +298,7 @@ void VprDeviceAnnotation::add_physical_pb_port_range(t_port* operating_pb_port, t_port* physical_pb_port, const BasicPort& port_range) { /* The port range must satify the port width*/ - VTR_ASSERT((size_t)operating_pb_port->num_pins == port_range.get_width()); + VTR_ASSERT((size_t)operating_pb_port->num_pins >= port_range.get_width()); /* Warn any override attempt */ std::map>::const_iterator it = physical_pb_port_ranges_.find(operating_pb_port); From f631245b2b85f79376a475b668ee767ca82057fe Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 13:41:04 -0600 Subject: [PATCH 029/148] bug fix and enriched debugging info print out --- openfpga/src/annotation/annotate_pb_graph.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openfpga/src/annotation/annotate_pb_graph.cpp b/openfpga/src/annotation/annotate_pb_graph.cpp index 93a154222..249cb5632 100644 --- a/openfpga/src/annotation/annotate_pb_graph.cpp +++ b/openfpga/src/annotation/annotate_pb_graph.cpp @@ -337,6 +337,9 @@ bool try_match_pb_graph_pin(t_pb_graph_pin* operating_pb_graph_pin, /* Reach here, it means all the requirements have been met */ return true; } + + /* If we reach here, we failed */ + return false; } /******************************************************************** @@ -426,7 +429,7 @@ void annotate_physical_pb_graph_pin(t_pb_graph_pin* operating_pb_graph_pin, /* If we reach here, it means that pin pairing fails, error out! */ VTR_LOG_ERROR("Fail to match a physical pin for '%s' from pb_graph_node '%s'!\n", - operating_pb_graph_pin->port->name, + operating_pb_graph_pin->to_string().c_str(), physical_pb_graph_node->hierarchical_type_name().c_str()); } From 3eea12ceaed12df0e6693310058d97dd3cce00d1 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 14:43:44 -0600 Subject: [PATCH 030/148] added a new XML syntax: initial offset for physical mode pin mapping --- .../src/pb_type_annotation.cpp | 45 ++++++++++++++----- .../libarchopenfpga/src/pb_type_annotation.h | 32 ++++++++++--- .../src/read_xml_pb_type_annotation.cpp | 25 +++++++++++ .../src/write_xml_pb_type_annotation.cpp | 10 ++++- openfpga/src/annotation/annotate_pb_graph.cpp | 28 ++++++++---- openfpga/src/annotation/annotate_pb_types.cpp | 7 +-- .../src/annotation/vpr_device_annotation.cpp | 30 ++++++++++++- .../src/annotation/vpr_device_annotation.h | 7 +++ 8 files changed, 152 insertions(+), 32 deletions(-) diff --git a/libopenfpga/libarchopenfpga/src/pb_type_annotation.cpp b/libopenfpga/libarchopenfpga/src/pb_type_annotation.cpp index 133ee000c..9a369e742 100644 --- a/libopenfpga/libarchopenfpga/src/pb_type_annotation.cpp +++ b/libopenfpga/libarchopenfpga/src/pb_type_annotation.cpp @@ -86,11 +86,11 @@ std::vector PbTypeAnnotation::port_names() const { return keys; } -std::map PbTypeAnnotation::physical_pb_type_port(const std::string& port_name) const { - std::map>::const_iterator it = operating_pb_type_ports_.find(port_name); +std::map> PbTypeAnnotation::physical_pb_type_port(const std::string& port_name) const { + std::map>>::const_iterator it = operating_pb_type_ports_.find(port_name); if (it == operating_pb_type_ports_.end()) { /* Return an empty port */ - return std::map(); + return std::map>(); } return operating_pb_type_ports_.at(port_name); } @@ -169,25 +169,25 @@ void PbTypeAnnotation::set_physical_pb_type_index_offset(const int& value) { void PbTypeAnnotation::add_pb_type_port_pair(const std::string& operating_pb_port_name, const BasicPort& physical_pb_port) { /* Give a warning if the operating_pb_port_name already exist */ - std::map>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); + std::map>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); /* If not exist, initialize and set a default value */ if (it == operating_pb_type_ports_.end()) { - operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = 0; + operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = {0, 0}; /* We can return early */ return; } /* If the physical port is not in the list, we create one and set a default value */ if (0 == operating_pb_type_ports_[operating_pb_port_name].count(physical_pb_port)) { - operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = 0; + operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = {0, 0}; } } -void PbTypeAnnotation::set_physical_pin_rotate_offset(const std::string& operating_pb_port_name, - const BasicPort& physical_pb_port, - const int& physical_pin_rotate_offset) { - std::map>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); +void PbTypeAnnotation::set_physical_pin_initial_offset(const std::string& operating_pb_port_name, + const BasicPort& physical_pb_port, + const int& physical_pin_initial_offset) { + std::map>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); if (it == operating_pb_type_ports_.end()) { VTR_LOG_ERROR("The operating pb_type port '%s' is not valid!\n", @@ -204,7 +204,30 @@ void PbTypeAnnotation::set_physical_pin_rotate_offset(const std::string& operati exit(1); } - operating_pb_type_ports_[operating_pb_port_name][physical_pb_port] = physical_pin_rotate_offset; + operating_pb_type_ports_[operating_pb_port_name][physical_pb_port][0] = physical_pin_initial_offset; +} + +void PbTypeAnnotation::set_physical_pin_rotate_offset(const std::string& operating_pb_port_name, + const BasicPort& physical_pb_port, + const int& physical_pin_rotate_offset) { + std::map>>::const_iterator it = operating_pb_type_ports_.find(operating_pb_port_name); + + if (it == operating_pb_type_ports_.end()) { + VTR_LOG_ERROR("The operating pb_type port '%s' is not valid!\n", + operating_pb_port_name.c_str()); + exit(1); + } + + if (operating_pb_type_ports_[operating_pb_port_name].end() == operating_pb_type_ports_[operating_pb_port_name].find(physical_pb_port)) { + VTR_LOG_ERROR("The physical pb_type port '%s[%lu:%lu]' definition for operating pb_type port '%s' is not valid!\n", + physical_pb_port.get_name().c_str(), + physical_pb_port.get_lsb(), + physical_pb_port.get_msb(), + operating_pb_port_name.c_str()); + exit(1); + } + + operating_pb_type_ports_[operating_pb_port_name][physical_pb_port][1] = physical_pin_rotate_offset; } void PbTypeAnnotation::add_interconnect_circuit_model_pair(const std::string& interc_name, diff --git a/libopenfpga/libarchopenfpga/src/pb_type_annotation.h b/libopenfpga/libarchopenfpga/src/pb_type_annotation.h index 4e1d4e8a2..33941bce3 100644 --- a/libopenfpga/libarchopenfpga/src/pb_type_annotation.h +++ b/libopenfpga/libarchopenfpga/src/pb_type_annotation.h @@ -6,6 +6,7 @@ *******************************************************************/ #include #include +#include #include "openfpga_port.h" @@ -48,7 +49,7 @@ class PbTypeAnnotation { float physical_pb_type_index_factor() const; int physical_pb_type_index_offset() const; std::vector port_names() const; - std::map physical_pb_type_port(const std::string& port_name) const; + std::map> physical_pb_type_port(const std::string& port_name) const; std::vector interconnect_names() const; std::string interconnect_circuit_model_name(const std::string& interc_name) const; public: /* Public mutators */ @@ -66,6 +67,9 @@ class PbTypeAnnotation { void set_physical_pb_type_index_offset(const int& value); void add_pb_type_port_pair(const std::string& operating_pb_port_name, const BasicPort& physical_pb_port); + void set_physical_pin_initial_offset(const std::string& operating_pb_port_name, + const BasicPort& physical_pb_port, + const int& physical_pin_initial_offset); void set_physical_pin_rotate_offset(const std::string& operating_pb_port_name, const BasicPort& physical_pb_port, const int& physical_pin_rotate_offset); @@ -134,8 +138,12 @@ class PbTypeAnnotation { int physical_pb_type_index_offset_; /* Link from the pins under an operating pb_type to pairs of - * its physical pb_type and its pin rotating offset - * The offset aims to align the pin indices for port of pb_type + * its physical pb_type and its pin initial & rotating offset + * + * Note that initial offset is the first element of the std::array + * Note that rotating offset is the second element of the std::array + * + * The offsets aim to align the pin indices for port of pb_type * between operating and physical modes, especially when an operating * mode contains multiple pb_type (num_pb>1) that are linked to * the same physical pb_type. @@ -143,11 +151,21 @@ class PbTypeAnnotation { * the pin index of pb_type (whose index is large than 1) * will be shifted by the given offset. * - * For example, an offset of 1 is used to map - * operating pb_type adder[0].pin[0] with a full path clb.fle[arith].adder[0] - * to physical pb_type adder[0].pin[1] with a full path clb.fle[physical].adder[0] + * For example, an initial offset of -32 is used to map + * operating pb_type bram[0].dout[32] with a full path memory[dual_port].bram[0] + * operating pb_type bram[0].dout[33] with a full path memory[dual_port].bram[0] + * to + * physical pb_type bram[0].dout_a[0] with a full path memory[physical].bram[0] + * physical pb_type bram[0].dout_a[1] with a full path memory[physical].bram[0] + * + * For example, a rotating offset of 1 is used to map + * operating pb_type mult_9x9[0].a[0:8] with a full path mult[frac].mult_9x9[0] + * operating pb_type mult_9x9[1].a[0:8] with a full path mult[frac].mult_9x9[1] + * to + * physical pb_type mult_36x36.a[0:8] with a full path mult[physical].mult_36x36[0] + * physical pb_type mult_36x36.a[9:17] with a full path mult[physical].mult_36x36[0] */ - std::map> operating_pb_type_ports_; + std::map>> operating_pb_type_ports_; /* Link between the interconnects under this pb_type and circuit model names */ std::map interconnect_circuit_model_names_; diff --git a/libopenfpga/libarchopenfpga/src/read_xml_pb_type_annotation.cpp b/libopenfpga/libarchopenfpga/src/read_xml_pb_type_annotation.cpp index a4d25e1cf..9831f6e23 100644 --- a/libopenfpga/libarchopenfpga/src/read_xml_pb_type_annotation.cpp +++ b/libopenfpga/libarchopenfpga/src/read_xml_pb_type_annotation.cpp @@ -65,6 +65,31 @@ void read_xml_pb_port_annotation(pugi::xml_node& xml_port, pb_type_annotation.add_pb_type_port_pair(name_attr, port_parser.port()); } + /* We have an optional attribute: physical_mode_pin_initial_offset + * Split based on the number of physical pb_type ports that have been defined + */ + const std::string& physical_pin_initial_offset_attr = get_attribute(xml_port, "physical_mode_pin_initial_offset", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string(); + + if (false == physical_pin_initial_offset_attr.empty()) { + /* Split the physical mode port attributes with space */ + openfpga::StringToken offset_tokenizer(physical_pin_initial_offset_attr); + const std::vector initial_offsets = offset_tokenizer.split(); + + /* Error out if the offset does not match the port definition */ + if (physical_mode_ports.size() != initial_offsets.size()) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_port), + "Defined %lu physical mode ports but only %lu physical pin initial offset are defined! Expect size matching.\n", + physical_mode_ports.size(), initial_offsets.size()); + } + + for (size_t iport = 0; iport < physical_mode_ports.size(); ++iport) { + openfpga::PortParser port_parser(physical_mode_ports[iport]); + pb_type_annotation.set_physical_pin_initial_offset(name_attr, + port_parser.port(), + std::stoi(initial_offsets[iport])); + } + } + /* We have an optional attribute: physical_mode_pin_rotate_offset * Split based on the number of physical pb_type ports that have been defined */ diff --git a/libopenfpga/libarchopenfpga/src/write_xml_pb_type_annotation.cpp b/libopenfpga/libarchopenfpga/src/write_xml_pb_type_annotation.cpp index b013e5801..2bc512d85 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_pb_type_annotation.cpp +++ b/libopenfpga/libarchopenfpga/src/write_xml_pb_type_annotation.cpp @@ -126,13 +126,21 @@ void write_xml_pb_port_annotation(std::fstream& fp, } write_xml_attribute(fp, "physical_mode_port", physical_mode_port_attr.c_str()); + std::string physical_mode_pin_initial_offset_attr; + for (const auto& physical_pb_port_pair : pb_type_annotation.physical_pb_type_port(port_name)) { + if (false == physical_mode_pin_initial_offset_attr.empty()) { + physical_mode_pin_initial_offset_attr += " "; + } + physical_mode_pin_initial_offset_attr += std::to_string(physical_pb_port_pair.second[0]); + } + write_xml_attribute(fp, "physical_mode_pin_initial_offset", physical_mode_pin_initial_offset_attr.c_str()); std::string physical_mode_pin_rotate_offset_attr; for (const auto& physical_pb_port_pair : pb_type_annotation.physical_pb_type_port(port_name)) { if (false == physical_mode_pin_rotate_offset_attr.empty()) { physical_mode_pin_rotate_offset_attr += " "; } - physical_mode_pin_rotate_offset_attr += std::to_string(physical_pb_port_pair.second); + physical_mode_pin_rotate_offset_attr += std::to_string(physical_pb_port_pair.second[1]); } write_xml_attribute(fp, "physical_mode_pin_rotate_offset", physical_mode_pin_rotate_offset_attr.c_str()); diff --git a/openfpga/src/annotation/annotate_pb_graph.cpp b/openfpga/src/annotation/annotate_pb_graph.cpp index 249cb5632..67a1baba8 100644 --- a/openfpga/src/annotation/annotate_pb_graph.cpp +++ b/openfpga/src/annotation/annotate_pb_graph.cpp @@ -308,16 +308,23 @@ bool try_match_pb_graph_pin(t_pb_graph_pin* operating_pb_graph_pin, /* If the parent ports of the two pins are not paired, fail */ for (t_port* candidate_port : vpr_device_annotation.physical_pb_port(operating_pb_graph_pin->port)) { if (physical_pb_graph_pin->port != candidate_port) { - return false; + /* Not the one we want, try the next candidate */ + continue; } /* Check the pin number of physical pb_graph_pin matches the pin number of - * operating pb_graph_pin plus a rotation offset - * operating port physical port - * LSB port_range.lsb() pin_number pin_number MSB - * | | | - * Operating port | | +------ | - * | |<----offset--->| - * Physical port | + + + + * operating pb_graph_pin plus a rotation offset with an initial offset, + * which is to align the lsb between operating and physical ports + * + * For example: + * We can align the operating_port[32] to physical_port[0] with an initial offset + * which is -32 + * + * operating port physical port + * LSB port_range.lsb() pin_number pin_number MSB + * | | init_offset | + * Operating port | | +------ + | + * | |<----acc_offset--->| + * Physical port | + + + * * Note: * - accumulated offset is NOT the pin rotate offset specified by users @@ -327,11 +334,14 @@ bool try_match_pb_graph_pin(t_pb_graph_pin* operating_pb_graph_pin, * The accumulated offset will be reset to 0 when it exceeds the msb() of the physical port */ int acc_offset = vpr_device_annotation.physical_pb_pin_offset(operating_pb_graph_pin->port, candidate_port); + int init_offset = vpr_device_annotation.physical_pb_pin_initial_offset(operating_pb_graph_pin->port, candidate_port); const BasicPort& physical_port_range = vpr_device_annotation.physical_pb_port_range(operating_pb_graph_pin->port, candidate_port); if (physical_pb_graph_pin->pin_number != operating_pb_graph_pin->pin_number + (int)physical_port_range.get_lsb() + + init_offset + acc_offset) { - return false; + /* Not the one we want, try the next candidate */ + continue; } /* Reach here, it means all the requirements have been met */ diff --git a/openfpga/src/annotation/annotate_pb_types.cpp b/openfpga/src/annotation/annotate_pb_types.cpp index cb6c9e9b3..92a6f6560 100644 --- a/openfpga/src/annotation/annotate_pb_types.cpp +++ b/openfpga/src/annotation/annotate_pb_types.cpp @@ -211,7 +211,7 @@ bool pair_operating_and_physical_pb_types(t_pb_type* operating_pb_type, * if not found, we assume that the physical port is the same as the operating pb_port */ for (t_port* operating_pb_port : pb_type_ports(operating_pb_type)) { - std::map expected_physical_pb_ports = pb_type_annotation.physical_pb_type_port(std::string(operating_pb_port->name)); + std::map> expected_physical_pb_ports = pb_type_annotation.physical_pb_type_port(std::string(operating_pb_port->name)); /* If not defined in the annotation, set the default pair: * rotate_offset is 0 by default! @@ -220,7 +220,7 @@ bool pair_operating_and_physical_pb_types(t_pb_type* operating_pb_type, BasicPort expected_physical_pb_port; expected_physical_pb_port.set_name(std::string(operating_pb_port->name)); expected_physical_pb_port.set_width(operating_pb_port->num_pins); - expected_physical_pb_ports[expected_physical_pb_port] = 0; + expected_physical_pb_ports[expected_physical_pb_port] = {0, 0}; } for (const auto& expected_physical_pb_port : expected_physical_pb_ports) { @@ -241,7 +241,8 @@ bool pair_operating_and_physical_pb_types(t_pb_type* operating_pb_type, */ vpr_device_annotation.add_physical_pb_port(operating_pb_port, physical_pb_port); vpr_device_annotation.add_physical_pb_port_range(operating_pb_port, physical_pb_port, expected_physical_pb_port.first); - vpr_device_annotation.add_physical_pb_pin_rotate_offset(operating_pb_port, physical_pb_port, expected_physical_pb_port.second); + vpr_device_annotation.add_physical_pb_pin_initial_offset(operating_pb_port, physical_pb_port, expected_physical_pb_port.second[0]); + vpr_device_annotation.add_physical_pb_pin_rotate_offset(operating_pb_port, physical_pb_port, expected_physical_pb_port.second[1]); } } diff --git a/openfpga/src/annotation/vpr_device_annotation.cpp b/openfpga/src/annotation/vpr_device_annotation.cpp index f196f50da..f07378634 100644 --- a/openfpga/src/annotation/vpr_device_annotation.cpp +++ b/openfpga/src/annotation/vpr_device_annotation.cpp @@ -190,6 +190,21 @@ int VprDeviceAnnotation::physical_pb_type_index_offset(t_pb_type* pb_type) const return physical_pb_type_index_offsets_.at(pb_type); } +int VprDeviceAnnotation::physical_pb_pin_initial_offset(t_port* operating_pb_port, + t_port* physical_pb_port) const { + /* Ensure that the pb_type is in the list */ + std::map>::const_iterator it = physical_pb_pin_initial_offsets_.find(operating_pb_port); + if (it == physical_pb_pin_initial_offsets_.end()) { + /* Default value is 0 */ + return 0; + } + if (0 == physical_pb_pin_initial_offsets_.at(operating_pb_port).count(physical_pb_port)) { + /* Default value is 0 */ + return 0; + } + return physical_pb_pin_initial_offsets_.at(operating_pb_port).at(physical_pb_port); +} + int VprDeviceAnnotation::physical_pb_pin_rotate_offset(t_port* operating_pb_port, t_port* physical_pb_port) const { /* Ensure that the pb_type is in the list */ @@ -220,7 +235,6 @@ int VprDeviceAnnotation::physical_pb_pin_offset(t_port* operating_pb_port, return physical_pb_pin_offsets_.at(operating_pb_port).at(physical_pb_port); } - t_pb_graph_pin* VprDeviceAnnotation::physical_pb_graph_pin(const t_pb_graph_pin* pb_graph_pin) const { /* Ensure that the pb_type is in the list */ std::map::const_iterator it = physical_pb_graph_pins_.find(pb_graph_pin); @@ -410,6 +424,20 @@ void VprDeviceAnnotation::add_physical_pb_type_index_offset(t_pb_type* pb_type, physical_pb_type_index_offsets_[pb_type] = offset; } +void VprDeviceAnnotation::add_physical_pb_pin_initial_offset(t_port* operating_pb_port, + t_port* physical_pb_port, + const int& offset) { + /* Warn any override attempt */ + std::map>::const_iterator it = physical_pb_pin_initial_offsets_.find(operating_pb_port); + if ( (it != physical_pb_pin_initial_offsets_.end()) + && (0 < physical_pb_pin_initial_offsets_[operating_pb_port].count(physical_pb_port)) ) { + VTR_LOG_WARN("Override the annotation between operating pb_port '%s' and it physical pb_port '%s' pin rotate offset '%d'!\n", + operating_pb_port->name, offset); + } + + physical_pb_pin_initial_offsets_[operating_pb_port][physical_pb_port] = offset; +} + void VprDeviceAnnotation::add_physical_pb_pin_rotate_offset(t_port* operating_pb_port, t_port* physical_pb_port, const int& offset) { diff --git a/openfpga/src/annotation/vpr_device_annotation.h b/openfpga/src/annotation/vpr_device_annotation.h index 6ac9ab4b5..0e290f18e 100644 --- a/openfpga/src/annotation/vpr_device_annotation.h +++ b/openfpga/src/annotation/vpr_device_annotation.h @@ -61,6 +61,9 @@ class VprDeviceAnnotation { float physical_pb_type_index_factor(t_pb_type* pb_type) const; int physical_pb_type_index_offset(t_pb_type* pb_type) const; + int physical_pb_pin_initial_offset(t_port* operating_pb_port, + t_port* physical_pb_port) const; + int physical_pb_pin_rotate_offset(t_port* operating_pb_port, t_port* physical_pb_port) const; @@ -96,6 +99,9 @@ class VprDeviceAnnotation { t_pb_graph_node* physical_pb_graph_node); void add_physical_pb_type_index_factor(t_pb_type* pb_type, const float& factor); void add_physical_pb_type_index_offset(t_pb_type* pb_type, const int& offset); + void add_physical_pb_pin_initial_offset(t_port* operating_pb_port, + t_port* physical_pb_port, + const int& offset); void add_physical_pb_pin_rotate_offset(t_port* operating_pb_port, t_port* physical_pb_port, const int& offset); @@ -148,6 +154,7 @@ class VprDeviceAnnotation { * - the parent of physical pb_port MUST be a physical pb_type */ std::map> physical_pb_ports_; + std::map> physical_pb_pin_initial_offsets_; std::map> physical_pb_pin_rotate_offsets_; /* Accumulated offsets for a physical pb_type port, just for internal usage */ From 161d66083719415b381f6bc25f6f8cd426090395 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 15:00:46 -0600 Subject: [PATCH 031/148] update documentation for the initial offset when mapping physical pins --- .../manual/arch_lang/annotate_vpr_arch.rst | 39 ++++++++++++++++--- .../libarchopenfpga/src/pb_type_annotation.h | 2 +- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/docs/source/manual/arch_lang/annotate_vpr_arch.rst b/docs/source/manual/arch_lang/annotate_vpr_arch.rst index 5a6d77b14..e2f22ae37 100644 --- a/docs/source/manual/arch_lang/annotate_vpr_arch.rst +++ b/docs/source/manual/arch_lang/annotate_vpr_arch.rst @@ -103,8 +103,9 @@ The ``circuit_model_name`` should match the given name of a ``circuit_model`` de .. note:: OpenFPGA will infer the physical mode for a single-mode ``pb_type`` defined in VPR architecture -.. option:: +.. option:: Specify the physical implementation for a primitive ``pb_type`` in VPR architecture @@ -128,7 +129,8 @@ The ``circuit_model_name`` should match the given name of a ``circuit_model`` de - ``circuit_model_name=""`` For the interconnection type direct, the type of the linked circuit model should be wire. For multiplexers, the type of linked circuit model should be ``mux``. For complete, the type of the linked circuit model can be either ``mux`` or ``wire``, depending on the case. -.. option:: +.. option:: Link a port of an operating ``pb_type`` to a port of a physical ``pb_type`` @@ -136,9 +138,36 @@ The ``circuit_model_name`` should match the given name of a ``circuit_model`` de - ``physical_mode_pin="" creates the link of ``port`` of ``pb_type`` between operating and physical modes. This syntax is mandatory for every primitive ``pb_type`` in an operating mode ``pb_type``. It should be a valid ``port`` name of leaf ``pb_type`` in physical mode and the port size should also match. - .. note:: Users can define multiple ports. For example: ``physical_mode_pin="a[0:1] b[2:2]"``. When multiple ports are used, the ``physical_mode_pin_rotate_offset`` should also be adapt. For example: ``physical_mode_pin_rotate_offset="1 0"``) + .. note:: Users can define multiple ports. For example: ``physical_mode_pin="a[0:1] b[2:2]"``. When multiple ports are used, the ``physical_mode_pin_initial_offset`` and ``physical_mode_pin_rotate_offset`` should also be adapt. For example: ``physical_mode_pin_rotate_offset="1 0"``) - - ``physical_mode_pin_rotate_offset=""`` aims to align the pin indices for ``port`` of ``pb_type`` between operating and physical modes, especially when an operating mode contains multiple ``pb_type`` (``num_pb``>1) that are linked to the same physical ``pb_type``. When ``physical_mode_pin_rotate_offset`` is larger than zero, the pin index of ``pb_type`` (whose index is large than 1) will be shifted by the given offset. + + - ``physical_mode_pin_initial_offset=""`` aims to align the pin indices for ``port`` of ``pb_type`` between operating and physical modes, especially when part of port of operating mode is mapped to a port in physical ``pb_type``. When ``physical_mode_pin_initial_offset`` is larger than zero, the pin index of ``pb_type`` (whose index is large than 1) will be shifted by the given offset. + + .. note:: A quick example to understand the initial offset + For example, an initial offset of -32 is used to map + + - operating pb_type ``bram[0].dout[32]`` with a full path ``memory[dual_port].bram[0]`` + - operating pb_type ``bram[0].dout[33]`` with a full path ``memory[dual_port].bram[0]`` + + to + + - physical pb_type ``bram[0].dout_a[0]`` with a full path ``memory[physical].bram[0]`` + - physical pb_type ``bram[0].dout_a[1]`` with a full path ``memory[physical].bram[0]`` + + .. note:: If not defined, the default value of ``physical_mode_pin_initial_offset`` is set to ``0``. + + - ``physical_mode_pin_rotate_offset=""`` aims to align the pin indices for ``port`` of ``pb_type`` between operating and physical modes, especially when an operating mode contains multiple ``pb_type`` (``num_pb``>1) that are linked to the same physical ``pb_type``. When ``physical_mode_pin_rotate_offset`` is larger than zero, the pin index of ``pb_type`` (whose index is large than 1) will be shifted by the given offset. + + .. note:: A quick example to understand the rotate offset + For example, a rotating offset of 9 is used to map + + - operating pb_type ``mult_9x9[0].a[0:8]`` with a full path ``mult[frac].mult_9x9[0]`` + - operating pb_type ``mult_9x9[1].a[0:8]`` with a full path ``mult[frac].mult_9x9[1]`` + + to + + - physical pb_type ``mult_36x36.a[0:8]`` with a full path ``mult[physical].mult_36x36[0]`` + - physical pb_type ``mult_36x36.a[9:17]`` with a full path ``mult[physical].mult_36x36[0]`` .. note:: If not defined, the default value of ``physical_mode_pin_rotate_offset`` is set to ``0``. diff --git a/libopenfpga/libarchopenfpga/src/pb_type_annotation.h b/libopenfpga/libarchopenfpga/src/pb_type_annotation.h index 33941bce3..96383679d 100644 --- a/libopenfpga/libarchopenfpga/src/pb_type_annotation.h +++ b/libopenfpga/libarchopenfpga/src/pb_type_annotation.h @@ -158,7 +158,7 @@ class PbTypeAnnotation { * physical pb_type bram[0].dout_a[0] with a full path memory[physical].bram[0] * physical pb_type bram[0].dout_a[1] with a full path memory[physical].bram[0] * - * For example, a rotating offset of 1 is used to map + * For example, a rotating offset of 9 is used to map * operating pb_type mult_9x9[0].a[0:8] with a full path mult[frac].mult_9x9[0] * operating pb_type mult_9x9[1].a[0:8] with a full path mult[frac].mult_9x9[1] * to From aa4a9b28cce77a3f4ec7825c9bcb831d0601ec9b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 15:03:46 -0600 Subject: [PATCH 032/148] start testing the initial offset in the flagship architecture --- ...c_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml index b2fa706af..1eddefe25 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml @@ -309,9 +309,9 @@ - + - + From 3273f441fe4bd36cf3e83c5f8a696ef3c6aebdfb Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 15:23:20 -0600 Subject: [PATCH 033/148] bug fix in the flagship vpr arch --- ...rac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml index c56ce6041..2f14a7c2f 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_frac_mem32K_frac_dsp36_40nm.xml @@ -321,7 +321,7 @@ - + 1 1 1 1 1 1 1 1 1 From 18735894f9ef167c2f30e7c927cdd29a83e08a22 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 15:27:30 -0600 Subject: [PATCH 034/148] bug fix in openfpga arch: data1 and out1 should have the same offset as the data2 and out2 --- ...c_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml index 1eddefe25..ef872ccbf 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml @@ -309,9 +309,9 @@ - + - + From f0bc6f83f1b0943fef95c81166196762c01974e6 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 15:34:59 -0600 Subject: [PATCH 035/148] disable buffer absorbing in the template script for bitstream generation. This is applicable to a wide range of benchmarks --- .../generate_bitstream_example_script.openfpga | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/OpenFPGAShellScripts/generate_bitstream_example_script.openfpga b/openfpga_flow/OpenFPGAShellScripts/generate_bitstream_example_script.openfpga index ab79ce90a..d1b8b4ada 100644 --- a/openfpga_flow/OpenFPGAShellScripts/generate_bitstream_example_script.openfpga +++ b/openfpga_flow/OpenFPGAShellScripts/generate_bitstream_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 route --absorb_buffer_luts off # Read OpenFPGA architecture definition read_openfpga_arch -f ${OPENFPGA_ARCH_FILE} From f3ca1c0973237fe845476704c76879e47ece4ec8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 17:28:25 -0600 Subject: [PATCH 036/148] fix rr_graph on thru routing channel support --- vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp b/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp index 4107d4538..3e9004175 100644 --- a/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp @@ -840,7 +840,7 @@ void load_chanx_rr_nodes_basic_info(RRGraph& rr_graph, * track0 ----->+-----------------------------+----> track0 * | | */ - if (true == is_chanx_exist(grids, chanx_coord)) { + if (true == is_chanx_exist(grids, chanx_coord, through_channel)) { /* Rotate the chanx_details by an offset of ix - 1, the distance to the most left channel */ /* For INC_DIRECTION, we use clockwise rotation * node_id A ----> -----> node_id D @@ -951,7 +951,7 @@ void load_chany_rr_nodes_basic_info(RRGraph& rr_graph, * | | * we should rotate only once at the bottom side of a grid */ - if (true == is_chany_exist(grids, chany_coord)) { + if (true == is_chany_exist(grids, chany_coord, through_channel)) { /* Rotate the chany_details by an offset of 1*/ /* For INC_DIRECTION, we use clockwise rotation * node_id A ----> -----> node_id D From bf08e1841caed464f7efc8ea5bdd95adf59562cd Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 17:58:34 -0600 Subject: [PATCH 037/148] add new test case using thru channels --- .../thru_channel/config/task.conf | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 openfpga_flow/tasks/fpga_verilog/thru_channel/config/task.conf diff --git a/openfpga_flow/tasks/fpga_verilog/thru_channel/config/task.conf b/openfpga_flow/tasks/fpga_verilog/thru_channel/config/task.conf new file mode 100644 index 000000000..1e2941de1 --- /dev/null +++ b/openfpga_flow/tasks/fpga_verilog/thru_channel/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=vpr_blif + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/OpenFPGAShellScripts/example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_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_thru_channel_adder_chain_mem16K_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= From d6d17675e26cf62bd9ef17aced1ab55f77fb9fa0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 18:01:32 -0600 Subject: [PATCH 038/148] update docoumentation about the constraints when using tileable rr_graph generator --- docs/source/manual/arch_lang/addon_vpr_syntax.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/manual/arch_lang/addon_vpr_syntax.rst b/docs/source/manual/arch_lang/addon_vpr_syntax.rst index 18f61703a..4af72aa7c 100644 --- a/docs/source/manual/arch_lang/addon_vpr_syntax.rst +++ b/docs/source/manual/arch_lang/addon_vpr_syntax.rst @@ -54,6 +54,8 @@ Layout .. warning:: Currently ``through_channel`` supports only a fixed routing channel width! + .. warning:: You must define custom 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!!! + A quick example to show tileable routing is enabled and through channels are disabled: From 47f15729adda78734f5222071f044ae2b145bd6b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 18:37:28 -0600 Subject: [PATCH 039/148] update doc about the limitation on using tileable routing --- docs/source/manual/arch_lang/addon_vpr_syntax.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/manual/arch_lang/addon_vpr_syntax.rst b/docs/source/manual/arch_lang/addon_vpr_syntax.rst index 4af72aa7c..dc7e868b6 100644 --- a/docs/source/manual/arch_lang/addon_vpr_syntax.rst +++ b/docs/source/manual/arch_lang/addon_vpr_syntax.rst @@ -54,7 +54,7 @@ Layout .. warning:: Currently ``through_channel`` supports only a fixed routing channel width! - .. warning:: You must define custom 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!!! + .. 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!!! A quick example to show tileable routing is enabled and through channels are disabled: From 881672d46a7ef58abf908fb9213340ca657162d7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 19:52:35 -0600 Subject: [PATCH 040/148] update thru channel arch for avoid buggy pin locations --- ...N10_tileable_thru_channel_adder_chain_mem16K_40nm.xml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_mem16K_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_mem16K_40nm.xml index 7e4cf8e7a..ffefc9146 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_mem16K_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_mem16K_40nm.xml @@ -188,7 +188,14 @@ - + + From 8041c90f127aceb35499fadc37aff1c4efee7a40 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 20:01:50 -0600 Subject: [PATCH 041/148] bug fix in through channel support in tileable routing --- vpr/src/route/route_common.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpr/src/route/route_common.cpp b/vpr/src/route/route_common.cpp index 8fd661d05..6ced2779f 100644 --- a/vpr/src/route/route_common.cpp +++ b/vpr/src/route/route_common.cpp @@ -292,7 +292,7 @@ bool try_route(int width_fac, segment_inf, router_opts.base_cost_type, router_opts.trim_empty_channels, - router_opts.trim_obs_channels, + router_opts.trim_obs_channels || det_routing_arch->through_channel, router_opts.clock_modeling, directs, num_directs, &warning_count); From 1a3e0201749c2078cc8dacde28be0d1b1c9f02dc Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 20:04:01 -0600 Subject: [PATCH 042/148] deploy through channel test case to CI --- .travis/fpga_verilog_reg_test.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis/fpga_verilog_reg_test.sh b/.travis/fpga_verilog_reg_test.sh index 7ee1a5095..8424475fb 100755 --- a/.travis/fpga_verilog_reg_test.sh +++ b/.travis/fpga_verilog_reg_test.sh @@ -81,6 +81,9 @@ python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/power_gated_design/p echo -e "Testing Depopulated crossbar in local routing"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/depopulate_crossbar --debug --show_thread_logs +echo -e "Testing through channels in tileable routing"; +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/thru_channel --debug --show_thread_logs + # Verify MCNC big20 benchmark suite with ModelSim # Please make sure you have ModelSim installed in the environment # Otherwise, it will fail From 6c925dcded560ac9b7525341830c26f12ad3661b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 20:11:37 -0600 Subject: [PATCH 043/148] [regression test] Add more tests for thru channels and deploy to CI --- .travis/fpga_verilog_reg_test.sh | 3 +- .../{ => thru_narrow_tile}/config/task.conf | 0 .../thru_wide_tile/config/task.conf | 36 + ..._tileable_adder_chain_wide_mem16K_40nm.xml | 2 +- ...u_channel_adder_chain_wide_mem16K_40nm.xml | 734 ++++++++++++++++++ 5 files changed, 773 insertions(+), 2 deletions(-) rename openfpga_flow/tasks/fpga_verilog/thru_channel/{ => thru_narrow_tile}/config/task.conf (100%) create mode 100644 openfpga_flow/tasks/fpga_verilog/thru_channel/thru_wide_tile/config/task.conf create mode 100644 openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_wide_mem16K_40nm.xml diff --git a/.travis/fpga_verilog_reg_test.sh b/.travis/fpga_verilog_reg_test.sh index 8424475fb..fde145744 100755 --- a/.travis/fpga_verilog_reg_test.sh +++ b/.travis/fpga_verilog_reg_test.sh @@ -82,7 +82,8 @@ echo -e "Testing Depopulated crossbar in local routing"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/depopulate_crossbar --debug --show_thread_logs echo -e "Testing through channels in tileable routing"; -python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/thru_channel --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/thru_channel/thru_narrow_tile --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/thru_channel/thru_wide_tile --debug --show_thread_logs # Verify MCNC big20 benchmark suite with ModelSim # Please make sure you have ModelSim installed in the environment diff --git a/openfpga_flow/tasks/fpga_verilog/thru_channel/config/task.conf b/openfpga_flow/tasks/fpga_verilog/thru_channel/thru_narrow_tile/config/task.conf similarity index 100% rename from openfpga_flow/tasks/fpga_verilog/thru_channel/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/thru_channel/thru_narrow_tile/config/task.conf diff --git a/openfpga_flow/tasks/fpga_verilog/thru_channel/thru_wide_tile/config/task.conf b/openfpga_flow/tasks/fpga_verilog/thru_channel/thru_wide_tile/config/task.conf new file mode 100644 index 000000000..5f6de1078 --- /dev/null +++ b/openfpga_flow/tasks/fpga_verilog/thru_channel/thru_wide_tile/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=vpr_blif + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/OpenFPGAShellScripts/example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_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_thru_channel_adder_chain_wide_mem16K_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/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml index f07f16c80..46b23c83e 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_chain_wide_mem16K_40nm.xml @@ -188,7 +188,7 @@ - + diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_wide_mem16K_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_wide_mem16K_40nm.xml new file mode 100644 index 000000000..bc5c762b3 --- /dev/null +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_wide_mem16K_40nm.xml @@ -0,0 +1,734 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + + + + + + + + + + + + + + + + + + + clb.clk + clb.cin + clb.O[9:0] clb.I[19:0] + clb.cout clb.O[19:10] clb.I[39:20] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 195e-12 + 195e-12 + 195e-12 + 195e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From fb5a5a244871c52e6608be163757a6ba942ee0b7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 19 Aug 2020 20:12:49 -0600 Subject: [PATCH 044/148] [documentation] remove the limitation on through channels --- docs/source/manual/arch_lang/addon_vpr_syntax.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/source/manual/arch_lang/addon_vpr_syntax.rst b/docs/source/manual/arch_lang/addon_vpr_syntax.rst index dc7e868b6..773ac0992 100644 --- a/docs/source/manual/arch_lang/addon_vpr_syntax.rst +++ b/docs/source/manual/arch_lang/addon_vpr_syntax.rst @@ -52,8 +52,6 @@ Layout .. warning:: Do NOT enable ``through_channel`` if you are not using the tileable routing resource graph generator! - .. warning:: Currently ``through_channel`` supports only a fixed routing channel width! - .. 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!!! From b83319bf14d6e4578af7564d833c1727bf057632 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 23 Aug 2020 13:48:22 -0600 Subject: [PATCH 045/148] [Check codes] add check codes for default circuit models. Error out when there is no default model in a defined group --- .../src/check_circuit_library.cpp | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp b/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp index ea6db0678..e438ba5f8 100644 --- a/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp @@ -197,6 +197,43 @@ size_t check_circuit_model_port_required(const CircuitLibrary& circuit_lib, return num_err; } +/************************************************************************ + * A generic function to search each default circuit model by types + * that have been defined by users. + * If a type of circuit model is defined, we expect there is a default model + * to be specified + ***********************************************************************/ +static +size_t check_default_circuit_model_by_types(const CircuitLibrary& circuit_lib) { + size_t num_err = 0; + + for (size_t itype = 0; itype < NUM_CIRCUIT_MODEL_TYPES; ++itype) { + std::vector curr_models = circuit_lib.models_by_type(e_circuit_model_type(itype)); + if (0 == curr_models.size()) { + continue; + } + /* Go through the models and try to find a default one */ + size_t found_default_counter = 0; + for (const auto& curr_model : curr_models) { + if (true == circuit_lib.model_is_default(curr_model)) { + found_default_counter++; + } + } + if (0 == found_default_counter) { + VTR_LOG_ERROR("Miss a default circuit model for the type %s! Try to define it in your architecture file!\n", + CIRCUIT_MODEL_TYPE_STRING[itype]); + num_err++; + } + if (1 < found_default_counter) { + VTR_LOG_ERROR("Found >1 default circuit models for the type %s! Expect only one!\n", + CIRCUIT_MODEL_TYPE_STRING[itype]); + num_err++; + } + } + + return num_err; +} + /************************************************************************ * A generic function to find the default circuit model with a given type * If not found, we give an error @@ -207,9 +244,9 @@ size_t check_required_default_circuit_model(const CircuitLibrary& circuit_lib, size_t num_err = 0; if (CircuitModelId::INVALID() == circuit_lib.default_model(circuit_model_type)) { - VTR_LOG_ERROR("A default circuit model for the type %s! Try to define it in your architecture file!\n", + VTR_LOG_ERROR("Miss a default circuit model for the type %s! Try to define it in your architecture file!\n", CIRCUIT_MODEL_TYPE_STRING[size_t(circuit_model_type)]); - exit(1); + num_err++; } return num_err; @@ -626,7 +663,10 @@ bool check_circuit_library(const CircuitLibrary& circuit_lib) { num_err += check_circuit_model_port_required(circuit_lib, CIRCUIT_MODEL_LUT, lut_port_types_required); - /* 10. We must have default circuit models for these types: MUX, channel wires and wires */ + /* 10. For each type of circuit models that are define, we must have 1 default model + * We must have default circuit models for these types: MUX, channel wires and wires + */ + num_err += check_default_circuit_model_by_types(circuit_lib); num_err += check_required_default_circuit_model(circuit_lib, CIRCUIT_MODEL_MUX); num_err += check_required_default_circuit_model(circuit_lib, CIRCUIT_MODEL_CHAN_WIRE); num_err += check_required_default_circuit_model(circuit_lib, CIRCUIT_MODEL_WIRE); From 9c66a35bf6861a993eb2f35581c7fc6fe19840cd Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 23 Aug 2020 14:06:03 -0600 Subject: [PATCH 046/148] [arch language] Now circuit library will automatically identify the default circuit model if needed --- .../libarchopenfpga/src/circuit_library.cpp | 18 ++++++++++++++++++ .../libarchopenfpga/src/circuit_library.h | 4 ++++ .../src/read_xml_openfpga_arch.cpp | 3 +++ 3 files changed, 25 insertions(+) diff --git a/libopenfpga/libarchopenfpga/src/circuit_library.cpp b/libopenfpga/libarchopenfpga/src/circuit_library.cpp index cccc62e71..881035ee7 100644 --- a/libopenfpga/libarchopenfpga/src/circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/circuit_library.cpp @@ -2,6 +2,7 @@ #include #include "vtr_assert.h" +#include "vtr_log.h" #include "openfpga_port_parser.h" #include "circuit_library.h" @@ -2108,6 +2109,23 @@ void CircuitLibrary::build_timing_graphs() { return; } +/* Automatically identify the default models for each type*/ +void CircuitLibrary::auto_detect_default_models() { + /* Go through the model fast look-up */ + for (const auto& curr_type_models : model_lookup_) { + if ( (1 == curr_type_models.size()) + && (false == model_is_default(curr_type_models[0]))) { + /* This is the only model in this type, + * it is safe to set it to be default + * Give a warning for users + */ + set_model_is_default(curr_type_models[0], true); + VTR_LOG_WARN("Automatically set circuit model '%s' to be default in its type.\n", + model_name(curr_type_models[0]).c_str()); + } + } +} + /************************************************************************ * Internal mutators: build timing graphs ***********************************************************************/ diff --git a/libopenfpga/libarchopenfpga/src/circuit_library.h b/libopenfpga/libarchopenfpga/src/circuit_library.h index abb03d1f8..5e023c665 100644 --- a/libopenfpga/libarchopenfpga/src/circuit_library.h +++ b/libopenfpga/libarchopenfpga/src/circuit_library.h @@ -461,6 +461,10 @@ class CircuitLibrary { public: /* Public Mutators: builders */ void build_model_links(); void build_timing_graphs(); + /* Automatically identify the default models for each type, + * suggest to do this after circuit library is built + */ + void auto_detect_default_models(); public: /* Internal mutators: build timing graphs */ void add_edge(const CircuitModelId& model_id, const CircuitPortId& from_port, const size_t& from_pin, diff --git a/libopenfpga/libarchopenfpga/src/read_xml_openfpga_arch.cpp b/libopenfpga/libarchopenfpga/src/read_xml_openfpga_arch.cpp index 6bc31ef46..e8ce6aeab 100644 --- a/libopenfpga/libarchopenfpga/src/read_xml_openfpga_arch.cpp +++ b/libopenfpga/libarchopenfpga/src/read_xml_openfpga_arch.cpp @@ -52,6 +52,9 @@ openfpga::Arch read_xml_openfpga_arch(const char* arch_file_name) { auto xml_circuit_models = get_single_child(xml_openfpga_arch, "circuit_library", loc_data); openfpga_arch.circuit_lib = read_xml_circuit_library(xml_circuit_models, loc_data); + /* Automatically identify the default models for circuit library */ + openfpga_arch.circuit_lib.auto_detect_default_models(); + /* Build the internal links for the circuit library */ openfpga_arch.circuit_lib.build_model_links(); From ac8e937a504747dd028e5288f8009ab858c150c4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 23 Aug 2020 14:08:38 -0600 Subject: [PATCH 047/148] [Documentation] Update for default circuit model rules --- docs/source/manual/arch_lang/circuit_library.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/manual/arch_lang/circuit_library.rst b/docs/source/manual/arch_lang/circuit_library.rst index 9fe29a0fa..59e5f055f 100644 --- a/docs/source/manual/arch_lang/circuit_library.rst +++ b/docs/source/manual/arch_lang/circuit_library.rst @@ -78,9 +78,9 @@ Here, we focus these common syntax and we will detail special syntax in :ref:`ci .. warning:: ``prefix`` may be deprecated soon -.. note:: Multiplexers cannot be user-defined. +.. warning:: Multiplexers cannot be user-defined. -.. note:: For a circuit model type, only one circuit model can be set as default. +.. warning:: For a circuit model type, only one circuit model is allowed to be set as default. If there is only one circuit model defined in a type, it will be considered as the default automatically. .. note:: If ```` or ```` are not specified, FPGA-Verilog/SPICE auto-generates the Verilog/SPICE netlists for multiplexers, wires, and LUTs. From 9101ba102143ef98de7a1f137a54956775c7a6d9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 23 Aug 2020 14:55:44 -0600 Subject: [PATCH 048/148] [Architecture Language] Update openfpga architecture files for default models --- .../openfpga_arch/k6_frac_N10_adder_chain_40nm_openfpga.xml | 2 +- ...c_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml | 2 +- .../k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml | 2 +- .../k6_frac_N10_adder_chain_mem16K_aib_40nm_openfpga.xml | 4 ++-- .../k6_frac_N10_adder_column_chain_40nm_openfpga.xml | 2 +- .../k6_frac_N10_adder_register_chain_40nm_openfpga.xml | 2 +- .../k6_frac_N10_adder_register_scan_chain_40nm_openfpga.xml | 2 +- ...ac_N10_adder_register_scan_chain_depop50_40nm_openfpga.xml | 2 +- ...adder_register_scan_chain_depop50_spypad_40nm_openfpga.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_40nm_openfpga.xml index bbc33f2e8..6d5df7467 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_40nm_openfpga.xml @@ -184,7 +184,7 @@ - + diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml index ef872ccbf..6ddb0d681 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_frac_mem32K_frac_dsp36_40nm_openfpga.xml @@ -184,7 +184,7 @@ - + diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml index c4d47ad2c..b8c99a382 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml @@ -184,7 +184,7 @@ - + diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_aib_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_aib_40nm_openfpga.xml index 5925aeb2d..c0c1aa581 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_aib_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_aib_40nm_openfpga.xml @@ -175,7 +175,7 @@ - + @@ -184,7 +184,7 @@ - + diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_column_chain_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_column_chain_40nm_openfpga.xml index f2265e48c..26af034f5 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_column_chain_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_column_chain_40nm_openfpga.xml @@ -184,7 +184,7 @@ - + diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_chain_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_chain_40nm_openfpga.xml index 3597e7c50..e10c12d27 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_chain_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_chain_40nm_openfpga.xml @@ -184,7 +184,7 @@ - + diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_40nm_openfpga.xml index b17169f78..8537cfc18 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_40nm_openfpga.xml @@ -189,7 +189,7 @@ - + diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_40nm_openfpga.xml index f26140913..b815842e7 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_40nm_openfpga.xml @@ -189,7 +189,7 @@ - + diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_spypad_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_spypad_40nm_openfpga.xml index 7f31c435f..943d3c040 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_spypad_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_spypad_40nm_openfpga.xml @@ -204,7 +204,7 @@ - + From 4b3142c4ee6fa4f8a71628e250aebf2236912cb9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 23 Aug 2020 15:13:28 -0600 Subject: [PATCH 049/148] [Architecture File] Patch openfpga architecture with default circuit model definition --- ...0_adder_register_scan_chain_depop50_spypad_40nm_openfpga.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_spypad_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_spypad_40nm_openfpga.xml index 943d3c040..c74625e33 100644 --- a/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_spypad_40nm_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k6_frac_N10_adder_register_scan_chain_depop50_spypad_40nm_openfpga.xml @@ -154,7 +154,7 @@ - + From b5251ce5af7616d87a8436c351a652ce314680bb Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 1 Sep 2020 11:07:50 -0600 Subject: [PATCH 050/148] [documentation] update motivation figure and layout licenses --- docs/source/figures/openfpga_motivation.png | Bin 279950 -> 279839 bytes docs/source/motivation.rst | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/figures/openfpga_motivation.png b/docs/source/figures/openfpga_motivation.png index 30002f5a1f62a01cba8bc19808703f22157c594a..33080f1dbc75ff13f7ffc456cc85cc6e5471a492 100644 GIT binary patch literal 279839 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
!pfm&K?GcT_?VB2;G%cXu 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
    K)#n*XZ45ryWBwmQl5bMvOG8U&mTA!84 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 literal 279950 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

    dFT|XlT`j5IhajngHkXJ#MB2!(){1HtC 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%0AH!FnUlO~(P0;hxnAxdQvnuB;P6)Hj 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

    *2_ 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 A2LJ#7 diff --git a/docs/source/motivation.rst b/docs/source/motivation.rst index 01ae7da54..b70b92ace 100644 --- a/docs/source/motivation.rst +++ b/docs/source/motivation.rst @@ -9,7 +9,7 @@ OpenFPGA aims to be an open-source framework that enables rapid prototyping of c :scale: 50% :alt: OpenFPGA: a fast prototyping framework for customizable FPGAs - Comparison on engineering time and effort to prototype an FPGA using OpenFPGA and conventional approaches + Comparison on engineering time and effort to prototype an FPGA using OpenFPGA and conventional approaches [All the layout figures are permitted to publish under proper licenses] Using OpenFPGA, the development cycle in both hardware and software can be significantly accelerated. OpenFPGA can automatically generate Verilog netlists describing a full FPGA fabric based on an XML-based description file. Thanks to modern semi-custom design tools, production-ready layout generation can be achieved within 24 hours. To help sign-off, OpenFPGA can auto-generate Verilog testbenches to validate the correctness of FPGA fabric using modern verification tools. OpenFPGA also provides native bitstream generation support based the same XML-based description file used in Verilog generation. This avoid the recurring engineering in developing CAD tools for different FPGAs. Once the FPGA architecture is finalized, the CAD tool is ready to use. From 04070fd4ca24aa3cdf9366dff3a054ec9093493c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 2 Sep 2020 22:16:10 -0600 Subject: [PATCH 051/148] [Debug aid] add pb_type full hierarchy path in the error message of architecture binding checker --- .../annotation/check_pb_type_annotation.cpp | 9 +++-- openfpga/src/utils/pb_type_utils.cpp | 38 +++++++++++++++++++ openfpga/src/utils/pb_type_utils.h | 2 + 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/openfpga/src/annotation/check_pb_type_annotation.cpp b/openfpga/src/annotation/check_pb_type_annotation.cpp index d0709c81c..1ed6fea8b 100644 --- a/openfpga/src/annotation/check_pb_type_annotation.cpp +++ b/openfpga/src/annotation/check_pb_type_annotation.cpp @@ -194,7 +194,7 @@ void rec_check_vpr_pb_type_circuit_model_annotation(t_pb_type* cur_pb_type, /* Every physical pb_type should be linked to a valid circuit model */ if (CircuitModelId::INVALID() == vpr_device_annotation.pb_type_circuit_model(cur_pb_type)) { VTR_LOG_ERROR("Found a physical pb_type '%s' missing circuit model binding!\n", - cur_pb_type->name); + generate_pb_type_hierarchy_path(cur_pb_type).c_str()); num_err++; return; /* Invalid id already, further check is not applicable */ } @@ -202,7 +202,8 @@ void rec_check_vpr_pb_type_circuit_model_annotation(t_pb_type* cur_pb_type, for (t_port* port : pb_type_ports(cur_pb_type)) { if (CircuitPortId::INVALID() == vpr_device_annotation.pb_circuit_port(port)) { VTR_LOG_ERROR("Found a port '%s' of physical pb_type '%s' missing circuit port binding!\n", - port->name, cur_pb_type->name); + port->name, + generate_pb_type_hierarchy_path(cur_pb_type).c_str()); num_err++; } } @@ -217,7 +218,7 @@ void rec_check_vpr_pb_type_circuit_model_annotation(t_pb_type* cur_pb_type, VTR_LOG_ERROR("Found an interconnect '%s' under physical mode '%s' of pb_type '%s' missing circuit model binding!\n", interc->name, physical_mode->name, - cur_pb_type->name); + generate_pb_type_hierarchy_path(cur_pb_type).c_str()); num_err++; continue; } @@ -226,7 +227,7 @@ void rec_check_vpr_pb_type_circuit_model_annotation(t_pb_type* cur_pb_type, VTR_LOG_ERROR("Found an interconnect '%s' under physical mode '%s' of pb_type '%s' linked to a circuit model '%s' with a wrong type!\nExpect: '%s' Linked: '%s'\n", interc->name, physical_mode->name, - cur_pb_type->name, + generate_pb_type_hierarchy_path(cur_pb_type).c_str(), circuit_lib.model_name(interc_circuit_model).c_str(), CIRCUIT_MODEL_TYPE_STRING[circuit_lib.model_type(interc_circuit_model)], CIRCUIT_MODEL_TYPE_STRING[required_circuit_model_type]); diff --git a/openfpga/src/utils/pb_type_utils.cpp b/openfpga/src/utils/pb_type_utils.cpp index 9166a16e8..c9fed784c 100644 --- a/openfpga/src/utils/pb_type_utils.cpp +++ b/openfpga/src/utils/pb_type_utils.cpp @@ -324,4 +324,42 @@ std::vector find_pb_type_ports_match_circuit_model_port_type(t_pb_type* return ports; } +/********************************************************************* + * Generate the full hierarchy for a pb_type + * The final name will be in the following format: + * []. ... + * + * TODO: This function should be part of the VPR libarchfpga parser + **********************************************************************/ +std::string generate_pb_type_hierarchy_path(t_pb_type* cur_pb_type) { + std::string hie_name(cur_pb_type->name); + + t_pb_type* parent_pb_type = cur_pb_type; + + /* Backward trace until we meet the top-level pb_type */ + while (1) { + /* If there is no parent mode, this is a top-level pb_type, quit the loop here */ + t_mode* parent_mode = parent_pb_type->parent_mode; + if (NULL == parent_mode) { + break; + } + + /* Add the mode name to the full hierarchy */ + hie_name = std::string("[") + std::string(parent_mode->name) + std::string("].") + hie_name; + + /* Backtrace to the upper level */ + parent_pb_type = parent_mode->parent_pb_type; + + /* If there is no parent pb_type, this is a top-level pb_type, quit the loop here */ + if (NULL == parent_pb_type) { + break; + } + + /* Add the current pb_type name to the hierarchy name */ + hie_name = std::string(parent_pb_type->name) + hie_name; + } + + return hie_name; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/utils/pb_type_utils.h b/openfpga/src/utils/pb_type_utils.h index 5d23f23bc..83ba9000b 100644 --- a/openfpga/src/utils/pb_type_utils.h +++ b/openfpga/src/utils/pb_type_utils.h @@ -57,6 +57,8 @@ std::vector find_pb_type_ports_match_circuit_model_port_type(t_pb_type* const e_circuit_model_port_type& port_type, const VprDeviceAnnotation& vpr_device_annotation); +std::string generate_pb_type_hierarchy_path(t_pb_type* cur_pb_type); + } /* end namespace openfpga */ #endif From 7a2502ddf9ee99b3710aa1a075a3f08ef52218fb Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 2 Sep 2020 22:47:14 -0600 Subject: [PATCH 052/148] [documentation] add more guidelines about the vpr-openfpga architecture annotation --- docs/source/manual/arch_lang/annotate_vpr_arch.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/source/manual/arch_lang/annotate_vpr_arch.rst b/docs/source/manual/arch_lang/annotate_vpr_arch.rst index e2f22ae37..d990e3092 100644 --- a/docs/source/manual/arch_lang/annotate_vpr_arch.rst +++ b/docs/source/manual/arch_lang/annotate_vpr_arch.rst @@ -111,6 +111,8 @@ The ``circuit_model_name`` should match the given name of a ``circuit_model`` de .. note:: This should be applied to primitive ``pb_type``, i.e., ``pb_type`` have no children. + .. note:: This definition should be placed directly under the XML node ```` without any intermediate XML nodes! + - ``name=""`` specifiy the full name of a ``pb_type`` in the hierarchy of VPR architecture. - ``physical_pb_type_name=`` creates the link on ``pb_type`` between operating and physical modes. This syntax is mandatory for every primitive ``pb_type`` in an operating mode ``pb_type``. It should be a valid name of primitive ``pb_type`` in physical mode. @@ -125,10 +127,12 @@ The ``circuit_model_name`` should match the given name of a ``circuit_model`` de .. option:: - - ``name=""`` specifiy the name of a ``interconnect`` in VPR architecture. Different from ``pb_type``, hierarchical name is not required here. + - ``name=""`` specify the name of a ``interconnect`` in VPR architecture. Different from ``pb_type``, hierarchical name is not required here. - ``circuit_model_name=""`` For the interconnection type direct, the type of the linked circuit model should be wire. For multiplexers, the type of linked circuit model should be ``mux``. For complete, the type of the linked circuit model can be either ``mux`` or ``wire``, depending on the case. + .. note:: A ```` parent XML node is required for the interconnect-to-circuit bindings whose interconnects are defined under the ``pb_type`` in VPR architecture description. + .. option:: From 9bf0e772a355beb8d148c6637ae0ee0feac8609b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 15:45:35 -0600 Subject: [PATCH 053/148] [Regression Tests]Add a new testcase for routing multiplexer designs without buffers --- .../mux_design/debuf_mux/config/task.conf | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 openfpga_flow/tasks/fpga_verilog/mux_design/debuf_mux/config/task.conf diff --git a/openfpga_flow/tasks/fpga_verilog/mux_design/debuf_mux/config/task.conf b/openfpga_flow/tasks/fpga_verilog/mux_design/debuf_mux/config/task.conf new file mode 100644 index 000000000..c076c6071 --- /dev/null +++ b/openfpga_flow/tasks/fpga_verilog/mux_design/debuf_mux/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=vpr_blif + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/OpenFPGAShellScripts/example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N8_debuf_mux_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_N8_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 eecfd186f0f2d7b80b67c8322291b029b2333c0f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 15:46:10 -0600 Subject: [PATCH 054/148] [OpenFPGA Architecture] Add the openfpga architecture for multiplexers without buffers --- .../k6_frac_N8_debuf_mux_40nm_openfpga.xml | 231 ++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k6_frac_N8_debuf_mux_40nm_openfpga.xml diff --git a/openfpga_flow/openfpga_arch/k6_frac_N8_debuf_mux_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N8_debuf_mux_40nm_openfpga.xml new file mode 100644 index 000000000..b0c8c6ba0 --- /dev/null +++ b/openfpga_flow/openfpga_arch/k6_frac_N8_debuf_mux_40nm_openfpga.xml @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a03f2fe974512ab465f7e06703ef8d88f6085ac0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 15:48:08 -0600 Subject: [PATCH 055/148] [Regression Test] Deploy the debuf mux test case to CI --- .travis/fpga_verilog_reg_test.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis/fpga_verilog_reg_test.sh b/.travis/fpga_verilog_reg_test.sh index fde145744..f4f0d82d0 100755 --- a/.travis/fpga_verilog_reg_test.sh +++ b/.travis/fpga_verilog_reg_test.sh @@ -55,6 +55,9 @@ python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/stdcell_m echo -e "Testing Verilog generation with routing multiplexers implemented by local encoders"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/local_encoder --debug --show_thread_logs +echo -e "Testing Verilog generation with routing multiplexers without buffers"; +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/debuf_mux --debug --show_thread_logs + echo -e "Testing Verilog generation with behavioral description"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/behavioral_verilog --debug --show_thread_logs From aa9521b23b667be3517926e7f8fcfb6ee2a4ee45 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 15:57:44 -0600 Subject: [PATCH 056/148] [OpenFPGA architecture] Add the architecture where routing multiplexers have only input buffers --- ...6_frac_N8_inbuf_only_mux_40nm_openfpga.xml | 231 ++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k6_frac_N8_inbuf_only_mux_40nm_openfpga.xml diff --git a/openfpga_flow/openfpga_arch/k6_frac_N8_inbuf_only_mux_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N8_inbuf_only_mux_40nm_openfpga.xml new file mode 100644 index 000000000..1cb12f465 --- /dev/null +++ b/openfpga_flow/openfpga_arch/k6_frac_N8_inbuf_only_mux_40nm_openfpga.xml @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From aaf63050bbedd5192ddcc5af5b290a52d323cfcf Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 15:58:34 -0600 Subject: [PATCH 057/148] [OpenFPGA architecture] Add the architecture where routing multiplexers have only output buffers --- ..._frac_N8_outbuf_only_mux_40nm_openfpga.xml | 231 ++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k6_frac_N8_outbuf_only_mux_40nm_openfpga.xml diff --git a/openfpga_flow/openfpga_arch/k6_frac_N8_outbuf_only_mux_40nm_openfpga.xml b/openfpga_flow/openfpga_arch/k6_frac_N8_outbuf_only_mux_40nm_openfpga.xml new file mode 100644 index 000000000..07b4e93d8 --- /dev/null +++ b/openfpga_flow/openfpga_arch/k6_frac_N8_outbuf_only_mux_40nm_openfpga.xml @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From f42411c29e327ed8b38c00ba2a5a8df3e51789ea Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 16:03:43 -0600 Subject: [PATCH 058/148] [Regression Tests] Add test cases for routing multiplexer design with input/output buffers only --- .../inbuf_only_mux/config/task.conf | 37 +++++++++++++++++++ .../outbuf_only_mux/config/task.conf | 37 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 openfpga_flow/tasks/fpga_verilog/mux_design/inbuf_only_mux/config/task.conf create mode 100644 openfpga_flow/tasks/fpga_verilog/mux_design/outbuf_only_mux/config/task.conf diff --git a/openfpga_flow/tasks/fpga_verilog/mux_design/inbuf_only_mux/config/task.conf b/openfpga_flow/tasks/fpga_verilog/mux_design/inbuf_only_mux/config/task.conf new file mode 100644 index 000000000..ed3766047 --- /dev/null +++ b/openfpga_flow/tasks/fpga_verilog/mux_design/inbuf_only_mux/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=vpr_blif + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/OpenFPGAShellScripts/example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N8_inbuf_only_mux_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_N8_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= diff --git a/openfpga_flow/tasks/fpga_verilog/mux_design/outbuf_only_mux/config/task.conf b/openfpga_flow/tasks/fpga_verilog/mux_design/outbuf_only_mux/config/task.conf new file mode 100644 index 000000000..da765eacd --- /dev/null +++ b/openfpga_flow/tasks/fpga_verilog/mux_design/outbuf_only_mux/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=vpr_blif + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/OpenFPGAShellScripts/example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N8_outbuf_only_mux_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_N8_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 f149c88548eaf92961ca7bb14af0c6a00546aadd Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 16:11:48 -0600 Subject: [PATCH 059/148] [Regression Test] Deploy input buffer only multiplexer testcase to CI --- .travis/fpga_verilog_reg_test.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis/fpga_verilog_reg_test.sh b/.travis/fpga_verilog_reg_test.sh index f4f0d82d0..0a46dd5de 100755 --- a/.travis/fpga_verilog_reg_test.sh +++ b/.travis/fpga_verilog_reg_test.sh @@ -58,6 +58,9 @@ python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/local_enc echo -e "Testing Verilog generation with routing multiplexers without buffers"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/debuf_mux --debug --show_thread_logs +echo -e "Testing Verilog generation with routing multiplexers with input buffers only"; +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/inbuf_only_mux --debug --show_thread_logs + echo -e "Testing Verilog generation with behavioral description"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/behavioral_verilog --debug --show_thread_logs From c31d36deb63b8fb0c1275249a56a47b3c0906af4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 16:16:03 -0600 Subject: [PATCH 060/148] [Regression Tests] Deploy output buffer only routing multiplexer testcase to CI --- .travis/fpga_verilog_reg_test.sh | 3 +++ libopenfpga/libarchopenfpga/src/circuit_library.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis/fpga_verilog_reg_test.sh b/.travis/fpga_verilog_reg_test.sh index 0a46dd5de..35086b65b 100755 --- a/.travis/fpga_verilog_reg_test.sh +++ b/.travis/fpga_verilog_reg_test.sh @@ -61,6 +61,9 @@ python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/debuf_mux echo -e "Testing Verilog generation with routing multiplexers with input buffers only"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/inbuf_only_mux --debug --show_thread_logs +echo -e "Testing Verilog generation with routing multiplexers with output buffers only"; +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/mux_design/outbuf_only_mux --debug --show_thread_logs + echo -e "Testing Verilog generation with behavioral description"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/behavioral_verilog --debug --show_thread_logs diff --git a/libopenfpga/libarchopenfpga/src/circuit_library.cpp b/libopenfpga/libarchopenfpga/src/circuit_library.cpp index 881035ee7..42df84407 100644 --- a/libopenfpga/libarchopenfpga/src/circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/circuit_library.cpp @@ -1963,7 +1963,7 @@ void CircuitLibrary::link_buffer_model(const CircuitModelId& model_id) { /* Get the circuit model id by name, skip those with empty names*/ for (size_t buffer_id = 0; buffer_id < buffer_model_names_[model_id].size(); ++buffer_id) { if (true == buffer_model_names_[model_id][buffer_id].empty()) { - return; + continue; } buffer_model_ids_[model_id][buffer_id] = model(buffer_model_names_[model_id][buffer_id]); } From e3559f0df98183a78cf670168150faa71467bd23 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 16:53:16 -0600 Subject: [PATCH 061/148] [Regression Test] Add compiler coverage test to CI --- .travis.yml | 61 ++++++++++++++++++++++++++++++++++++++++++++--- .travis/common.sh | 4 ++-- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index bf9c42724..55bcf6254 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ cache: # Supported Operating systems dist: bionic -compiler: g++-8 +#compiler: g++-8 addons: apt: sources: @@ -33,8 +33,6 @@ addons: - doxygen - flex - fontconfig - - g++-8 - - gcc-8 - gdb - git - gperf @@ -56,6 +54,17 @@ addons: - zip - qt5-default - clang-format-7 +# Add all the supported compilers + - g++-6 + - gcc-6 + - g++-7 + - gcc-7 + - g++-8 + - gcc-8 + - g++-9 + - gcc-9 + - clang-6.0 + - clang-8 #- os: osx # osx_image: xcode10.2 # we target latest MacOS Mojave # sudo: true @@ -76,6 +85,10 @@ addons: # - libxml++ # - qt5 +# Use gcc-8 as default compiler +env: + - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + before_script: - source .travis/common.sh - source .travis/install.sh @@ -116,6 +129,48 @@ jobs: - source .travis/build.sh - source .travis/fpga_spice_reg_test.sh + - stage: Test + name: "Build Compatibility: GCC-6 (Ubuntu Bionic 18.04)" + env: + - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" + script: + - source .travis/build.sh + + - stage: Test + name: "Build Compatibility: GCC-7 (Ubuntu Bionic 18.04)" + env: + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" + script: + - source .travis/build.sh + + - stage: Test + name: "Build Compatibility: GCC-8 (Ubuntu Bionic 18.04)" + env: + - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + script: + - source .travis/build.sh + + - stage: Test + name: "Build Compatibility: GCC-9 (Ubuntu Bionic 18.04)" + env: + - MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" + script: + - source .travis/build.sh + + - stage: Test + name: "Build Compatibility: Clang-6 (Ubuntu Bionic 18.04)" + env: + - MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0" + script: + - source .travis/build.sh + + - stage: Test + name: "Build Compatibility: Clang-8 (Ubuntu Bionic 18.04)" + env: + - MATRIX_EVAL="CC=clang-8 && CXX=clang++-8" + script: + - source .travis/build.sh + #after_failure: # - .travis/after_failure.sh diff --git a/.travis/common.sh b/.travis/common.sh index a293cb00f..880d64ae8 100644 --- a/.travis/common.sh +++ b/.travis/common.sh @@ -39,8 +39,8 @@ if [[ $TRAVIS_OS_NAME == 'osx' ]]; then sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target / -allowUntrusted else # For linux, we use g++-8 and gcc-8 as default compilers - export CC=gcc-8 - export CXX=g++-8 + #export CC=gcc-8 + #export CXX=g++-8 fi From c08d4f5cd9c00110f9702948b7cd364fed3c5378 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 16:59:08 -0600 Subject: [PATCH 062/148] [Regression Test] Patch travis script --- .travis/common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/common.sh b/.travis/common.sh index 880d64ae8..bee008a88 100644 --- a/.travis/common.sh +++ b/.travis/common.sh @@ -37,7 +37,7 @@ if [[ $TRAVIS_OS_NAME == 'osx' ]]; then # export PATH="/usr/local/opt/qt/bin:$PATH" # Install header files in Mojave, if not gcc-4.9 cannot spot stdio.h sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target / -allowUntrusted -else +#else # For linux, we use g++-8 and gcc-8 as default compilers #export CC=gcc-8 #export CXX=g++-8 From d4bac95cd4d354839f8381f8ff166a411b47cb69 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 17:07:14 -0600 Subject: [PATCH 063/148] [Regression Tests] Enable matrix eval parameter in setting up compilers --- .travis/common.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis/common.sh b/.travis/common.sh index bee008a88..a5a7274de 100644 --- a/.travis/common.sh +++ b/.travis/common.sh @@ -37,10 +37,11 @@ if [[ $TRAVIS_OS_NAME == 'osx' ]]; then # export PATH="/usr/local/opt/qt/bin:$PATH" # Install header files in Mojave, if not gcc-4.9 cannot spot stdio.h sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target / -allowUntrusted -#else +else # For linux, we use g++-8 and gcc-8 as default compilers - #export CC=gcc-8 - #export CXX=g++-8 + eval "${MATRIX_EVAL}" + export "CC=$CC" + export "CXX=$CXX" fi From fc6bfdc7a22cf2be757911d976b01da408257991 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 18:55:21 -0600 Subject: [PATCH 064/148] [OpenFPGA Code] Patch syntax compatibility for older gcc --- libopenfpga/libopenfpgautil/src/openfpga_decode.h | 1 + libopenfpga/libopenfpgautil/src/openfpga_scale.cpp | 6 +++--- openfpga/src/base/io_location_map.h | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libopenfpga/libopenfpgautil/src/openfpga_decode.h b/libopenfpga/libopenfpgautil/src/openfpga_decode.h index ab6778c86..85809fee2 100644 --- a/libopenfpga/libopenfpgautil/src/openfpga_decode.h +++ b/libopenfpga/libopenfpgautil/src/openfpga_decode.h @@ -4,6 +4,7 @@ /******************************************************************** * Include header files that are required by function declaration *******************************************************************/ +#include #include /******************************************************************** diff --git a/libopenfpga/libopenfpgautil/src/openfpga_scale.cpp b/libopenfpga/libopenfpgautil/src/openfpga_scale.cpp index 9baa753d1..66364501d 100644 --- a/libopenfpga/libopenfpgautil/src/openfpga_scale.cpp +++ b/libopenfpga/libopenfpgautil/src/openfpga_scale.cpp @@ -140,7 +140,7 @@ float string_to_unit(const std::string& scale) { /* Invalid unit report error */ VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid unit %s!\nAcceptable units are [a|f|p|n|u|k|M|B|T] or empty\n", - scale); + scale.c_str()); exit(1); } @@ -153,13 +153,13 @@ float string_to_time_unit(const std::string& scale) { && (2 != scale.length()) ) { VTR_LOGF_ERROR(__FILE__, __LINE__, "Time unit (='%s') must contain only one or two characters!\n", - scale); + scale.c_str()); } /* The last character must be 's' */ if ('s' != scale.back()) { VTR_LOGF_ERROR(__FILE__, __LINE__, "Time unit (='%s') must end with 's'!\n", - scale); + scale.c_str()); } float unit = 1.; diff --git a/openfpga/src/base/io_location_map.h b/openfpga/src/base/io_location_map.h index de6422d23..ef326a66a 100644 --- a/openfpga/src/base/io_location_map.h +++ b/openfpga/src/base/io_location_map.h @@ -4,6 +4,7 @@ /******************************************************************** * Include header files required by the data structure definition *******************************************************************/ +#include #include /* Begin namespace openfpga */ From c23742c7517e70cc39f5f5d0ec35ac39e9e38fb0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 20:13:27 -0600 Subject: [PATCH 065/148] [OpenFPGA code] fix bug for clang compatibility --- libopenfpga/libopenfpgashell/src/command_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libopenfpga/libopenfpgashell/src/command_parser.cpp b/libopenfpga/libopenfpgashell/src/command_parser.cpp index 5e0397421..b2daf12f7 100644 --- a/libopenfpga/libopenfpgashell/src/command_parser.cpp +++ b/libopenfpga/libopenfpgashell/src/command_parser.cpp @@ -67,7 +67,7 @@ bool parse_command(const std::vector& argv, /* Validate that the command name matches argv[0] */ if (argv[0] != cmd.name()) { VTR_LOG("Unexpected command name '%s'!\n", - argv[0]); + argv[0].c_str()); return false; } From b43cd2741df94a9a164a0d68dbf3eb4d2dfc01c0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 20:14:16 -0600 Subject: [PATCH 066/148] [Regression Tests] Add gcc-5 compatibility test to Travis CI --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.travis.yml b/.travis.yml index 55bcf6254..c4349f39c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,6 +55,8 @@ addons: - qt5-default - clang-format-7 # Add all the supported compilers + - g++-5 + - gcc-5 - g++-6 - gcc-6 - g++-7 @@ -129,6 +131,13 @@ jobs: - source .travis/build.sh - source .travis/fpga_spice_reg_test.sh + - stage: Test + name: "Build Compatibility: GCC-5 (Ubuntu Bionic 18.04)" + env: + - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" + script: + - source .travis/build.sh + - stage: Test name: "Build Compatibility: GCC-6 (Ubuntu Bionic 18.04)" env: From 8b6c8f73e9a1a7a54f7019e2ef1c8d0da0dba792 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 14 Sep 2020 21:26:53 -0600 Subject: [PATCH 067/148] [OpenFPGA code] fix bug for clang compatibility --- libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp b/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp index 9fb61a654..0954a0086 100644 --- a/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp @@ -459,7 +459,7 @@ void read_xml_output_mask(pugi::xml_node& xml_port, if (circuit_lib.port_size(port) != mask_vector.size()) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_port), "Invalid lut_output_mask attribute '%s'! It must match the port size (=%lu)\n", - output_mask_attr, circuit_lib.port_size(port)); + output_mask_attr.c_str(), circuit_lib.port_size(port)); } } else { /* By default, we give a mask vector covering each pin of the port */ From 3c0faf0021a9507a2a7b13f0c1d815aa68e00f8a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 16 Sep 2020 17:27:24 -0600 Subject: [PATCH 068/148] [OpenFPGA Architecture] Add a new architecture with fully connected crossbar at CLB outputs --- ..._N4_tileable_full_output_crossbar_40nm.xml | 291 ++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 openfpga_flow/vpr_arch/k4_N4_tileable_full_output_crossbar_40nm.xml diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_full_output_crossbar_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_full_output_crossbar_40nm.xml new file mode 100644 index 000000000..f1d8ac1d3 --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_full_output_crossbar_40nm.xml @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + 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 30fb99095fc557f7be1ec1e4d6b3062cd889a816 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 16 Sep 2020 17:29:15 -0600 Subject: [PATCH 069/148] [Regression Tests] Add new test case for fully connected output crossbar --- .../config/task.conf | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 openfpga_flow/tasks/fpga_verilog/fully_connected_output_crossbar/config/task.conf diff --git a/openfpga_flow/tasks/fpga_verilog/fully_connected_output_crossbar/config/task.conf b/openfpga_flow/tasks/fpga_verilog/fully_connected_output_crossbar/config/task.conf new file mode 100644 index 000000000..e9e253d1d --- /dev/null +++ b/openfpga_flow/tasks/fpga_verilog/fully_connected_output_crossbar/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/OpenFPGAShellScripts/example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_frame_40nm_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +external_fabric_key_file= + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_N4_tileable_full_output_crossbar_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 030d7f02f84c7275fd83c8156066ff94c7b6c0ed Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 16 Sep 2020 17:30:08 -0600 Subject: [PATCH 070/148] [OpenFPGA architecture] bug fix in the fully connected output crossbar architecture --- .../vpr_arch/k4_N4_tileable_full_output_crossbar_40nm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_full_output_crossbar_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_full_output_crossbar_40nm.xml index f1d8ac1d3..b4be8823f 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileable_full_output_crossbar_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_full_output_crossbar_40nm.xml @@ -268,7 +268,7 @@ Since all our outputs LUT outputs go to a BLE output, and have a delay of 25 ps to do so, we subtract 25 ps from the 100 ps delay of a feedback to get the part that should be marked on the crossbar. --> - + From 35d47ee0e74a656d3c712a8052cf28d6b962d07e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 16 Sep 2020 17:33:54 -0600 Subject: [PATCH 071/148] [Regression tests] bug fix in the test case for fully connected output crossbar --- .../fully_connected_output_crossbar/config/task.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/tasks/fpga_verilog/fully_connected_output_crossbar/config/task.conf b/openfpga_flow/tasks/fpga_verilog/fully_connected_output_crossbar/config/task.conf index e9e253d1d..f8717b5fd 100644 --- a/openfpga_flow/tasks/fpga_verilog/fully_connected_output_crossbar/config/task.conf +++ b/openfpga_flow/tasks/fpga_verilog/fully_connected_output_crossbar/config/task.conf @@ -17,12 +17,12 @@ fpga_flow=vpr_blif [OpenFPGA_SHELL] openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/OpenFPGAShellScripts/example_script.openfpga -openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_frame_40nm_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 external_fabric_key_file= [ARCHITECTURES] -arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_N4_tileable_full_output_crossbar_40nm.xml +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_full_output_crossbar_40nm.xml [BENCHMARKS] bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.blif From 5fe039dd7cc7c8aed34e414ca9e9fb45ed2904c0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 16 Sep 2020 17:35:49 -0600 Subject: [PATCH 072/148] [Regression Tests] Deploy the fully connected crossbar test to CI --- .travis/fpga_verilog_reg_test.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis/fpga_verilog_reg_test.sh b/.travis/fpga_verilog_reg_test.sh index 35086b65b..8956cab1e 100755 --- a/.travis/fpga_verilog_reg_test.sh +++ b/.travis/fpga_verilog_reg_test.sh @@ -90,6 +90,9 @@ python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/power_gated_design/p echo -e "Testing Depopulated crossbar in local routing"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/depopulate_crossbar --debug --show_thread_logs +echo -e "Testing Fully connected output crossbar in local routing"; +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fully_connected_output_crossbar --debug --show_thread_logs + echo -e "Testing through channels in tileable routing"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/thru_channel/thru_narrow_tile --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/thru_channel/thru_wide_tile --debug --show_thread_logs From f5b7ac62698ec5ca541327d8a1ad5866d0ec1bba Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 16 Sep 2020 18:04:55 -0600 Subject: [PATCH 073/148] [OpenFPGA Architecture] Add a new architecture with no local routing --- ...4_no_local_routing_40nm_frame_openfpga.xml | 196 ++++++++++++ .../k4_N4_tileable_no_local_routing_40nm.xml | 286 ++++++++++++++++++ 2 files changed, 482 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k4_N4_no_local_routing_40nm_frame_openfpga.xml create mode 100644 openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_40nm.xml diff --git a/openfpga_flow/openfpga_arch/k4_N4_no_local_routing_40nm_frame_openfpga.xml b/openfpga_flow/openfpga_arch/k4_N4_no_local_routing_40nm_frame_openfpga.xml new file mode 100644 index 000000000..9ba39b3ce --- /dev/null +++ b/openfpga_flow/openfpga_arch/k4_N4_no_local_routing_40nm_frame_openfpga.xml @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + + + + + 10e-12 5e-12 5e-12 + + + 10e-12 5e-12 5e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_40nm.xml new file mode 100644 index 000000000..4605da01e --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_40nm.xml @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + + + 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 c40c9f58767d29965ba35f678aa146b34b7718e0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 16 Sep 2020 18:05:33 -0600 Subject: [PATCH 074/148] [Regression test] add test case for no local routing architecture --- .../no_local_routing/config/task.conf | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 openfpga_flow/tasks/fpga_verilog/no_local_routing/config/task.conf diff --git a/openfpga_flow/tasks/fpga_verilog/no_local_routing/config/task.conf b/openfpga_flow/tasks/fpga_verilog/no_local_routing/config/task.conf new file mode 100644 index 000000000..c076b156c --- /dev/null +++ b/openfpga_flow/tasks/fpga_verilog/no_local_routing/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/OpenFPGAShellScripts/example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_no_local_routing_40nm_frame_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +external_fabric_key_file= + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_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 c22d8e242136d4b955b65e055a2d7e5f58560420 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 16 Sep 2020 18:07:52 -0600 Subject: [PATCH 075/148] [Architecture] Bug fix in no local routing architecture --- .../vpr_arch/k4_N4_tileable_no_local_routing_40nm.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_40nm.xml index 4605da01e..06499b328 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_40nm.xml @@ -265,10 +265,10 @@ - - - - + + + + - + + +--> OUT + * | | + * | - + * +--|| + * - + * + * ... + * + * | + * - + * EN[1] -|| + * - + * | + * - + * EN[0] -|| + * - + * | + * LGND + * + *******************************************************************/ +static +int print_spice_powergated_inverter_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + std::vector input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); + std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + + /* Make sure: + * There is only 1 input port and 1 output port, + * each size of which is 1 + */ + VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) ); + VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); + + /* If the circuit model is power-gated, we need to find at least one global config_enable signals */ + VTR_ASSERT(true == circuit_lib.is_power_gated(circuit_model)); + CircuitPortId en_port = find_circuit_model_power_gate_en_port(circuit_lib, circuit_model); + CircuitPortId enb_port = find_circuit_model_power_gate_enb_port(circuit_lib, circuit_model); + VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(en_port)); + VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(enb_port)); + + int status = CMD_EXEC_SUCCESS; + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + float total_pmos_width = circuit_lib.buffer_size(circuit_model) + * tech_lib.model_pn_ratio(tech_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); + float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); + for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { + float curr_bin_width = regular_pmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_pmos_bins - 1) + && (0. != last_pmos_bin_width)) { + curr_bin_width = last_pmos_bin_width; + } + status = print_spice_powergated_inverter_pmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + circuit_lib, + enb_port, + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = circuit_lib.buffer_size(circuit_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + status = print_spice_powergated_inverter_nmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + circuit_lib, + en_port, + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE modeling for the PMOS part of a regular inverter + * + * This function is created to be shared by inverter and buffer SPICE netlist writer + * + * Note: + * - This function does NOT create a file + * but requires a file stream created + * - This function only output SPICE modeling for + * an inverter. Any preprocessing or subckt definition should not be included! + *******************************************************************/ +static +int print_spice_regular_inverter_pmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Write transistor pairs using the technology model */ + fp << "Xpmos_" << trans_name_postfix << " "; + fp << output_port_name << " "; + fp << input_port_name << " "; + fp << "LVDD "; + fp << "LVDD "; + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE modeling for the NMOS part of a regular inverter + * + * This function is created to be shared by inverter and buffer SPICE netlist writer + * + * Note: + * - This function does NOT create a file + * but requires a file stream created + * - This function only output SPICE modeling for + * an inverter. Any preprocessing or subckt definition should not be included! + *******************************************************************/ +static +int print_spice_regular_inverter_nmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + fp << "Xnmos_" << trans_name_postfix << " "; + fp << output_port_name << " "; + fp << input_port_name << " "; + fp << "LGND "; + fp << "LGND "; + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE subckt for a regular inverter + * + * Note: + * - This function does NOT support power-gating + * It should be managed in a separated function + * + * Schematic + * LVDD + * | + * - + * +-o|| + * | - + * | | + * in-->+ +--> OUT + * | | + * | - + * +--|| + * - + * | + * LGND + * + *******************************************************************/ +static +int print_spice_regular_inverter_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + std::vector input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); + std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + + /* Make sure: + * There is only 1 input port and 1 output port, + * each size of which is 1 + */ + VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) ); + VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); + + int status = CMD_EXEC_SUCCESS; + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + float total_pmos_width = circuit_lib.buffer_size(circuit_model) + * tech_lib.model_pn_ratio(tech_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); + float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); + for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { + float curr_bin_width = regular_pmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_pmos_bins - 1) + && (0. != last_pmos_bin_width)) { + curr_bin_width = last_pmos_bin_width; + } + + status = print_spice_regular_inverter_pmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = circuit_lib.buffer_size(circuit_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + status = print_spice_regular_inverter_nmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return status; +} + +/******************************************************************** + * Generate the SPICE subckt for an inverter + * Branch on the different circuit topologies + *******************************************************************/ +int print_spice_inverter_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + int status = CMD_EXEC_SUCCESS; + if (true == circuit_lib.is_power_gated(circuit_model)) { + status = print_spice_powergated_inverter_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } else { + VTR_ASSERT_SAFE(false == circuit_lib.is_power_gated(circuit_model)); + status = print_spice_regular_inverter_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } + + return status; +} + +/******************************************************************** + * Generate the SPICE subckt for a power-gated buffer + * which contains at least 2 stages + * + * Schematic of a multi-stage buffer + * + * LVDD LVDD + * | | + * - - + * ENb[0] -o|| ENb[0] -o|| + * - - + * | | + * - - + * ENb[1] -o|| ENb[1] -o|| + * - - + * | | + * + * ... + * + * | | + * - - + * +-o|| +-o|| + * | - | - + * | | | | + * in-->+ +-- ... ---+---->+---> out + * | | | | + * | - | - + * +--|| +--|| + * - - + * | | + * + * ... + * + * | | + * - - + * EN[0] -|| EN[0] -|| + * - - + * | | + * - - + * EN[1] -|| EN[1] -|| + * - - + * | | + + * | | + * LGND LGND + * + *******************************************************************/ +static +int print_spice_powergated_buffer_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + std::vector input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); + std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + + /* Make sure: + * There is only 1 input port and 1 output port, + * each size of which is 1 + */ + VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) ); + VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); + + /* If the circuit model is power-gated, we need to find at least one global config_enable signals */ + VTR_ASSERT(true == circuit_lib.is_power_gated(circuit_model)); + CircuitPortId en_port = find_circuit_model_power_gate_en_port(circuit_lib, circuit_model); + CircuitPortId enb_port = find_circuit_model_power_gate_enb_port(circuit_lib, circuit_model); + VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(en_port)); + VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(enb_port)); + + int status = CMD_EXEC_SUCCESS; + + /* Buffers must have >= 2 stages */ + VTR_ASSERT(2 <= circuit_lib.buffer_num_levels(circuit_model)); + + /* Build the array denoting width of inverters per stage */ + std::vector buffer_widths(circuit_lib.buffer_num_levels(circuit_model), 1); + for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { + buffer_widths[level] = circuit_lib.buffer_size(circuit_model) + * std::pow(circuit_lib.buffer_f_per_stage(circuit_model), level); + } + + for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { + std::string input_port_name = circuit_lib.port_prefix(input_ports[0]); + std::string output_port_name = circuit_lib.port_prefix(output_ports[0]); + + /* Special for first stage: output port should be an intermediate node + * Special for rest of stages: input port should be the output of previous stage + */ + if (0 == level) { + output_port_name += std::string("_level") + std::to_string(level); + } else { + VTR_ASSERT(0 < level); + input_port_name += std::string("_level") + std::to_string(level - 1); + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + float total_pmos_width = buffer_widths[level] + * tech_lib.model_pn_ratio(tech_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); + float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); + + for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { + float curr_bin_width = regular_pmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_pmos_bins - 1) + && (0. != last_pmos_bin_width)) { + curr_bin_width = last_pmos_bin_width; + } + + std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); + + status = print_spice_powergated_inverter_pmos_modeling(fp, + name_postfix, + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + circuit_lib, + enb_port, + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = buffer_widths[level] + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); + + status = print_spice_powergated_inverter_nmos_modeling(fp, + name_postfix, + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + circuit_lib, + en_port, + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return CMD_EXEC_SUCCESS; +} + + +/******************************************************************** + * Generate the SPICE subckt for a regular buffer + * which contains at least 2 stages + * + * Note: + * - This function does NOT support power-gating + * It should be managed in a separated function + * + * Schematic of a multi-stage buffer + * + * LVDD LVDD + * | | + * - - + * +-o|| +-o|| + * | - | - + * | | | | + * in-->+ +-- ... ---+---->+---> out + * | | | | + * | - | - + * +--|| +--|| + * - - + * | | + * LGND LGND + * + *******************************************************************/ +static +int print_spice_regular_buffer_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + std::vector input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); + std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + + /* Make sure: + * There is only 1 input port and 1 output port, + * each size of which is 1 + */ + VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) ); + VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); + + int status = CMD_EXEC_SUCCESS; + + /* Buffers must have >= 2 stages */ + VTR_ASSERT(2 <= circuit_lib.buffer_num_levels(circuit_model)); + + /* Build the array denoting width of inverters per stage */ + std::vector buffer_widths(circuit_lib.buffer_num_levels(circuit_model), 1); + for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { + buffer_widths[level] = circuit_lib.buffer_size(circuit_model) + * std::pow(circuit_lib.buffer_f_per_stage(circuit_model), level); + } + + for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { + std::string input_port_name = circuit_lib.port_prefix(input_ports[0]); + std::string output_port_name = circuit_lib.port_prefix(output_ports[0]); + + /* Special for first stage: output port should be an intermediate node + * Special for rest of stages: input port should be the output of previous stage + */ + if (0 == level) { + output_port_name += std::string("_level") + std::to_string(level); + } else { + VTR_ASSERT(0 < level); + input_port_name += std::string("_level") + std::to_string(level - 1); + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + float total_pmos_width = buffer_widths[level] + * tech_lib.model_pn_ratio(tech_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); + float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); + + for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { + float curr_bin_width = regular_pmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_pmos_bins - 1) + && (0. != last_pmos_bin_width)) { + curr_bin_width = last_pmos_bin_width; + } + + std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); + + status = print_spice_regular_inverter_pmos_modeling(fp, + name_postfix, + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = buffer_widths[level] + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); + + status = print_spice_regular_inverter_nmos_modeling(fp, + name_postfix, + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return status; +} + +/******************************************************************** + * Generate the SPICE subckt for an buffer + * which consists of multiple stage of inverters + *******************************************************************/ +int print_spice_buffer_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + int status = CMD_EXEC_SUCCESS; + if (true == circuit_lib.is_power_gated(circuit_model)) { + status = print_spice_powergated_buffer_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } else { + VTR_ASSERT_SAFE(false == circuit_lib.is_power_gated(circuit_model)); + status = print_spice_regular_buffer_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_buffer.h b/openfpga/src/fpga_spice/spice_buffer.h new file mode 100644 index 000000000..d7019f995 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_buffer.h @@ -0,0 +1,39 @@ +#ifndef SPICE_BUFFER_H +#define SPICE_BUFFER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "module_manager.h" +#include "circuit_library.h" +#include "technology_library.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_inverter_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model); + +int print_spice_buffer_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model); + + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index 2a916f60e..fe554c328 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -22,972 +22,13 @@ #include "spice_constants.h" #include "spice_writer_utils.h" +#include "spice_buffer.h" #include "spice_passgate.h" #include "spice_essential_gates.h" /* begin namespace openfpga */ namespace openfpga { -/******************************************************************** - * Print a SPICE model wrapper for a transistor model - *******************************************************************/ -static -int print_spice_transistor_model_wrapper(std::fstream& fp, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& model) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Transistor model followed a fixed port mapping - * [X|M] - * which is a standard in SPICE modeling - * We will output the pmos and nmos transistors wrappers - * which are defined in this model - */ - for (int itype = TECH_LIB_TRANSISTOR_PMOS; - itype < NUM_TECH_LIB_TRANSISTOR_TYPES; - ++itype) { - const e_tech_lib_transistor_type& trans_type = static_cast(itype); - fp << ".subckt "; - fp << tech_lib.transistor_model_name(model, trans_type) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " drain gate source bulk"; - fp << " L=" << std::setprecision(10) << tech_lib.transistor_model_chan_length(model, trans_type); - fp << " W=" << std::setprecision(10) << tech_lib.transistor_model_min_width(model, trans_type); - fp << "\n"; - - fp << tech_lib.model_ref(model); - fp << "1"; - fp << " drain gate source bulk"; - fp << " " << tech_lib.transistor_model_name(model, trans_type); - fp << " L=L W=W"; - fp << "\n"; - - fp << ".ends"; - fp << "\n"; - } - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE netlist for transistors - *******************************************************************/ -int print_spice_transistor_wrapper(NetlistManager& netlist_manager, - const TechnologyLibrary& tech_lib, - const std::string& submodule_dir) { - std::string spice_fname = submodule_dir + std::string(TRANSISTORS_SPICE_FILE_NAME); - - std::fstream fp; - - /* Create the file stream */ - fp.open(spice_fname, std::fstream::out | std::fstream::trunc); - /* Check if the file stream if valid or not */ - check_file_stream(spice_fname.c_str(), fp); - - /* Create file */ - VTR_LOG("Generating SPICE netlist '%s' for transistors...", - spice_fname.c_str()); - - print_spice_file_header(fp, std::string("Transistor wrappers")); - - /* Iterate over the transistor models */ - for (const TechnologyModelId& model : tech_lib.models()) { - /* Focus on transistor model */ - if (TECH_LIB_MODEL_TRANSISTOR != tech_lib.model_type(model)) { - continue; - } - /* Write a wrapper for the transistor model */ - if (CMD_EXEC_SUCCESS == print_spice_transistor_model_wrapper(fp, tech_lib, model)) { - return CMD_EXEC_FATAL_ERROR; - } - } - - /* Close file handler*/ - fp.close(); - - /* Add fname to the netlist name list */ - NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); - VTR_ASSERT(NetlistId::INVALID() != nlist_id); - netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); - - VTR_LOG("Done\n"); - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE modeling for a power-gated inverter - * - * This function is created to be shared by inverter and buffer SPICE netlist writer - * - * Note: - * - This function does NOT create a file - * but requires a file stream created - * - This function only output SPICE modeling for - * an inverter. Any preprocessing or subckt definition should not be included! - *******************************************************************/ -static -int print_spice_powergated_inverter_pmos_modeling(std::fstream& fp, - const std::string& trans_name_postfix, - const std::string& input_port_name, - const std::string& output_port_name, - const CircuitLibrary& circuit_lib, - const CircuitPortId& enb_port, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model, - const float& trans_width) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Write power-gating transistor pairs using the technology model - * Note that for a mulit-bit power gating port, we should cascade the transistors - */ - bool first_enb_pin = true; - size_t last_enb_pin; - for (const auto& power_gate_pin : circuit_lib.pins(enb_port)) { - BasicPort enb_pin(circuit_lib.port_prefix(enb_port), power_gate_pin, power_gate_pin); - fp << "Xpmos_powergate_" << trans_name_postfix << "_pin_" << power_gate_pin << " "; - /* For the first pin, we should connect it to local VDD*/ - if (true == first_enb_pin) { - fp << output_port_name << "_pmos_pg_" << power_gate_pin << " "; - fp << generate_spice_port(enb_pin) << " "; - fp << "LVDD "; - fp << "LVDD "; - first_enb_pin = false; - } else { - VTR_ASSERT_SAFE(false == first_enb_pin); - fp << output_port_name << "_pmos_pg_" << last_enb_pin << " "; - fp << generate_spice_port(enb_pin) << " "; - fp << output_port_name << "_pmos_pg_" << power_gate_pin << " "; - fp << "LVDD "; - } - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - /* Cache the last pin*/ - last_enb_pin = power_gate_pin; - } - - /* Write transistor pairs using the technology model */ - fp << "Xpmos_" << trans_name_postfix << " "; - fp << output_port_name << " "; - fp << input_port_name << " "; - fp << output_port_name << "_pmos_pg_" << circuit_lib.pins(enb_port).back() << " "; - fp << "LVDD "; - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE modeling for the NMOS part of a power-gated inverter - * - * This function is created to be shared by inverter and buffer SPICE netlist writer - * - * Note: - * - This function does NOT create a file - * but requires a file stream created - * - This function only output SPICE modeling for - * an inverter. Any preprocessing or subckt definition should not be included! - *******************************************************************/ -static -int print_spice_powergated_inverter_nmos_modeling(std::fstream& fp, - const std::string& trans_name_postfix, - const std::string& input_port_name, - const std::string& output_port_name, - const CircuitLibrary& circuit_lib, - const CircuitPortId& en_port, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model, - const float& trans_width) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - bool first_en_pin = true; - size_t last_en_pin; - for (const auto& power_gate_pin : circuit_lib.pins(en_port)) { - BasicPort en_pin(circuit_lib.port_prefix(en_port), power_gate_pin, power_gate_pin); - fp << "Xnmos_powergate_" << trans_name_postfix << "_pin_" << power_gate_pin << " "; - /* For the first pin, we should connect it to local VDD*/ - if (true == first_en_pin) { - fp << output_port_name << "_nmos_pg_" << power_gate_pin << " "; - fp << generate_spice_port(en_pin) << " "; - fp << "LGND "; - fp << "LGND "; - first_en_pin = false; - } else { - VTR_ASSERT_SAFE(false == first_en_pin); - fp << output_port_name << "_nmos_pg_" << last_en_pin << " "; - fp << circuit_lib.port_prefix(en_port) << " "; - fp << output_port_name << "_nmos_pg_" << power_gate_pin << " "; - fp << "LGND "; - } - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - /* Cache the last pin*/ - last_en_pin = power_gate_pin; - } - - fp << "Xnmos_" << trans_name_postfix << " "; - fp << output_port_name << " "; - fp << input_port_name << " "; - fp << output_port_name << " _nmos_pg_" << circuit_lib.pins(en_port).back() << " "; - fp << "LGND "; - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE subckt for a power gated inverter - * The Enable signal controlled the power gating - * - * Note: - * - This function supports multi-bit power gating - * - * Schematic - * LVDD - * | - * - - * ENb[0] -o|| - * - - * | - * - - * ENb[1] -o|| - * - - * | - * - * ... - * - * | - * - - * +-o|| - * | - - * | | - * in-->+ +--> OUT - * | | - * | - - * +--|| - * - - * - * ... - * - * | - * - - * EN[1] -|| - * - - * | - * - - * EN[0] -|| - * - - * | - * LGND - * - *******************************************************************/ -static -int print_spice_powergated_inverter_subckt(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& module_id, - const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model) { - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Print the inverter subckt definition */ - print_spice_subckt_definition(fp, module_manager, module_id); - - /* Find the input and output ports: - * we do NOT support global ports here, - * it should be handled in another type of inverter subckt (power-gated) - */ - std::vector input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); - std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); - - /* Make sure: - * There is only 1 input port and 1 output port, - * each size of which is 1 - */ - VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) ); - VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); - - /* If the circuit model is power-gated, we need to find at least one global config_enable signals */ - VTR_ASSERT(true == circuit_lib.is_power_gated(circuit_model)); - CircuitPortId en_port = find_circuit_model_power_gate_en_port(circuit_lib, circuit_model); - CircuitPortId enb_port = find_circuit_model_power_gate_enb_port(circuit_lib, circuit_model); - VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(en_port)); - VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(enb_port)); - - int status = CMD_EXEC_SUCCESS; - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - float total_pmos_width = circuit_lib.buffer_size(circuit_model) - * tech_lib.model_pn_ratio(tech_model) - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); - float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); - for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { - float curr_bin_width = regular_pmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_pmos_bins - 1) - && (0. != last_pmos_bin_width)) { - curr_bin_width = last_pmos_bin_width; - } - status = print_spice_powergated_inverter_pmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - circuit_lib, - enb_port, - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - float total_nmos_width = circuit_lib.buffer_size(circuit_model) - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); - float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); - for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { - float curr_bin_width = regular_nmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_nmos_bins - 1) - && (0. != last_nmos_bin_width)) { - curr_bin_width = last_nmos_bin_width; - } - - status = print_spice_powergated_inverter_nmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - circuit_lib, - en_port, - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - - print_spice_subckt_end(fp, module_manager.module_name(module_id)); - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE modeling for the PMOS part of a regular inverter - * - * This function is created to be shared by inverter and buffer SPICE netlist writer - * - * Note: - * - This function does NOT create a file - * but requires a file stream created - * - This function only output SPICE modeling for - * an inverter. Any preprocessing or subckt definition should not be included! - *******************************************************************/ -static -int print_spice_regular_inverter_pmos_modeling(std::fstream& fp, - const std::string& trans_name_postfix, - const std::string& input_port_name, - const std::string& output_port_name, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model, - const float& trans_width) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Write transistor pairs using the technology model */ - fp << "Xpmos_" << trans_name_postfix << " "; - fp << output_port_name << " "; - fp << input_port_name << " "; - fp << "LVDD "; - fp << "LVDD "; - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE modeling for the NMOS part of a regular inverter - * - * This function is created to be shared by inverter and buffer SPICE netlist writer - * - * Note: - * - This function does NOT create a file - * but requires a file stream created - * - This function only output SPICE modeling for - * an inverter. Any preprocessing or subckt definition should not be included! - *******************************************************************/ -static -int print_spice_regular_inverter_nmos_modeling(std::fstream& fp, - const std::string& trans_name_postfix, - const std::string& input_port_name, - const std::string& output_port_name, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model, - const float& trans_width) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - fp << "Xnmos_" << trans_name_postfix << " "; - fp << output_port_name << " "; - fp << input_port_name << " "; - fp << "LGND "; - fp << "LGND "; - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE subckt for a regular inverter - * - * Note: - * - This function does NOT support power-gating - * It should be managed in a separated function - * - * Schematic - * LVDD - * | - * - - * +-o|| - * | - - * | | - * in-->+ +--> OUT - * | | - * | - - * +--|| - * - - * | - * LGND - * - *******************************************************************/ -static -int print_spice_regular_inverter_subckt(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& module_id, - const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model) { - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Print the inverter subckt definition */ - print_spice_subckt_definition(fp, module_manager, module_id); - - /* Find the input and output ports: - * we do NOT support global ports here, - * it should be handled in another type of inverter subckt (power-gated) - */ - std::vector input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); - std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); - - /* Make sure: - * There is only 1 input port and 1 output port, - * each size of which is 1 - */ - VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) ); - VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); - - int status = CMD_EXEC_SUCCESS; - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - float total_pmos_width = circuit_lib.buffer_size(circuit_model) - * tech_lib.model_pn_ratio(tech_model) - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); - float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); - for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { - float curr_bin_width = regular_pmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_pmos_bins - 1) - && (0. != last_pmos_bin_width)) { - curr_bin_width = last_pmos_bin_width; - } - - status = print_spice_regular_inverter_pmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - float total_nmos_width = circuit_lib.buffer_size(circuit_model) - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); - float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); - - for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { - float curr_bin_width = regular_nmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_nmos_bins - 1) - && (0. != last_nmos_bin_width)) { - curr_bin_width = last_nmos_bin_width; - } - - status = print_spice_regular_inverter_nmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - - print_spice_subckt_end(fp, module_manager.module_name(module_id)); - - return status; -} - -/******************************************************************** - * Generate the SPICE subckt for an inverter - * Branch on the different circuit topologies - *******************************************************************/ -static -int print_spice_inverter_subckt(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& module_id, - const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model) { - int status = CMD_EXEC_SUCCESS; - if (true == circuit_lib.is_power_gated(circuit_model)) { - status = print_spice_powergated_inverter_subckt(fp, - module_manager, module_id, - circuit_lib, circuit_model, - tech_lib, tech_model); - } else { - VTR_ASSERT_SAFE(false == circuit_lib.is_power_gated(circuit_model)); - status = print_spice_regular_inverter_subckt(fp, - module_manager, module_id, - circuit_lib, circuit_model, - tech_lib, tech_model); - } - - return status; -} - -/******************************************************************** - * Generate the SPICE subckt for a power-gated buffer - * which contains at least 2 stages - * - * Schematic of a multi-stage buffer - * - * LVDD LVDD - * | | - * - - - * ENb[0] -o|| ENb[0] -o|| - * - - - * | | - * - - - * ENb[1] -o|| ENb[1] -o|| - * - - - * | | - * - * ... - * - * | | - * - - - * +-o|| +-o|| - * | - | - - * | | | | - * in-->+ +-- ... ---+---->+---> out - * | | | | - * | - | - - * +--|| +--|| - * - - - * | | - * - * ... - * - * | | - * - - - * EN[0] -|| EN[0] -|| - * - - - * | | - * - - - * EN[1] -|| EN[1] -|| - * - - - * | | - - * | | - * LGND LGND - * - *******************************************************************/ -static -int print_spice_powergated_buffer_subckt(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& module_id, - const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model) { - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Print the inverter subckt definition */ - print_spice_subckt_definition(fp, module_manager, module_id); - - /* Find the input and output ports: - * we do NOT support global ports here, - * it should be handled in another type of inverter subckt (power-gated) - */ - std::vector input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); - std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); - - /* Make sure: - * There is only 1 input port and 1 output port, - * each size of which is 1 - */ - VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) ); - VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); - - /* If the circuit model is power-gated, we need to find at least one global config_enable signals */ - VTR_ASSERT(true == circuit_lib.is_power_gated(circuit_model)); - CircuitPortId en_port = find_circuit_model_power_gate_en_port(circuit_lib, circuit_model); - CircuitPortId enb_port = find_circuit_model_power_gate_enb_port(circuit_lib, circuit_model); - VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(en_port)); - VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(enb_port)); - - int status = CMD_EXEC_SUCCESS; - - /* Buffers must have >= 2 stages */ - VTR_ASSERT(2 <= circuit_lib.buffer_num_levels(circuit_model)); - - /* Build the array denoting width of inverters per stage */ - std::vector buffer_widths(circuit_lib.buffer_num_levels(circuit_model), 1); - for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { - buffer_widths[level] = circuit_lib.buffer_size(circuit_model) - * std::pow(circuit_lib.buffer_f_per_stage(circuit_model), level); - } - - for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { - std::string input_port_name = circuit_lib.port_prefix(input_ports[0]); - std::string output_port_name = circuit_lib.port_prefix(output_ports[0]); - - /* Special for first stage: output port should be an intermediate node - * Special for rest of stages: input port should be the output of previous stage - */ - if (0 == level) { - output_port_name += std::string("_level") + std::to_string(level); - } else { - VTR_ASSERT(0 < level); - input_port_name += std::string("_level") + std::to_string(level - 1); - } - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - float total_pmos_width = buffer_widths[level] - * tech_lib.model_pn_ratio(tech_model) - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); - float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); - - for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { - float curr_bin_width = regular_pmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_pmos_bins - 1) - && (0. != last_pmos_bin_width)) { - curr_bin_width = last_pmos_bin_width; - } - - std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); - - status = print_spice_powergated_inverter_pmos_modeling(fp, - name_postfix, - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - circuit_lib, - enb_port, - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - float total_nmos_width = buffer_widths[level] - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); - float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); - - for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { - float curr_bin_width = regular_nmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_nmos_bins - 1) - && (0. != last_nmos_bin_width)) { - curr_bin_width = last_nmos_bin_width; - } - - std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); - - status = print_spice_powergated_inverter_nmos_modeling(fp, - name_postfix, - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - circuit_lib, - en_port, - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - } - - print_spice_subckt_end(fp, module_manager.module_name(module_id)); - - return CMD_EXEC_SUCCESS; -} - - -/******************************************************************** - * Generate the SPICE subckt for a regular buffer - * which contains at least 2 stages - * - * Note: - * - This function does NOT support power-gating - * It should be managed in a separated function - * - * Schematic of a multi-stage buffer - * - * LVDD LVDD - * | | - * - - - * +-o|| +-o|| - * | - | - - * | | | | - * in-->+ +-- ... ---+---->+---> out - * | | | | - * | - | - - * +--|| +--|| - * - - - * | | - * LGND LGND - * - *******************************************************************/ -static -int print_spice_regular_buffer_subckt(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& module_id, - const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model) { - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Print the inverter subckt definition */ - print_spice_subckt_definition(fp, module_manager, module_id); - - /* Find the input and output ports: - * we do NOT support global ports here, - * it should be handled in another type of inverter subckt (power-gated) - */ - std::vector input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); - std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); - - /* Make sure: - * There is only 1 input port and 1 output port, - * each size of which is 1 - */ - VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) ); - VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); - - int status = CMD_EXEC_SUCCESS; - - /* Buffers must have >= 2 stages */ - VTR_ASSERT(2 <= circuit_lib.buffer_num_levels(circuit_model)); - - /* Build the array denoting width of inverters per stage */ - std::vector buffer_widths(circuit_lib.buffer_num_levels(circuit_model), 1); - for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { - buffer_widths[level] = circuit_lib.buffer_size(circuit_model) - * std::pow(circuit_lib.buffer_f_per_stage(circuit_model), level); - } - - for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { - std::string input_port_name = circuit_lib.port_prefix(input_ports[0]); - std::string output_port_name = circuit_lib.port_prefix(output_ports[0]); - - /* Special for first stage: output port should be an intermediate node - * Special for rest of stages: input port should be the output of previous stage - */ - if (0 == level) { - output_port_name += std::string("_level") + std::to_string(level); - } else { - VTR_ASSERT(0 < level); - input_port_name += std::string("_level") + std::to_string(level - 1); - } - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - float total_pmos_width = buffer_widths[level] - * tech_lib.model_pn_ratio(tech_model) - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); - float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); - - for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { - float curr_bin_width = regular_pmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_pmos_bins - 1) - && (0. != last_pmos_bin_width)) { - curr_bin_width = last_pmos_bin_width; - } - - std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); - - status = print_spice_regular_inverter_pmos_modeling(fp, - name_postfix, - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - float total_nmos_width = buffer_widths[level] - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); - float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); - - for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { - float curr_bin_width = regular_nmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_nmos_bins - 1) - && (0. != last_nmos_bin_width)) { - curr_bin_width = last_nmos_bin_width; - } - - std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); - - status = print_spice_regular_inverter_nmos_modeling(fp, - name_postfix, - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - } - - print_spice_subckt_end(fp, module_manager.module_name(module_id)); - - return status; -} - -/******************************************************************** - * Generate the SPICE subckt for an buffer - * which consists of multiple stage of inverters - *******************************************************************/ -static -int print_spice_buffer_subckt(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& module_id, - const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model) { - int status = CMD_EXEC_SUCCESS; - if (true == circuit_lib.is_power_gated(circuit_model)) { - status = print_spice_powergated_buffer_subckt(fp, - module_manager, module_id, - circuit_lib, circuit_model, - tech_lib, tech_model); - } else { - VTR_ASSERT_SAFE(false == circuit_lib.is_power_gated(circuit_model)); - status = print_spice_regular_buffer_subckt(fp, - module_manager, module_id, - circuit_lib, circuit_model, - tech_lib, tech_model); - } - - return status; -} - /******************************************************************** * Generate the SPICE netlist for essential gates: * - inverters and their templates diff --git a/openfpga/src/fpga_spice/spice_essential_gates.h b/openfpga/src/fpga_spice/spice_essential_gates.h index de9ab3cdb..0fbd1895d 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.h +++ b/openfpga/src/fpga_spice/spice_essential_gates.h @@ -18,10 +18,6 @@ /* begin namespace openfpga */ namespace openfpga { -int print_spice_transistor_wrapper(NetlistManager& netlist_manager, - const TechnologyLibrary& tech_lib, - const std::string& submodule_dir); - int print_spice_essential_gates(NetlistManager& netlist_manager, const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, diff --git a/openfpga/src/fpga_spice/spice_submodule.cpp b/openfpga/src/fpga_spice/spice_submodule.cpp index 4de678af5..ad72e8515 100644 --- a/openfpga/src/fpga_spice/spice_submodule.cpp +++ b/openfpga/src/fpga_spice/spice_submodule.cpp @@ -10,6 +10,7 @@ /* Headers from openfpgashell library */ #include "command_exit_codes.h" +#include "spice_transistor_wrapper.h" #include "spice_essential_gates.h" #include "spice_constants.h" @@ -21,7 +22,7 @@ namespace openfpga { /********************************************************************* * Top-level function to generate primitive modules: * 1. Transistor wrapper - * 2. TODO: Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor + * 2. Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor * 3. TODO: Routing multiplexers * 4. TODO: Local encoders for routing multiplexers * 5. TODO: Wires diff --git a/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp b/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp new file mode 100644 index 000000000..140d68181 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp @@ -0,0 +1,117 @@ +/************************************************ + * This file includes functions on + * outputting wrapper SPICE netlists for transistor + ***********************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "circuit_library_utils.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_transistor_wrapper.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print a SPICE model wrapper for a transistor model + *******************************************************************/ +static +int print_spice_transistor_model_wrapper(std::fstream& fp, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& model) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Transistor model followed a fixed port mapping + * [X|M] + * which is a standard in SPICE modeling + * We will output the pmos and nmos transistors wrappers + * which are defined in this model + */ + for (int itype = TECH_LIB_TRANSISTOR_PMOS; + itype < NUM_TECH_LIB_TRANSISTOR_TYPES; + ++itype) { + const e_tech_lib_transistor_type& trans_type = static_cast(itype); + fp << ".subckt "; + fp << tech_lib.transistor_model_name(model, trans_type) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " drain gate source bulk"; + fp << " L=" << std::setprecision(10) << tech_lib.transistor_model_chan_length(model, trans_type); + fp << " W=" << std::setprecision(10) << tech_lib.transistor_model_min_width(model, trans_type); + fp << "\n"; + + fp << tech_lib.model_ref(model); + fp << "1"; + fp << " drain gate source bulk"; + fp << " " << tech_lib.transistor_model_name(model, trans_type); + fp << " L=L W=W"; + fp << "\n"; + + fp << ".ends"; + fp << "\n"; + } + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE netlist for transistors + *******************************************************************/ +int print_spice_transistor_wrapper(NetlistManager& netlist_manager, + const TechnologyLibrary& tech_lib, + const std::string& submodule_dir) { + std::string spice_fname = submodule_dir + std::string(TRANSISTORS_SPICE_FILE_NAME); + + std::fstream fp; + + /* Create the file stream */ + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + /* Check if the file stream if valid or not */ + check_file_stream(spice_fname.c_str(), fp); + + /* Create file */ + VTR_LOG("Generating SPICE netlist '%s' for transistors...", + spice_fname.c_str()); + + print_spice_file_header(fp, std::string("Transistor wrappers")); + + /* Iterate over the transistor models */ + for (const TechnologyModelId& model : tech_lib.models()) { + /* Focus on transistor model */ + if (TECH_LIB_MODEL_TRANSISTOR != tech_lib.model_type(model)) { + continue; + } + /* Write a wrapper for the transistor model */ + if (CMD_EXEC_SUCCESS == print_spice_transistor_model_wrapper(fp, tech_lib, model)) { + return CMD_EXEC_FATAL_ERROR; + } + } + + /* Close file handler*/ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); + + VTR_LOG("Done\n"); + + return CMD_EXEC_SUCCESS; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_transistor_wrapper.h b/openfpga/src/fpga_spice/spice_transistor_wrapper.h new file mode 100644 index 000000000..86115c5c4 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_transistor_wrapper.h @@ -0,0 +1,25 @@ +#ifndef SPICE_TRANSISTOR_WRAPPER_H +#define SPICE_TRANSISTOR_WRAPPER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "netlist_manager.h" +#include "technology_library.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_transistor_wrapper(NetlistManager& netlist_manager, + const TechnologyLibrary& tech_lib, + const std::string& submodule_dir); + +} /* end namespace openfpga */ + +#endif From 3262ceb2769d12d9c19018ddd2fa97293b6fe317 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 15:24:40 -0600 Subject: [PATCH 087/148] [FPGA-SPICE] Bug fix for pass gate transistor sizing --- openfpga/src/fpga_spice/spice_passgate.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/openfpga/src/fpga_spice/spice_passgate.cpp b/openfpga/src/fpga_spice/spice_passgate.cpp index a54956849..20b8ef84b 100644 --- a/openfpga/src/fpga_spice/spice_passgate.cpp +++ b/openfpga/src/fpga_spice/spice_passgate.cpp @@ -243,7 +243,6 @@ int print_spice_transmission_gate_subckt(std::fstream& fp, */ float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); float total_pmos_width = circuit_lib.pass_gate_logic_pmos_size(circuit_model) - * tech_lib.model_pn_ratio(tech_model) * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); From 482d90018f4fcf777603d39313b33cb2068838d1 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 15:33:28 -0600 Subject: [PATCH 088/148] [FPGA-SPICE] Create generic PMOS/NMOS instanciation function --- openfpga/src/fpga_spice/spice_passgate.cpp | 126 ++++-------------- .../fpga_spice/spice_transistor_wrapper.cpp | 73 ++++++++++ .../src/fpga_spice/spice_transistor_wrapper.h | 18 +++ 3 files changed, 116 insertions(+), 101 deletions(-) diff --git a/openfpga/src/fpga_spice/spice_passgate.cpp b/openfpga/src/fpga_spice/spice_passgate.cpp index 20b8ef84b..d26ac5bfb 100644 --- a/openfpga/src/fpga_spice/spice_passgate.cpp +++ b/openfpga/src/fpga_spice/spice_passgate.cpp @@ -20,88 +20,12 @@ #include "spice_constants.h" #include "spice_writer_utils.h" +#include "spice_transistor_wrapper.h" #include "spice_passgate.h" /* begin namespace openfpga */ namespace openfpga { -/******************************************************************** - * Generate the SPICE modeling for the PMOS part of a pass-gate logic - * - * This function is created to be shared by pass-transistor and - * transmission-gate SPICE netlist writer - * - * Note: - * - This function does NOT create a file - * but requires a file stream created - * - This function only output SPICE modeling for - * a pass-gate. Any preprocessing or subckt definition should not be included! - *******************************************************************/ -static -int print_spice_passgate_pmos_modeling(std::fstream& fp, - const std::string& trans_name_postfix, - const std::string& input_port_name, - const std::string& gate_port_name, - const std::string& output_port_name, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model, - const float& trans_width) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Write transistor pairs using the technology model */ - fp << "Xpmos_" << trans_name_postfix << " "; - fp << input_port_name << " "; - fp << gate_port_name << " "; - fp << output_port_name << " "; - fp << "LVDD "; - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE modeling for the NMOS part of a pass-gate logic - * - * This function is created to be shared by pass-transistor and - * transmission-gate SPICE netlist writer - * - * Note: - * - This function does NOT create a file - * but requires a file stream created - * - This function only output SPICE modeling for - * a pass-gate. Any preprocessing or subckt definition should not be included! - *******************************************************************/ -static -int print_spice_passgate_nmos_modeling(std::fstream& fp, - const std::string& trans_name_postfix, - const std::string& input_port_name, - const std::string& gate_port_name, - const std::string& output_port_name, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model, - const float& trans_width) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - fp << "Xnmos_" << trans_name_postfix << " "; - fp << input_port_name << " "; - fp << gate_port_name << " "; - fp << output_port_name << " "; - fp << "LGND "; - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - return CMD_EXEC_SUCCESS; -} - /******************************************************************** * Generate the SPICE subckt for a pass-transistor * @@ -167,14 +91,14 @@ int print_spice_pass_transistor_subckt(std::fstream& fp, curr_bin_width = last_nmos_bin_width; } - status = print_spice_passgate_nmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(input_ports[1]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); + status = print_spice_generic_nmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(input_ports[1]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); if (CMD_EXEC_FATAL_ERROR == status) { return status; } @@ -254,14 +178,14 @@ int print_spice_transmission_gate_subckt(std::fstream& fp, curr_bin_width = last_pmos_bin_width; } - status = print_spice_passgate_pmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(input_ports[2]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); + status = print_spice_generic_pmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(input_ports[2]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); if (CMD_EXEC_FATAL_ERROR == status) { return status; } @@ -285,14 +209,14 @@ int print_spice_transmission_gate_subckt(std::fstream& fp, curr_bin_width = last_nmos_bin_width; } - status = print_spice_passgate_nmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(input_ports[1]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); + status = print_spice_generic_nmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(input_ports[1]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); if (CMD_EXEC_FATAL_ERROR == status) { return status; } diff --git a/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp b/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp index 140d68181..882d43f4d 100644 --- a/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp +++ b/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp @@ -114,4 +114,77 @@ int print_spice_transistor_wrapper(NetlistManager& netlist_manager, return CMD_EXEC_SUCCESS; } +/******************************************************************** + * Generate the SPICE modeling for the PMOS part of a logic gate + * + * This function is created to be shared by pass-transistor and + * transmission-gate SPICE netlist writer + * + * Note: + * - This function does NOT create a file + * but requires a file stream created + * - This function only output SPICE modeling for + * a PMOS. Any preprocessing or subckt definition should not be included! + *******************************************************************/ +int print_spice_generic_pmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& gate_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Write transistor pairs using the technology model */ + fp << "Xpmos_" << trans_name_postfix << " "; + fp << input_port_name << " "; + fp << gate_port_name << " "; + fp << output_port_name << " "; + fp << "LVDD "; + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE modeling for the NMOS part of a logic gate + * + * Note: + * - This function does NOT create a file + * but requires a file stream created + * - This function only output SPICE modeling for + * a NMOS. Any preprocessing or subckt definition should not be included! + *******************************************************************/ +int print_spice_generic_nmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& gate_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + fp << "Xnmos_" << trans_name_postfix << " "; + fp << input_port_name << " "; + fp << gate_port_name << " "; + fp << output_port_name << " "; + fp << "LGND "; + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + return CMD_EXEC_SUCCESS; +} + + } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_transistor_wrapper.h b/openfpga/src/fpga_spice/spice_transistor_wrapper.h index 86115c5c4..7b3bef199 100644 --- a/openfpga/src/fpga_spice/spice_transistor_wrapper.h +++ b/openfpga/src/fpga_spice/spice_transistor_wrapper.h @@ -20,6 +20,24 @@ int print_spice_transistor_wrapper(NetlistManager& netlist_manager, const TechnologyLibrary& tech_lib, const std::string& submodule_dir); +int print_spice_generic_pmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& gate_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width); + +int print_spice_generic_nmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& gate_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width); + } /* end namespace openfpga */ #endif From e102e30d197578f77089a269c41793889dac8eba Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 16:20:21 -0600 Subject: [PATCH 089/148] [FPGA-SPICE] Add support for AND/OR logic gate --- openfpga/src/fpga_spice/spice_buffer.cpp | 24 +- openfpga/src/fpga_spice/spice_constants.h | 3 + .../src/fpga_spice/spice_essential_gates.cpp | 26 +- openfpga/src/fpga_spice/spice_logic_gate.cpp | 346 ++++++++++++++++++ openfpga/src/fpga_spice/spice_logic_gate.h | 38 ++ openfpga/src/fpga_spice/spice_passgate.cpp | 2 +- .../fpga_spice/spice_transistor_wrapper.cpp | 6 +- 7 files changed, 428 insertions(+), 17 deletions(-) create mode 100644 openfpga/src/fpga_spice/spice_logic_gate.cpp create mode 100644 openfpga/src/fpga_spice/spice_logic_gate.h diff --git a/openfpga/src/fpga_spice/spice_buffer.cpp b/openfpga/src/fpga_spice/spice_buffer.cpp index 59fa06d7f..824e3debc 100644 --- a/openfpga/src/fpga_spice/spice_buffer.cpp +++ b/openfpga/src/fpga_spice/spice_buffer.cpp @@ -63,15 +63,15 @@ int print_spice_powergated_inverter_pmos_modeling(std::fstream& fp, if (true == first_enb_pin) { fp << output_port_name << "_pmos_pg_" << power_gate_pin << " "; fp << generate_spice_port(enb_pin) << " "; - fp << "LVDD "; - fp << "LVDD "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; first_enb_pin = false; } else { VTR_ASSERT_SAFE(false == first_enb_pin); fp << output_port_name << "_pmos_pg_" << last_enb_pin << " "; fp << generate_spice_port(enb_pin) << " "; fp << output_port_name << "_pmos_pg_" << power_gate_pin << " "; - fp << "LVDD "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; } fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; @@ -86,7 +86,7 @@ int print_spice_powergated_inverter_pmos_modeling(std::fstream& fp, fp << output_port_name << " "; fp << input_port_name << " "; fp << output_port_name << "_pmos_pg_" << circuit_lib.pins(enb_port).back() << " "; - fp << "LVDD "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; fp << "\n"; @@ -129,15 +129,15 @@ int print_spice_powergated_inverter_nmos_modeling(std::fstream& fp, if (true == first_en_pin) { fp << output_port_name << "_nmos_pg_" << power_gate_pin << " "; fp << generate_spice_port(en_pin) << " "; - fp << "LGND "; - fp << "LGND "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; first_en_pin = false; } else { VTR_ASSERT_SAFE(false == first_en_pin); fp << output_port_name << "_nmos_pg_" << last_en_pin << " "; fp << circuit_lib.port_prefix(en_port) << " "; fp << output_port_name << "_nmos_pg_" << power_gate_pin << " "; - fp << "LGND "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; } fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; @@ -151,7 +151,7 @@ int print_spice_powergated_inverter_nmos_modeling(std::fstream& fp, fp << output_port_name << " "; fp << input_port_name << " "; fp << output_port_name << " _nmos_pg_" << circuit_lib.pins(en_port).back() << " "; - fp << "LGND "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; fp << "\n"; @@ -338,8 +338,8 @@ int print_spice_regular_inverter_pmos_modeling(std::fstream& fp, fp << "Xpmos_" << trans_name_postfix << " "; fp << output_port_name << " "; fp << input_port_name << " "; - fp << "LVDD "; - fp << "LVDD "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; fp << "\n"; @@ -374,8 +374,8 @@ int print_spice_regular_inverter_nmos_modeling(std::fstream& fp, fp << "Xnmos_" << trans_name_postfix << " "; fp << output_port_name << " "; fp << input_port_name << " "; - fp << "LGND "; - fp << "LGND "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; fp << "\n"; diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 770e1b595..80a13af36 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -10,4 +10,7 @@ constexpr char* TRANSISTOR_WRAPPER_POSTFIX = "_wrapper"; constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp"; constexpr char* ESSENTIALS_SPICE_FILE_NAME = "inv_buf_passgate.sp"; +constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; +constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; + #endif diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index fe554c328..6cad5c6f6 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -24,6 +24,7 @@ #include "spice_writer_utils.h" #include "spice_buffer.h" #include "spice_passgate.h" +#include "spice_logic_gate.h" #include "spice_essential_gates.h" /* begin namespace openfpga */ @@ -112,7 +113,7 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, continue; } - /* Now branch on netlist writing: for inverter/buffers */ + /* Now branch on netlist writing: for pass-gate logic */ if (CIRCUIT_MODEL_PASSGATE == circuit_lib.model_type(circuit_model)) { status = print_spice_passgate_subckt(fp, module_manager, module_id, @@ -126,6 +127,29 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, /* Finish, go to the next */ continue; } + + /* Now branch on netlist writing: for logic gate */ + if (CIRCUIT_MODEL_GATE == circuit_lib.model_type(circuit_model)) { + if (CIRCUIT_MODEL_GATE_AND == circuit_lib.gate_type(circuit_model)) { + status = print_spice_and_gate_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } else if (CIRCUIT_MODEL_GATE_OR == circuit_lib.gate_type(circuit_model)) { + status = print_spice_or_gate_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } + + if (CMD_EXEC_FATAL_ERROR == status) { + break; + } + + /* Finish, go to the next */ + continue; + } + } /* Close file handler*/ diff --git a/openfpga/src/fpga_spice/spice_logic_gate.cpp b/openfpga/src/fpga_spice/spice_logic_gate.cpp new file mode 100644 index 000000000..6eba3da7a --- /dev/null +++ b/openfpga/src/fpga_spice/spice_logic_gate.cpp @@ -0,0 +1,346 @@ +/************************************************ + * This file includes functions on + * outputting SPICE netlists for logic gates: + * - N-input AND gate + * - N-input OR gate + ***********************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "circuit_library_utils.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_transistor_wrapper.h" +#include "spice_logic_gate.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Generate the SPICE subckt for a N-input AND gate + * + * Schematic + * + * VDD VDD VDD + * | | | + * - - - + * in0 -o|| in1 -o|| ... in[N-1] -o|| + * - - - + * | | | + * +----+-----+- ... -------------+ + * | + * - + * in0 -|| + * - + * | + * - + * in1 -|| + * - + * | + * ... + * | + * - + * in[N-1] -|| + * - + * | + * GND + *******************************************************************/ +int print_spice_and_gate_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + std::vector input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); + std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + + /* Make sure: + * There are at least 2 input ports and 1 output port, + * each size of which is 1 + */ + VTR_ASSERT(2 <= input_ports.size()); + for (const auto& input_port : input_ports) { + VTR_ASSERT(1 == circuit_lib.port_size(input_port)); + } + + VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); + + int status = CMD_EXEC_SUCCESS; + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + float total_pmos_width = 1. /* TODO: allow users to define gate strength */ + * tech_lib.model_pn_ratio(tech_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); + float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); + + + /* Output the PMOS network */ + for (const auto& input_port : input_ports) { + for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { + float curr_bin_width = regular_pmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_pmos_bins - 1) + && (0. != last_pmos_bin_width)) { + curr_bin_width = last_pmos_bin_width; + } + + status = print_spice_generic_pmos_modeling(fp, + std::to_string(ibin), + std::string(SPICE_SUBCKT_VDD_PORT_NAME), + circuit_lib.port_prefix(input_port), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = 1. /* TODO: allow users to define gate strength */ + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + + /* Output the NMOS network */ + for (size_t input_id = 0; input_id < input_ports.size(); ++input_id) { + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + /* Depending on the input id, we assign different port names to source/drain */ + std::string source_port_name; + std::string drain_port_name; + + if (0 == input_id) { + /* First transistor should connect to the output port and an internal node */ + source_port_name = circuit_lib.port_prefix(output_ports[0]); + drain_port_name = std::string("internal_node") + std::to_string(input_id); + } else if (input_id == input_ports.size() - 1) { + /* Last transistor should connect to an internal node and GND */ + source_port_name = std::string("internal_node") + std::to_string(input_id - 1); + drain_port_name = std::string(SPICE_SUBCKT_GND_PORT_NAME); + } else { + /* Other transistors should connect to two internal nodes */ + source_port_name = std::string("internal_node") + std::to_string(input_id - 1); + drain_port_name = std::string("internal_node") + std::to_string(input_id); + } + + status = print_spice_generic_nmos_modeling(fp, + std::to_string(ibin), + source_port_name, + circuit_lib.port_prefix(input_ports[input_id]), + drain_port_name, + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return status; +} + +/******************************************************************** + * Generate the SPICE subckt for a N-input OR gate + * + * Schematic + * + * + * VDD + * | + * - + * in0 -o|| + * - + * | + * - + * in1 -o|| + * - + * | + * ... + * | + * - + * in[N-1] -o|| + * - + * | + * +----+-----+- ... -------------+ + * | | | + * - - - + * in0 -|| in1 -|| ... in[N-1] -|| + * - - - + * | | | + * GND GND GND + *******************************************************************/ +int print_spice_or_gate_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + std::vector input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); + std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + + /* Make sure: + * There are at least 2 input ports and 1 output port, + * each size of which is 1 + */ + VTR_ASSERT(2 <= input_ports.size()); + for (const auto& input_port : input_ports) { + VTR_ASSERT(1 == circuit_lib.port_size(input_port)); + } + + VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); + + int status = CMD_EXEC_SUCCESS; + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + float total_pmos_width = 1. /* TODO: allow users to define gate strength */ + * tech_lib.model_pn_ratio(tech_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); + float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); + + + /* Output the PMOS network */ + for (size_t input_id = 0; input_id < input_ports.size(); ++input_id) { + for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { + float curr_bin_width = regular_pmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_pmos_bins - 1) + && (0. != last_pmos_bin_width)) { + curr_bin_width = last_pmos_bin_width; + } + + /* Depending on the input id, we assign different port names to source/drain */ + std::string source_port_name; + std::string drain_port_name; + + if (0 == input_id) { + /* First transistor should connect to the output port and an internal node */ + source_port_name = circuit_lib.port_prefix(output_ports[0]); + drain_port_name = std::string("internal_node") + std::to_string(input_id); + } else if (input_id == input_ports.size() - 1) { + /* Last transistor should connect to an internal node and GND */ + source_port_name = std::string("internal_node") + std::to_string(input_id - 1); + drain_port_name = std::string(SPICE_SUBCKT_VDD_PORT_NAME); + } else { + /* Other transistors should connect to two internal nodes */ + source_port_name = std::string("internal_node") + std::to_string(input_id - 1); + drain_port_name = std::string("internal_node") + std::to_string(input_id); + } + + status = print_spice_generic_pmos_modeling(fp, + std::to_string(ibin), + source_port_name, + circuit_lib.port_prefix(input_ports[input_id]), + drain_port_name, + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = 1. /* TODO: allow users to define gate strength */ + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + + /* Output the NMOS network */ + for (const auto& input_port : input_ports) { + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + status = print_spice_generic_nmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(output_ports[0]), + circuit_lib.port_prefix(input_port), + std::string(SPICE_SUBCKT_GND_PORT_NAME), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_logic_gate.h b/openfpga/src/fpga_spice/spice_logic_gate.h new file mode 100644 index 000000000..4a72c803f --- /dev/null +++ b/openfpga/src/fpga_spice/spice_logic_gate.h @@ -0,0 +1,38 @@ +#ifndef SPICE_LOGIC_GATE_H +#define SPICE_LOGIC_GATE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "module_manager.h" +#include "circuit_library.h" +#include "technology_library.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_and_gate_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model); + +int print_spice_or_gate_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_passgate.cpp b/openfpga/src/fpga_spice/spice_passgate.cpp index d26ac5bfb..cf3702ba7 100644 --- a/openfpga/src/fpga_spice/spice_passgate.cpp +++ b/openfpga/src/fpga_spice/spice_passgate.cpp @@ -248,7 +248,7 @@ int print_spice_passgate_subckt(std::fstream& fp, module_manager, module_id, circuit_lib, circuit_model, tech_lib, tech_model); - } else if (CIRCUIT_MODEL_PASS_GATE_TRANSMISSION == circuit_lib.is_power_gated(circuit_model)) { + } else if (CIRCUIT_MODEL_PASS_GATE_TRANSMISSION == circuit_lib.pass_gate_logic_type(circuit_model)) { status = print_spice_transmission_gate_subckt(fp, module_manager, module_id, circuit_lib, circuit_model, diff --git a/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp b/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp index 882d43f4d..6a90b0c96 100644 --- a/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp +++ b/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp @@ -96,7 +96,7 @@ int print_spice_transistor_wrapper(NetlistManager& netlist_manager, continue; } /* Write a wrapper for the transistor model */ - if (CMD_EXEC_SUCCESS == print_spice_transistor_model_wrapper(fp, tech_lib, model)) { + if (CMD_EXEC_SUCCESS != print_spice_transistor_model_wrapper(fp, tech_lib, model)) { return CMD_EXEC_FATAL_ERROR; } } @@ -144,7 +144,7 @@ int print_spice_generic_pmos_modeling(std::fstream& fp, fp << input_port_name << " "; fp << gate_port_name << " "; fp << output_port_name << " "; - fp << "LVDD "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; fp << "\n"; @@ -178,7 +178,7 @@ int print_spice_generic_nmos_modeling(std::fstream& fp, fp << input_port_name << " "; fp << gate_port_name << " "; fp << output_port_name << " "; - fp << "LGND "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; fp << "\n"; From 26a0a769eabe06658c576d601b91c5e7cb3bdd41 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 16:45:26 -0600 Subject: [PATCH 090/148] [FPGA-SPICE] Split essential gate SPICE netlists into separated files --- openfpga/src/fpga_spice/spice_constants.h | 1 - .../src/fpga_spice/spice_essential_gates.cpp | 78 +++++++++++-------- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 80a13af36..48482d703 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -8,7 +8,6 @@ constexpr char* SPICE_NETLIST_FILE_POSTFIX = ".sp"; constexpr char* TRANSISTOR_WRAPPER_POSTFIX = "_wrapper"; constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp"; -constexpr char* ESSENTIALS_SPICE_FILE_NAME = "inv_buf_passgate.sp"; constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index 6cad5c6f6..780a31776 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -43,21 +43,6 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, const TechnologyLibrary& tech_lib, const std::map& circuit_tech_binding, const std::string& submodule_dir) { - std::string spice_fname = submodule_dir + std::string(ESSENTIALS_SPICE_FILE_NAME); - - std::fstream fp; - - /* Create the file stream */ - fp.open(spice_fname, std::fstream::out | std::fstream::trunc); - /* Check if the file stream if valid or not */ - check_file_stream(spice_fname.c_str(), fp); - - /* Create file */ - VTR_LOG("Generating SPICE netlist '%s' for essential gates...", - spice_fname.c_str()); - - print_spice_file_header(fp, std::string("Essential gates")); - int status = CMD_EXEC_SUCCESS; /* Iterate over the circuit models */ @@ -89,6 +74,26 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, VTR_ASSERT(TECH_LIB_MODEL_TRANSISTOR == tech_lib.model_type(tech_model)); } + /* Create file stream */ + std::string spice_fname = submodule_dir + circuit_lib.model_name(circuit_model); + + std::fstream fp; + + /* Create the file stream */ + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + /* Check if the file stream if valid or not */ + check_file_stream(spice_fname.c_str(), fp); + + /* Create file */ + VTR_LOG("Generating SPICE netlist '%s' for circuit model '%s'...", + spice_fname.c_str(), + circuit_lib.model_name(circuit_model).c_str()); + + print_spice_file_header(fp, circuit_lib.model_name(circuit_model)); + + /* A flag to record if any logic has been filled to the netlist */ + bool netlist_filled = false; + /* Now branch on netlist writing: for inverter/buffers */ if (CIRCUIT_MODEL_INVBUF == circuit_lib.model_type(circuit_model)) { if (CIRCUIT_MODEL_BUF_INV == circuit_lib.buffer_type(circuit_model)) { @@ -97,20 +102,19 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, module_manager, module_id, circuit_lib, circuit_model, tech_lib, tech_model); + netlist_filled = true; } else { VTR_ASSERT(CIRCUIT_MODEL_BUF_BUF == circuit_lib.buffer_type(circuit_model)); status = print_spice_buffer_subckt(fp, module_manager, module_id, circuit_lib, circuit_model, tech_lib, tech_model); + netlist_filled = true; } if (CMD_EXEC_FATAL_ERROR == status) { break; } - - /* Finish, go to the next */ - continue; } /* Now branch on netlist writing: for pass-gate logic */ @@ -119,13 +123,11 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, module_manager, module_id, circuit_lib, circuit_model, tech_lib, tech_model); + netlist_filled = true; if (CMD_EXEC_FATAL_ERROR == status) { break; } - - /* Finish, go to the next */ - continue; } /* Now branch on netlist writing: for logic gate */ @@ -135,33 +137,41 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, module_manager, module_id, circuit_lib, circuit_model, tech_lib, tech_model); + netlist_filled = true; } else if (CIRCUIT_MODEL_GATE_OR == circuit_lib.gate_type(circuit_model)) { status = print_spice_or_gate_subckt(fp, module_manager, module_id, circuit_lib, circuit_model, tech_lib, tech_model); + netlist_filled = true; } if (CMD_EXEC_FATAL_ERROR == status) { break; } - - /* Finish, go to the next */ - continue; } + /* Check if the netlist has been filled or not. + * If not, flag a fatal error + */ + if (false == netlist_filled) { + VTR_LOG_ERROR("Cannot auto-generate netlist for circuit model '%s'!\n\tThe circuit topology is not supported yet!\n", + circuit_lib.model_name(circuit_model).c_str()); + status = CMD_EXEC_FATAL_ERROR; + break; + } + + /* Close file handler*/ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); + + VTR_LOG("Done\n"); } - /* Close file handler*/ - fp.close(); - - /* Add fname to the netlist name list */ - NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); - VTR_ASSERT(NetlistId::INVALID() != nlist_id); - netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); - - VTR_LOG("Done\n"); - return status; } From 1b2762386cfa5b56bd67ee2406f5fd7b465140e4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 16:52:30 -0600 Subject: [PATCH 091/148] [FPGA-SPICE] Bug fix for essential gate netlist writing --- .../src/fpga_spice/spice_essential_gates.cpp | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index 780a31776..107077bcc 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -52,6 +52,15 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, continue; } + /* Output only the model type is supported in auto-generation */ + if ( (CIRCUIT_MODEL_INVBUF != circuit_lib.model_type(circuit_model)) + && (CIRCUIT_MODEL_PASSGATE != circuit_lib.model_type(circuit_model)) + && (CIRCUIT_MODEL_CHAN_WIRE != circuit_lib.model_type(circuit_model)) + && (CIRCUIT_MODEL_WIRE != circuit_lib.model_type(circuit_model)) + && (CIRCUIT_MODEL_GATE != circuit_lib.model_type(circuit_model))) { + continue; + } + /* Spot module id */ const ModuleId& module_id = module_manager.find_module(circuit_lib.model_name(circuit_model)); @@ -75,7 +84,7 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, } /* Create file stream */ - std::string spice_fname = submodule_dir + circuit_lib.model_name(circuit_model); + std::string spice_fname = submodule_dir + circuit_lib.model_name(circuit_model) + std::string(SPICE_NETLIST_FILE_POSTFIX); std::fstream fp; @@ -151,6 +160,21 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, } } + /* Now branch on netlist writing: for routing channel wires */ + if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(circuit_model)) { + netlist_filled = true; + if (CMD_EXEC_FATAL_ERROR == status) { + break; + } + } + /* Now branch on netlist writing: for regular wires */ + if (CIRCUIT_MODEL_WIRE == circuit_lib.model_type(circuit_model)) { + netlist_filled = true; + if (CMD_EXEC_FATAL_ERROR == status) { + break; + } + } + /* Check if the netlist has been filled or not. * If not, flag a fatal error */ From 82e137cbe40c2edf652d8a4df4284660727cb4d2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 19:31:16 -0600 Subject: [PATCH 092/148] [FPGA-SPICE] Add wire module SPICE writer --- .../src/fpga_spice/spice_essential_gates.cpp | 14 +- openfpga/src/fpga_spice/spice_wire.cpp | 408 ++++++++++++++++++ openfpga/src/fpga_spice/spice_wire.h | 27 ++ .../src/fpga_spice/spice_writer_utils.cpp | 155 +++++++ openfpga/src/fpga_spice/spice_writer_utils.h | 20 + 5 files changed, 615 insertions(+), 9 deletions(-) create mode 100644 openfpga/src/fpga_spice/spice_wire.cpp create mode 100644 openfpga/src/fpga_spice/spice_wire.h diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index 107077bcc..eafe37a24 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -25,6 +25,7 @@ #include "spice_buffer.h" #include "spice_passgate.h" #include "spice_logic_gate.h" +#include "spice_wire.h" #include "spice_essential_gates.h" /* begin namespace openfpga */ @@ -55,7 +56,6 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, /* Output only the model type is supported in auto-generation */ if ( (CIRCUIT_MODEL_INVBUF != circuit_lib.model_type(circuit_model)) && (CIRCUIT_MODEL_PASSGATE != circuit_lib.model_type(circuit_model)) - && (CIRCUIT_MODEL_CHAN_WIRE != circuit_lib.model_type(circuit_model)) && (CIRCUIT_MODEL_WIRE != circuit_lib.model_type(circuit_model)) && (CIRCUIT_MODEL_GATE != circuit_lib.model_type(circuit_model))) { continue; @@ -160,15 +160,11 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, } } - /* Now branch on netlist writing: for routing channel wires */ - if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(circuit_model)) { - netlist_filled = true; - if (CMD_EXEC_FATAL_ERROR == status) { - break; - } - } - /* Now branch on netlist writing: for regular wires */ + /* Now branch on netlist writing: for wires */ if (CIRCUIT_MODEL_WIRE == circuit_lib.model_type(circuit_model)) { + status = print_spice_wire_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model); netlist_filled = true; if (CMD_EXEC_FATAL_ERROR == status) { break; diff --git a/openfpga/src/fpga_spice/spice_wire.cpp b/openfpga/src/fpga_spice/spice_wire.cpp new file mode 100644 index 000000000..097515899 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_wire.cpp @@ -0,0 +1,408 @@ +/************************************************ + * This file includes functions on + * outputting SPICE netlists for routing wires: + * - regular wires (1 input and 1 output) + * - routing track wires (1 input and 2 outputs) + ***********************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "circuit_library_utils.h" +#include "build_module_graph_utils.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_wire.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print SPICE modeling for pie-type RC network + * + * Schematic + * middle out + * | + * in ---wwww----wwww--- ... --wwww---out + * | | | | + * = = = = + * | | | | + * GND GND GND GND + *******************************************************************/ +static +int print_spice_wire_pi_type_rc_modeling(std::fstream& fp, + const std::string& input_port_name, + const std::string& output_port_name, + const std::string& middle_output_port_name, + const float& res_total, + const float& cap_total, + const size_t& num_levels) { + + /* Determine the resistance and capacitance of each level*/ + float res_per_level = res_total / ((float)(2 * num_levels)); + float cap_per_level = cap_total / ((float)(2 * num_levels)); + + /* All the resistance and capacitance value should be larger than or equal to zero*/ + VTR_ASSERT(0. <= res_per_level); + VTR_ASSERT(0. <= cap_per_level); + + for (size_t ilvl = 0; ilvl < num_levels; ++ilvl) { + /* Print the first capacitor if this is the first level */ + if ((0 == ilvl) && (0. < cap_per_level)) { + print_spice_capacitor(fp, input_port_name, std::string(SPICE_SUBCKT_GND_PORT_NAME), cap_per_level); + } + /* Output a regular RC pair + * + * midnode + * ^ + * | + * ------+-ww-+-ww-+------ + * | | + * = = + * | | + * GND GND + */ + + std::string lvl_input_port_name = std::string("rc_network_node") + std::to_string(ilvl); + if (0 == ilvl) { + lvl_input_port_name = input_port_name; + } + + std::string lvl_middle_port_name = std::string("rc_network_midnode") + std::to_string(ilvl); + + std::string lvl_output_port_name = std::string("rc_network_node") + std::to_string(ilvl + 1); + if (ilvl == num_levels - 1) { + lvl_output_port_name = output_port_name; + } + + print_spice_resistor(fp, lvl_input_port_name, lvl_middle_port_name, res_per_level); + print_spice_resistor(fp, lvl_middle_port_name, lvl_output_port_name, res_per_level); + + /* Last level only require 1 unit of cap_per_level */ + float cap_curr_level = 2. * cap_per_level; + if (ilvl == num_levels - 1) { + cap_curr_level = cap_per_level; + } + + if (0. < cap_curr_level) { + print_spice_capacitor(fp, lvl_output_port_name, std::string(SPICE_SUBCKT_GND_PORT_NAME), cap_curr_level); + } + } + + /* If the middle output is required, create a short connection to + * - when the number of levels is odd + * + * middle_output + * ^ + * | + * ---ww-+-ww-+-ww-+-ww--- + * | | + * = = + * | | + * GND GND + * + * - when the number of levels is even: + * + * middle_output + * ^ + * | + * -+-ww--ww-+-ww--ww-+- + * | | | + * = = = + * | | | + * GND GND GND + * + */ + if (!middle_output_port_name.empty()) { + print_spice_comment(fp, std::string("Connect to the middle output")); + std::string rc_midnode_name = std::string("rc_network_node") + std::to_string(num_levels / 2 + 1); + if (1 == num_levels % 2) { + rc_midnode_name = std::string("rc_network_midnode") + std::to_string(num_levels / 2); + } + print_spice_short_connection(fp, rc_midnode_name, middle_output_port_name); + } + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Print SPICE modeling for T-type RC network + * + * Schematic + * middle out + * | + * in ---ww-+--ww--+--ww--+--ww--- ... --ww--+--ww--- out + * | | | | + * = = = = + * | | | | + * GND GND GND GND + *******************************************************************/ +static +int print_spice_wire_t_type_rc_modeling(std::fstream& fp, + const std::string& input_port_name, + const std::string& output_port_name, + const std::string& middle_output_port_name, + const float& res_total, + const float& cap_total, + const size_t& num_levels) { + + /* Determine the resistance and capacitance of each level*/ + float res_per_level = res_total / ((float)(2 * num_levels)); + float cap_per_level = cap_total / ((float)(num_levels)); + + /* All the resistance and capacitance value should be larger than or equal to zero*/ + VTR_ASSERT(0. <= res_per_level); + VTR_ASSERT(0. <= cap_per_level); + + for (size_t ilvl = 0; ilvl < num_levels; ++ilvl) { + /* Output a regular RC pair + * + * midnode + * ^ + * | + * --------ww-+-ww-------- + * | + * = + * | + * GND + */ + + std::string lvl_input_port_name = std::string("rc_network_node") + std::to_string(ilvl); + if (0 == ilvl) { + lvl_input_port_name = input_port_name; + } + + std::string lvl_middle_port_name = std::string("rc_network_midnode") + std::to_string(ilvl); + + std::string lvl_output_port_name = std::string("rc_network_node") + std::to_string(ilvl + 1); + if (ilvl == num_levels - 1) { + lvl_output_port_name = output_port_name; + } + + print_spice_resistor(fp, lvl_input_port_name, lvl_middle_port_name, res_per_level); + print_spice_resistor(fp, lvl_middle_port_name, lvl_output_port_name, res_per_level); + + if (0. < cap_per_level) { + print_spice_capacitor(fp, lvl_middle_port_name, std::string(SPICE_SUBCKT_GND_PORT_NAME), cap_per_level); + } + } + + /* If the middle output is required, create a short connection to + * - when the number of levels is even + * + * middle_output + * ^ + * | + * ---ww-+-ww-+-ww-+-ww--- + * | | + * = = + * | | + * GND GND + * + * - when the number of levels is odd: + * + * middle_output + * ^ + * | + * -+-ww--ww-+-ww--ww-+- + * | | | + * = = = + * | | | + * GND GND GND + * + */ + if (!middle_output_port_name.empty()) { + print_spice_comment(fp, std::string("Connect to the middle output")); + std::string rc_midnode_name = std::string("rc_network_midnode") + std::to_string(num_levels / 2); + if (0 == num_levels % 2) { + rc_midnode_name = std::string("rc_network_node") + std::to_string(num_levels / 2 + 1); + } + print_spice_short_connection(fp, rc_midnode_name, middle_output_port_name); + } + + return CMD_EXEC_SUCCESS; +} + + +/******************************************************************** + * Generate the SPICE subckt for a regular wire + * + * Schematic + * + * Middle output (only for routing track wires) + * ^ + * | + * +--------------------+ +---------------+ +--------------------+ + * in ->| Inverter or buffer |--->| RC Network |---->| Inverter or buffer |---> out + * | Optional | | | | Optional | + * +--------------------- +---------------+ +--------------------+ + * + *******************************************************************/ +int print_spice_wire_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + std::vector input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); + std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + + /* Make sure: + * There are 1 input ports and 1 output port, + * each size of which is 1 + */ + VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) ); + VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); + + int status = CMD_EXEC_SUCCESS; + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + std::string input_port_name = circuit_lib.port_prefix(input_ports[0]); + std::string output_port_name = circuit_lib.port_prefix(output_ports[0]); + std::string middle_output_port_name; + if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(circuit_model)) { + middle_output_port_name = std::string("middle") + output_port_name; + } + + std::string rc_ntwk_input_port_name = std::string("rc_network_node") + std::to_string(0); + std::string rc_ntwk_output_port_name = std::string("rc_network_node") + std::to_string(circuit_lib.wire_num_level(circuit_model) - 1); + std::string rc_ntwk_middle_output_port_name; + if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(circuit_model)) { + rc_ntwk_middle_output_port_name = std::string("middle") + rc_ntwk_output_port_name; + } + + ModulePortId wire_module_input_port = module_manager.find_module_port(module_id, input_port_name); + ModulePortId wire_module_output_port = module_manager.find_module_port(module_id, output_port_name); + ModulePortId wire_module_middle_output_port = ModulePortId::INVALID(); + if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(circuit_model)) { + wire_module_middle_output_port = module_manager.find_module_port(module_id, output_port_name); + } + + /* Add input buffer: + * - There is a valid buffer model, instanciate it + * - There is no buffer, set a short connection + */ + if (circuit_lib.input_buffer_model(circuit_model)) { + std::string instance_name = std::string("input_buffer"); + std::map port2port_name_map; + + ModuleId buffer_module = module_manager.find_module(circuit_lib.model_name(circuit_lib.input_buffer_model(circuit_model))); + VTR_ASSERT(true == module_manager.valid_module_id(buffer_module)); + + ModulePortId module_input_port_id = find_inverter_buffer_module_port(module_manager, buffer_module, circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_INPUT); + ModulePortId module_output_port_id = find_inverter_buffer_module_port(module_manager, buffer_module, circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_OUTPUT); + + /* Port size should be 1 ! */ + VTR_ASSERT(1 == module_manager.module_port(buffer_module, module_input_port_id).get_width()); + VTR_ASSERT(1 == module_manager.module_port(buffer_module, module_output_port_id).get_width()); + + port2port_name_map[module_manager.module_port(buffer_module, module_input_port_id).get_name()] = module_manager.module_port(module_id, wire_module_input_port); + port2port_name_map[module_manager.module_port(buffer_module, module_output_port_id).get_name()] = BasicPort(rc_ntwk_input_port_name, 1); + + print_spice_subckt_instance(fp, + module_manager, + buffer_module, + instance_name, + port2port_name_map); + } else { + print_spice_short_connection(fp, circuit_lib.port_prefix(input_ports[0]), rc_ntwk_input_port_name); + } + + /* Determine which type of model to print*/ + switch (circuit_lib.wire_type(circuit_model)) { + case WIRE_MODEL_PI: + status = print_spice_wire_pi_type_rc_modeling(fp, + rc_ntwk_input_port_name, + rc_ntwk_output_port_name, + rc_ntwk_middle_output_port_name, + circuit_lib.wire_r(circuit_model), + circuit_lib.wire_c(circuit_model), + circuit_lib.wire_num_level(circuit_model)); + break; + case WIRE_MODEL_T: + status = print_spice_wire_t_type_rc_modeling(fp, + rc_ntwk_input_port_name, + rc_ntwk_output_port_name, + rc_ntwk_middle_output_port_name, + circuit_lib.wire_r(circuit_model), + circuit_lib.wire_c(circuit_model), + circuit_lib.wire_num_level(circuit_model)); + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Unsupport wire model type for circuit model '%s.\n", + circuit_lib.model_name(circuit_model).c_str()); + return CMD_EXEC_FATAL_ERROR; + } + + /* Add output buffer: + * - There is a valid buffer model, instanciate it + * - There is no buffer, set a short connection + */ + if (circuit_lib.output_buffer_model(circuit_model)) { + std::string instance_name = std::string("output_buffer"); + std::map port2port_name_map; + + ModuleId buffer_module = module_manager.find_module(circuit_lib.model_name(circuit_lib.output_buffer_model(circuit_model))); + VTR_ASSERT(true == module_manager.valid_module_id(buffer_module)); + + ModulePortId module_input_port_id = find_inverter_buffer_module_port(module_manager, buffer_module, circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_INPUT); + ModulePortId module_output_port_id = find_inverter_buffer_module_port(module_manager, buffer_module, circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_OUTPUT); + + /* Port size should be 1 ! */ + VTR_ASSERT(1 == module_manager.module_port(buffer_module, module_input_port_id).get_width()); + VTR_ASSERT(1 == module_manager.module_port(buffer_module, module_output_port_id).get_width()); + + port2port_name_map[module_manager.module_port(buffer_module, module_input_port_id).get_name()] = BasicPort(rc_ntwk_output_port_name, 1); + port2port_name_map[module_manager.module_port(buffer_module, module_output_port_id).get_name()] = module_manager.module_port(module_id, wire_module_output_port); + + print_spice_subckt_instance(fp, + module_manager, + buffer_module, + instance_name, + port2port_name_map); + + if (!rc_ntwk_middle_output_port_name.empty()) { + instance_name = std::string("middle_output_buffer"); + port2port_name_map[module_manager.module_port(buffer_module, module_output_port_id).get_name()] = module_manager.module_port(module_id, wire_module_middle_output_port); + + print_spice_subckt_instance(fp, + module_manager, + buffer_module, + instance_name, + port2port_name_map); + } + } else { + print_spice_short_connection(fp, rc_ntwk_output_port_name, circuit_lib.port_prefix(output_ports[0])); + if (!rc_ntwk_middle_output_port_name.empty()) { + print_spice_short_connection(fp, rc_ntwk_middle_output_port_name, circuit_lib.port_prefix(output_ports[0])); + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_wire.h b/openfpga/src/fpga_spice/spice_wire.h new file mode 100644 index 000000000..5af9542a0 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_wire.h @@ -0,0 +1,27 @@ +#ifndef SPICE_WIRE_H +#define SPICE_WIRE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "module_manager.h" +#include "circuit_library.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_wire_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_writer_utils.cpp b/openfpga/src/fpga_spice/spice_writer_utils.cpp index 89b82a027..889ee8c19 100644 --- a/openfpga/src/fpga_spice/spice_writer_utils.cpp +++ b/openfpga/src/fpga_spice/spice_writer_utils.cpp @@ -168,4 +168,159 @@ void print_spice_subckt_end(std::fstream& fp, fp << std::endl; } +/************************************************ + * Print a resistor in SPICE syntax + ***********************************************/ +void print_spice_resistor(std::fstream& fp, + const std::string& input_port, + const std::string& output_port, + const float& resistance) { + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Set an unique name to the resistor */ + fp << "R" << input_port << "_to_" << output_port; + fp << " " << input_port; + fp << " " << output_port; + fp << " " << std::setprecision(10) << resistance; + fp << std::endl; +} + +/************************************************ + * Print a capacitor in SPICE syntax + ***********************************************/ +void print_spice_capacitor(std::fstream& fp, + const std::string& input_port, + const std::string& output_port, + const float& capacitance) { + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Set an unique name to the capacitor */ + fp << "C" << input_port << "_to_" << output_port; + fp << " " << input_port; + fp << " " << output_port; + fp << " " << std::setprecision(10) << capacitance; + fp << std::endl; +} + +/************************************************ + * Print a short-connected wire using zero resistance in SPICE syntax + ***********************************************/ +void print_spice_short_connection(std::fstream& fp, + const std::string& input_port, + const std::string& output_port) { + print_spice_resistor(fp, input_port, output_port, 0.); +} + +/******************************************************************** + * Print an instance in SPICE format (a generic version) + * This function will require user to provide an instance name + * + * This function will output the port map by referring to a port-to-port + * mapping: + * -> + * The key of the port-to-port mapping is the port name of the module: + * The value of the port-to-port mapping is the port information of the instance + * With link between module and instance, the function can output a SPICE + * instance easily, by following the define port sequence of the module + * + * Note that, it is not necessary that the port-to-port mapping + * covers all the module ports. + * Any instance/module port which are not specified in the port-to-port + * mapping will be output by the module port name. + *******************************************************************/ +void print_spice_subckt_instance(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const std::string& instance_name, + const std::map& port2port_name_map) { + + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Check: all the key ports in the port2port_name_map does exist in the child module */ + for (const auto& kv : port2port_name_map) { + ModulePortId module_port_id = module_manager.find_module_port(module_id, kv.first); + VTR_ASSERT(ModulePortId::INVALID() != module_port_id); + } + + /* Print instance name */ + std::string instance_head_line = "X " + instance_name + " "; + fp << instance_head_line; + + /* Port sequence: global, inout, input, output and clock ports, */ + bool fit_one_line = true; + bool new_line = false; + size_t pin_cnt = 0; + for (int port_type = ModuleManager::MODULE_GLOBAL_PORT; + port_type < ModuleManager::NUM_MODULE_PORT_TYPES; + ++port_type) { + for (const auto& port : module_manager.module_ports_by_type(module_id, static_cast(port_type))) { + /* Deposit a default port name */ + BasicPort port_to_print = port; + /* Try to find the instanced port name in the name map */ + auto port_search_result = port2port_name_map.find(port.get_name()); + if (port_search_result != port2port_name_map.end()) { + /* Found it, we assign the port name */ + /* TODO: make sure the port width matches! */ + ModulePortId module_port_id = module_manager.find_module_port(module_id, port.get_name()); + /* Get the port from module */ + BasicPort module_port = module_manager.module_port(module_id, module_port_id); + VTR_ASSERT(module_port.get_width() == port_search_result->second.get_width()); + + port_to_print = port_search_result->second; + } + + /* Print port: only the port name is enough */ + for (const auto& pin : port_to_print.pins()) { + + if (true == new_line) { + std::string port_whitespace(instance_head_line.length() - 2, ' '); + fp << "+ " << port_whitespace; + } + + if (0 != pin_cnt) { + write_space_to_file(fp, 1); + } + + BasicPort port_pin(port.get_name(), pin, pin); + + /* For single-bit port, + * we can print the port name directly + */ + bool omit_pin_zero = false; + if ((1 == port.pins().size()) + && (0 == pin)) { + omit_pin_zero = true; + } + + fp << generate_spice_port(port_pin, omit_pin_zero); + + /* Increase the counter */ + pin_cnt++; + + /* Currently we limit 10 ports per line to keep a clean netlist */ + new_line = false; + if (10 == pin_cnt) { + pin_cnt = 0; + fp << std::endl; + new_line = true; + fit_one_line = false; + } + } + } + } + + /* Print module name: + * if port print cannot fit one line, we create a new line for the module for a clean format + */ + if (false == fit_one_line) { + fp << std::endl; + fp << "+"; + } + write_space_to_file(fp, 1); + fp << module_manager.module_name(module_id); + + /* Print an end to the instance */ + fp << std::endl; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_writer_utils.h b/openfpga/src/fpga_spice/spice_writer_utils.h index 3da2a6339..b01d5a35c 100644 --- a/openfpga/src/fpga_spice/spice_writer_utils.h +++ b/openfpga/src/fpga_spice/spice_writer_utils.h @@ -48,6 +48,26 @@ void print_spice_subckt_definition(std::fstream& fp, void print_spice_subckt_end(std::fstream& fp, const std::string& module_name); +void print_spice_resistor(std::fstream& fp, + const std::string& input_port, + const std::string& output_port, + const float& resistance); + +void print_spice_capacitor(std::fstream& fp, + const std::string& input_port, + const std::string& output_port, + const float& capacitance); + +void print_spice_short_connection(std::fstream& fp, + const std::string& input_port, + const std::string& output_port); + +void print_spice_subckt_instance(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const std::string& instance_name, + const std::map& port2port_name_map); + } /* end namespace openfpga */ #endif From 15df9b3893515a66f392f23197444de90b62e630 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 23:01:44 -0600 Subject: [PATCH 093/148] [FPGA-SPICE] Add SPICE subcircuit writer --- .../src/fpga_spice/spice_subckt_writer.cpp | 449 ++++++++++++++++++ openfpga/src/fpga_spice/spice_subckt_writer.h | 23 + 2 files changed, 472 insertions(+) create mode 100644 openfpga/src/fpga_spice/spice_subckt_writer.cpp create mode 100644 openfpga/src/fpga_spice/spice_subckt_writer.h diff --git a/openfpga/src/fpga_spice/spice_subckt_writer.cpp b/openfpga/src/fpga_spice/spice_subckt_writer.cpp new file mode 100644 index 000000000..c90c0b5ac --- /dev/null +++ b/openfpga/src/fpga_spice/spice_subckt_writer.cpp @@ -0,0 +1,449 @@ +/******************************************************************** + * This file includes functions to write a SPICE module + * based on its definition in Module Manager + * + * Note that SPICE writer functions are just an outputter for the + * module definition. + * You should NOT modify any content of the module manager + * Please use const keyword to restrict this! + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" + +/* Headers from openfpgautil library */ +#include "openfpga_port.h" +#include "openfpga_digest.h" + +#include "openfpga_naming.h" + +#include "module_manager_utils.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Generate the name of a local wire for a undriven port inside SPICE + * module + *******************************************************************/ +static +std::string generate_spice_undriven_local_wire_name(const ModuleManager& module_manager, + const ModuleId& parent, + const ModuleId& child, + const size_t& instance_id, + const ModulePortId& child_port_id) { + std::string wire_name; + if (!module_manager.instance_name(parent, child, instance_id).empty()) { + wire_name = module_manager.instance_name(parent, child, instance_id); + } else { + wire_name = module_manager.module_name(parent) + std::string("_") + std::to_string(instance_id); + wire_name += std::string("_"); + } + + wire_name += std::string("_undriven_"); + wire_name += module_manager.module_port(child, child_port_id).get_name(); + + return wire_name; +} + + +/******************************************************************** + * Name a net for a local wire for a SPICE subckt + * 1. If this is a local wire, name it after the __ + * 2. If this is not a local wire, name it after the port name of parent module + * + * In addition, it will assign the pin index as well + * + * Restriction: this function requires each net has single driver + * which is definitely always true in circuits. + *******************************************************************/ +static +BasicPort generate_spice_port_for_module_net(const ModuleManager& module_manager, + const ModuleId& module_id, + const ModuleNetId& module_net) { + /* Check all the sink modules of the net, + * if we have a source module is the current module, this is not local wire + */ + for (ModuleNetSrcId src_id : module_manager.module_net_sources(module_id, module_net)) { + if (module_id == module_manager.net_source_modules(module_id, module_net)[src_id]) { + /* Here, this is not a local wire, return the port name of the src_port */ + ModulePortId net_src_port = module_manager.net_source_ports(module_id, module_net)[src_id]; + size_t src_pin_index = module_manager.net_source_pins(module_id, module_net)[src_id]; + return BasicPort(module_manager.module_port(module_id, net_src_port).get_name(), src_pin_index, src_pin_index); + } + } + + /* Check all the sink modules of the net */ + for (ModuleNetSinkId sink_id : module_manager.module_net_sinks(module_id, module_net)) { + if (module_id == module_manager.net_sink_modules(module_id, module_net)[sink_id]) { + /* Here, this is not a local wire, return the port name of the sink_port */ + ModulePortId net_sink_port = module_manager.net_sink_ports(module_id, module_net)[sink_id]; + size_t sink_pin_index = module_manager.net_sink_pins(module_id, module_net)[sink_id]; + return BasicPort(module_manager.module_port(module_id, net_sink_port).get_name(), sink_pin_index, sink_pin_index); + } + } + + /* Reach here, this is a local wire */ + std::string net_name; + + /* Each net must only one 1 source */ + VTR_ASSERT(1 == module_manager.net_source_modules(module_id, module_net).size()); + + /* Get the source module */ + ModuleId net_src_module = module_manager.net_source_modules(module_id, module_net)[ModuleNetSrcId(0)]; + /* Get the instance id */ + size_t net_src_instance = module_manager.net_source_instances(module_id, module_net)[ModuleNetSrcId(0)]; + /* Get the port id */ + ModulePortId net_src_port = module_manager.net_source_ports(module_id, module_net)[ModuleNetSrcId(0)]; + /* Get the pin id */ + size_t net_src_pin = module_manager.net_source_pins(module_id, module_net)[ModuleNetSrcId(0)]; + + /* Load user-defined name if we have it */ + if (false == module_manager.net_name(module_id, module_net).empty()) { + net_name = module_manager.net_name(module_id, module_net); + } else { + net_name = module_manager.module_name(net_src_module); + net_name += std::string("_") + std::to_string(net_src_instance) + std::string("_"); + net_name += module_manager.module_port(net_src_module, net_src_port).get_name(); + } + + return BasicPort(net_name, net_src_pin, net_src_pin); +} + +/******************************************************************** + * Print a SPICE wire connection + * We search all the sinks of the net, + * if we find a module output, we try to find the next module output + * among the sinks of the net + * For each module output (except the first one), we print a wire connection + *******************************************************************/ +static +void print_spice_subckt_output_short_connection(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const ModuleNetId& module_net) { + /* Ensure a valid file stream */ + VTR_ASSERT(true == valid_file_stream(fp)); + + bool first_port = true; + BasicPort src_port; + + /* We have found a module input, now check all the sink modules of the net */ + for (ModuleNetSinkId net_sink : module_manager.module_net_sinks(module_id, module_net)) { + ModuleId sink_module = module_manager.net_sink_modules(module_id, module_net)[net_sink]; + if (module_id != sink_module) { + continue; + } + + /* Find the sink port and pin information */ + ModulePortId sink_port_id = module_manager.net_sink_ports(module_id, module_net)[net_sink]; + size_t sink_pin = module_manager.net_sink_pins(module_id, module_net)[net_sink]; + BasicPort sink_port(module_manager.module_port(module_id, sink_port_id).get_name(), sink_pin, sink_pin); + + /* For the first module output, this is the source port, we do nothing and go to the next */ + if (true == first_port) { + src_port = sink_port; + /* Flip the flag */ + first_port = false; + continue; + } + + /* We need to print a wire connection here */ + VTR_ASSERT(src_port.get_width() == sink_port.get_width()); + for (size_t ipin = 0; ipin < src_port.pins().size(); ++ipin) { + BasicPort src_spice_pin(src_port.get_name(), src_port.pins()[ipin], src_port.pins()[ipin]); + BasicPort sink_spice_pin(sink_port.get_name(), sink_port.pins()[ipin], sink_port.pins()[ipin]); + print_spice_short_connection(fp, + generate_spice_port(src_spice_pin), + generate_spice_port(sink_spice_pin)); + } + } +} + + +/******************************************************************** + * Print a SPICE wire connection + * We search all the sources of the net, + * if we find a module input, we try to find a module output + * among the sinks of the net + * If we find such a pair, we print a wire connection + *******************************************************************/ +static +void print_spice_subckt_local_short_connection(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const ModuleNetId& module_net) { + /* Ensure a valid file stream */ + VTR_ASSERT(true == valid_file_stream(fp)); + + for (ModuleNetSrcId net_src : module_manager.module_net_sources(module_id, module_net)) { + ModuleId src_module = module_manager.net_source_modules(module_id, module_net)[net_src]; + if (module_id != src_module) { + continue; + } + /* Find the source port and pin information */ + print_spice_comment(fp, std::string("Net source id " + std::to_string(size_t(net_src)))); + ModulePortId src_port_id = module_manager.net_source_ports(module_id, module_net)[net_src]; + size_t src_pin = module_manager.net_source_pins(module_id, module_net)[net_src]; + BasicPort src_port(module_manager.module_port(module_id, src_port_id).get_name(), src_pin, src_pin); + + /* We have found a module input, now check all the sink modules of the net */ + for (ModuleNetSinkId net_sink : module_manager.module_net_sinks(module_id, module_net)) { + ModuleId sink_module = module_manager.net_sink_modules(module_id, module_net)[net_sink]; + if (module_id != sink_module) { + continue; + } + + /* Find the sink port and pin information */ + print_spice_comment(fp, std::string("Net sink id " + std::to_string(size_t(net_sink)))); + ModulePortId sink_port_id = module_manager.net_sink_ports(module_id, module_net)[net_sink]; + size_t sink_pin = module_manager.net_sink_pins(module_id, module_net)[net_sink]; + BasicPort sink_port(module_manager.module_port(module_id, sink_port_id).get_name(), sink_pin, sink_pin); + + /* We need to print a wire connection here */ + VTR_ASSERT(src_port.get_width() == sink_port.get_width()); + for (size_t ipin = 0; ipin < src_port.pins().size(); ++ipin) { + BasicPort src_spice_pin(src_port.get_name(), src_port.pins()[ipin], src_port.pins()[ipin]); + BasicPort sink_spice_pin(sink_port.get_name(), sink_port.pins()[ipin], sink_port.pins()[ipin]); + print_spice_short_connection(fp, + generate_spice_port(src_spice_pin), + generate_spice_port(sink_spice_pin)); + } + } + } +} + +/******************************************************************** + * Print short connections inside a SPICE module + * The short connection is defined as the direct connection + * between an input port of the module and an output port of the module + * This type of connection is not covered when printing SPICE instances + * Therefore, they are covered in this function + * + * module + * +-----------------------------+ + * | | + * inputA--->|---------------------------->|--->outputB + * | | + * | | + * | | + * +-----------------------------+ + *******************************************************************/ +static +void print_spice_subckt_local_short_connections(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id) { + /* Local wires come from the child modules */ + for (ModuleNetId module_net : module_manager.module_nets(module_id)) { + /* We only care the nets that indicate short connections */ + if (false == module_net_include_local_short_connection(module_manager, module_id, module_net)) { + continue; + } + print_spice_comment(fp, std::string("Local connection due to Wire " + std::to_string(size_t(module_net)))); + print_spice_subckt_local_short_connection(fp, module_manager, module_id, module_net); + } +} + +/******************************************************************** + * Print output short connections inside a SPICE module + * The output short connection is defined as the direct connection + * between two output ports of the module + * This type of connection is not covered when printing SPICE instances + * Therefore, they are covered in this function + * + * module + * +-----------------------------+ + * | + * src------>+--------------->|--->outputA + * | | + * | | + * +--------------->|--->outputB + * +-----------------------------+ + *******************************************************************/ +static +void print_spice_subckt_output_short_connections(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id) { + /* Local wires come from the child modules */ + for (ModuleNetId module_net : module_manager.module_nets(module_id)) { + /* We only care the nets that indicate short connections */ + if (false == module_net_include_output_short_connection(module_manager, module_id, module_net)) { + continue; + } + print_spice_subckt_output_short_connection(fp, module_manager, module_id, module_net); + } +} + +/******************************************************************** + * Write a SPICE instance to a file + * This function will name the input and output connections to + * the inputs/output or local wires available in the parent module + * + * Parent_module + * +-----------------------------+ + * | | + * | +--------------+ | + * | | | | + * | | child_module | | + * | | [instance] | | + * | +--------------+ | + * | | + * +-----------------------------+ + * + *******************************************************************/ +static +void write_spice_instance_to_file(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const ModuleId& child_module, + const size_t& instance_id) { + /* Ensure a valid file stream */ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Print instance name: + * if we have an instance name, use it; + * if not, we use a default name _ + */ + std::string instance_head_line = "X "; + if (true == module_manager.instance_name(parent_module, child_module, instance_id).empty()) { + instance_head_line += generate_instance_name(module_manager.module_name(child_module), instance_id); + } else { + instance_head_line += module_manager.instance_name(parent_module, child_module, instance_id); + } + instance_head_line += " "; + fp << instance_head_line; + + /* Port sequence: global, inout, input, output and clock ports, */ + bool fit_one_line = true; + bool new_line = false; + size_t pin_cnt = 0; + for (int port_type = ModuleManager::MODULE_GLOBAL_PORT; + port_type < ModuleManager::NUM_MODULE_PORT_TYPES; + ++port_type) { + for (const auto& child_port_id : module_manager.module_port_ids_by_type(child_module, static_cast(port_type))) { + + BasicPort child_port = module_manager.module_port(child_module, child_port_id); + + /* Create the port name and width to be used by the instance */ + std::vector instance_ports; + for (size_t child_pin : child_port.pins()) { + /* Find the net linked to the pin */ + ModuleNetId net = module_manager.module_instance_port_net(parent_module, child_module, instance_id, + child_port_id, child_pin); + BasicPort instance_port; + if (ModuleNetId::INVALID() == net) { + /* We give the same port name as child module, this case happens to global ports */ + instance_port.set_name(generate_spice_undriven_local_wire_name(module_manager, parent_module, child_module, instance_id, child_port_id)); + instance_port.set_width(child_pin, child_pin); + } else { + /* Find the name for this child port */ + instance_port = generate_spice_port_for_module_net(module_manager, parent_module, net); + } + + if (true == new_line) { + std::string port_whitespace(instance_head_line.length() - 2, ' '); + fp << "+ " << port_whitespace; + } + + if (0 != pin_cnt) { + write_space_to_file(fp, 1); + } + + VTR_ASSERT(1 == instance_port.get_width()); + + /* For single-bit port, + * we can print the port name directly + */ + bool omit_pin_zero = false; + if ((1 == instance_port.pins().size()) + && (0 == instance_port.get_lsb())) { + omit_pin_zero = true; + } + + fp << generate_spice_port(instance_port, omit_pin_zero); + + /* Increase the counter */ + pin_cnt++; + + /* Currently we limit 10 ports per line to keep a clean netlist */ + new_line = false; + if (10 == pin_cnt) { + pin_cnt = 0; + fp << std::endl; + new_line = true; + fit_one_line = false; + } + } + } + } + + /* Print module name: + * if port print cannot fit one line, we create a new line for the module for a clean format + */ + if (false == fit_one_line) { + fp << std::endl; + fp << "+"; + } + write_space_to_file(fp, 1); + fp << module_manager.module_name(child_module); + + /* Print an end to the instance */ + fp << std::endl; +} + +/******************************************************************** + * Write a SPICE sub-circuit to a file + * This is a key function, maybe most frequently called in our SPICE writer + * Note that file stream must be valid + *******************************************************************/ +void write_spice_subckt_to_file(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id) { + + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Ensure we have a valid module_id */ + VTR_ASSERT(module_manager.valid_module_id(module_id)); + + /* Print module declaration */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Print an empty line as splitter */ + fp << std::endl; + + /* Print an empty line as splitter */ + fp << std::endl; + + /* Print local connection (from module inputs to output! */ + print_spice_comment(fp, std::string("BEGIN Local short connections")); + print_spice_subckt_local_short_connections(fp, module_manager, module_id); + print_spice_comment(fp, std::string("END Local short connections")); + + print_spice_comment(fp, std::string("BEGIN Local output short connections")); + print_spice_subckt_output_short_connections(fp, module_manager, module_id); + + print_spice_comment(fp, std::string("END Local output short connections")); + /* Print an empty line as splitter */ + fp << std::endl; + + /* Print instances */ + for (ModuleId child_module : module_manager.child_modules(module_id)) { + for (size_t instance : module_manager.child_module_instances(module_id, child_module)) { + /* Print an instance */ + write_spice_instance_to_file(fp, module_manager, module_id, child_module, instance); + /* Print an empty line as splitter */ + fp << std::endl; + } + } + + /* Print an end for the module */ + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + /* Print an empty line as splitter */ + fp << std::endl; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_subckt_writer.h b/openfpga/src/fpga_spice/spice_subckt_writer.h new file mode 100644 index 000000000..f47e3915f --- /dev/null +++ b/openfpga/src/fpga_spice/spice_subckt_writer.h @@ -0,0 +1,23 @@ +#ifndef SPICE_SUBCKT_WRITER_H +#define SPICE_SUBCKT_WRITER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "module_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void write_spice_subckt_to_file(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id); + +} /* end namespace openfpga */ + +#endif From c7e3d97d1b2b3a99fd39a6f5220e359fbd784a92 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 11:19:06 -0600 Subject: [PATCH 094/148] [FPGA-SPICE] Add supply voltage generator --- openfpga/src/fpga_spice/spice_constants.h | 1 + .../src/fpga_spice/spice_essential_gates.cpp | 87 +++++++++++++++++++ .../src/fpga_spice/spice_essential_gates.h | 4 + openfpga/src/fpga_spice/spice_submodule.cpp | 43 ++++++++- 4 files changed, 134 insertions(+), 1 deletion(-) diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 48482d703..ab5991de3 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -8,6 +8,7 @@ constexpr char* SPICE_NETLIST_FILE_POSTFIX = ".sp"; constexpr char* TRANSISTOR_WRAPPER_POSTFIX = "_wrapper"; constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp"; +constexpr char* SUPPLY_WRAPPER_SPICE_FILE_NAME = "supply_wrapper.sp"; constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index eafe37a24..889e9d8c1 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -18,6 +18,7 @@ /* Headers from openfpgautil library */ #include "openfpga_digest.h" +#include "openfpga_naming.h" #include "circuit_library_utils.h" #include "spice_constants.h" @@ -31,6 +32,92 @@ /* begin namespace openfpga */ namespace openfpga { +/************************************************ + * Generate the SPICE netlist for a constant generator, + * i.e., either VDD or GND + ***********************************************/ +static +void print_spice_supply_wrapper_subckt(const ModuleManager& module_manager, + std::fstream& fp, + const size_t& const_value) { + /* Find the module in module manager */ + std::string module_name = generate_const_value_module_name(const_value); + ModuleId const_val_module = module_manager.find_module(module_name); + VTR_ASSERT(true == module_manager.valid_module_id(const_val_module)); + + /* Ensure a valid file handler*/ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* dump module definition + ports */ + print_spice_subckt_definition(fp, module_manager, const_val_module); + /* Finish dumping ports */ + + /* Find the only output*/ + for (const ModulePortId& module_port_id : module_manager.module_ports(const_val_module)) { + BasicPort module_port = module_manager.module_port(const_val_module, module_port_id); + for (const auto& pin : module_port.pins()) { + BasicPort spice_pin(module_port.get_name(), pin, pin); + std::string const_pin_name = std::string(SPICE_SUBCKT_VDD_PORT_NAME); + if (0 == const_value) { + const_pin_name = std::string(SPICE_SUBCKT_GND_PORT_NAME); + } else { + VTR_ASSERT(1 == const_value); + } + + print_spice_short_connection(fp, + generate_spice_port(spice_pin, true), + const_pin_name); + } + } + + /* Put an end to the SPICE subcircuit */ + print_spice_subckt_end(fp, module_name); +} + +/******************************************************************** + * Create supply voltage wrappers + * The wrappers are used for constant inputs of routing multiplexers + * + *******************************************************************/ +int print_spice_supply_wrappers(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& submodule_dir) { + int status = CMD_EXEC_SUCCESS; + + /* Create file stream */ + std::string spice_fname = submodule_dir + std::string(SUPPLY_WRAPPER_SPICE_FILE_NAME); + + std::fstream fp; + + /* Create the file stream */ + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + /* Check if the file stream if valid or not */ + check_file_stream(spice_fname.c_str(), fp); + + /* Create file */ + VTR_LOG("Generating SPICE netlist '%s' for voltage supply wrappers...", + spice_fname.c_str()); + + print_spice_file_header(fp, std::string("Voltage Supply Wrappers")); + + /* VDD */ + print_spice_supply_wrapper_subckt(module_manager, fp, 0); + /* GND */ + print_spice_supply_wrapper_subckt(module_manager, fp, 1); + + /* Close file handler*/ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); + + VTR_LOG("Done\n"); + + return status; +} + /******************************************************************** * Generate the SPICE netlist for essential gates: * - inverters and their templates diff --git a/openfpga/src/fpga_spice/spice_essential_gates.h b/openfpga/src/fpga_spice/spice_essential_gates.h index 0fbd1895d..dc40a2531 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.h +++ b/openfpga/src/fpga_spice/spice_essential_gates.h @@ -18,6 +18,10 @@ /* begin namespace openfpga */ namespace openfpga { +int print_spice_supply_wrappers(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& submodule_dir); + int print_spice_essential_gates(NetlistManager& netlist_manager, const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, diff --git a/openfpga/src/fpga_spice/spice_submodule.cpp b/openfpga/src/fpga_spice/spice_submodule.cpp index ad72e8515..a0c93de1b 100644 --- a/openfpga/src/fpga_spice/spice_submodule.cpp +++ b/openfpga/src/fpga_spice/spice_submodule.cpp @@ -25,7 +25,7 @@ namespace openfpga { * 2. Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor * 3. TODO: Routing multiplexers * 4. TODO: Local encoders for routing multiplexers - * 5. TODO: Wires + * 5. Wires * 6. TODO: Configuration memory blocks ********************************************************************/ int print_spice_submodule(NetlistManager& netlist_manager, @@ -35,10 +35,32 @@ int print_spice_submodule(NetlistManager& netlist_manager, int status = CMD_EXEC_SUCCESS; + /* Transistor wrapper */ status = print_spice_transistor_wrapper(netlist_manager, openfpga_arch.tech_lib, submodule_dir); + /* Error out if fatal errors have been reported */ + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Constant modules: VDD and GND */ + status = print_spice_supply_wrappers(netlist_manager, + module_manager, + submodule_dir); + + /* Error out if fatal errors have been reported */ + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Logic gates: + * - AND/OR, + * - inverter, buffer + * - transmission-gate/pass-transistor + * - wires + */ status = print_spice_essential_gates(netlist_manager, module_manager, openfpga_arch.circuit_lib, @@ -46,6 +68,25 @@ int print_spice_submodule(NetlistManager& netlist_manager, openfpga_arch.circuit_tech_binding, submodule_dir); + /* Error out if fatal errors have been reported */ + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Routing multiplexers */ + /* + status = print_spice_submodule_muxes(netlist_manager, + module_manager, + mux_lib, + openfpga_arch.circuit_lib, + submodule_dir); + */ + + /* Error out if fatal errors have been reported */ + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + return status; } From 0f9fce92b245f7bbe506a5c7e614d9a0f939301f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 11:49:02 -0600 Subject: [PATCH 095/148] [FPGA-SPICE] Add SPICE writer for routing multiplexers --- openfpga/src/base/openfpga_spice.cpp | 1 + openfpga/src/fpga_spice/spice_api.cpp | 2 + openfpga/src/fpga_spice/spice_api.h | 2 + openfpga/src/fpga_spice/spice_constants.h | 4 + openfpga/src/fpga_spice/spice_mux.cpp | 189 ++++++++++++++++++++ openfpga/src/fpga_spice/spice_mux.h | 31 ++++ openfpga/src/fpga_spice/spice_submodule.cpp | 6 +- openfpga/src/fpga_spice/spice_submodule.h | 2 + 8 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 openfpga/src/fpga_spice/spice_mux.cpp create mode 100644 openfpga/src/fpga_spice/spice_mux.h diff --git a/openfpga/src/base/openfpga_spice.cpp b/openfpga/src/base/openfpga_spice.cpp index 4ef4058ee..e47acba3a 100644 --- a/openfpga/src/base/openfpga_spice.cpp +++ b/openfpga/src/base/openfpga_spice.cpp @@ -40,6 +40,7 @@ int write_fabric_spice(OpenfpgaContext& openfpga_ctx, status = fpga_fabric_spice(openfpga_ctx.module_graph(), openfpga_ctx.mutable_spice_netlists(), openfpga_ctx.arch(), + openfpga_ctx.mux_lib(), options); return status; diff --git a/openfpga/src/fpga_spice/spice_api.cpp b/openfpga/src/fpga_spice/spice_api.cpp index 658d43851..299a62950 100644 --- a/openfpga/src/fpga_spice/spice_api.cpp +++ b/openfpga/src/fpga_spice/spice_api.cpp @@ -40,6 +40,7 @@ namespace openfpga { int fpga_fabric_spice(const ModuleManager& module_manager, NetlistManager& netlist_manager, const Arch& openfpga_arch, + const MuxLibrary& mux_lib, const FabricSpiceOption& options) { vtr::ScopedStartFinishTimer timer("Write SPICE netlists for FPGA fabric\n"); @@ -73,6 +74,7 @@ int fpga_fabric_spice(const ModuleManager& module_manager, status = print_spice_submodule(netlist_manager, module_manager, openfpga_arch, + mux_lib, submodule_dir_path); if (CMD_EXEC_SUCCESS != status) { diff --git a/openfpga/src/fpga_spice/spice_api.h b/openfpga/src/fpga_spice/spice_api.h index cf1ce5a0a..4ab8994bc 100644 --- a/openfpga/src/fpga_spice/spice_api.h +++ b/openfpga/src/fpga_spice/spice_api.h @@ -10,6 +10,7 @@ #include "netlist_manager.h" #include "module_manager.h" #include "openfpga_arch.h" +#include "mux_library.h" #include "fabric_spice_options.h" /******************************************************************** @@ -22,6 +23,7 @@ namespace openfpga { int fpga_fabric_spice(const ModuleManager& module_manager, NetlistManager& netlist_manager, const Arch& openfpga_arch, + const MuxLibrary& mux_lib, const FabricSpiceOption& options); } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index ab5991de3..774591d02 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -9,8 +9,12 @@ constexpr char* TRANSISTOR_WRAPPER_POSTFIX = "_wrapper"; constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp"; constexpr char* SUPPLY_WRAPPER_SPICE_FILE_NAME = "supply_wrapper.sp"; +constexpr char* MUXES_SPICE_FILE_NAME = "muxes.sp"; constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; +constexpr char* SPICE_MUX_BASIS_POSTFIX = "_basis"; +constexpr char* SPICE_MEM_POSTFIX = "_mem"; + #endif diff --git a/openfpga/src/fpga_spice/spice_mux.cpp b/openfpga/src/fpga_spice/spice_mux.cpp new file mode 100644 index 000000000..b1436d57e --- /dev/null +++ b/openfpga/src/fpga_spice/spice_mux.cpp @@ -0,0 +1,189 @@ +/*********************************************** + * This file includes functions to generate + * SPICE subcircuits for multiplexers. + * including both fundamental submodules + * such as a branch in a multiplexer + * and the full multiplexer + **********************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from readarch library */ +#include "physical_types.h" + +/* Headers from readarcopenfpga library */ +#include "circuit_types.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" + +#include "mux_graph.h" +#include "module_manager.h" +#include "mux_utils.h" +#include "circuit_library_utils.h" +#include "decoder_library_utils.h" + +#include "openfpga_naming.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" +#include "spice_mux.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/*********************************************** + * Generate SPICE modeling for an branch circuit + * for a multiplexer with the given size + **********************************************/ +static +void generate_spice_mux_branch_subckt(const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const CircuitModelId& mux_model, + const size_t& mux_size, + const MuxGraph& mux_graph) { + std::string module_name = generate_mux_branch_subckt_name(circuit_lib, mux_model, mux_size, mux_graph.num_inputs(), SPICE_MUX_BASIS_POSTFIX); + + /* Multiplexers built with different technology is in different organization */ + switch (circuit_lib.design_tech_type(mux_model)) { + case CIRCUIT_MODEL_DESIGN_CMOS: { + /* Skip module writing if the branch subckt is a standard cell! */ + if (true == circuit_lib.valid_model_id(circuit_lib.model(module_name))) { + /* This model must be a MUX2 gate */ + VTR_ASSERT(CIRCUIT_MODEL_GATE == circuit_lib.model_type(circuit_lib.model(module_name))); + VTR_ASSERT(CIRCUIT_MODEL_GATE_MUX2 == circuit_lib.gate_type(circuit_lib.model(module_name))); + break; + } + /* Structural verilog can be easily generated by module writer */ + ModuleId mux_module = module_manager.find_module(module_name); + VTR_ASSERT(true == module_manager.valid_module_id(mux_module)); + write_spice_subckt_to_file(fp, module_manager, mux_module); + /* Add an empty line as a splitter */ + fp << std::endl; + break; + } + case CIRCUIT_MODEL_DESIGN_RRAM: + /* TODO: RRAM-based Multiplexer SPICE module generation */ + VTR_LOGF_ERROR(__FILE__, __LINE__, + "RRAM multiplexer '%s' is not supported yet\n", + circuit_lib.model_name(mux_model).c_str()); + exit(1); + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid design technology of multiplexer '%s'\n", + circuit_lib.model_name(mux_model).c_str()); + exit(1); + } +} + +/*********************************************** + * Generate SPICE modeling for a multiplexer + * with the given graph-level description + **********************************************/ +static +void generate_spice_mux_subckt(const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const CircuitModelId& mux_model, + const MuxGraph& mux_graph) { + 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("")); + + /* Multiplexers built with different technology is in different organization */ + switch (circuit_lib.design_tech_type(mux_model)) { + case CIRCUIT_MODEL_DESIGN_CMOS: { + /* Use Verilog writer to print the module to file */ + ModuleId mux_module = module_manager.find_module(module_name); + VTR_ASSERT(true == module_manager.valid_module_id(mux_module)); + write_spice_subckt_to_file(fp, module_manager, mux_module); + /* Add an empty line as a splitter */ + fp << std::endl; + break; + } + case CIRCUIT_MODEL_DESIGN_RRAM: + /* TODO: RRAM-based Multiplexer SPICE module generation */ + VTR_LOGF_ERROR(__FILE__, __LINE__, + "RRAM multiplexer '%s' is not supported yet\n", + circuit_lib.model_name(mux_model).c_str()); + exit(1); + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid design technology of multiplexer '%s'\n", + circuit_lib.model_name(mux_model).c_str()); + exit(1); + } +} + +/*********************************************** + * Generate SPICE subcircuits for all the unique + * multiplexers in the FPGA device + **********************************************/ +int print_spice_submodule_muxes(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::string& submodule_dir) { + int status = CMD_EXEC_SUCCESS; + + std::string spice_fname(submodule_dir + std::string(MUXES_SPICE_FILE_NAME)); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + /* Print out debugging information for if the file is not opened/created properly */ + VTR_LOG("Writing SPICE netlist for Multiplexers '%s' ...", + spice_fname.c_str()); + + print_spice_file_header(fp, "Multiplexers"); + + /* Generate basis sub-circuit for unique branches shared by the multiplexers */ + for (auto mux : mux_lib.muxes()) { + const MuxGraph& mux_graph = mux_lib.mux_graph(mux); + CircuitModelId mux_circuit_model = mux_lib.mux_circuit_model(mux); + /* Create a mux graph for the branch circuit */ + std::vector branch_mux_graphs = mux_graph.build_mux_branch_graphs(); + /* Create branch circuits, which are N:1 one-level or 2:1 tree-like MUXes */ + for (auto branch_mux_graph : branch_mux_graphs) { + generate_spice_mux_branch_subckt(module_manager, circuit_lib, fp, mux_circuit_model, + find_mux_num_datapath_inputs(circuit_lib, mux_circuit_model, mux_graph.num_inputs()), + branch_mux_graph); + } + } + + /* Generate unique Verilog modules for the multiplexers */ + for (auto mux : mux_lib.muxes()) { + const MuxGraph& mux_graph = mux_lib.mux_graph(mux); + CircuitModelId mux_circuit_model = mux_lib.mux_circuit_model(mux); + /* Create MUX circuits */ + generate_spice_mux_subckt(module_manager, circuit_lib, fp, mux_circuit_model, mux_graph); + } + + /* Close the file stream */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); + + VTR_LOG("Done\n"); + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_mux.h b/openfpga/src/fpga_spice/spice_mux.h new file mode 100644 index 000000000..a7658667e --- /dev/null +++ b/openfpga/src/fpga_spice/spice_mux.h @@ -0,0 +1,31 @@ +#ifndef SPICE_MUX_H +#define SPICE_MUX_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include + +#include "circuit_library.h" +#include "mux_graph.h" +#include "mux_library.h" +#include "module_manager.h" +#include "netlist_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_submodule_muxes(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::string& submodule_dir); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_submodule.cpp b/openfpga/src/fpga_spice/spice_submodule.cpp index a0c93de1b..4e7505b60 100644 --- a/openfpga/src/fpga_spice/spice_submodule.cpp +++ b/openfpga/src/fpga_spice/spice_submodule.cpp @@ -1,5 +1,5 @@ /********************************************************************* - * This file includes top-level function to generate Spice primitive modules + * This file includes top-level function to generate SPICE primitive modules * and print them to files ********************************************************************/ @@ -12,6 +12,7 @@ #include "spice_transistor_wrapper.h" #include "spice_essential_gates.h" +#include "spice_mux.h" #include "spice_constants.h" #include "spice_submodule.h" @@ -31,6 +32,7 @@ namespace openfpga { int print_spice_submodule(NetlistManager& netlist_manager, const ModuleManager& module_manager, const Arch& openfpga_arch, + const MuxLibrary& mux_lib, const std::string& submodule_dir) { int status = CMD_EXEC_SUCCESS; @@ -74,13 +76,11 @@ int print_spice_submodule(NetlistManager& netlist_manager, } /* Routing multiplexers */ - /* status = print_spice_submodule_muxes(netlist_manager, module_manager, mux_lib, openfpga_arch.circuit_lib, submodule_dir); - */ /* Error out if fatal errors have been reported */ if (CMD_EXEC_SUCCESS != status) { diff --git a/openfpga/src/fpga_spice/spice_submodule.h b/openfpga/src/fpga_spice/spice_submodule.h index 27a49de87..551fd772e 100644 --- a/openfpga/src/fpga_spice/spice_submodule.h +++ b/openfpga/src/fpga_spice/spice_submodule.h @@ -7,6 +7,7 @@ #include "netlist_manager.h" #include "module_manager.h" #include "openfpga_arch.h" +#include "mux_library.h" /******************************************************************** * Function declaration @@ -18,6 +19,7 @@ namespace openfpga { int print_spice_submodule(NetlistManager& netlist_manager, const ModuleManager& module_manager, const Arch& openfpga_arch, + const MuxLibrary& mux_lib, const std::string& submodule_dir); } /* end namespace openfpga */ From 6801d260e97ad84fd8018bce431cb56de5c59748 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 11:58:11 -0600 Subject: [PATCH 096/148] [FPGA-SPICE] Add SPICE writer for LUT --- openfpga/src/fpga_spice/spice_constants.h | 1 + openfpga/src/fpga_spice/spice_lut.cpp | 82 +++++++++++++++++++++ openfpga/src/fpga_spice/spice_lut.h | 28 +++++++ openfpga/src/fpga_spice/spice_submodule.cpp | 12 +++ 4 files changed, 123 insertions(+) create mode 100644 openfpga/src/fpga_spice/spice_lut.cpp create mode 100644 openfpga/src/fpga_spice/spice_lut.h diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 774591d02..4b983aa42 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -10,6 +10,7 @@ constexpr char* TRANSISTOR_WRAPPER_POSTFIX = "_wrapper"; constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp"; constexpr char* SUPPLY_WRAPPER_SPICE_FILE_NAME = "supply_wrapper.sp"; constexpr char* MUXES_SPICE_FILE_NAME = "muxes.sp"; +constexpr char* LUTS_SPICE_FILE_NAME = "luts.sp"; constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; diff --git a/openfpga/src/fpga_spice/spice_lut.cpp b/openfpga/src/fpga_spice/spice_lut.cpp new file mode 100644 index 000000000..af8dadda1 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_lut.cpp @@ -0,0 +1,82 @@ +/******************************************************************** + * This file includes functions to generate SPICE subcircuits for LUTs + ********************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" + +#include "mux_graph.h" +#include "module_manager.h" +#include "mux_utils.h" + +#include "openfpga_naming.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" +#include "spice_lut.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print SPICE modules for the Look-Up Tables (LUTs) + * in the circuit library + ********************************************************************/ +int print_spice_submodule_luts(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const std::string& submodule_dir) { + int status = CMD_EXEC_SUCCESS; + + std::string spice_fname = submodule_dir + std::string(LUTS_SPICE_FILE_NAME); + + std::fstream fp; + + /* Create the file stream */ + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + /* Check if the file stream if valid or not */ + check_file_stream(spice_fname.c_str(), fp); + + /* Create file */ + VTR_LOG("Writing SPICE netlist for LUTs '%s'...", + spice_fname.c_str()); + + print_spice_file_header(fp, "Look-Up Tables"); + + /* Search for each LUT circuit model */ + for (const auto& lut_model : circuit_lib.models()) { + /* Bypass user-defined and non-LUT modules */ + if ( (!circuit_lib.model_circuit_netlist(lut_model).empty()) + || (CIRCUIT_MODEL_LUT != circuit_lib.model_type(lut_model)) ) { + continue; + } + /* Find the module id */ + ModuleId lut_module = module_manager.find_module(circuit_lib.model_name(lut_model)); + VTR_ASSERT(true == module_manager.valid_module_id(lut_module)); + write_spice_subckt_to_file(fp, module_manager, lut_module); + } + + /* Close the file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); + + VTR_LOG("Done\n"); + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_lut.h b/openfpga/src/fpga_spice/spice_lut.h new file mode 100644 index 000000000..5ca3f4368 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_lut.h @@ -0,0 +1,28 @@ +#ifndef SPICE_LUT_H +#define SPICE_LUT_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include + +#include "circuit_library.h" +#include "module_manager.h" +#include "netlist_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_submodule_luts(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const std::string& submodule_dir); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_submodule.cpp b/openfpga/src/fpga_spice/spice_submodule.cpp index 4e7505b60..714747dce 100644 --- a/openfpga/src/fpga_spice/spice_submodule.cpp +++ b/openfpga/src/fpga_spice/spice_submodule.cpp @@ -13,6 +13,7 @@ #include "spice_transistor_wrapper.h" #include "spice_essential_gates.h" #include "spice_mux.h" +#include "spice_lut.h" #include "spice_constants.h" #include "spice_submodule.h" @@ -87,6 +88,17 @@ int print_spice_submodule(NetlistManager& netlist_manager, return CMD_EXEC_FATAL_ERROR; } + /* Look-Up Tables */ + status = print_spice_submodule_luts(netlist_manager, + module_manager, + openfpga_arch.circuit_lib, + submodule_dir); + + /* Error out if fatal errors have been reported */ + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + return status; } From f284f6f8d05cb9b763621d9d5eda4f5a4d0f715a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:03:10 -0600 Subject: [PATCH 097/148] [OPENFPGA LIBRARY] change method names to be consistent with FPGA-SPICE needs --- .../libarchopenfpga/src/circuit_library.cpp | 14 +++++++------- libopenfpga/libarchopenfpga/src/circuit_library.h | 8 ++++---- .../src/read_xml_circuit_library.cpp | 2 +- .../src/write_xml_circuit_library.cpp | 4 ++-- openfpga/src/fabric/build_essential_modules.cpp | 4 ++-- openfpga/src/fabric/build_wire_modules.cpp | 2 +- openfpga/src/fpga_spice/spice_essential_gates.cpp | 2 +- openfpga/src/fpga_spice/spice_lut.cpp | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/libopenfpga/libarchopenfpga/src/circuit_library.cpp b/libopenfpga/libarchopenfpga/src/circuit_library.cpp index 42df84407..bbf9c50c4 100644 --- a/libopenfpga/libarchopenfpga/src/circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/circuit_library.cpp @@ -79,11 +79,11 @@ std::string CircuitLibrary::model_verilog_netlist(const CircuitModelId& model_id return model_verilog_netlists_[model_id]; } -/* Access the path + file of user-defined circuit netlist of a circuit model */ -std::string CircuitLibrary::model_circuit_netlist(const CircuitModelId& model_id) const { +/* Access the path + file of user-defined spice netlist of a circuit model */ +std::string CircuitLibrary::model_spice_netlist(const CircuitModelId& model_id) const { /* validate the model_id */ VTR_ASSERT(valid_model_id(model_id)); - return model_circuit_netlists_[model_id]; + return model_spice_netlists_[model_id]; } /* Access the is_default flag (check if this is the default circuit model in the type) of a circuit model */ @@ -1132,7 +1132,7 @@ CircuitModelId CircuitLibrary::add_model(const enum e_circuit_model_type& type) model_names_.emplace_back(); model_prefix_.emplace_back(); model_verilog_netlists_.emplace_back(); - model_circuit_netlists_.emplace_back(); + model_spice_netlists_.emplace_back(); model_is_default_.push_back(false); sub_models_.emplace_back(); @@ -1226,11 +1226,11 @@ void CircuitLibrary::set_model_verilog_netlist(const CircuitModelId& model_id, c return; } -/* Set the circuit_netlist of a Circuit Model */ -void CircuitLibrary::set_model_circuit_netlist(const CircuitModelId& model_id, const std::string& circuit_netlist) { +/* Set the spice_netlist of a Circuit Model */ +void CircuitLibrary::set_model_spice_netlist(const CircuitModelId& model_id, const std::string& spice_netlist) { /* validate the model_id */ VTR_ASSERT(valid_model_id(model_id)); - model_circuit_netlists_[model_id] = circuit_netlist; + model_spice_netlists_[model_id] = spice_netlist; return; } diff --git a/libopenfpga/libarchopenfpga/src/circuit_library.h b/libopenfpga/libarchopenfpga/src/circuit_library.h index 5e023c665..bd082f738 100644 --- a/libopenfpga/libarchopenfpga/src/circuit_library.h +++ b/libopenfpga/libarchopenfpga/src/circuit_library.h @@ -48,7 +48,7 @@ * It should be the same as user-defined Verilog modules, if it is not auto-generated * 4. model_prefix_: the prefix of a circuit model when it is instanciated * 5. verilog_netlist_: specified path and file name of Verilog netlist if a circuit model is not auto-generated - * 6. circuit_netlist_: specified path and file name of CIRCUIT netlist if a circuit model is not auto-generated + * 6. spice_netlist_: specified path and file name of CIRCUIT netlist if a circuit model is not auto-generated * 7. is_default_: indicate if the circuit model is the default one among all those in the same type * 8. sub_models_: the sub circuit models included by a circuit model. It is a collection of unique circuit model ids * found in the CircuitModelId of pass-gate/buffers/port-related circuit models. @@ -190,7 +190,7 @@ class CircuitLibrary { std::string model_name(const CircuitModelId& model_id) const; std::string model_prefix(const CircuitModelId& model_id) const; std::string model_verilog_netlist(const CircuitModelId& model_id) const; - std::string model_circuit_netlist(const CircuitModelId& model_id) const; + std::string model_spice_netlist(const CircuitModelId& model_id) const; bool model_is_default(const CircuitModelId& model_id) const; bool dump_structural_verilog(const CircuitModelId& model_id) const; bool dump_explicit_port_map(const CircuitModelId& model_id) const; @@ -314,7 +314,7 @@ class CircuitLibrary { void set_model_name(const CircuitModelId& model_id, const std::string& name); void set_model_prefix(const CircuitModelId& model_id, const std::string& prefix); void set_model_verilog_netlist(const CircuitModelId& model_id, const std::string& verilog_netlist); - void set_model_circuit_netlist(const CircuitModelId& model_id, const std::string& circuit_netlist); + void set_model_spice_netlist(const CircuitModelId& model_id, const std::string& spice_netlist); void set_model_is_default(const CircuitModelId& model_id, const bool& is_default); /* Verilog generator options */ void set_model_dump_structural_verilog(const CircuitModelId& model_id, const bool& dump_structural_verilog); @@ -499,7 +499,7 @@ class CircuitLibrary { vtr::vector model_names_; vtr::vector model_prefix_; vtr::vector model_verilog_netlists_; - vtr::vector model_circuit_netlists_; + vtr::vector model_spice_netlists_; vtr::vector model_is_default_; /* Submodules that a circuit model contains */ diff --git a/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp b/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp index 0954a0086..36e837814 100644 --- a/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp @@ -674,7 +674,7 @@ void read_xml_circuit_model(pugi::xml_node& xml_model, circuit_lib.set_model_prefix(model, std::string(prefix_attr)); /* Find a SPICE netlist which is an optional attribute*/ - circuit_lib.set_model_circuit_netlist(model, get_attribute(xml_model, "spice_netlist", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string("")); + circuit_lib.set_model_spice_netlist(model, get_attribute(xml_model, "spice_netlist", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string("")); /* Find a Verilog netlist which is an optional attribute*/ circuit_lib.set_model_verilog_netlist(model, get_attribute(xml_model, "verilog_netlist", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string("")); diff --git a/libopenfpga/libarchopenfpga/src/write_xml_circuit_library.cpp b/libopenfpga/libarchopenfpga/src/write_xml_circuit_library.cpp index 5dd69ca8e..b141b0fe2 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/write_xml_circuit_library.cpp @@ -409,8 +409,8 @@ void write_xml_circuit_model(std::fstream& fp, if (true == circuit_lib.dump_structural_verilog(model)) { write_xml_attribute(fp, "dump_structural_verilog", "true"); } - if (!circuit_lib.model_circuit_netlist(model).empty()) { - write_xml_attribute(fp, "circuit_netlist", circuit_lib.model_circuit_netlist(model).c_str()); + if (!circuit_lib.model_spice_netlist(model).empty()) { + write_xml_attribute(fp, "spice_netlist", circuit_lib.model_spice_netlist(model).c_str()); } if (!circuit_lib.model_verilog_netlist(model).empty()) { write_xml_attribute(fp, "verilog_netlist", circuit_lib.model_verilog_netlist(model).c_str()); diff --git a/openfpga/src/fabric/build_essential_modules.cpp b/openfpga/src/fabric/build_essential_modules.cpp index 481781c5a..19e1a10ea 100644 --- a/openfpga/src/fabric/build_essential_modules.cpp +++ b/openfpga/src/fabric/build_essential_modules.cpp @@ -187,7 +187,7 @@ void build_user_defined_modules(ModuleManager& module_manager, for (const auto& model : circuit_lib.models()) { /* We only care about user-defined models */ if ( (true == circuit_lib.model_verilog_netlist(model).empty()) - && (true == circuit_lib.model_circuit_netlist(model).empty()) ) { + && (true == circuit_lib.model_spice_netlist(model).empty()) ) { continue; } /* Skip Routing channel wire models because they need a different name. Do it later */ @@ -255,7 +255,7 @@ void rename_primitive_module_port_names(ModuleManager& module_manager, for (const CircuitModelId& model : circuit_lib.models()) { /* We only care about user-defined models */ if ( (true == circuit_lib.model_verilog_netlist(model).empty()) - && (true == circuit_lib.model_circuit_netlist(model).empty()) ) { + && (true == circuit_lib.model_spice_netlist(model).empty()) ) { continue; } /* Skip Routing channel wire models because they need a different name. Do it later */ diff --git a/openfpga/src/fabric/build_wire_modules.cpp b/openfpga/src/fabric/build_wire_modules.cpp index da3b03e23..d505f93e6 100644 --- a/openfpga/src/fabric/build_wire_modules.cpp +++ b/openfpga/src/fabric/build_wire_modules.cpp @@ -62,7 +62,7 @@ void build_wire_modules(ModuleManager& module_manager, /* Print Verilog models for regular wires*/ for (const auto& wire_model : circuit_lib.models_by_type(CIRCUIT_MODEL_WIRE)) { /* Bypass user-defined circuit models */ - if ( (!circuit_lib.model_circuit_netlist(wire_model).empty()) + if ( (!circuit_lib.model_spice_netlist(wire_model).empty()) && (!circuit_lib.model_verilog_netlist(wire_model).empty()) ) { continue; } diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index 889e9d8c1..301b81de1 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -136,7 +136,7 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, /* Iterate over the circuit models */ for (const CircuitModelId& circuit_model : circuit_lib.models()) { /* Bypass models require extern netlists */ - if (!circuit_lib.model_circuit_netlist(circuit_model).empty()) { + if (!circuit_lib.model_spice_netlist(circuit_model).empty()) { continue; } diff --git a/openfpga/src/fpga_spice/spice_lut.cpp b/openfpga/src/fpga_spice/spice_lut.cpp index af8dadda1..2d68d014b 100644 --- a/openfpga/src/fpga_spice/spice_lut.cpp +++ b/openfpga/src/fpga_spice/spice_lut.cpp @@ -56,7 +56,7 @@ int print_spice_submodule_luts(NetlistManager& netlist_manager, /* Search for each LUT circuit model */ for (const auto& lut_model : circuit_lib.models()) { /* Bypass user-defined and non-LUT modules */ - if ( (!circuit_lib.model_circuit_netlist(lut_model).empty()) + if ( (!circuit_lib.model_spice_netlist(lut_model).empty()) || (CIRCUIT_MODEL_LUT != circuit_lib.model_type(lut_model)) ) { continue; } From 2fae311c8e6be2f769c7e875e1106c713f01ec34 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:14:34 -0600 Subject: [PATCH 098/148] [FPGA-SPICE] Add SPICE writer for memories --- openfpga/src/fpga_spice/spice_constants.h | 1 + openfpga/src/fpga_spice/spice_memory.cpp | 196 ++++++++++++++++++++ openfpga/src/fpga_spice/spice_memory.h | 30 +++ openfpga/src/fpga_spice/spice_submodule.cpp | 13 ++ 4 files changed, 240 insertions(+) create mode 100644 openfpga/src/fpga_spice/spice_memory.cpp create mode 100644 openfpga/src/fpga_spice/spice_memory.h diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 4b983aa42..95e7f00b9 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -11,6 +11,7 @@ constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp"; constexpr char* SUPPLY_WRAPPER_SPICE_FILE_NAME = "supply_wrapper.sp"; constexpr char* MUXES_SPICE_FILE_NAME = "muxes.sp"; constexpr char* LUTS_SPICE_FILE_NAME = "luts.sp"; +constexpr char* MEMORIES_SPICE_FILE_NAME = "memories.sp"; constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; diff --git a/openfpga/src/fpga_spice/spice_memory.cpp b/openfpga/src/fpga_spice/spice_memory.cpp new file mode 100644 index 000000000..e1da21d28 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_memory.cpp @@ -0,0 +1,196 @@ +/********************************************************************* + * This file includes functions to generate SPICE sub-circuits for + * the memories that are affiliated to multiplexers and other programmable + * circuit models, such as IOPADs, LUTs, etc. + ********************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" + +#include "mux_graph.h" +#include "module_manager.h" +#include "circuit_library_utils.h" +#include "mux_utils.h" + +#include "openfpga_naming.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" +#include "spice_memory.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/********************************************************************* + * Generate Verilog modules for the memories that are used + * by multiplexers + * + * +----------------+ + * mem_in --->| Memory Module |---> mem_out + * +----------------+ + * | | ... | | + * v v v v SRAM ports of multiplexer + * +---------------------+ + * in--->| Multiplexer Module |---> out + * +---------------------+ + ********************************************************************/ +static +void print_spice_mux_memory_module(const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const CircuitModelId& mux_model, + const MuxGraph& mux_graph) { + /* 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(SPICE_MEM_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 */ + write_spice_subckt_to_file(fp, module_manager, mem_module); + + /* Add an empty line as a splitter */ + fp << std::endl; + 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); + } +} + +/********************************************************************* + * Generate Verilog modules for + * the memories that are affiliated to multiplexers and other programmable + * circuit models, such as IOPADs, LUTs, etc. + * + * We keep the memory modules separated from the multiplexers and other + * programmable circuit models, for the sake of supporting + * various configuration schemes. + * By following such organiztion, the Verilog modules of the circuit models + * implements the functionality (circuit logic) only, while the memory Verilog + * modules implements the memory circuits as well as configuration protocols. + * For example, the local decoders of multiplexers are implemented in the + * memory modules. + * Take another example, the memory circuit can implement the scan-chain or + * memory-bank organization for the memories. + ********************************************************************/ +int print_spice_submodule_memories(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::string& submodule_dir) { + int status = CMD_EXEC_SUCCESS; + + /* Plug in with the mux subckt */ + std::string spice_fname(submodule_dir + std::string(MEMORIES_SPICE_FILE_NAME)); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + /* Print out debugging information for if the file is not opened/created properly */ + VTR_LOG("Writing SPICE netlist for memories '%s' ...", + spice_fname.c_str()); + + print_spice_file_header(fp, "Memories used in FPGA"); + + /* Create the memory circuits for the multiplexer */ + for (auto mux : mux_lib.muxes()) { + const MuxGraph& mux_graph = mux_lib.mux_graph(mux); + CircuitModelId mux_model = mux_lib.mux_circuit_model(mux); + /* Bypass the non-MUX circuit models (i.e., LUTs). + * They should be handled in a different way + * Memory circuits of LUT includes both regular and mode-select ports + */ + if (CIRCUIT_MODEL_MUX != circuit_lib.model_type(mux_model)) { + continue; + } + /* Create a Verilog module for the memories used by the multiplexer */ + print_spice_mux_memory_module(module_manager, circuit_lib, fp, mux_model, mux_graph); + } + + /* Create the memory circuits for non-MUX circuit models. + * In this case, the memory modules are designed to interface + * the mode-select ports + */ + for (const auto& model : circuit_lib.models()) { + /* Bypass MUXes, they have already been considered */ + if (CIRCUIT_MODEL_MUX == circuit_lib.model_type(model)) { + continue; + } + /* Bypass those modules without any SRAM ports */ + std::vector sram_ports = circuit_lib.model_ports_by_type(model, CIRCUIT_MODEL_PORT_SRAM, true); + if (0 == sram_ports.size()) { + continue; + } + /* Find the name of memory module */ + /* Get the total number of SRAMs */ + size_t num_mems = 0; + for (const auto& port : sram_ports) { + num_mems += circuit_lib.port_size(port); + } + /* Get the circuit model for the memory circuit used by the multiplexer */ + std::vector sram_models; + for (const auto& port : sram_ports) { + CircuitModelId sram_model = circuit_lib.port_tri_state_model(port); + VTR_ASSERT(CircuitModelId::INVALID() != sram_model); + /* Found in the vector of sram_models, do not update and go to the next */ + if (sram_models.end() != std::find(sram_models.begin(), sram_models.end(), sram_model)) { + continue; + } + /* sram_model not found in the vector, update the sram_models */ + sram_models.push_back(sram_model); + } + /* Should have only 1 SRAM model */ + VTR_ASSERT( 1 == sram_models.size() ); + + /* Create the module name for the memory block */ + std::string module_name = generate_memory_module_name(circuit_lib, model, sram_models[0], std::string(SPICE_MEM_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 */ + write_spice_subckt_to_file(fp, module_manager, mem_module); + + /* Add an empty line as a splitter */ + fp << std::endl; + } + + /* Close the file stream */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); + + VTR_LOG("Done\n"); + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_memory.h b/openfpga/src/fpga_spice/spice_memory.h new file mode 100644 index 000000000..48eaf5651 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_memory.h @@ -0,0 +1,30 @@ +#ifndef SPICE_MEMORY_H +#define SPICE_MEMORY_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +#include "circuit_library.h" +#include "mux_graph.h" +#include "mux_library.h" +#include "module_manager.h" +#include "netlist_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_submodule_memories(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::string& submodule_dir); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_submodule.cpp b/openfpga/src/fpga_spice/spice_submodule.cpp index 714747dce..7fd5564be 100644 --- a/openfpga/src/fpga_spice/spice_submodule.cpp +++ b/openfpga/src/fpga_spice/spice_submodule.cpp @@ -14,6 +14,7 @@ #include "spice_essential_gates.h" #include "spice_mux.h" #include "spice_lut.h" +#include "spice_memory.h" #include "spice_constants.h" #include "spice_submodule.h" @@ -99,6 +100,18 @@ int print_spice_submodule(NetlistManager& netlist_manager, return CMD_EXEC_FATAL_ERROR; } + /* Memories */ + status = print_spice_submodule_memories(netlist_manager, + module_manager, + mux_lib, + openfpga_arch.circuit_lib, + submodule_dir); + + /* Error out if fatal errors have been reported */ + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + return status; } From 0f25b529075ff73aaf1b3774a1c5a50937587ff7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:18:22 -0600 Subject: [PATCH 099/148] [FPGA-Verilog] code format fix --- openfpga/src/fpga_spice/spice_submodule.cpp | 8 +- openfpga/src/fpga_verilog/verilog_api.cpp | 327 ++++++++++---------- 2 files changed, 165 insertions(+), 170 deletions(-) diff --git a/openfpga/src/fpga_spice/spice_submodule.cpp b/openfpga/src/fpga_spice/spice_submodule.cpp index 7fd5564be..26b166a59 100644 --- a/openfpga/src/fpga_spice/spice_submodule.cpp +++ b/openfpga/src/fpga_spice/spice_submodule.cpp @@ -26,10 +26,10 @@ namespace openfpga { * Top-level function to generate primitive modules: * 1. Transistor wrapper * 2. Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor - * 3. TODO: Routing multiplexers + * 3. Routing multiplexers * 4. TODO: Local encoders for routing multiplexers * 5. Wires - * 6. TODO: Configuration memory blocks + * 6. Configuration memory blocks ********************************************************************/ int print_spice_submodule(NetlistManager& netlist_manager, const ModuleManager& module_manager, @@ -77,6 +77,8 @@ int print_spice_submodule(NetlistManager& netlist_manager, return CMD_EXEC_FATAL_ERROR; } + /* TODO: local decoders for routing multiplexers */ + /* Routing multiplexers */ status = print_spice_submodule_muxes(netlist_manager, module_manager, @@ -112,6 +114,8 @@ int print_spice_submodule(NetlistManager& netlist_manager, return CMD_EXEC_FATAL_ERROR; } + /* TODO: architecture decoders */ + return status; } diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 7f4c0d9b7..d02ced128 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -33,7 +33,7 @@ namespace openfpga { - /******************************************************************** +/******************************************************************** * A top-level function of FPGA-Verilog which focuses on fabric Verilog generation * This function will generate * - primitive modules required by the full fabric @@ -52,97 +52,93 @@ namespace openfpga * The only exception now is the user-defined modules. * We should think clearly about how to handle them for both Verilog and SPICE generators! ********************************************************************/ - void fpga_fabric_verilog(ModuleManager &module_manager, - NetlistManager &netlist_manager, - 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) - { +void fpga_fabric_verilog(ModuleManager &module_manager, + NetlistManager &netlist_manager, + 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) { - vtr::ScopedStartFinishTimer timer("Write Verilog netlists for FPGA fabric\n"); + vtr::ScopedStartFinishTimer timer("Write Verilog netlists for FPGA fabric\n"); - std::string src_dir_path = format_dir_path(options.output_directory()); + std::string src_dir_path = format_dir_path(options.output_directory()); - /* Create directories */ - create_directory(src_dir_path); + /* Create directories */ + create_directory(src_dir_path); - /* Sub directory under SRC directory to contain all the primitive block netlists */ - std::string submodule_dir_path = src_dir_path + std::string(DEFAULT_SUBMODULE_DIR_NAME); - create_directory(submodule_dir_path); + /* Sub directory under SRC directory to contain all the primitive block netlists */ + std::string submodule_dir_path = src_dir_path + std::string(DEFAULT_SUBMODULE_DIR_NAME); + create_directory(submodule_dir_path); - /* Sub directory under SRC directory to contain all the logic block netlists */ - std::string lb_dir_path = src_dir_path + std::string(DEFAULT_LB_DIR_NAME); - create_directory(lb_dir_path); + /* Sub directory under SRC directory to contain all the logic block netlists */ + std::string lb_dir_path = src_dir_path + std::string(DEFAULT_LB_DIR_NAME); + create_directory(lb_dir_path); - /* Sub directory under SRC directory to contain all the routing block netlists */ - 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 routing block netlists */ + std::string rr_dir_path = src_dir_path + std::string(DEFAULT_RR_DIR_NAME); + create_directory(rr_dir_path); - /* Print Verilog files containing preprocessing flags */ - print_verilog_preprocessing_flags_netlist(std::string(src_dir_path), - options); + /* Print Verilog files containing preprocessing flags */ + print_verilog_preprocessing_flags_netlist(std::string(src_dir_path), + options); - /* Generate primitive Verilog modules, which are corner stones of FPGA fabric - * Note that this function MUST be called before Verilog generation of - * core logic (i.e., logic blocks and routing resources) !!! - * This is because that this function will add the primitive Verilog modules to - * the module manager. - * Without the modules in the module manager, core logic generation is not possible!!! - */ - print_verilog_submodule(module_manager, netlist_manager, - mux_lib, decoder_lib, circuit_lib, - submodule_dir_path, - options); + /* Generate primitive Verilog modules, which are corner stones of FPGA fabric + * Note that this function MUST be called before Verilog generation of + * core logic (i.e., logic blocks and routing resources) !!! + * This is because that this function will add the primitive Verilog modules to + * the module manager. + * Without the modules in the module manager, core logic generation is not possible!!! + */ + print_verilog_submodule(module_manager, netlist_manager, + mux_lib, decoder_lib, circuit_lib, + submodule_dir_path, + options); - /* Generate routing blocks */ - if (true == options.compress_routing()) - { - print_verilog_unique_routing_modules(netlist_manager, - const_cast(module_manager), - device_rr_gsb, - rr_dir_path, - options.explicit_port_mapping()); - } - else - { - VTR_ASSERT(false == options.compress_routing()); - print_verilog_flatten_routing_modules(netlist_manager, - const_cast(module_manager), - device_rr_gsb, - rr_dir_path, - options.explicit_port_mapping()); - } - - /* Generate grids */ - print_verilog_grids(netlist_manager, - const_cast(module_manager), - device_ctx, device_annotation, - lb_dir_path, - options.explicit_port_mapping(), - options.verbose_output()); - - /* Generate FPGA fabric */ - print_verilog_top_module(netlist_manager, - const_cast(module_manager), - src_dir_path, - options.explicit_port_mapping()); - - /* Generate an netlist including all the fabric-related netlists */ - print_fabric_include_netlist(const_cast(netlist_manager), - src_dir_path, - circuit_lib); - - /* Given a brief stats on how many Verilog modules have been written to files */ - VTR_LOGV(options.verbose_output(), - "Written %lu Verilog modules in total\n", - module_manager.num_modules()); + /* Generate routing blocks */ + if (true == options.compress_routing()) { + print_verilog_unique_routing_modules(netlist_manager, + const_cast(module_manager), + device_rr_gsb, + rr_dir_path, + options.explicit_port_mapping()); + } else { + VTR_ASSERT(false == options.compress_routing()); + print_verilog_flatten_routing_modules(netlist_manager, + const_cast(module_manager), + device_rr_gsb, + rr_dir_path, + options.explicit_port_mapping()); } - /******************************************************************** + /* Generate grids */ + print_verilog_grids(netlist_manager, + const_cast(module_manager), + device_ctx, device_annotation, + lb_dir_path, + options.explicit_port_mapping(), + options.verbose_output()); + + /* Generate FPGA fabric */ + print_verilog_top_module(netlist_manager, + const_cast(module_manager), + src_dir_path, + options.explicit_port_mapping()); + + /* Generate an netlist including all the fabric-related netlists */ + print_fabric_include_netlist(const_cast(netlist_manager), + src_dir_path, + circuit_lib); + + /* Given a brief stats on how many Verilog modules have been written to files */ + VTR_LOGV(options.verbose_output(), + "Written %lu Verilog modules in total\n", + module_manager.num_modules()); +} + +/******************************************************************** * A top-level function of FPGA-Verilog which focuses on fabric Verilog generation * This function will generate * - A wrapper module, which encapsulate the FPGA module in a Verilog module which have the same port as the input benchmark @@ -151,100 +147,95 @@ namespace openfpga * This testbench is created for quick verification and formal verification purpose. * - Verilog netlist including preprocessing flags and all the Verilog netlists that have been generated ********************************************************************/ - void fpga_verilog_testbench(const ModuleManager &module_manager, - const BitstreamManager &bitstream_manager, - const FabricBitstream &fabric_bitstream, - const AtomContext &atom_ctx, - const PlacementContext &place_ctx, - const IoLocationMap &io_location_map, - const VprNetlistAnnotation &netlist_annotation, - const CircuitLibrary &circuit_lib, - const SimulationSetting &simulation_setting, - const e_config_protocol_type &config_protocol_type, - const VerilogTestbenchOption &options) - { +void fpga_verilog_testbench(const ModuleManager &module_manager, + const BitstreamManager &bitstream_manager, + const FabricBitstream &fabric_bitstream, + const AtomContext &atom_ctx, + const PlacementContext &place_ctx, + const IoLocationMap &io_location_map, + const VprNetlistAnnotation &netlist_annotation, + const CircuitLibrary &circuit_lib, + const SimulationSetting &simulation_setting, + const e_config_protocol_type &config_protocol_type, + const VerilogTestbenchOption &options) { - vtr::ScopedStartFinishTimer timer("Write Verilog testbenches for FPGA fabric\n"); + vtr::ScopedStartFinishTimer timer("Write Verilog testbenches for FPGA fabric\n"); - std::string src_dir_path = format_dir_path(options.output_directory()); + std::string src_dir_path = format_dir_path(options.output_directory()); - std::string netlist_name = atom_ctx.nlist.netlist_name(); + std::string netlist_name = atom_ctx.nlist.netlist_name(); - /* Create directories */ - create_directory(src_dir_path); + /* Create directories */ + create_directory(src_dir_path); - /* TODO: check if this works here. This function was in fabric generator */ - print_verilog_simulation_preprocessing_flags(std::string(src_dir_path), - options); + /* TODO: check if this works here. This function was in fabric generator */ + print_verilog_simulation_preprocessing_flags(std::string(src_dir_path), + options); - /* Collect global ports from the circuit library: - * TODO: should we place this in the OpenFPGA context? - */ - std::vector global_ports = find_circuit_library_global_ports(circuit_lib); + /* Collect global ports from the circuit library: + * TODO: should we place this in the OpenFPGA context? + */ + std::vector global_ports = find_circuit_library_global_ports(circuit_lib); - /* Generate wrapper module for FPGA fabric (mapped by the input benchmark and pre-configured testbench for verification */ - if (true == options.print_formal_verification_top_netlist()) - { - std::string formal_verification_top_netlist_file_path = src_dir_path + netlist_name + std::string(FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX); - print_verilog_preconfig_top_module(module_manager, bitstream_manager, - circuit_lib, global_ports, - atom_ctx, place_ctx, io_location_map, - netlist_annotation, - netlist_name, - formal_verification_top_netlist_file_path, - options.explicit_port_mapping()); - } - - if (true == options.print_preconfig_top_testbench()) - { - /* Generate top-level testbench using random vectors */ - std::string random_top_testbench_file_path = src_dir_path + netlist_name + std::string(RANDOM_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); - print_verilog_random_top_testbench(netlist_name, - random_top_testbench_file_path, - atom_ctx, - netlist_annotation, - simulation_setting, - options.explicit_port_mapping()); - } - - /* Generate full testbench for verification, including configuration phase and operating phase */ - if (true == options.print_top_testbench()) - { - std::string top_testbench_file_path = src_dir_path + netlist_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); - print_verilog_top_testbench(module_manager, - bitstream_manager, fabric_bitstream, - config_protocol_type, - circuit_lib, global_ports, - atom_ctx, place_ctx, io_location_map, - netlist_annotation, - netlist_name, - top_testbench_file_path, - simulation_setting, - options.fast_configuration(), - options.explicit_port_mapping()); - } - - /* Generate exchangeable files which contains simulation settings */ - if (true == options.print_simulation_ini()) - { - std::string simulation_ini_file_name = options.simulation_ini_path(); - VTR_ASSERT(true != options.simulation_ini_path().empty()); - print_verilog_simulation_info(simulation_ini_file_name, - netlist_name, - src_dir_path, - atom_ctx, place_ctx, io_location_map, - module_manager, - config_protocol_type, - bitstream_manager.num_bits(), - simulation_setting.num_clock_cycles(), - simulation_setting.programming_clock_frequency(), - simulation_setting.operating_clock_frequency()); - } - - /* Generate a Verilog file including all the netlists that have been generated */ - print_include_netlists(src_dir_path, - netlist_name, - options.reference_benchmark_file_path()); + /* Generate wrapper module for FPGA fabric (mapped by the input benchmark and pre-configured testbench for verification */ + if (true == options.print_formal_verification_top_netlist()) { + std::string formal_verification_top_netlist_file_path = src_dir_path + netlist_name + std::string(FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX); + print_verilog_preconfig_top_module(module_manager, bitstream_manager, + circuit_lib, global_ports, + atom_ctx, place_ctx, io_location_map, + netlist_annotation, + netlist_name, + formal_verification_top_netlist_file_path, + options.explicit_port_mapping()); } + if (true == options.print_preconfig_top_testbench()) { + /* Generate top-level testbench using random vectors */ + std::string random_top_testbench_file_path = src_dir_path + netlist_name + std::string(RANDOM_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); + print_verilog_random_top_testbench(netlist_name, + random_top_testbench_file_path, + atom_ctx, + netlist_annotation, + simulation_setting, + options.explicit_port_mapping()); + } + + /* Generate full testbench for verification, including configuration phase and operating phase */ + if (true == options.print_top_testbench()) { + std::string top_testbench_file_path = src_dir_path + netlist_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); + print_verilog_top_testbench(module_manager, + bitstream_manager, fabric_bitstream, + config_protocol_type, + circuit_lib, global_ports, + atom_ctx, place_ctx, io_location_map, + netlist_annotation, + netlist_name, + top_testbench_file_path, + simulation_setting, + options.fast_configuration(), + options.explicit_port_mapping()); + } + + /* Generate exchangeable files which contains simulation settings */ + if (true == options.print_simulation_ini()) { + std::string simulation_ini_file_name = options.simulation_ini_path(); + VTR_ASSERT(true != options.simulation_ini_path().empty()); + print_verilog_simulation_info(simulation_ini_file_name, + netlist_name, + src_dir_path, + atom_ctx, place_ctx, io_location_map, + module_manager, + config_protocol_type, + bitstream_manager.num_bits(), + simulation_setting.num_clock_cycles(), + simulation_setting.programming_clock_frequency(), + simulation_setting.operating_clock_frequency()); + } + + /* Generate a Verilog file including all the netlists that have been generated */ + print_include_netlists(src_dir_path, + netlist_name, + options.reference_benchmark_file_path()); +} + } /* end namespace openfpga */ From 5e78e91fdf15ca21ddd27c41fd58d3f5146b0f3c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:27:48 -0600 Subject: [PATCH 100/148] [FPGA-SPICE] Add SPICE writer for routing blocks --- openfpga/src/base/openfpga_spice.cpp | 1 + openfpga/src/fpga_spice/spice_api.cpp | 16 + openfpga/src/fpga_spice/spice_api.h | 2 + openfpga/src/fpga_spice/spice_constants.h | 4 + openfpga/src/fpga_spice/spice_routing.cpp | 346 ++++++++++++++++++++++ openfpga/src/fpga_spice/spice_routing.h | 32 ++ 6 files changed, 401 insertions(+) create mode 100644 openfpga/src/fpga_spice/spice_routing.cpp create mode 100644 openfpga/src/fpga_spice/spice_routing.h diff --git a/openfpga/src/base/openfpga_spice.cpp b/openfpga/src/base/openfpga_spice.cpp index e47acba3a..0ddab0c91 100644 --- a/openfpga/src/base/openfpga_spice.cpp +++ b/openfpga/src/base/openfpga_spice.cpp @@ -41,6 +41,7 @@ int write_fabric_spice(OpenfpgaContext& openfpga_ctx, openfpga_ctx.mutable_spice_netlists(), openfpga_ctx.arch(), openfpga_ctx.mux_lib(), + openfpga_ctx.device_rr_gsb(), options); return status; diff --git a/openfpga/src/fpga_spice/spice_api.cpp b/openfpga/src/fpga_spice/spice_api.cpp index 299a62950..852d29a8d 100644 --- a/openfpga/src/fpga_spice/spice_api.cpp +++ b/openfpga/src/fpga_spice/spice_api.cpp @@ -16,6 +16,7 @@ #include "spice_constants.h" #include "spice_submodule.h" +#include "spice_routing.h" /* Header file for this source file */ #include "spice_api.h" @@ -41,6 +42,7 @@ int fpga_fabric_spice(const ModuleManager& module_manager, NetlistManager& netlist_manager, const Arch& openfpga_arch, const MuxLibrary& mux_lib, + const DeviceRRGSB &device_rr_gsb, const FabricSpiceOption& options) { vtr::ScopedStartFinishTimer timer("Write SPICE netlists for FPGA fabric\n"); @@ -81,6 +83,20 @@ int fpga_fabric_spice(const ModuleManager& module_manager, return status; } + /* Generate routing blocks */ + if (true == options.compress_routing()) { + print_spice_unique_routing_modules(netlist_manager, + module_manager, + device_rr_gsb, + rr_dir_path); + } else { + VTR_ASSERT(false == options.compress_routing()); + print_spice_flatten_routing_modules(netlist_manager, + module_manager, + device_rr_gsb, + rr_dir_path); + } + /* Given a brief stats on how many Spice modules have been written to files */ VTR_LOGV(options.verbose_output(), "Written %lu SPICE modules in total\n", diff --git a/openfpga/src/fpga_spice/spice_api.h b/openfpga/src/fpga_spice/spice_api.h index 4ab8994bc..2d418432d 100644 --- a/openfpga/src/fpga_spice/spice_api.h +++ b/openfpga/src/fpga_spice/spice_api.h @@ -11,6 +11,7 @@ #include "module_manager.h" #include "openfpga_arch.h" #include "mux_library.h" +#include "device_rr_gsb.h" #include "fabric_spice_options.h" /******************************************************************** @@ -24,6 +25,7 @@ int fpga_fabric_spice(const ModuleManager& module_manager, NetlistManager& netlist_manager, const Arch& openfpga_arch, const MuxLibrary& mux_lib, + const DeviceRRGSB &device_rr_gsb, const FabricSpiceOption& options); } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 95e7f00b9..97b1733c2 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -19,4 +19,8 @@ constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; constexpr char* SPICE_MUX_BASIS_POSTFIX = "_basis"; constexpr char* SPICE_MEM_POSTFIX = "_mem"; +constexpr char* SB_SPICE_FILE_NAME_PREFIX = "sb_"; +constexpr char* LOGICAL_MODULE_SPICE_FILE_NAME_PREFIX = "logical_tile_"; +constexpr char* GRID_SPICE_FILE_NAME_PREFIX = "grid_"; + #endif diff --git a/openfpga/src/fpga_spice/spice_routing.cpp b/openfpga/src/fpga_spice/spice_routing.cpp new file mode 100644 index 000000000..9c83aef3c --- /dev/null +++ b/openfpga/src/fpga_spice/spice_routing.cpp @@ -0,0 +1,346 @@ +/********************************************************************* + * This file includes functions that are used for + * SPICE generation of FPGA routing architecture (global routing) + *********************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_time.h" +#include "vtr_log.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +/* Include FPGA-Verilog header files*/ +#include "openfpga_naming.h" +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" +#include "spice_routing.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print the sub-circuit of a connection Box (Type: [CHANX|CHANY]) + * Actually it is very similiar to switch box but + * the difference is connection boxes connect Grid INPUT Pins to channels + * NOTE: direct connection between CLBs should NOT be included inside this + * module! They should be added in the top-level module as their connection + * is not limited to adjacent CLBs!!! + * + * Location of a X- and Y-direction Connection Block in FPGA fabric + * +------------+ +-------------+ + * | |------>| | + * | CLB |<------| Y-direction | + * | | ... | Connection | + * | |------>| Block | + * +------------+ +-------------+ + * | ^ ... | | ^ ... | + * v | v v | v + * +-------------------+ +-------------+ + * --->| |--->| | + * <---| X-direction |<---| Switch | + * ...| Connection block |... | Block | + * --->| |--->| | + * +-------------------+ +-------------+ + * + * Internal structure: + * This is an example of a X-direction connection block + * Note that middle output ports are shorted wire from inputs of routing tracks, + * which are also the inputs of routing multiplexer of the connection block + * + * CLB Input Pins + * (IPINs) + * ^ ^ ^ + * | | ... | + * +--------------------------+ + * | ^ ^ ^ | + * | | | ... | | + * | +--------------------+ | + * | | routing | | + * | | multiplexers | | + * | +--------------------+ | + * | middle outputs | + * | of routing channel | + * | ^ ^ ^ ^ ^ ^ ^ ^ | + * | | | | | ... | | | | | + * in[0] -->|------------------------->|---> out[0] + * out[1] <--|<-------------------------|<--- in[1] + * | ... | + * in[W-2] -->|------------------------->|---> out[W-2] + * out[W-1] <--|<-------------------------|<--- in[W-1] + * +--------------------------+ + * + * W: routing channel width + * + ********************************************************************/ +static +void print_spice_routing_connection_box_unique_module(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& subckt_dir, + const RRGSB& rr_gsb, + const t_rr_type& cb_type) { + /* Create the netlist */ + vtr::Point gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + std::string spice_fname(subckt_dir + generate_connection_block_netlist_name(cb_type, gsb_coordinate, std::string(SPICE_NETLIST_FILE_POSTFIX))); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + print_spice_file_header(fp, std::string("SPICE modules for Unique Connection Blocks[" + std::to_string(rr_gsb.get_cb_x(cb_type)) + "]["+ std::to_string(rr_gsb.get_cb_y(cb_type)) + "]")); + + /* Create a Verilog Module based on the circuit model, and add to module manager */ + ModuleId cb_module = module_manager.find_module(generate_connection_block_module_name(cb_type, gsb_coordinate)); + VTR_ASSERT(true == module_manager.valid_module_id(cb_module)); + + /* Write the spice module */ + write_spice_subckt_to_file(fp, module_manager, cb_module); + + /* 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 = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::ROUTING_MODULE_NETLIST); +} + +/********************************************************************* + * Generate the SPICE module for a Switch Box. + * A Switch Box module consists of following ports: + * 1. Channel Y [x][y] inputs + * 2. Channel X [x+1][y] inputs + * 3. Channel Y [x][y-1] outputs + * 4. Channel X [x][y] outputs + * 5. Grid[x][y+1] Right side outputs pins + * 6. Grid[x+1][y+1] Left side output pins + * 7. Grid[x+1][y+1] Bottom side output pins + * 8. Grid[x+1][y] Top side output pins + * 9. Grid[x+1][y] Left side output pins + * 10. Grid[x][y] Right side output pins + * 11. Grid[x][y] Top side output pins + * 12. Grid[x][y+1] Bottom side output pins + * + * Location of a Switch Box in FPGA fabric: + * + * -------------- -------------- + * | | | | + * | Grid | ChanY | Grid | + * | [x][y+1] | [x][y+1] | [x+1][y+1] | + * | | | | + * -------------- -------------- + * ---------- + * ChanX | Switch | ChanX + * [x][y] | Box | [x+1][y] + * | [x][y] | + * ---------- + * -------------- -------------- + * | | | | + * | Grid | ChanY | Grid | + * | [x][y] | [x][y] | [x+1][y] | + * | | | | + * -------------- -------------- + * + * Switch Block pin location map + * + * Grid[x][y+1] ChanY[x][y+1] Grid[x+1][y+1] + * right_pins inputs/outputs left_pins + * | ^ | + * | | | + * v v v + * +-----------------------------------------------+ + * | | + * Grid[x][y+1] | | Grid[x+1][y+1] + * bottom_pins---->| |<---- bottom_pins + * | | + * ChanX[x][y] | Switch Box [x][y] | ChanX[x+1][y] + * inputs/outputs<--->| |<---> inputs/outputs + * | | + * Grid[x][y+1] | | Grid[x+1][y+1] + * top_pins---->| |<---- top_pins + * | | + * +-----------------------------------------------+ + * ^ ^ ^ + * | | | + * | v | + * Grid[x][y] ChanY[x][y] Grid[x+1][y] + * right_pins inputs/outputs left_pins + * + * + ********************************************************************/ +static +void print_spice_routing_switch_box_unique_module(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& subckt_dir, + const RRGSB& rr_gsb) { + /* Create the netlist */ + vtr::Point gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + std::string spice_fname(subckt_dir + generate_routing_block_netlist_name(SB_SPICE_FILE_NAME_PREFIX, gsb_coordinate, std::string(SPICE_NETLIST_FILE_POSTFIX))); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + print_spice_file_header(fp, std::string("SPICE subcircuits for Unique Switch Blocks[" + std::to_string(rr_gsb.get_sb_x()) + "]["+ std::to_string(rr_gsb.get_sb_y()) + "]")); + + /* Create a Verilog Module based on the circuit model, and add to module manager */ + ModuleId sb_module = module_manager.find_module(generate_switch_block_module_name(gsb_coordinate)); + VTR_ASSERT(true == module_manager.valid_module_id(sb_module)); + + /* Write the spice module */ + write_spice_subckt_to_file(fp, module_manager, sb_module); + + /* Close file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::ROUTING_MODULE_NETLIST); +} + +/******************************************************************** + * Iterate over all the connection blocks in a device + * and build a module for each of them + *******************************************************************/ +static +void print_spice_flatten_connection_block_modules(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceRRGSB& device_rr_gsb, + const std::string& subckt_dir, + const t_rr_type& cb_type) { + /* Build unique X-direction connection block modules */ + vtr::Point cb_range = device_rr_gsb.get_gsb_range(); + + 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); + if (true != rr_gsb.is_cb_exist(cb_type)) { + continue; + } + print_spice_routing_connection_box_unique_module(netlist_manager, + module_manager, + subckt_dir, + rr_gsb, cb_type); + } + } +} + +/******************************************************************** + * A top-level function of this file + * Print all the modules for global routing architecture of a FPGA fabric + * in Verilog format in a flatten way: + * Each connection block and switch block will be generated as a unique module + * Covering: + * 1. Connection blocks + * 2. Switch blocks + *******************************************************************/ +void print_spice_flatten_routing_modules(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceRRGSB& device_rr_gsb, + const std::string& subckt_dir) { + /* Create a vector to contain all the Verilog netlist names that have been generated in this function */ + std::vector netlist_names; + + vtr::Point sb_range = device_rr_gsb.get_gsb_range(); + + /* Build unique switch block 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 (true != rr_gsb.is_sb_exist()) { + continue; + } + print_spice_routing_switch_box_unique_module(netlist_manager, + module_manager, + subckt_dir, + rr_gsb); + } + } + + print_spice_flatten_connection_block_modules(netlist_manager, module_manager, device_rr_gsb, subckt_dir, CHANX); + + print_spice_flatten_connection_block_modules(netlist_manager, module_manager, device_rr_gsb, subckt_dir, CHANY); + + /* + VTR_LOG("Writing header file for routing submodules '%s'...", + ROUTING_VERILOG_FILE_NAME); + print_spice_netlist_include_header_file(netlist_names, + subckt_dir.c_str(), + ROUTING_VERILOG_FILE_NAME); + VTR_LOG("Done\n"); + VTR_LOG("\n"); + */ +} + + +/******************************************************************** + * A top-level function of this file + * Print all the unique modules for global routing architecture of a FPGA fabric + * in Verilog format, including: + * 1. Connection blocks + * 2. Switch blocks + * + * Note: this function SHOULD be called only when + * the option compact_routing_hierarchy is turned on!!! + *******************************************************************/ +void print_spice_unique_routing_modules(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceRRGSB& device_rr_gsb, + const std::string& subckt_dir) { + /* Create a vector to contain all the Verilog netlist names that have been generated in this function */ + std::vector netlist_names; + + /* Build unique switch block modules */ + for (size_t isb = 0; isb < device_rr_gsb.get_num_sb_unique_module(); ++isb) { + const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(isb); + print_spice_routing_switch_box_unique_module(netlist_manager, + module_manager, + subckt_dir, + unique_mirror); + } + + /* Build unique X-direction connection block modules */ + for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANX); ++icb) { + const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANX, icb); + + print_spice_routing_connection_box_unique_module(netlist_manager, + module_manager, + subckt_dir, + unique_mirror, CHANX); + } + + /* Build unique X-direction connection block modules */ + for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANY); ++icb) { + const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANY, icb); + + print_spice_routing_connection_box_unique_module(netlist_manager, + module_manager, + subckt_dir, + unique_mirror, CHANY); + } + + /* + VTR_LOG("Writing header file for routing submodules '%s'...", + ROUTING_VERILOG_FILE_NAME); + print_spice_netlist_include_header_file(netlist_names, + subckt_dir.c_str(), + ROUTING_VERILOG_FILE_NAME); + VTR_LOG("Done\n"); + */ + VTR_LOG("\n"); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_routing.h b/openfpga/src/fpga_spice/spice_routing.h new file mode 100644 index 000000000..3d2e499cc --- /dev/null +++ b/openfpga/src/fpga_spice/spice_routing.h @@ -0,0 +1,32 @@ +#ifndef SPICE_ROUTING_H +#define SPICE_ROUTING_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ + +#include "mux_library.h" +#include "module_manager.h" +#include "netlist_manager.h" +#include "device_rr_gsb.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_spice_flatten_routing_modules(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceRRGSB& device_rr_gsb, + const std::string& subckt_dir); + +void print_spice_unique_routing_modules(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceRRGSB& device_rr_gsb, + const std::string& subckt_dir); + +} /* end namespace openfpga */ + +#endif From 1dfb3e06ccafc68fecef77860807b48bf85bbdba Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:38:24 -0600 Subject: [PATCH 101/148] [FPGA-SPICE] add SPICE writer for logic blocks --- openfpga/src/base/openfpga_spice.cpp | 2 + openfpga/src/fpga_spice/spice_api.cpp | 10 + openfpga/src/fpga_spice/spice_api.h | 4 + openfpga/src/fpga_spice/spice_grid.cpp | 434 +++++++++++++++++++++++++ openfpga/src/fpga_spice/spice_grid.h | 30 ++ 5 files changed, 480 insertions(+) create mode 100644 openfpga/src/fpga_spice/spice_grid.cpp create mode 100644 openfpga/src/fpga_spice/spice_grid.h diff --git a/openfpga/src/base/openfpga_spice.cpp b/openfpga/src/base/openfpga_spice.cpp index 0ddab0c91..c1b418835 100644 --- a/openfpga/src/base/openfpga_spice.cpp +++ b/openfpga/src/base/openfpga_spice.cpp @@ -41,6 +41,8 @@ int write_fabric_spice(OpenfpgaContext& openfpga_ctx, openfpga_ctx.mutable_spice_netlists(), openfpga_ctx.arch(), openfpga_ctx.mux_lib(), + g_vpr_ctx.device(), + openfpga_ctx.vpr_device_annotation(), openfpga_ctx.device_rr_gsb(), options); diff --git a/openfpga/src/fpga_spice/spice_api.cpp b/openfpga/src/fpga_spice/spice_api.cpp index 852d29a8d..7787f8ba0 100644 --- a/openfpga/src/fpga_spice/spice_api.cpp +++ b/openfpga/src/fpga_spice/spice_api.cpp @@ -17,6 +17,7 @@ #include "spice_constants.h" #include "spice_submodule.h" #include "spice_routing.h" +#include "spice_grid.h" /* Header file for this source file */ #include "spice_api.h" @@ -42,6 +43,8 @@ int fpga_fabric_spice(const ModuleManager& module_manager, NetlistManager& netlist_manager, const Arch& openfpga_arch, const MuxLibrary& mux_lib, + const DeviceContext &device_ctx, + const VprDeviceAnnotation &device_annotation, const DeviceRRGSB &device_rr_gsb, const FabricSpiceOption& options) { @@ -97,6 +100,13 @@ int fpga_fabric_spice(const ModuleManager& module_manager, rr_dir_path); } + /* Generate grids */ + print_spice_grids(netlist_manager, + module_manager, + device_ctx, device_annotation, + lb_dir_path, + options.verbose_output()); + /* Given a brief stats on how many Spice modules have been written to files */ VTR_LOGV(options.verbose_output(), "Written %lu SPICE modules in total\n", diff --git a/openfpga/src/fpga_spice/spice_api.h b/openfpga/src/fpga_spice/spice_api.h index 2d418432d..0e08aa440 100644 --- a/openfpga/src/fpga_spice/spice_api.h +++ b/openfpga/src/fpga_spice/spice_api.h @@ -11,6 +11,8 @@ #include "module_manager.h" #include "openfpga_arch.h" #include "mux_library.h" +#include "vpr_context.h" +#include "vpr_device_annotation.h" #include "device_rr_gsb.h" #include "fabric_spice_options.h" @@ -25,6 +27,8 @@ int fpga_fabric_spice(const ModuleManager& module_manager, NetlistManager& netlist_manager, const Arch& openfpga_arch, const MuxLibrary& mux_lib, + const DeviceContext &device_ctx, + const VprDeviceAnnotation &device_annotation, const DeviceRRGSB &device_rr_gsb, const FabricSpiceOption& options); diff --git a/openfpga/src/fpga_spice/spice_grid.cpp b/openfpga/src/fpga_spice/spice_grid.cpp new file mode 100644 index 000000000..c04ed8920 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_grid.cpp @@ -0,0 +1,434 @@ +/******************************************************************** + * This file includes functions to print SPICE subckts for a Grid + * (CLBs, I/Os, heterogeneous blocks etc.) + *******************************************************************/ +/* System header files */ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_geometry.h" +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from readarch library */ +#include "physical_types.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" +#include "openfpga_side_manager.h" + +/* Headers from vpr library */ +#include "vpr_utils.h" + +#include "openfpga_reserved_words.h" +#include "openfpga_naming.h" +#include "openfpga_physical_tile_utils.h" +#include "pb_type_utils.h" +#include "circuit_library_utils.h" +#include "module_manager_utils.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" +#include "spice_grid.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print SPICE subckts of a primitive node in the pb_graph_node graph + * This generic function can support all the different types of primitive nodes + * i.e., Look-Up Tables (LUTs), Flip-flops (FFs) and hard logic blocks such as adders. + * + * The SPICE subckt will consist of two parts: + * 1. Logic module of the primitive node + * This module performs the logic function of the block + * 2. Memory module of the primitive node + * This module stores the configuration bits for the logic module + * if the logic module is a programmable resource, such as LUT + * + * SPICE subckt structure: + * + * Primitive block + * +---------------------------------------+ + * | | + * | +---------+ +---------+ | + * in |----->| |--->| |<------|configuration lines + * | | Logic |... | Memory | | + * out|<-----| |--->| | | + * | +---------+ +---------+ | + * | | + * +---------------------------------------+ + * + *******************************************************************/ +static +void print_spice_primitive_block(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& subckt_dir, + t_pb_graph_node* primitive_pb_graph_node, + const bool& verbose) { + /* Ensure a valid pb_graph_node */ + if (nullptr == primitive_pb_graph_node) { + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid primitive_pb_graph_node!\n"); + exit(1); + } + + /* Give a name to the Verilog netlist */ + /* Create the file name for Verilog */ + std::string spice_fname(subckt_dir + + generate_logical_tile_netlist_name(std::string(), primitive_pb_graph_node, std::string(SPICE_NETLIST_FILE_POSTFIX)) + ); + + VTR_LOG("Writing SPICE netlist '%s' for primitive pb_type '%s' ...", + spice_fname.c_str(), primitive_pb_graph_node->pb_type->name); + VTR_LOGV(verbose, "\n"); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + print_spice_file_header(fp, std::string("SPICE subckts for primitive pb_type: " + std::string(primitive_pb_graph_node->pb_type->name))); + + /* Generate the module name for this primitive pb_graph_node*/ + std::string primitive_module_name = generate_physical_block_module_name(primitive_pb_graph_node->pb_type); + + /* Create a module of the primitive LUT and register it to module manager */ + ModuleId primitive_module = module_manager.find_module(primitive_module_name); + /* Ensure that the module has been created and thus unique! */ + VTR_ASSERT(true == module_manager.valid_module_id(primitive_module)); + + VTR_LOGV(verbose, + "Writing SPICE codes of logical tile primitive block '%s'...", + module_manager.module_name(primitive_module).c_str()); + + /* Write the spice module */ + write_spice_subckt_to_file(fp, module_manager, primitive_module); + + /* Close file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::LOGIC_BLOCK_NETLIST); + + VTR_LOGV(verbose, "Done\n"); +} + +/******************************************************************** + * Print SPICE subckts of physical blocks inside a grid (CLB, I/O. etc.) + * This function will traverse the graph of complex logic block (t_pb_graph_node) + * in a recursive way, using a Depth First Search (DFS) algorithm. + * As such, primitive physical blocks (LUTs, FFs, etc.), leaf node of the pb_graph + * will be printed out first, while the top-level will be printed out in the last + * + * Note: this function will print a unique SPICE subckt for each type of + * t_pb_graph_node, i.e., t_pb_type, in the graph, in order to enable highly + * hierarchical Verilog organization as well as simplify the Verilog file sizes. + * + * Note: DFS is the right way. Do NOT use BFS. + * DFS can guarantee that all the sub-modules can be registered properly + * to its parent in module manager + *******************************************************************/ +static +void rec_print_spice_logical_tile(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const VprDeviceAnnotation& device_annotation, + const std::string& subckt_dir, + t_pb_graph_node* physical_pb_graph_node, + const bool& verbose) { + + /* Check cur_pb_graph_node*/ + if (nullptr == physical_pb_graph_node) { + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid physical_pb_graph_node\n"); + exit(1); + } + + /* Get the pb_type definition related to the node */ + t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type; + + /* Find the mode that physical implementation of a pb_type */ + t_mode* physical_mode = device_annotation.physical_mode(physical_pb_type); + + /* For non-leaf node in the pb_type graph: + * Recursively Depth-First Generate all the child pb_type at the level + */ + if (false == is_primitive_pb_type(physical_pb_type)) { + for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) { + /* Go recursive to visit the children */ + rec_print_spice_logical_tile(netlist_manager, + module_manager, device_annotation, + subckt_dir, + &(physical_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][0]), + verbose); + } + } + + /* For leaf node, a primitive SPICE subckt will be generated. + * Note that the primitive may be mapped to a standard cell, we force to use + * explict port mapping. This aims to avoid any port sequence issues!!! + */ + if (true == is_primitive_pb_type(physical_pb_type)) { + print_spice_primitive_block(netlist_manager, + module_manager, + subckt_dir, + physical_pb_graph_node, + verbose); + /* Finish for primitive node, return */ + return; + } + + /* Give a name to the Verilog netlist */ + /* Create the file name for Verilog */ + std::string spice_fname(subckt_dir + + generate_logical_tile_netlist_name(std::string(), physical_pb_graph_node, std::string(SPICE_NETLIST_FILE_POSTFIX)) + ); + + VTR_LOG("Writing SPICE netlist '%s' for pb_type '%s' ...", + spice_fname.c_str(), physical_pb_type->name); + VTR_LOGV(verbose, "\n"); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + print_spice_file_header(fp, std::string("SPICE subckts for pb_type: " + std::string(physical_pb_type->name))); + + /* Generate the name of the SPICE subckt for this pb_type */ + std::string pb_module_name = generate_physical_block_module_name(physical_pb_type); + + /* Register the SPICE subckt in module manager */ + ModuleId pb_module = module_manager.find_module(pb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(pb_module)); + + VTR_LOGV(verbose, + "Writing SPICE codes of pb_type '%s'...", + module_manager.module_name(pb_module).c_str()); + + /* Comment lines */ + print_spice_comment(fp, std::string("BEGIN Physical programmable logic block SPICE subckt: " + std::string(physical_pb_type->name))); + + /* Write the spice module */ + write_spice_subckt_to_file(fp, module_manager, pb_module); + + print_spice_comment(fp, std::string("END Physical programmable logic block SPICE subckt: " + std::string(physical_pb_type->name))); + + /* Close file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::LOGIC_BLOCK_NETLIST); + + VTR_LOGV(verbose, "Done\n"); +} + +/***************************************************************************** + * This function will create a Verilog file and print out a Verilog netlist + * for the logical tile (pb_graph/pb_type) + *****************************************************************************/ +static +void print_spice_logical_tile_netlist(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const VprDeviceAnnotation& device_annotation, + const std::string& subckt_dir, + t_pb_graph_node* pb_graph_head, + const bool& verbose) { + + VTR_LOG("Writing Verilog netlists for logic tile '%s' ...", + pb_graph_head->pb_type->name); + VTR_LOG("\n"); + + /* Print SPICE subckts for all the pb_types/pb_graph_nodes + * use a Depth-First Search Algorithm to print the sub-modules + * Note: DFS is the right way. Do NOT use BFS. + * DFS can guarantee that all the sub-modules can be registered properly + * to its parent in module manager + */ + /* Print SPICE subckts starting from the top-level pb_type/pb_graph_node, and traverse the graph in a recursive way */ + rec_print_spice_logical_tile(netlist_manager, + module_manager, + device_annotation, + subckt_dir, + pb_graph_head, + verbose); + + VTR_LOG("Done\n"); + VTR_LOG("\n"); +} + +/***************************************************************************** + * This function will create a Verilog file and print out a Verilog netlist + * for a type of physical block + * + * For IO blocks: + * The param 'border_side' is required, which is specify which side of fabric + * the I/O block locates at. + *****************************************************************************/ +static +void print_spice_physical_tile_netlist(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& subckt_dir, + t_physical_tile_type_ptr phy_block_type, + const e_side& border_side) { + /* Check code: if this is an IO block, the border side MUST be valid */ + if (true == is_io_type(phy_block_type)) { + VTR_ASSERT(NUM_SIDES != border_side); + } + + /* Give a name to the Verilog netlist */ + /* Create the file name for Verilog */ + std::string spice_fname(subckt_dir + + generate_grid_block_netlist_name(std::string(GRID_MODULE_NAME_PREFIX) + std::string(phy_block_type->name), + is_io_type(phy_block_type), + border_side, + std::string(SPICE_NETLIST_FILE_POSTFIX)) + ); + + /* Echo status */ + if (true == is_io_type(phy_block_type)) { + SideManager side_manager(border_side); + VTR_LOG("Writing SPICE Netlist '%s' for physical tile '%s' at %s side ...", + spice_fname.c_str(), phy_block_type->name, + side_manager.c_str()); + } else { + VTR_LOG("Writing SPICE Netlist '%s' for physical_tile '%s'...", + spice_fname.c_str(), phy_block_type->name); + } + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + print_spice_file_header(fp, std::string("SPICE subckts for physical tile: " + std::string(phy_block_type->name) + "]")); + + /* Create a Verilog Module for the top-level physical block, and add to module manager */ + std::string grid_module_name = generate_grid_block_module_name(std::string(GRID_SPICE_FILE_NAME_PREFIX), std::string(phy_block_type->name), is_io_type(phy_block_type), border_side); + ModuleId grid_module = module_manager.find_module(grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); + + /* Write the spice module */ + print_spice_comment(fp, std::string("BEGIN Grid SPICE subckt: " + module_manager.module_name(grid_module))); + write_spice_subckt_to_file(fp, module_manager, grid_module); + + print_spice_comment(fp, std::string("END Grid SPICE subckt: " + module_manager.module_name(grid_module))); + + /* 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 = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::LOGIC_BLOCK_NETLIST); + + VTR_LOG("Done\n"); +} + +/***************************************************************************** + * Create logic block modules in a compact way: + * 1. Only one module for each I/O on each border side (IO_TYPE) + * 2. Only one module for each CLB (FILL_TYPE) + * 3. Only one module for each heterogeneous block + ****************************************************************************/ +void print_spice_grids(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const std::string& subckt_dir, + const bool& verbose) { + /* Create a vector to contain all the Verilog netlist names that have been generated in this function */ + std::vector netlist_names; + + /* Enumerate the types of logical tiles, and build a module for each + * Write modules for all the pb_types/pb_graph_nodes + * use a Depth-First Search Algorithm to print the sub-modules + * Note: DFS is the right way. Do NOT use BFS. + * DFS can guarantee that all the sub-modules can be registered properly + * to its parent in module manager + */ + VTR_LOG("Writing logical tiles..."); + VTR_LOGV(verbose, "\n"); + for (const t_logical_block_type& logical_tile : device_ctx.logical_block_types) { + /* Bypass empty pb_graph */ + if (nullptr == logical_tile.pb_graph_head) { + continue; + } + print_spice_logical_tile_netlist(netlist_manager, + module_manager, + device_annotation, + subckt_dir, + logical_tile.pb_graph_head, + verbose); + } + VTR_LOG("Writing logical tiles..."); + VTR_LOG("Done\n"); + + VTR_LOG("\n"); + + /* Enumerate the types of physical tiles + * Use the logical tile module to build the physical tiles + */ + VTR_LOG("Building physical tiles..."); + VTR_LOGV(verbose, "\n"); + for (const t_physical_tile_type& physical_tile : device_ctx.physical_tile_types) { + /* Bypass empty type or nullptr */ + if (true == is_empty_type(&physical_tile)) { + continue; + } else if (true == is_io_type(&physical_tile)) { + /* Special for I/O block: + * We will search the grids and see where the I/O blocks are located: + * - If a I/O block locates on border sides of FPGA fabric: + * i.e., one or more from {TOP, RIGHT, BOTTOM, LEFT}, + * we will generate one module for each border side + * - If a I/O block locates in the center of FPGA fabric: + * we will generate one module with NUM_SIDES (same treatment as regular grids) + */ + 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) { + print_spice_physical_tile_netlist(netlist_manager, + module_manager, + subckt_dir, + &physical_tile, + io_type_side); + } + continue; + } else { + /* For CLB and heterogenenous blocks */ + print_spice_physical_tile_netlist(netlist_manager, + module_manager, + subckt_dir, + &physical_tile, + NUM_SIDES); + } + } + VTR_LOG("Building physical tiles..."); + VTR_LOG("Done\n"); + VTR_LOG("\n"); + + /* Output a header file for all the logic blocks */ + /* + std::string grid_spice_fname(LOGIC_BLOCK_VERILOG_FILE_NAME); + VTR_LOG("Writing header file for grid SPICE subckts '%s' ...", + grid_spice_fname.c_str()); + print_spice_netlist_include_header_file(netlist_names, + subckt_dir.c_str(), + grid_spice_fname.c_str()); + VTR_LOG("Done\n"); + */ +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_grid.h b/openfpga/src/fpga_spice/spice_grid.h new file mode 100644 index 000000000..5f45e2d61 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_grid.h @@ -0,0 +1,30 @@ +#ifndef SPICE_GRID_H +#define SPICE_GRID_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "vpr_context.h" +#include "module_manager.h" +#include "netlist_manager.h" +#include "vpr_device_annotation.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_spice_grids(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const std::string& subckt_dir, + const bool& verbose); + + +} /* end namespace openfpga */ + +#endif From 06c0073a3ea49d4610d680e02699b3a7e0267ffa Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:43:48 -0600 Subject: [PATCH 102/148] [FPGA-SPICE] Add SPICE writer for fpga top module --- openfpga/src/fpga_spice/spice_api.cpp | 13 ++++ openfpga/src/fpga_spice/spice_top_module.cpp | 76 ++++++++++++++++++++ openfpga/src/fpga_spice/spice_top_module.h | 24 +++++++ 3 files changed, 113 insertions(+) create mode 100644 openfpga/src/fpga_spice/spice_top_module.cpp create mode 100644 openfpga/src/fpga_spice/spice_top_module.h diff --git a/openfpga/src/fpga_spice/spice_api.cpp b/openfpga/src/fpga_spice/spice_api.cpp index 7787f8ba0..a06a3fb8e 100644 --- a/openfpga/src/fpga_spice/spice_api.cpp +++ b/openfpga/src/fpga_spice/spice_api.cpp @@ -18,6 +18,7 @@ #include "spice_submodule.h" #include "spice_routing.h" #include "spice_grid.h" +#include "spice_top_module.h" /* Header file for this source file */ #include "spice_api.h" @@ -107,6 +108,18 @@ int fpga_fabric_spice(const ModuleManager& module_manager, lb_dir_path, options.verbose_output()); + /* Generate FPGA fabric */ + print_spice_top_module(netlist_manager, + module_manager, + src_dir_path); + + /* Generate an netlist including all the fabric-related netlists */ + /* + print_fabric_include_netlist(const_cast(netlist_manager), + src_dir_path, + circuit_lib); + */ + /* Given a brief stats on how many Spice modules have been written to files */ VTR_LOGV(options.verbose_output(), "Written %lu SPICE modules in total\n", diff --git a/openfpga/src/fpga_spice/spice_top_module.cpp b/openfpga/src/fpga_spice/spice_top_module.cpp new file mode 100644 index 000000000..d153d9ef7 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_top_module.cpp @@ -0,0 +1,76 @@ +/******************************************************************** + * This file includes functions that are used to print the top-level + * module for the FPGA fabric in SPICE format + *******************************************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "openfpga_naming.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" +#include "spice_top_module.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print the top-level module for the FPGA fabric in SPICE 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 + *******************************************************************/ +void print_spice_top_module(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& spice_dir) { + /* 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.find_module(top_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(top_module)); + + /* Create the file name for SPICE netlist */ + std::string spice_fname(spice_dir + generate_fpga_top_netlist_name(std::string(SPICE_NETLIST_FILE_POSTFIX))); + + VTR_LOG("Writing SPICE netlist for top-level module of FPGA fabric '%s'...", + spice_fname.c_str()); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + print_spice_file_header(fp, std::string("Top-level SPICE subckt for FPGA")); + + /* Write the module content in Verilog format */ + write_spice_subckt_to_file(fp, module_manager, top_module); + + /* 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 = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::TOP_MODULE_NETLIST); + + VTR_LOG("Done\n"); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_top_module.h b/openfpga/src/fpga_spice/spice_top_module.h new file mode 100644 index 000000000..9cea104cc --- /dev/null +++ b/openfpga/src/fpga_spice/spice_top_module.h @@ -0,0 +1,24 @@ +#ifndef SPICE_TOP_MODULE_H +#define SPICE_TOP_MODULE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "module_manager.h" +#include "netlist_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_spice_top_module(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& spice_dir); + +} /* end namespace openfpga */ + +#endif From 222bc86cbfaa00bb5ad54df6c2f28008b58f0b12 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:53:28 -0600 Subject: [PATCH 103/148] [FPGA-SPICE] Add auxiliary SPICE netlist writer --- openfpga/src/fpga_spice/spice_api.cpp | 9 +- .../fpga_spice/spice_auxiliary_netlists.cpp | 86 +++++++++++++++++++ .../src/fpga_spice/spice_auxiliary_netlists.h | 24 ++++++ openfpga/src/fpga_spice/spice_constants.h | 1 + openfpga/src/utils/circuit_library_utils.cpp | 23 +++++ openfpga/src/utils/circuit_library_utils.h | 2 + 6 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 openfpga/src/fpga_spice/spice_auxiliary_netlists.cpp create mode 100644 openfpga/src/fpga_spice/spice_auxiliary_netlists.h diff --git a/openfpga/src/fpga_spice/spice_api.cpp b/openfpga/src/fpga_spice/spice_api.cpp index a06a3fb8e..22c433bd6 100644 --- a/openfpga/src/fpga_spice/spice_api.cpp +++ b/openfpga/src/fpga_spice/spice_api.cpp @@ -19,6 +19,7 @@ #include "spice_routing.h" #include "spice_grid.h" #include "spice_top_module.h" +#include "spice_auxiliary_netlists.h" /* Header file for this source file */ #include "spice_api.h" @@ -114,11 +115,9 @@ int fpga_fabric_spice(const ModuleManager& module_manager, src_dir_path); /* Generate an netlist including all the fabric-related netlists */ - /* - print_fabric_include_netlist(const_cast(netlist_manager), - src_dir_path, - circuit_lib); - */ + print_spice_fabric_include_netlist(const_cast(netlist_manager), + src_dir_path, + openfpga_arch.circuit_lib); /* Given a brief stats on how many Spice modules have been written to files */ VTR_LOGV(options.verbose_output(), diff --git a/openfpga/src/fpga_spice/spice_auxiliary_netlists.cpp b/openfpga/src/fpga_spice/spice_auxiliary_netlists.cpp new file mode 100644 index 000000000..f76464898 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_auxiliary_netlists.cpp @@ -0,0 +1,86 @@ +/******************************************************************** + * This file includes functions that are used to generate SPICE files + * or code blocks, with a focus on + * `include user-defined or auto-generated netlists in SPICE format + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "openfpga_naming.h" +#include "circuit_library_utils.h" +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_auxiliary_netlists.h" + +/* begin namespace openfpga */ +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_spice_fabric_include_netlist(const NetlistManager& netlist_manager, + const std::string& src_dir, + const CircuitLibrary& circuit_lib) { + std::string spice_fname = src_dir + std::string(FABRIC_INCLUDE_SPICE_NETLIST_FILE_NAME); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + check_file_stream(spice_fname.c_str(), fp); + + /* Print the title */ + print_spice_file_header(fp, std::string("Fabric Netlist Summary")); + + /* Include all the user-defined netlists */ + print_spice_comment(fp, std::string("Include user-defined netlists")); + for (const std::string& user_defined_netlist : find_circuit_library_unique_spice_netlists(circuit_lib)) { + print_spice_include_netlist(fp, user_defined_netlist); + } + + /* Include all the primitive modules */ + print_spice_comment(fp, std::string("Include primitive module netlists")); + for (const NetlistId& nlist_id : netlist_manager.netlists_by_type(NetlistManager::SUBMODULE_NETLIST)) { + print_spice_include_netlist(fp, netlist_manager.netlist_name(nlist_id)); + } + fp << std::endl; + + /* Include all the CLB, heterogeneous block modules */ + print_spice_comment(fp, std::string("Include logic block netlists")); + for (const NetlistId& nlist_id : netlist_manager.netlists_by_type(NetlistManager::LOGIC_BLOCK_NETLIST)) { + print_spice_include_netlist(fp, netlist_manager.netlist_name(nlist_id)); + } + fp << std::endl; + + /* Include all the routing architecture modules */ + print_spice_comment(fp, std::string("Include routing module netlists")); + for (const NetlistId& nlist_id : netlist_manager.netlists_by_type(NetlistManager::ROUTING_MODULE_NETLIST)) { + print_spice_include_netlist(fp, netlist_manager.netlist_name(nlist_id)); + } + fp << std::endl; + + /* Include FPGA top module */ + print_spice_comment(fp, std::string("Include fabric top-level netlists")); + for (const NetlistId& nlist_id : netlist_manager.netlists_by_type(NetlistManager::TOP_MODULE_NETLIST)) { + print_spice_include_netlist(fp, netlist_manager.netlist_name(nlist_id)); + } + fp << std::endl; + + /* Close the file stream */ + fp.close(); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_auxiliary_netlists.h b/openfpga/src/fpga_spice/spice_auxiliary_netlists.h new file mode 100644 index 000000000..aa04a54b9 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_auxiliary_netlists.h @@ -0,0 +1,24 @@ +#ifndef SPICE_AUXILIARY_NETLISTS_H +#define SPICE_AUXILIARY_NETLISTS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "circuit_library.h" +#include "netlist_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_spice_fabric_include_netlist(const NetlistManager& netlist_manager, + const std::string& src_dir, + const CircuitLibrary& circuit_lib); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 97b1733c2..68026a0be 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -12,6 +12,7 @@ constexpr char* SUPPLY_WRAPPER_SPICE_FILE_NAME = "supply_wrapper.sp"; constexpr char* MUXES_SPICE_FILE_NAME = "muxes.sp"; constexpr char* LUTS_SPICE_FILE_NAME = "luts.sp"; constexpr char* MEMORIES_SPICE_FILE_NAME = "memories.sp"; +constexpr char* FABRIC_INCLUDE_SPICE_NETLIST_FILE_NAME = "fabric_netlists.sp"; constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; diff --git a/openfpga/src/utils/circuit_library_utils.cpp b/openfpga/src/utils/circuit_library_utils.cpp index cd59545ae..8fb5aa7bc 100644 --- a/openfpga/src/utils/circuit_library_utils.cpp +++ b/openfpga/src/utils/circuit_library_utils.cpp @@ -251,6 +251,29 @@ std::vector find_circuit_library_unique_verilog_netlists(const Circ return netlists; } +/******************************************************************** + * A generic function to find all the unique user-defined + * Verilog netlists in a circuit library + * Netlists with same names will be considered as one + *******************************************************************/ +std::vector find_circuit_library_unique_spice_netlists(const CircuitLibrary& circuit_lib) { + std::vector netlists; + + for (const CircuitModelId& model : circuit_lib.models()) { + /* Skip empty netlist names */ + if (true == circuit_lib.model_spice_netlist(model).empty()) { + continue; + } + /* See if the netlist name is already in the list */ + std::vector::iterator it = std::find(netlists.begin(), netlists.end(), circuit_lib.model_spice_netlist(model)); + if (it == netlists.end()) { + netlists.push_back(circuit_lib.model_spice_netlist(model)); + } + } + + return netlists; +} + /************************************************************************ * Advanced check if the circuit model of configurable memory * satisfy the needs of configuration protocol diff --git a/openfpga/src/utils/circuit_library_utils.h b/openfpga/src/utils/circuit_library_utils.h index 584871868..dc7fd0e34 100644 --- a/openfpga/src/utils/circuit_library_utils.h +++ b/openfpga/src/utils/circuit_library_utils.h @@ -39,6 +39,8 @@ std::vector find_circuit_library_global_ports(const CircuitLibrar std::vector find_circuit_library_unique_verilog_netlists(const CircuitLibrary& circuit_lib); +std::vector find_circuit_library_unique_spice_netlists(const CircuitLibrary& circuit_lib); + bool check_configurable_memory_circuit_model(const e_config_protocol_type& config_protocol_type, const CircuitLibrary& circuit_lib, const CircuitModelId& config_mem_circuit_model); From 460fef580717ff23285102bbf098578a2bc009b8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:58:55 -0600 Subject: [PATCH 104/148] [FPGA-Verilog] Rename files and functions to distinguish from FPGA-SPICE files and functions --- openfpga/src/fpga_verilog/verilog_api.cpp | 14 +++++++------- .../verilog_auxiliary_netlists.cpp | 18 +++++++++--------- .../fpga_verilog/verilog_auxiliary_netlists.h | 12 ++++++------ openfpga/src/fpga_verilog/verilog_constants.h | 4 ++-- ....cpp => verilog_simulation_info_writer.cpp} | 4 ++-- ...iter.h => verilog_simulation_info_writer.h} | 4 ++-- 6 files changed, 28 insertions(+), 28 deletions(-) rename openfpga/src/fpga_verilog/{simulation_info_writer.cpp => verilog_simulation_info_writer.cpp} (98%) rename openfpga/src/fpga_verilog/{simulation_info_writer.h => verilog_simulation_info_writer.h} (94%) diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index d02ced128..940d78953 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -24,7 +24,7 @@ #include "verilog_preconfig_top_module.h" #include "verilog_formal_random_top_testbench.h" #include "verilog_top_testbench.h" -#include "simulation_info_writer.h" +#include "verilog_simulation_info_writer.h" /* Header file for this source file */ #include "verilog_api.h" @@ -128,9 +128,9 @@ void fpga_fabric_verilog(ModuleManager &module_manager, options.explicit_port_mapping()); /* Generate an netlist including all the fabric-related netlists */ - print_fabric_include_netlist(const_cast(netlist_manager), - src_dir_path, - circuit_lib); + print_verilog_fabric_include_netlist(const_cast(netlist_manager), + src_dir_path, + circuit_lib); /* Given a brief stats on how many Verilog modules have been written to files */ VTR_LOGV(options.verbose_output(), @@ -233,9 +233,9 @@ void fpga_verilog_testbench(const ModuleManager &module_manager, } /* Generate a Verilog file including all the netlists that have been generated */ - print_include_netlists(src_dir_path, - netlist_name, - options.reference_benchmark_file_path()); + print_verilog_testbench_include_netlists(src_dir_path, + netlist_name, + options.reference_benchmark_file_path()); } } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp index 65e0d1ca2..93fdb2161 100644 --- a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp +++ b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp @@ -30,10 +30,10 @@ namespace openfpga { * This does NOT include any testbenches! * Some netlists are open to compile under specific preprocessing flags *******************************************************************/ -void print_fabric_include_netlist(const NetlistManager& netlist_manager, - const std::string& src_dir, - const CircuitLibrary& circuit_lib) { - std::string verilog_fname = src_dir + std::string(FABRIC_INCLUDE_NETLIST_FILE_NAME); +void print_verilog_fabric_include_netlist(const NetlistManager& netlist_manager, + const std::string& src_dir, + const CircuitLibrary& circuit_lib) { + std::string verilog_fname = src_dir + std::string(FABRIC_INCLUDE_VERILOG_NETLIST_FILE_NAME); /* Create the file stream */ std::fstream fp; @@ -94,10 +94,10 @@ void print_fabric_include_netlist(const NetlistManager& netlist_manager, * that have been generated and user-defined. * Some netlists are open to compile under specific preprocessing flags *******************************************************************/ -void print_include_netlists(const std::string& src_dir, - const std::string& circuit_name, - const std::string& reference_benchmark_file) { - std::string verilog_fname = src_dir + circuit_name + std::string(TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX); +void print_verilog_testbench_include_netlists(const std::string& src_dir, + const std::string& circuit_name, + const std::string& reference_benchmark_file) { + std::string verilog_fname = src_dir + circuit_name + std::string(TOP_VERILOG_TESTBENCH_INCLUDE_NETLIST_FILE_NAME_POSTFIX); /* Create the file stream */ std::fstream fp; @@ -116,7 +116,7 @@ void print_include_netlists(const std::string& src_dir, /* Include FPGA top module */ print_verilog_comment(fp, std::string("------ Include fabric top-level netlists -----")); - print_verilog_include_netlist(fp, src_dir + std::string(FABRIC_INCLUDE_NETLIST_FILE_NAME)); + print_verilog_include_netlist(fp, src_dir + std::string(FABRIC_INCLUDE_VERILOG_NETLIST_FILE_NAME)); fp << std::endl; /* Include reference benchmark netlist only when auto-check flag is enabled */ diff --git a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h index 57f04032f..c3952af85 100644 --- a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h +++ b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h @@ -17,13 +17,13 @@ /* begin namespace openfpga */ namespace openfpga { -void print_fabric_include_netlist(const NetlistManager& netlist_manager, - const std::string& src_dir, - const CircuitLibrary& circuit_lib); +void print_verilog_fabric_include_netlist(const NetlistManager& netlist_manager, + const std::string& src_dir, + const CircuitLibrary& circuit_lib); -void print_include_netlists(const std::string& src_dir, - const std::string& circuit_name, - const std::string& reference_benchmark_file); +void print_verilog_testbench_include_netlists(const std::string& src_dir, + const std::string& circuit_name, + const std::string& reference_benchmark_file); void print_verilog_preprocessing_flags_netlist(const std::string& src_dir, const FabricVerilogOption& fabric_verilog_opts); diff --git a/openfpga/src/fpga_verilog/verilog_constants.h b/openfpga/src/fpga_verilog/verilog_constants.h index a09425d4f..66ff018ff 100644 --- a/openfpga/src/fpga_verilog/verilog_constants.h +++ b/openfpga/src/fpga_verilog/verilog_constants.h @@ -19,8 +19,8 @@ constexpr char* MODELSIM_SIMULATION_TIME_UNIT = "ms"; constexpr char* ICARUS_SIMULATOR_FLAG = "ICARUS_SIMULATOR"; // the flag to enable specific Verilog code in testbenches // End of Icarus variables and flag -constexpr char* FABRIC_INCLUDE_NETLIST_FILE_NAME = "fabric_netlists.v"; -constexpr char* TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX = "_include_netlists.v"; +constexpr char* FABRIC_INCLUDE_VERILOG_NETLIST_FILE_NAME = "fabric_netlists.v"; +constexpr char* TOP_VERILOG_TESTBENCH_INCLUDE_NETLIST_FILE_NAME_POSTFIX = "_include_netlists.v"; constexpr char* VERILOG_TOP_POSTFIX = "_top.v"; constexpr char* FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX = "_top_formal_verification.v"; constexpr char* TOP_TESTBENCH_VERILOG_FILE_POSTFIX = "_top_tb.v"; /* !!! must be consist with the modelsim_testbench_module_postfix */ diff --git a/openfpga/src/fpga_verilog/simulation_info_writer.cpp b/openfpga/src/fpga_verilog/verilog_simulation_info_writer.cpp similarity index 98% rename from openfpga/src/fpga_verilog/simulation_info_writer.cpp rename to openfpga/src/fpga_verilog/verilog_simulation_info_writer.cpp index 134b34f0a..d4a64fcd0 100644 --- a/openfpga/src/fpga_verilog/simulation_info_writer.cpp +++ b/openfpga/src/fpga_verilog/verilog_simulation_info_writer.cpp @@ -21,7 +21,7 @@ #include "simulation_utils.h" #include "verilog_constants.h" -#include "simulation_info_writer.h" +#include "verilog_simulation_info_writer.h" /* begin namespace openfpga */ namespace openfpga { @@ -77,7 +77,7 @@ void print_verilog_simulation_info(const std::string& ini_fname, ini["SIMULATION_DECK"]["UNIT "] = "ms"; ini["SIMULATION_DECK"]["VERILOG_PATH "] = std::string(src_dir); ini["SIMULATION_DECK"]["VERILOG_FILE1"] = std::string(DEFINES_VERILOG_FILE_NAME); - ini["SIMULATION_DECK"]["VERILOG_FILE2"] = std::string(circuit_name + std::string(TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX)); + ini["SIMULATION_DECK"]["VERILOG_FILE2"] = std::string(circuit_name + std::string(TOP_VERILOG_TESTBENCH_INCLUDE_NETLIST_FILE_NAME_POSTFIX)); ini["SIMULATION_DECK"]["CONFIG_PROTOCOL"] = std::string(CONFIG_PROTOCOL_TYPE_STRING[config_protocol_type]); /* Information required by UVM */ diff --git a/openfpga/src/fpga_verilog/simulation_info_writer.h b/openfpga/src/fpga_verilog/verilog_simulation_info_writer.h similarity index 94% rename from openfpga/src/fpga_verilog/simulation_info_writer.h rename to openfpga/src/fpga_verilog/verilog_simulation_info_writer.h index ff421fadc..8806a71ac 100644 --- a/openfpga/src/fpga_verilog/simulation_info_writer.h +++ b/openfpga/src/fpga_verilog/verilog_simulation_info_writer.h @@ -1,5 +1,5 @@ -#ifndef SIMULATION_INFO_WRITER_H -#define SIMULATION_INFO_WRITER_H +#ifndef VERILOG_SIMULATION_INFO_WRITER_H +#define VERILOG_SIMULATION_INFO_WRITER_H /******************************************************************** * Include header files that are required by function declaration From 615a24999aad52f4a4d22380178afe591ca2d311 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 14:45:33 -0600 Subject: [PATCH 105/148] [Documentation] Remove out-of-date description --- README_Benchmarks.md | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 README_Benchmarks.md diff --git a/README_Benchmarks.md b/README_Benchmarks.md deleted file mode 100644 index b9797186e..000000000 --- a/README_Benchmarks.md +++ /dev/null @@ -1,9 +0,0 @@ -[TODO]: # (This document might be incomplete. It is important to keep it updated with the right names) - -### Benchmarks README ### -######### - -fpga_flow is a folder which contains benchmarks testing the performances of the tool on a known variety of benchmarks. In order to launch them, a script called run_fpga_spice_testbench_study.sh is called. This script is based on different other scripts which can be found in the script folder. - -The different benchmarks can be found in the fpga_spice_bench.txt found in the benchmarks folder. By commenting them, the script will not read them and not run through them. This can be useful if you want to focus on one benchmark in particular for example. - From 544c44fe4618fdfaa1e346cfc93544618f1fc9b7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 14:58:15 -0600 Subject: [PATCH 106/148] [FPGA-SPICE] Add VDD and VSS port to module definition --- .../src/fpga_spice/spice_writer_utils.cpp | 28 ++++++++++++++++++- openfpga/src/fpga_spice/spice_writer_utils.h | 4 ++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/openfpga/src/fpga_spice/spice_writer_utils.cpp b/openfpga/src/fpga_spice/spice_writer_utils.cpp index 889ee8c19..75f22196c 100644 --- a/openfpga/src/fpga_spice/spice_writer_utils.cpp +++ b/openfpga/src/fpga_spice/spice_writer_utils.cpp @@ -18,6 +18,7 @@ /* Headers from openfpgautil library */ #include "openfpga_digest.h" +#include "spice_constants.h" #include "spice_writer_utils.h" /* begin namespace openfpga */ @@ -98,7 +99,9 @@ std::string generate_spice_port(const BasicPort& port, * module (); ***********************************************/ void print_spice_subckt_definition(std::fstream& fp, - const ModuleManager& module_manager, const ModuleId& module_id) { + const ModuleManager& module_manager, + const ModuleId& module_id, + const bool& include_supply_ports) { VTR_ASSERT(true == valid_file_stream(fp)); print_spice_comment(fp, std::string("SPICE module for " + module_manager.module_name(module_id))); @@ -153,6 +156,29 @@ void print_spice_subckt_definition(std::fstream& fp, } } } + + /* Add supply ports if specified */ + if (true == include_supply_ports) { + /* Check if we need a new line */ + new_line = false; + if (10 == pin_cnt) { + pin_cnt = 0; + fp << std::endl; + new_line = true; + } + /* Print VDD and VSS ports + * TODO: the supply ports should be derived from module manager + */ + if (true == new_line) { + std::string port_whitespace(module_head_line.length() - 2, ' '); + fp << "+ " << port_whitespace; + } + write_space_to_file(fp, 1); + fp << SPICE_SUBCKT_VDD_PORT_NAME; + write_space_to_file(fp, 1); + fp << SPICE_SUBCKT_GND_PORT_NAME; + } + fp << std::endl; } diff --git a/openfpga/src/fpga_spice/spice_writer_utils.h b/openfpga/src/fpga_spice/spice_writer_utils.h index b01d5a35c..a3617a43a 100644 --- a/openfpga/src/fpga_spice/spice_writer_utils.h +++ b/openfpga/src/fpga_spice/spice_writer_utils.h @@ -43,7 +43,9 @@ std::string generate_spice_port(const BasicPort& port, const bool& omit_pin_zero = false); void print_spice_subckt_definition(std::fstream& fp, - const ModuleManager& module_manager, const ModuleId& module_id); + const ModuleManager& module_manager, + const ModuleId& module_id, + const bool& include_supply_ports = true); void print_spice_subckt_end(std::fstream& fp, const std::string& module_name); From e867e203f44561fae5ceb0a862ce20e4fc63bff9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 15:00:56 -0600 Subject: [PATCH 107/148] [Documentation] Use release mode in Docker settings --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 771ab425c..94f753737 100755 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,7 @@ RUN mkdir -p /release /dev RUN cd release && git clone --single-branch --branch master https://github.com/LNIS-Projects/OpenFPGA.git OpenFPGA -RUN cd /release/OpenFPGA && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=debug -DCMAKE_NO_GRAPHICS=on && make +RUN cd /release/OpenFPGA && mkdir build && cd build && cmake .. -DCMAKE_NO_GRAPHICS=on && make RUN rm -rf /var/lib/apt/lists/* From c6ac02d210466fb0d31aa77520e7e0a7c39733b8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 15:21:33 -0600 Subject: [PATCH 108/148] [FPGA-SPICE] Add VDD/VSS ports to SPICE subckt instanciation --- openfpga/src/fpga_spice/spice_constants.h | 1 + .../src/fpga_spice/spice_subckt_writer.cpp | 28 ++++++++++++++- .../src/fpga_spice/spice_writer_utils.cpp | 34 ++++++++++++++----- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 68026a0be..3e045b40c 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -2,6 +2,7 @@ #define SPICE_CONSTANTS_H /* global parameters for dumping spice netlists */ +constexpr size_t SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE = 10; constexpr char* SPICE_NETLIST_FILE_POSTFIX = ".sp"; diff --git a/openfpga/src/fpga_spice/spice_subckt_writer.cpp b/openfpga/src/fpga_spice/spice_subckt_writer.cpp index c90c0b5ac..291404ec6 100644 --- a/openfpga/src/fpga_spice/spice_subckt_writer.cpp +++ b/openfpga/src/fpga_spice/spice_subckt_writer.cpp @@ -19,6 +19,9 @@ #include "openfpga_naming.h" #include "module_manager_utils.h" + + +#include "spice_constants.h" #include "spice_writer_utils.h" #include "spice_subckt_writer.h" @@ -370,7 +373,7 @@ void write_spice_instance_to_file(std::fstream& fp, /* Currently we limit 10 ports per line to keep a clean netlist */ new_line = false; - if (10 == pin_cnt) { + if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) { pin_cnt = 0; fp << std::endl; new_line = true; @@ -380,6 +383,29 @@ void write_spice_instance_to_file(std::fstream& fp, } } + /* Print VDD and VSS ports + * TODO: the supply ports should be derived from module manager + */ + if (true == new_line) { + std::string port_whitespace(instance_head_line.length() - 2, ' '); + fp << "+ " << port_whitespace; + } + write_space_to_file(fp, 1); + fp << SPICE_SUBCKT_VDD_PORT_NAME; + write_space_to_file(fp, 1); + fp << SPICE_SUBCKT_GND_PORT_NAME; + + pin_cnt += 2; + + /* Check if we need a new line */ + new_line = false; + if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) { + pin_cnt = 0; + fp << std::endl; + new_line = true; + fit_one_line = false; + } + /* Print module name: * if port print cannot fit one line, we create a new line for the module for a clean format */ diff --git a/openfpga/src/fpga_spice/spice_writer_utils.cpp b/openfpga/src/fpga_spice/spice_writer_utils.cpp index 75f22196c..68d3360b1 100644 --- a/openfpga/src/fpga_spice/spice_writer_utils.cpp +++ b/openfpga/src/fpga_spice/spice_writer_utils.cpp @@ -148,7 +148,7 @@ void print_spice_subckt_definition(std::fstream& fp, /* Currently we limit 10 ports per line to keep a clean netlist */ new_line = false; - if (10 == pin_cnt) { + if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) { pin_cnt = 0; fp << std::endl; new_line = true; @@ -159,13 +159,6 @@ void print_spice_subckt_definition(std::fstream& fp, /* Add supply ports if specified */ if (true == include_supply_ports) { - /* Check if we need a new line */ - new_line = false; - if (10 == pin_cnt) { - pin_cnt = 0; - fp << std::endl; - new_line = true; - } /* Print VDD and VSS ports * TODO: the supply ports should be derived from module manager */ @@ -325,7 +318,7 @@ void print_spice_subckt_instance(std::fstream& fp, /* Currently we limit 10 ports per line to keep a clean netlist */ new_line = false; - if (10 == pin_cnt) { + if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) { pin_cnt = 0; fp << std::endl; new_line = true; @@ -335,6 +328,29 @@ void print_spice_subckt_instance(std::fstream& fp, } } + /* Print VDD and VSS ports + * TODO: the supply ports should be derived from module manager + */ + if (true == new_line) { + std::string port_whitespace(instance_head_line.length() - 2, ' '); + fp << "+ " << port_whitespace; + } + write_space_to_file(fp, 1); + fp << SPICE_SUBCKT_VDD_PORT_NAME; + write_space_to_file(fp, 1); + fp << SPICE_SUBCKT_GND_PORT_NAME; + + pin_cnt += 2; + + /* Check if we need a new line */ + new_line = false; + if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) { + pin_cnt = 0; + fp << std::endl; + new_line = true; + fit_one_line = false; + } + /* Print module name: * if port print cannot fit one line, we create a new line for the module for a clean format */ From defb5dea4b1b5c7bd0f4dbf7514d4ae61c44d8a3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 17:08:07 -0600 Subject: [PATCH 109/148] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..dd84ea782 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 5d9228eb2e68f4fedc16271b53ee4d4879e5d1ba Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 17:24:29 -0600 Subject: [PATCH 110/148] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea782..b465fdee4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -23,16 +23,10 @@ A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] +**Enviornment (please complete the following information):** + - OS: [e.g. CentOs, Ubuntu] + - Compiler [e.g. gcc, clang] + - Version [e.g. Github commit id] **Additional context** Add any other context about the problem here. From 60f328a2ab9aee6e69e370deda087564c2e9a1cd Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 21 Sep 2020 17:36:57 -0600 Subject: [PATCH 111/148] [Architecture] Add openfpga architecture for a small k4 fracturable FPGA --- .../k4_frac_N4_40nm_cc_openfpga.xml | 231 ++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k4_frac_N4_40nm_cc_openfpga.xml diff --git a/openfpga_flow/openfpga_arch/k4_frac_N4_40nm_cc_openfpga.xml b/openfpga_flow/openfpga_arch/k4_frac_N4_40nm_cc_openfpga.xml new file mode 100644 index 000000000..ee5c4df5f --- /dev/null +++ b/openfpga_flow/openfpga_arch/k4_frac_N4_40nm_cc_openfpga.xml @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From e9c0e90544b4c62c1a8ad5731d599296ed741673 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 21 Sep 2020 17:37:26 -0600 Subject: [PATCH 112/148] [Architecture] Add a VPR architectue using fracturable LUT4 --- .../vpr_arch/k4_frac_N4_tileable_40nm.xml | 437 ++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 openfpga_flow/vpr_arch/k4_frac_N4_tileable_40nm.xml diff --git a/openfpga_flow/vpr_arch/k4_frac_N4_tileable_40nm.xml b/openfpga_flow/vpr_arch/k4_frac_N4_tileable_40nm.xml new file mode 100644 index 000000000..ad69a6808 --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_frac_N4_tileable_40nm.xml @@ -0,0 +1,437 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a83bc3f75c5c06589f26d9aa78711a472eabaef2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 21 Sep 2020 17:38:16 -0600 Subject: [PATCH 113/148] [Regression tests] Add test cases for the fracturable LUT4 architecture and deploy it to CI --- .travis/fpga_verilog_reg_test.sh | 5 +- .../lut_design/frac_lut4/config/task.conf | 49 +++++++++++++++++++ .../{frac_lut => frac_lut6}/config/task.conf | 0 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 openfpga_flow/tasks/fpga_verilog/lut_design/frac_lut4/config/task.conf rename openfpga_flow/tasks/fpga_verilog/lut_design/{frac_lut => frac_lut6}/config/task.conf (100%) diff --git a/.travis/fpga_verilog_reg_test.sh b/.travis/fpga_verilog_reg_test.sh index a8d1e8eb7..f08ebed0e 100755 --- a/.travis/fpga_verilog_reg_test.sh +++ b/.travis/fpga_verilog_reg_test.sh @@ -13,8 +13,11 @@ echo -e "FPGA-Verilog Feature Tests"; echo -e "Testing Verilog generation for LUTs: a single mode LUT6 FPGA using micro benchmarks"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/lut_design/single_mode --debug --show_thread_logs +echo -e "Testing Verilog generation for LUTs: simple fracturable LUT4 "; +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/lut_design/frac_lut4 --debug --show_thread_logs + echo -e "Testing Verilog generation for LUTs: simple fracturable LUT6 "; -python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/lut_design/frac_lut --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/lut_design/frac_lut6 --debug --show_thread_logs echo -e "Testing Verilog generation for LUTs: LUT6 with intermediate buffers"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/lut_design/intermediate_buffer --debug --show_thread_logs diff --git a/openfpga_flow/tasks/fpga_verilog/lut_design/frac_lut4/config/task.conf b/openfpga_flow/tasks/fpga_verilog/lut_design/frac_lut4/config/task.conf new file mode 100644 index 000000000..805502d33 --- /dev/null +++ b/openfpga_flow/tasks/fpga_verilog/lut_design/frac_lut4/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 = 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/OpenFPGAShellScripts/example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_frac_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_frac_N4_tileable_40nm.xml + +[BENCHMARKS] +# +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.blif +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/routing_test/routing_test.blif +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_or2/and2_or2.blif +# Modelsim is ok with this but icarus fails due to poor support on timing and looping +#bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.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 + +bench1_top = routing_test +bench1_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/routing_test/routing_test.act +bench1_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/routing_test/routing_test.v + +bench2_top = and2_or2 +bench2_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_or2/and2_or2.act +bench2_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_or2/and2_or2.v + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= +vpr_fpga_verilog_formal_verification_top_netlist= diff --git a/openfpga_flow/tasks/fpga_verilog/lut_design/frac_lut/config/task.conf b/openfpga_flow/tasks/fpga_verilog/lut_design/frac_lut6/config/task.conf similarity index 100% rename from openfpga_flow/tasks/fpga_verilog/lut_design/frac_lut/config/task.conf rename to openfpga_flow/tasks/fpga_verilog/lut_design/frac_lut6/config/task.conf From d7f8b3abadc751327cf4c1040e59a17b5f858fc5 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 21 Sep 2020 17:44:37 -0600 Subject: [PATCH 114/148] [Architecture] Add k4 N4 untilable architecture --- openfpga_flow/vpr_arch/k4_frac_N4_40nm.xml | 437 +++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 openfpga_flow/vpr_arch/k4_frac_N4_40nm.xml diff --git a/openfpga_flow/vpr_arch/k4_frac_N4_40nm.xml b/openfpga_flow/vpr_arch/k4_frac_N4_40nm.xml new file mode 100644 index 000000000..872c92a0e --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_frac_N4_40nm.xml @@ -0,0 +1,437 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 936a164eee5d25f2510cf731da87b6a7bbe92b0b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 21 Sep 2020 17:48:28 -0600 Subject: [PATCH 115/148] [OpenFPGA flow] Add a new template script to use a fixed device layout --- .../fix_device_example_script.openfpga | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 openfpga_flow/OpenFPGAShellScripts/fix_device_example_script.openfpga diff --git a/openfpga_flow/OpenFPGAShellScripts/fix_device_example_script.openfpga b/openfpga_flow/OpenFPGAShellScripts/fix_device_example_script.openfpga new file mode 100644 index 000000000..3d5418466 --- /dev/null +++ b/openfpga_flow/OpenFPGAShellScripts/fix_device_example_script.openfpga @@ -0,0 +1,74 @@ +# Run VPR for the 'and' design +#--write_rr_graph example_rr_graph.xml +vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route --device ${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 clustering nets based on routing results +pb_pin_fixup --verbose + +# 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.xml --format xml + +# 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 --include_signal_init --support_icarus_simulator --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_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini ./SimulationDeck/simulation_deck.ini --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 e1c5947143bb30df5e1dc03b12b983b80fb0517f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 21 Sep 2020 18:01:51 -0600 Subject: [PATCH 116/148] [Architecture] Add auto layout and fixed layout to architectures --- openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml | 9 +++++++-- .../k4_N4_tileable_full_output_crossbar_40nm.xml | 9 +++++++-- .../k4_N4_tileable_no_local_routing_40nm.xml | 9 +++++++-- openfpga_flow/vpr_arch/k4_frac_N4_40nm.xml | 9 +++++++-- .../vpr_arch/k4_frac_N4_tileable_40nm.xml | 9 +++++++-- openfpga_flow/vpr_arch/k6_frac_N10_40nm.xml | 9 +++++++-- .../vpr_arch/k6_frac_N10_adder_chain_40nm.xml | 9 +++++++-- .../k6_frac_N10_adder_chain_mem16K_40nm.xml | 12 ++++++++++-- .../vpr_arch/k6_frac_N10_tileable_40nm.xml | 9 +++++++-- .../k6_frac_N10_tileable_adder_chain_40nm.xml | 9 +++++++-- ..._frac_N10_tileable_adder_chain_mem16K_40nm.xml | 12 ++++++++++-- ...c_N10_tileable_adder_chain_mem16K_aib_40nm.xml | 14 ++++++++++++-- ..._adder_chain_mem16K_multi_io_capacity_40nm.xml | 15 +++++++++++++-- ...ileable_adder_chain_mem16K_reduced_io_40nm.xml | 15 ++++++++++++--- ..._N10_tileable_adder_chain_wide_mem16K_40nm.xml | 12 ++++++++++-- ...rac_N10_tileable_adder_register_chain_40nm.xml | 9 +++++++-- ...10_tileable_adder_register_scan_chain_40nm.xml | 9 +++++++-- ...er_register_scan_chain_depop50_spypad_40nm.xml | 11 +++++++++-- ...er_register_scan_chain_mem16K_depop50_12nm.xml | 12 ++++++++++-- ...eable_thru_channel_adder_chain_mem16K_40nm.xml | 12 ++++++++++-- ..._thru_channel_adder_chain_wide_mem16K_40nm.xml | 12 ++++++++++-- .../vpr_arch/k6_frac_N8_tileable_40nm.xml | 9 +++++++-- 22 files changed, 190 insertions(+), 45 deletions(-) diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml index 533704db8..90a484880 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml @@ -63,7 +63,13 @@ - + + + + + + + @@ -71,7 +77,6 @@ - - + + + + + + + @@ -72,7 +78,6 @@ - - + + + + + + + @@ -75,7 +81,6 @@ - - + + + + + + + @@ -85,7 +91,6 @@ - - + + + + + + + @@ -85,7 +91,6 @@ - - + + + + + + + @@ -85,7 +91,6 @@ - - + + + + + + + @@ -168,7 +174,6 @@ - - + + + + + + + + + + @@ -205,7 +214,6 @@ - - + + + + + + + @@ -85,7 +91,6 @@ - - + + + + + + + @@ -168,7 +174,6 @@ - - + + + + + + + + + + @@ -205,7 +214,6 @@ - - + + + + + + + + + + + + @@ -237,7 +248,6 @@ - - + + + + + + + + + + + + + @@ -239,7 +251,6 @@ - - + + + + + + + + + + + + @@ -206,9 +217,7 @@ - - - + + + + + + + + + + @@ -205,7 +214,6 @@ - - + + + + + + + @@ -172,7 +178,6 @@ - - + + + + + + + @@ -187,7 +193,6 @@ - - + + + + + + + + + - diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_register_scan_chain_mem16K_depop50_12nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_register_scan_chain_mem16K_depop50_12nm.xml index 15281812f..2ee4db855 100755 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_register_scan_chain_mem16K_depop50_12nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_adder_register_scan_chain_mem16K_depop50_12nm.xml @@ -184,7 +184,16 @@ This is strongly recommended if you want to PnR large FPGA fabric --> - + + + + + + + + + + diff --git a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_mem16K_40nm.xml b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_mem16K_40nm.xml index ffefc9146..aaf06d70c 100644 --- a/openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_mem16K_40nm.xml +++ b/openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_mem16K_40nm.xml @@ -201,7 +201,16 @@ - + + + + + + + + + + @@ -212,7 +221,6 @@ - - + + + + + + + + + + @@ -205,7 +214,6 @@ - - + + + + + + + @@ -85,7 +91,6 @@ - + + + + + + + + + + + + + + - - - - + + + + + +------>+------->|I[0] LUT + * + * - This function is designed for passing wires inside pb graph + * + * root pb_graph_node + * +------------------------------+ + * | Intermediate pb_graph_node | + * | +-------------+ | + * | | | | + * | | | | + * I[0]----->+------>+--- ... ---->+------->+------>O[0] + * + ***************************************************************************************/ +static +bool rec_direct_search_sink_pb_graph_pins(const t_pb_graph_pin* source_pb_pin, + std::vector& sink_pb_pins) { + + std::vector sink_pb_pins_to_search; + + for (int iedge = 0; iedge < source_pb_pin->num_output_edges; ++iedge) { + if (DIRECT_INTERC != source_pb_pin->output_edges[iedge]->interconnect->type) { + return false; + } + for (int ipin = 0; ipin < source_pb_pin->output_edges[iedge]->num_output_pins; ++ipin) { + t_pb_graph_pin* cand_sink_pb_pin = source_pb_pin->output_edges[iedge]->output_pins[ipin]; + if ( (true == is_primitive_pb_type(cand_sink_pb_pin->parent_node->pb_type)) + && (IN_PORT == cand_sink_pb_pin->port->type)) { + sink_pb_pins.push_back(cand_sink_pb_pin); + } else if ( (true == cand_sink_pb_pin->parent_node->is_root()) + && (OUT_PORT == cand_sink_pb_pin->port->type)) { + sink_pb_pins.push_back(cand_sink_pb_pin); + } else { + sink_pb_pins_to_search.push_back(cand_sink_pb_pin); + } + } + } + + for (t_pb_graph_pin* sink_pb_pin : sink_pb_pins_to_search) { + bool direct_search_status = rec_direct_search_sink_pb_graph_pins(sink_pb_pin, sink_pb_pins); + if (false == direct_search_status) { + return false; + } + } + + /* Reach here, we succeed. */ + return true; +} + /*************************************************************************************** * Try find all the sink pins which is mapped to a routing trace in the context of pb route * This function uses a recursive walk-through over the pb_route @@ -133,13 +200,32 @@ void rec_find_routed_sink_pb_graph_pins(const t_pb* pb, static std::vector find_routed_pb_graph_pins_atom_net(const t_pb* pb, const t_pb_graph_pin* source_pb_pin, + const t_pb_graph_pin* packing_source_pb_pin, const AtomNetId& atom_net_id, const VprDeviceAnnotation& device_annotation, const std::map& pb_pin_mapped_nets, t_pb_graph_pin** pb_graph_pin_lookup_from_index) { std::vector sink_pb_pins; - rec_find_routed_sink_pb_graph_pins(pb, source_pb_pin, atom_net_id, device_annotation, pb_pin_mapped_nets, pb_graph_pin_lookup_from_index, sink_pb_pins); + /* Try to directly search for sink pb_pins from the source_pb_pin, + * which is the actual source pin to be routed from + * Note that the packing source_pb_pin is the source pin considered by + * VPR packer, but may not be the actual source!!! + */ + if (true == source_pb_pin->parent_node->is_root()) { + bool direct_search_status = rec_direct_search_sink_pb_graph_pins(source_pb_pin, sink_pb_pins); + if (true == direct_search_status) { + VTR_ASSERT(!sink_pb_pins.empty()); + /* We have find through direct searching, return now */ + return sink_pb_pins; + } + + /* Cannot find through direct searching, reset results */ + VTR_ASSERT_SAFE(false == direct_search_status); + sink_pb_pins.clear(); + } + + rec_find_routed_sink_pb_graph_pins(pb, packing_source_pb_pin, atom_net_id, device_annotation, pb_pin_mapped_nets, pb_graph_pin_lookup_from_index, sink_pb_pins); return sink_pb_pins; } @@ -339,7 +425,7 @@ void add_lb_router_nets(LbRouter& lb_router, VTR_ASSERT(nullptr != packing_source_pb_pin); /* Find all the sink pins in the pb_route, we walk through the input pins and find the pin */ - std::vector sink_pb_graph_pins = find_routed_pb_graph_pins_atom_net(pb, packing_source_pb_pin, atom_net_id, device_annotation, pb_pin_mapped_nets, pb_graph_pin_lookup_from_index); + std::vector sink_pb_graph_pins = find_routed_pb_graph_pins_atom_net(pb, source_pb_pin, packing_source_pb_pin, atom_net_id, device_annotation, pb_pin_mapped_nets, pb_graph_pin_lookup_from_index); std::vector sink_lb_rr_nodes = find_lb_net_physical_sink_lb_rr_nodes(lb_rr_graph, sink_pb_graph_pins, device_annotation); VTR_ASSERT(sink_lb_rr_nodes.size() == sink_pb_graph_pins.size()); @@ -404,7 +490,7 @@ void add_lb_router_nets(LbRouter& lb_router, VTR_ASSERT(AtomNetId::INVALID() != atom_net_id); /* Find all the sink pins in the pb_route */ - std::vector sink_pb_graph_pins = find_routed_pb_graph_pins_atom_net(pb, source_pb_pin, atom_net_id, device_annotation, pb_pin_mapped_nets, pb_graph_pin_lookup_from_index); + std::vector sink_pb_graph_pins = find_routed_pb_graph_pins_atom_net(pb, physical_source_pb_pin, source_pb_pin, atom_net_id, device_annotation, pb_pin_mapped_nets, pb_graph_pin_lookup_from_index); std::vector sink_lb_rr_nodes = find_lb_net_physical_sink_lb_rr_nodes(lb_rr_graph, sink_pb_graph_pins, device_annotation); VTR_ASSERT(sink_lb_rr_nodes.size() == sink_pb_graph_pins.size()); From aa5f5fc7e0d2e29bf97577b44ad73ad4d5bf4598 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 21 Sep 2020 22:22:39 -0600 Subject: [PATCH 123/148] [Architecture] Bring back pin equivalence for no local routing architecture --- .../k4_N4_tileable_no_local_routing_40nm.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_40nm.xml b/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_40nm.xml index 4c44908a3..b4ebcc711 100644 --- a/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_40nm.xml +++ b/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_40nm.xml @@ -54,10 +54,10 @@ - - - - + + + + @@ -211,10 +211,10 @@ However, current annotation engine does not support this The feature should be enabled after patching --> - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 195e-12 + 195e-12 + 195e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From dd192a2f5407382ef693d168210a617b05c033a3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 11:34:23 -0600 Subject: [PATCH 125/148] [Architecture] Add a k4k4 openfpga architecture with carry chain for quick test --- ...4_frac_N4_adder_chain_40nm_cc_openfpga.xml | 254 ++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_40nm_cc_openfpga.xml diff --git a/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_40nm_cc_openfpga.xml b/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_40nm_cc_openfpga.xml new file mode 100644 index 000000000..7033f1eda --- /dev/null +++ b/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_40nm_cc_openfpga.xml @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From dad19cac9a2791e1be1bedae657cb3f67275f9d8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 11:39:18 -0600 Subject: [PATCH 126/148] [Regression test] Add k4 series architecture: fracturable adder --- .../k4_series/k4n4_adder/config/task.conf | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/k4_series/k4n4_adder/config/task.conf diff --git a/openfpga_flow/tasks/basic_tests/k4_series/k4n4_adder/config/task.conf b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_adder/config/task.conf new file mode 100644 index 000000000..e1beaf610 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_adder/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/OpenFPGAShellScripts/fix_device_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_40nm_cc_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +external_fabric_key_file= +openfpga_vpr_device_layout=2x2 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_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 ea4dd410b71ee1db53adf8b78377ac4084b9f19a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 11:41:36 -0600 Subject: [PATCH 127/148] [Regression Test] Add k4n4 fracturable lut test case to basic test --- .../k4_series/k4n4_frac_lut/config/task.conf | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/k4_series/k4n4_frac_lut/config/task.conf diff --git a/openfpga_flow/tasks/basic_tests/k4_series/k4n4_frac_lut/config/task.conf b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_frac_lut/config/task.conf new file mode 100644 index 000000000..805502d33 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_frac_lut/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 = 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/OpenFPGAShellScripts/example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_frac_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_frac_N4_tileable_40nm.xml + +[BENCHMARKS] +# +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.blif +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/routing_test/routing_test.blif +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_or2/and2_or2.blif +# Modelsim is ok with this but icarus fails due to poor support on timing and looping +#bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.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 + +bench1_top = routing_test +bench1_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/routing_test/routing_test.act +bench1_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/routing_test/routing_test.v + +bench2_top = and2_or2 +bench2_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_or2/and2_or2.act +bench2_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_or2/and2_or2.v + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= +vpr_fpga_verilog_formal_verification_top_netlist= From 28813126378b137070278e2ed987d84982e19676 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 11:43:34 -0600 Subject: [PATCH 128/148] [Regression test] deploy k4 series test cases to CI --- .travis/basic_reg_test.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis/basic_reg_test.sh b/.travis/basic_reg_test.sh index ff29f7b71..3865c66e9 100755 --- a/.travis/basic_reg_test.sh +++ b/.travis/basic_reg_test.sh @@ -38,4 +38,10 @@ python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/generate_testbench -- echo -e "Testing user-defined simulation settings: clock frequency and number of cycles"; python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/fixed_simulation_settings --debug --show_thread_logs +echo -e "Testing K4 series FPGA"; +echo -e "Testing K4N4 with facturable LUTs"; +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_frac_lut --debug --show_thread_logs +echo -e "Testing K4N4 with hard adders"; +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_adder --debug --show_thread_logs + end_section "OpenFPGA.TaskTun" From 2dea97afb65b9185251a4ad093bfd1bb8d04bd85 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 11:45:29 -0600 Subject: [PATCH 129/148] [Regression test] reduce runtime for k4n4 test in basic testing --- .../k4_series/k4n4_frac_lut/config/task.conf | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/openfpga_flow/tasks/basic_tests/k4_series/k4n4_frac_lut/config/task.conf b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_frac_lut/config/task.conf index 805502d33..fa6394642 100644 --- a/openfpga_flow/tasks/basic_tests/k4_series/k4n4_frac_lut/config/task.conf +++ b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_frac_lut/config/task.conf @@ -25,24 +25,12 @@ arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_frac_N4_tileable_40nm.xml [BENCHMARKS] # -bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.blif -bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/routing_test/routing_test.blif -bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_or2/and2_or2.blif -# Modelsim is ok with this but icarus fails due to poor support on timing and looping -#bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.blif +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_or2/and2_or2.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 - -bench1_top = routing_test -bench1_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/routing_test/routing_test.act -bench1_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/routing_test/routing_test.v - -bench2_top = and2_or2 -bench2_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_or2/and2_or2.act -bench2_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_or2/and2_or2.v +bench0_top = and2_or2 +bench0_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_or2/and2_or2.act +bench0_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_or2/and2_or2.v [SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] end_flow_with_test= From 7ed9f76b069ecfbd828c4277a33533f56621bb0b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 11:47:03 -0600 Subject: [PATCH 130/148] [Regression test] Move k4n4 no local routing to basic test --- .../k4n4_no_local_routing/config/task.conf | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/k4_series/k4n4_no_local_routing/config/task.conf diff --git a/openfpga_flow/tasks/basic_tests/k4_series/k4n4_no_local_routing/config/task.conf b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_no_local_routing/config/task.conf new file mode 100644 index 000000000..c076b156c --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_no_local_routing/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/OpenFPGAShellScripts/example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_no_local_routing_40nm_frame_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +external_fabric_key_file= + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_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 3bf94b8e3462003abcdcc4f82bebf7d6fd123e2e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 11:48:19 -0600 Subject: [PATCH 131/148] [Regression test] Remove no local routing from fpga verilog tests --- .travis/fpga_verilog_reg_test.sh | 3 -- .../no_local_routing/config/task.conf | 38 ------------------- 2 files changed, 41 deletions(-) delete mode 100644 openfpga_flow/tasks/fpga_verilog/no_local_routing/config/task.conf diff --git a/.travis/fpga_verilog_reg_test.sh b/.travis/fpga_verilog_reg_test.sh index f08ebed0e..ce8c57b92 100755 --- a/.travis/fpga_verilog_reg_test.sh +++ b/.travis/fpga_verilog_reg_test.sh @@ -96,9 +96,6 @@ python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/depopulate_crossbar echo -e "Testing Fully connected output crossbar in local routing"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/fully_connected_output_crossbar --debug --show_thread_logs -echo -e "Testing no local routing architecture"; -python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/no_local_routing --debug --show_thread_logs - echo -e "Testing through channels in tileable routing"; python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/thru_channel/thru_narrow_tile --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py fpga_verilog/thru_channel/thru_wide_tile --debug --show_thread_logs diff --git a/openfpga_flow/tasks/fpga_verilog/no_local_routing/config/task.conf b/openfpga_flow/tasks/fpga_verilog/no_local_routing/config/task.conf deleted file mode 100644 index c076b156c..000000000 --- a/openfpga_flow/tasks/fpga_verilog/no_local_routing/config/task.conf +++ /dev/null @@ -1,38 +0,0 @@ -# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -# 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/OpenFPGAShellScripts/example_script.openfpga -openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_no_local_routing_40nm_frame_openfpga.xml -openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml -external_fabric_key_file= - -[ARCHITECTURES] -arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_no_local_routing_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 237fc2e63607f2a0c6dfbe7b9da0d5f6703e6f45 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 11:49:16 -0600 Subject: [PATCH 132/148] [Regression test] Deploy no local routing in basic tests to CI --- .travis/basic_reg_test.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis/basic_reg_test.sh b/.travis/basic_reg_test.sh index 3865c66e9..84543bf8d 100755 --- a/.travis/basic_reg_test.sh +++ b/.travis/basic_reg_test.sh @@ -43,5 +43,7 @@ echo -e "Testing K4N4 with facturable LUTs"; python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_frac_lut --debug --show_thread_logs echo -e "Testing K4N4 with hard adders"; python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_adder --debug --show_thread_logs +echo -e "Testing K4N4 without local routing architecture"; +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_no_local_routing --debug --show_thread_logs end_section "OpenFPGA.TaskTun" From daf776b7b1ce9a3293cc5419dafa51c2995653a4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 12:22:32 -0600 Subject: [PATCH 133/148] [Architecture] Add k4n4 architecture with bram block for basic tests --- ...rac_N4_tileable_adder_chain_mem1K_40nm.xml | 702 ++++++++++++++++++ 1 file changed, 702 insertions(+) create mode 100644 openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_40nm.xml diff --git a/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_40nm.xml b/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_40nm.xml new file mode 100644 index 000000000..68f4bdcb6 --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_40nm.xml @@ -0,0 +1,702 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 195e-12 + 195e-12 + 195e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 26fba4a94be3b7ac6900463a8010cd7da4564a98 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 12:22:59 -0600 Subject: [PATCH 134/148] [Architecture] Add openfpga architectue for k4n4 with bram blocks --- ..._adder_chain_mem1K_40nm_frame_openfpga.xml | 271 ++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_40nm_frame_openfpga.xml diff --git a/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_40nm_frame_openfpga.xml b/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_40nm_frame_openfpga.xml new file mode 100644 index 000000000..508ceab90 --- /dev/null +++ b/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_40nm_frame_openfpga.xml @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From ddf999b6b901fb94e6cc39f7d0d6e5cdc654512e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 12:23:28 -0600 Subject: [PATCH 135/148] [Architecture] Add verilog HDL for dual-port BRAM 1k --- openfpga_flow/VerilogNetlists/dpram1k.v | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 openfpga_flow/VerilogNetlists/dpram1k.v diff --git a/openfpga_flow/VerilogNetlists/dpram1k.v b/openfpga_flow/VerilogNetlists/dpram1k.v new file mode 100644 index 000000000..427d6d199 --- /dev/null +++ b/openfpga_flow/VerilogNetlists/dpram1k.v @@ -0,0 +1,56 @@ +//----------------------------------------------------- +// Design Name : dual_port_ram +// File Name : dpram.v +// Function : Dual port RAM 8x128 +// Coder : Xifan Tang +//----------------------------------------------------- + +module dpram_128x8 ( + input clk, + input wen, + input ren, + input[0:6] waddr, + input[0:6] raddr, + input[0:7] d_in, + output[0:7] d_out ); + + dual_port_sram memory_0 ( + .wclk (clk), + .wen (wen), + .waddr (waddr), + .data_in (d_in), + .rclk (clk), + .ren (ren), + .raddr (raddr), + .d_out (d_out) ); + +endmodule + +module dual_port_sram ( + input wclk, + input wen, + input[0:6] waddr, + input[0:7] data_in, + input rclk, + input ren, + input[0:6] raddr, + output[0:7] d_out ); + + reg[0:7] ram[0:127]; + reg[0:7] internal; + + assign d_out = internal; + + always @(posedge wclk) begin + if(wen) begin + ram[waddr] <= data_in; + end + end + + always @(posedge rclk) begin + if(ren) begin + internal <= ram[raddr]; + end + end + +endmodule From 5741664580a7b677125f103af00eb633e134d10c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 12:23:56 -0600 Subject: [PATCH 136/148] [Regression Test] Add test case for k4n4 bram architecture --- .../k4_series/k4n4_bram/config/task.conf | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/k4_series/k4n4_bram/config/task.conf diff --git a/openfpga_flow/tasks/basic_tests/k4_series/k4n4_bram/config/task.conf b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_bram/config/task.conf new file mode 100644 index 000000000..6ed4e217e --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_bram/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/OpenFPGAShellScripts/fix_device_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_40nm_frame_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout=3x2 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_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 8fff2b77eb8a546107be5fbf048554568eb60374 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 12:24:54 -0600 Subject: [PATCH 137/148] [Regression Test] Deploy k4n4 BRAM test case to CI --- .travis/basic_reg_test.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis/basic_reg_test.sh b/.travis/basic_reg_test.sh index 84543bf8d..873e0c875 100755 --- a/.travis/basic_reg_test.sh +++ b/.travis/basic_reg_test.sh @@ -45,5 +45,7 @@ echo -e "Testing K4N4 with hard adders"; python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_adder --debug --show_thread_logs echo -e "Testing K4N4 without local routing architecture"; python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_no_local_routing --debug --show_thread_logs +echo -e "Testing K4N4 with block RAM"; +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_bram --debug --show_thread_logs end_section "OpenFPGA.TaskTun" From 8a3934b749c82aebb0045b2d47e99a6664fb89ae Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 12:35:39 -0600 Subject: [PATCH 138/148] [Architecture Add vpr architecture for k4n4 using multiple wire segments --- ...4_tileable_adder_chain_mem1K_L124_40nm.xml | 717 ++++++++++++++++++ 1 file changed, 717 insertions(+) create mode 100644 openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124_40nm.xml diff --git a/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124_40nm.xml b/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124_40nm.xml new file mode 100644 index 000000000..a5172d3f9 --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124_40nm.xml @@ -0,0 +1,717 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 13df6c1c21fe17d7a1b51699cad4fe6b94ad0756 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 12:36:11 -0600 Subject: [PATCH 139/148] [Architecture] Add openfpga architecture for k4n4 using multiple segments --- ...r_chain_mem1K_L124_40nm_frame_openfpga.xml | 275 ++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_L124_40nm_frame_openfpga.xml diff --git a/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_L124_40nm_frame_openfpga.xml b/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_L124_40nm_frame_openfpga.xml new file mode 100644 index 000000000..8bba60744 --- /dev/null +++ b/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_L124_40nm_frame_openfpga.xml @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 801055b007eab71d9113eaf4e5654e75ab74f375 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 12:47:02 -0600 Subject: [PATCH 140/148] [OpenFPGA Tool] Bug Fix on the tileable RRG for multi segment --- vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp index ad4944bb4..2cef83e5c 100755 --- a/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp @@ -1211,7 +1211,7 @@ t_track2pin_map build_gsb_track_to_ipin_map(const RRGraph& rr_graph, ipin_Fc_out.push_back(ipin_Fc); if (0 != ipin_Fc) { skip_conn2track = false; - break; + continue; } } @@ -1290,7 +1290,7 @@ t_pin2track_map build_gsb_opin_to_track_map(const RRGraph& rr_graph, opin_Fc_out.push_back(opin_Fc); if (0 != opin_Fc) { skip_conn2track = false; - break; + continue; } } From 3d1f49fb2f7300ed19a0e72fced5d4e3f14d748f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 12:47:41 -0600 Subject: [PATCH 141/148] [Regression Test] Add testcase for k4n4 with multiple segments --- .../k4_series/k4n4_L124/config/task.conf | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/k4_series/k4n4_L124/config/task.conf diff --git a/openfpga_flow/tasks/basic_tests/k4_series/k4n4_L124/config/task.conf b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_L124/config/task.conf new file mode 100644 index 000000000..c59ceb467 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_L124/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/OpenFPGAShellScripts/fix_device_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 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_L124_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 a61d161cbed864e6f6fffbf88b8d128b79c206f5 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 12:48:53 -0600 Subject: [PATCH 142/148] [Regression Test] Deploy k4n4 with multiple segments to CI --- .travis/basic_reg_test.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis/basic_reg_test.sh b/.travis/basic_reg_test.sh index 873e0c875..2a0ae6bb2 100755 --- a/.travis/basic_reg_test.sh +++ b/.travis/basic_reg_test.sh @@ -47,5 +47,7 @@ echo -e "Testing K4N4 without local routing architecture"; python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_no_local_routing --debug --show_thread_logs echo -e "Testing K4N4 with block RAM"; python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_bram --debug --show_thread_logs +echo -e "Testing K4N4 with multiple lengths of routing segments"; +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_L124 --debug --show_thread_logs end_section "OpenFPGA.TaskTun" From 61bcbaafd81f87bb2294aeea744cbe160921e5d7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 15:15:19 -0600 Subject: [PATCH 143/148] [Architecture] Add Verilog HDL for fracturable 32-bit multiplier --- openfpga_flow/VerilogNetlists/mult_32x32.v | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 openfpga_flow/VerilogNetlists/mult_32x32.v diff --git a/openfpga_flow/VerilogNetlists/mult_32x32.v b/openfpga_flow/VerilogNetlists/mult_32x32.v new file mode 100644 index 000000000..f2e0bea9b --- /dev/null +++ b/openfpga_flow/VerilogNetlists/mult_32x32.v @@ -0,0 +1,35 @@ +//----------------------------------------------------- +// Design Name : mult_32x32 +// File Name : mult_32x32.v +// Function : A 32-bit multiplier which can operate in fracturable modes: +// 1. four 8-bit multipliers +// 2. two 16-bit multipliers +// 3. one 32-bit multipliers +// Coder : Xifan Tang +//----------------------------------------------------- + +module mult_32x32 ( + input [0:31] a, + input [0:31] b, + output [0:63] out, + input [0:1] mode); + + reg [0:63] out_reg; + + always @(mode, a, b) begin + if (2'b01 == mode) begin + out_reg[0:15] <= a[0:7] * b[0:7]; + out_reg[16:31] <= a[8:15] * b[8:15]; + out_reg[32:47] <= a[16:23] * b[16:23]; + out_reg[48:63] <= a[24:31] * b[24:31]; + end else if (2'b10 == mode) begin + out_reg[0:31] <= a[0:15] * b[0:15]; + out_reg[32:63] <= a[16:31] * b[16:31]; + end else begin + out_reg <= a * b; + end + end + + assign out = out_reg; + +endmodule From 72749be4bd3153f34965648fae8bb82c616704a7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 15:31:34 -0600 Subject: [PATCH 144/148] [Architecture] Add OpenFPGA architecture for k4n4 with fracturable 32-bit multiplier --- ...n_mem1K_frac_dsp32_40nm_frame_openfpga.xml | 304 ++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_frac_dsp32_40nm_frame_openfpga.xml diff --git a/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_frac_dsp32_40nm_frame_openfpga.xml b/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_frac_dsp32_40nm_frame_openfpga.xml new file mode 100644 index 000000000..eb5e3c659 --- /dev/null +++ b/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_frac_dsp32_40nm_frame_openfpga.xml @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 70b8b02f74ad3e38dd2b1536f73d26fb8c5039b6 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 15:32:11 -0600 Subject: [PATCH 145/148] [Architecture] Add vpr architecture for k4n4 with fracturable 32-bit multiplier --- ...able_adder_chain_mem1K_frac_dsp32_40nm.xml | 902 ++++++++++++++++++ 1 file changed, 902 insertions(+) create mode 100644 openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_frac_dsp32_40nm.xml diff --git a/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_frac_dsp32_40nm.xml b/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_frac_dsp32_40nm.xml new file mode 100644 index 000000000..eff6cc604 --- /dev/null +++ b/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_frac_dsp32_40nm.xml @@ -0,0 +1,902 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 195e-12 + 195e-12 + 195e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 51c03196575fc77e2effdcbbf99a7d1f940d4ebe Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 15:32:54 -0600 Subject: [PATCH 146/148] [Regression tests] Add test case for the k4n4 with fracturable 32-bit multiplier --- .../k4_series/k4n4_frac_mult/config/task.conf | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/k4_series/k4n4_frac_mult/config/task.conf diff --git a/openfpga_flow/tasks/basic_tests/k4_series/k4n4_frac_mult/config/task.conf b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_frac_mult/config/task.conf new file mode 100644 index 000000000..d338a3409 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/k4_series/k4n4_frac_mult/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/OpenFPGAShellScripts/fix_device_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_frac_N4_adder_chain_mem1K_frac_dsp32_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 + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_frac_N4_tileable_adder_chain_mem1K_frac_dsp32_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 8f4cca789a3201c7fea6936241ff769dd08160a9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 15:34:44 -0600 Subject: [PATCH 147/148] [Regression Test] Add k4n4 with fracturable multiplier test case to CI --- .travis/basic_reg_test.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis/basic_reg_test.sh b/.travis/basic_reg_test.sh index 2a0ae6bb2..85cb514e4 100755 --- a/.travis/basic_reg_test.sh +++ b/.travis/basic_reg_test.sh @@ -49,5 +49,7 @@ echo -e "Testing K4N4 with block RAM"; python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_bram --debug --show_thread_logs echo -e "Testing K4N4 with multiple lengths of routing segments"; python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_L124 --debug --show_thread_logs +echo -e "Testing K4N4 with 32-bit fracturable multiplier"; +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/k4_series/k4n4_frac_mult --debug --show_thread_logs end_section "OpenFPGA.TaskTun" From 7729f671ab6123fa3193a71ff1d172000e39afd9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 22 Sep 2020 18:35:41 -0600 Subject: [PATCH 148/148] [Regression Tests] Remove deadlink --- openfpga_flow/tasks/generate_testbenches | 1 - 1 file changed, 1 deletion(-) delete mode 120000 openfpga_flow/tasks/generate_testbenches diff --git a/openfpga_flow/tasks/generate_testbenches b/openfpga_flow/tasks/generate_testbenches deleted file mode 120000 index 67a0f73fe..000000000 --- a/openfpga_flow/tasks/generate_testbenches +++ /dev/null @@ -1 +0,0 @@ -/research/ece/lnis/USERS/DARPA_ERI/Tapeout/May2020/release/FROG_v1.0/SCRIPTS/generate_testbenches \ No newline at end of file