mirror of https://github.com/lnis-uofu/SOFA.git
[HDL] Add python script to adapt OpenFPGA MUX primitives to use custom cells
This commit is contained in:
parent
6039ae92ca
commit
22f2b3aa90
|
@ -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")
|
Loading…
Reference in New Issue