Merge pull request #56 from lnis-uofu/xt_dev

Python script to adapt OpenFPGA netlist to use custom MUX cells
This commit is contained in:
Laboratory for Nano Integrated Systems (LNIS) 2020-12-05 22:11:36 -07:00 committed by GitHub
commit f572be8fc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 197 additions and 2 deletions

View File

@ -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

View File

@ -43,6 +43,18 @@
10e-12
</delay_matrix>
</circuit_model>
<circuit_model type="inv_buf" name="sky130_fd_sc_hd__buf_1" prefix="sky130_fd_sc_hd__buf_1" is_default="false" verilog_netlist="${SKYWATER_OPENFPGA_HOME}/PDK/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/buf/sky130_fd_sc_hd__buf_1.v">
<design_technology type="cmos" topology="buffer" size="1"/>
<device_technology device_model_name="logic"/>
<port type="input" prefix="in" lib_name="A" size="1"/>
<port type="output" prefix="out" lib_name="X" size="1"/>
<delay_matrix type="rise" in_port="in" out_port="out">
10e-12
</delay_matrix>
<delay_matrix type="fall" in_port="in" out_port="out">
10e-12
</delay_matrix>
</circuit_model>
<circuit_model type="inv_buf" name="sky130_fd_sc_hd__buf_2" prefix="sky130_fd_sc_hd__buf_2" is_default="false" verilog_netlist="${SKYWATER_OPENFPGA_HOME}/PDK/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/buf/sky130_fd_sc_hd__buf_2.v">
<design_technology type="cmos" topology="buffer" size="1" num_level="2" f_per_stage="2"/>
<device_technology device_model_name="logic"/>
@ -160,7 +172,7 @@
<circuit_model type="mux" name="mux_2level" prefix="mux_2level" dump_structural_verilog="true">
<design_technology type="cmos" structure="multi_level" num_level="2" add_const_input="true" const_input_val="1" local_encoder="true"/>
<input_buffer exist="false"/>
<output_buffer exist="true" circuit_model_name="sky130_fd_sc_hd__inv_1"/>
<output_buffer exist="true" circuit_model_name="sky130_fd_sc_hd__buf_1"/>
<pass_gate_logic circuit_model_name="TGATE"/>
<port type="input" prefix="in" size="1"/>
<port type="output" prefix="out" size="1"/>
@ -169,7 +181,7 @@
<circuit_model type="mux" name="mux_2level_tapbuf" prefix="mux_2level_tapbuf" dump_structural_verilog="true">
<design_technology type="cmos" structure="multi_level" num_level="2" add_const_input="true" const_input_val="1" local_encoder="true"/>
<input_buffer exist="false"/>
<output_buffer exist="true" circuit_model_name="sky130_fd_sc_hd__inv_4"/>
<output_buffer exist="true" circuit_model_name="sky130_fd_sc_hd__buf_4"/>
<pass_gate_logic circuit_model_name="TGATE"/>
<port type="input" prefix="in" size="1"/>
<port type="output" prefix="out" size="1"/>

View File

@ -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")