From 6039ae92ca061103adc5bad19ea9295561151ce4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 5 Dec 2020 19:37:34 -0700 Subject: [PATCH 1/3] [Arch] Bug fix for buffering two-level routing multiplexers using custom cells --- ...vel_io_skywater130nm_customhd_cc_openfpga.xml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ARCH/openfpga_arch_template/k4_frac_N8_reset_softadder_register_scan_chain_caravel_io_skywater130nm_customhd_cc_openfpga.xml b/ARCH/openfpga_arch_template/k4_frac_N8_reset_softadder_register_scan_chain_caravel_io_skywater130nm_customhd_cc_openfpga.xml index b427d66..479c05b 100644 --- a/ARCH/openfpga_arch_template/k4_frac_N8_reset_softadder_register_scan_chain_caravel_io_skywater130nm_customhd_cc_openfpga.xml +++ b/ARCH/openfpga_arch_template/k4_frac_N8_reset_softadder_register_scan_chain_caravel_io_skywater130nm_customhd_cc_openfpga.xml @@ -43,6 +43,18 @@ 10e-12 + + + + + + + 10e-12 + + + 10e-12 + + @@ -160,7 +172,7 @@ - + @@ -169,7 +181,7 @@ - + From 22f2b3aa90fc9082c89a54159fd91a2e72a198a3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 5 Dec 2020 21:14:56 -0700 Subject: [PATCH 2/3] [HDL] Add python script to adapt OpenFPGA MUX primitives to use custom cells --- .../custom_cell_mux_primitive_generator.py | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 HDL/common/custom_cell_mux_primitive_generator.py diff --git a/HDL/common/custom_cell_mux_primitive_generator.py b/HDL/common/custom_cell_mux_primitive_generator.py new file mode 100644 index 0000000..45b8c88 --- /dev/null +++ b/HDL/common/custom_cell_mux_primitive_generator.py @@ -0,0 +1,179 @@ +##################################################################### +# Python script generate Verilog codes for the primitive modules +# that is used to build routing multiplexers +# The Verilog codes will exploit the custom cells built for MUX primitives +# including: +# - 2-input MUX +# - 3-input MUX +# - Skywater MUX2 standard cell +##################################################################### + +import os +from os.path import dirname, abspath, isfile +import shutil +import re +import argparse +import logging +import json + +##################################################################### +# Initialize logger +##################################################################### +logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG) + +##################################################################### +# Parse the options +# - OpenFPGA root path is a manadatory option +##################################################################### +parser = argparse.ArgumentParser( + description='Generator for custom cells of routing multiplexer primitives') +parser.add_argument('--template_netlist', required=True, + help='Specify template verilog netlist') +parser.add_argument('--output_verilog', required=True, + help='Specify output verilog file path') +args = parser.parse_args() + +##################################################################### +# Check options: +# - Input file must be valid +# Otherwise, error out +# - Remove any output file if already exist +# TODO: give a warning when remove files +##################################################################### +if not isfile(args.template_netlist): + logging.error("Invalid template netlist: " + args.template_netlist + "\nFile does not exist!\n") + exit(1) +if isfile(args.output_verilog): + logging.warn("Remove existing output netlist: " + args.output_verilog + "!\n") + os.remove(args.output_verilog) + +##################################################################### +# Open the template Verilog netlist and start modification +##################################################################### +logging.info("Converting template netlist:"+ args.template_netlist) +logging.info(" To custom cell netlist:"+ args.output_verilog) +# Create output file handler +custom_nlist = open(args.output_verilog, "w") + +####################################################################### +# A function to generate Verilog codes for a MUX3 custom cell +# Given an input index +def generate_verilog_codes_custom_cell_mux3(first_input_index, instance_index): + lines = [] + + lines.append("\tscs8hd_muxinv3_1 scs8hd_muxinv3_1_" + str(instance_index) + "(") + lines.append("\t .Q1(in[" + str(first_input_index) + "]),") + lines.append("\t .Q2(in[" + str(first_input_index + 1) + "]),") + lines.append("\t .Q3(in[" + str(first_input_index + 2) + "]),") + lines.append("\t .S0(mem[" + str(first_input_index) + "]),") + lines.append("\t .S0B(mem_inv[" + str(first_input_index) + "]),") + lines.append("\t .S1(mem[" + str(first_input_index + 1) + "]),") + lines.append("\t .S1B(mem_inv[" + str(first_input_index + 1) + "]),") + lines.append("\t .S2(mem[" + str(first_input_index + 2) + "]),") + lines.append("\t .S2B(mem_inv[" + str(first_input_index + 2) + "]),") + lines.append("\t .Z(out[0])") + lines.append("\t );") + + return lines + +####################################################################### +# A function to generate Verilog codes for a MUX3 custom cell +# Given an input index +def generate_verilog_codes_custom_cell_mux2(first_input_index, instance_index): + lines = [] + + lines.append("\tscs8hd_muxinv2_1 scs8hd_muxinv2_1_" + str(instance_index) + "(") + lines.append("\t .Q1(in[" + str(first_input_index) + "]),") + lines.append("\t .Q2(in[" + str(first_input_index + 1) + "]),") + lines.append("\t .S0(mem[" + str(first_input_index) + "]),") + lines.append("\t .S0B(mem_inv[" + str(first_input_index) + "]),") + lines.append("\t .S1(mem[" + str(first_input_index + 1) + "]),") + lines.append("\t .S1B(mem_inv[" + str(first_input_index + 1) + "]),") + lines.append("\t .Z(out[0])") + lines.append("\t );") + + return lines + + +####################################################################### +# A function to output custom cells of multiplexing structure to a file +# based on the input size and memory size +# - If the memory size is 1, the input size should be 2 +# In this case, an standard cell will be outputted +# - If the memory size is larger than 1, the input size should be the same +# as memory size. In this case, we will output custom cells +def write_custom_mux_cells_to_file(custom_nlist, input_size, mem_size): + lines = [] + if (1 == mem_size): + assert(2 == input_size) + # Output a standard cell, currently we support HD cell MUX2 + lines.append("\tsky130_fd_sc_hd_mux2_1 sky130_fd_sc_hd_mux2_1_0(") + lines.append("\t .A1(in[0]),") + lines.append("\t .A0(in[1]),") + lines.append("\t .S(mem[0]),") + lines.append("\t .X(out[0])") + lines.append("\t );") + else: + assert(1 < mem_size) + assert(mem_size == input_size) + # Currently we support MUX2 and MUX3 custom cells + # - If the input size is an odd number, we will use + # - 1 MUX3 cell + # - a few MUX2 cells + if (1 == input_size % 2): + assert(3 <= input_size) + for line in generate_verilog_codes_custom_cell_mux3(0, 0): + lines.append(line) + for mux2_inst in range(int((input_size - 3) / 2)): + for line in generate_verilog_codes_custom_cell_mux2(3 + 2 * mux2_inst, mux2_inst): + lines.append(line) + # - If the input size is an even number, we will use + # - a few MUX2 cells + else: + assert (0 == input_size % 2) + for mux2_inst in range(int(input_size / 2)): + for line in generate_verilog_codes_custom_cell_mux2(2 * mux2_inst, mux2_inst): + lines.append(line) + + # Output lines to file + for line in lines: + custom_nlist.write(line + "\n") + +# Read line by line from template netlist +with open(args.template_netlist, "r") as wp: + template_nlist = wp.readlines() + # A flag for write the current line or skip + output_action = "copy" + input_size = 0 + mem_size = 0 + for line_num, curr_line in enumerate(template_nlist): + # If the current line satisfy the following conditions + # It should be modified and outputted to custom netlist + # Other lines can be directly copied to custom netlist + line2output = curr_line + # Once current line starts with a module definition + # Find the input size and memory size + if (curr_line.startswith("module ")): + input_size = int(re.findall("input(\d+)_mem(\d+)\(", curr_line)[0][0]) + mem_size = int(re.findall("input(\d+)_mem(\d+)\(", curr_line)[0][1]) + assert(input_size > 0) + assert(mem_size > 0) + # Change status indicating that we are now inside a module + output_action = "copy" + + # If a line contains the keyword TGATE + # we will bypass all the lines until reach the endmodule line + if (curr_line.startswith("\tTGATE TGATE")): + output_action = "skip" + + # Reaching the end of the current module + # Now output the custom cell instanciation + if (curr_line.startswith("endmodule")): + write_custom_mux_cells_to_file(custom_nlist, input_size, mem_size) + output_action = "copy" + + if ("skip" != output_action): + custom_nlist.write(line2output) + +custom_nlist.close() +logging.info("Done") From 443eb1271047ff5a6bbf4c3882cb5413815c6d0b Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 5 Dec 2020 21:17:59 -0700 Subject: [PATCH 3/3] [CI] Add test to CI --- .github/workflows/quick_test.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/quick_test.sh b/.github/workflows/quick_test.sh index d6d92dc..b3f3a05 100755 --- a/.github/workflows/quick_test.sh +++ b/.github/workflows/quick_test.sh @@ -12,6 +12,9 @@ set -e # - Run FPGA tasks to validate netlist generations python3 SCRIPT/repo_setup.py --openfpga_root_path ./OpenFPGA +# Post processing netlist to use custom cells +python3 HDL/common/custom_cell_mux_primitive_generator.py --template_netlist HDL/k4_N8_reset_softadder_caravel_io_FPGA_12x12_customhd_cc/SRC/sub_module/mux_primitives.v --output_verilog HDL/k4_N8_reset_softadder_caravel_io_FPGA_12x12_customhd_cc/SRC/sub_module/mux_primitives_hd.v + ############################################## # Generate wrapper HDL codes to bridge Caravel I/Os and FPGA I/Os python3 HDL/common/wrapper_lines_generator.py --template_netlist HDL/common/caravel_fpga_wrapper_hd_template.v --pin_assignment_file HDL/common/caravel_wrapper_pin_assignment_v1.0.json --output_verilog HDL/common/caravel_fpga_wrapper_hd_v1.0.v @@ -30,3 +33,4 @@ python3 TESTBENCH/common/post_pnr_wrapper_testbench_converter.py --post_pnr_test # Generate wrapper testbenches from template tesbenches for scan chain tests python3 TESTBENCH/common/post_pnr_wrapper_testbench_converter.py --post_pnr_testbench TESTBENCH/common/scff_test_post_pnr_v1.0.v --pin_assignment_file HDL/common/caravel_wrapper_pin_assignment_v1.0.json --wrapper_testbench TESTBENCH/k4_N8_caravel_io_FPGA_12x12_fdhd_cc/postpnr/verilog_testbench/scff_test_post_pnr_wrapper.v python3 TESTBENCH/common/post_pnr_wrapper_testbench_converter.py --post_pnr_testbench TESTBENCH/common/scff_test_post_pnr_v1.1.v --pin_assignment_file HDL/common/caravel_wrapper_pin_assignment_v1.1.json --wrapper_testbench TESTBENCH/k4_N8_reset_softadder_caravel_io_FPGA_12x12_fdhd_cc/postpnr/verilog_testbench/scff_test_post_pnr_wrapper.v +