blob: 1b1ddf26da515e926db8a5d6579208d473f2f153 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2020 The Skywater PDK Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Go through `.v` line by line and correct port naming & case
#
#
import argparse
import re
import os
import json
from pathlib import Path
from collections import defaultdict
from common import files, lib_extract_from_path, mod_extract_from_path, version_extract_from_path, convert_libname, strip_strength
debug = False
debug_print = lambda x: print(x) if debug else 0
indent = " " * 4
sourcetodests = defaultdict(list)
desttosource = defaultdict(list)
# Regular expressions
all_lib_re = "(s|scs)8(hd|hdll|hs|ms|ls|hvl|lp|iom0s8|x|_esd|_rd|_rf2|phirs_10r|phirs_10r_old)?"
Copyright_header = ('// Copyright 2020 The Skywater PDK Authors\n'
'//\n'
'// Licensed under the Apache License, Version 2.0 (the "License");\n'
'// you may not use this file except in compliance with the License.\n'
'// You may obtain a copy of the License at\n'
'//\n'
'// https://www.apache.org/licenses/LICENSE-2.0\n'
'//\n'
'// Unless required by applicable law or agreed to in writing, software\n'
'// distributed under the License is distributed on an "AS IS" BASIS,\n'
'// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
'// See the License for the specific language governing permissions and\n'
'// limitations under the License.\n'
'//\n')
def remap_path(old_path, input_dir, output_dir):
"""
>>> remap_path('skywater-src-nda/scs8ms/V0.0.0/verilog/scs8ms_a2bb2oi_2.v', 'skywater-src-nda', 'tmp')
'tmp/skywater-pdk/libraries/sky130_fd_sc_ms/V0.0.0/cells/a2bb2oi/sky130_fd_sc_ms__a2bb2oi_2.v'
>>> remap_path('skywater-src-nda/openfpga/Skywater_130/SRC/routing/sb_22_22.v', '../skywater-src-nda', 'tmp')
'tmp/skywater-pdk/libraries/openfpga/Skywater_130/SRC/routing/sb_22_22.v'
>>> remap_path('skywater-src-nda/s8iom0s8/V0.2.1/oa/s8iom0s8/s8iom0s8_top_hvclamp_wopad/behavioral_tmax/verilog.v', 'skywater-src-nda', 'tmp')
'tmp/skywater-pdk/libraries/sky130_fd_io/V0.2.1/cells/top_hvclamp_wopad/sky130_fd_io__top_hvclamp_wopad.behavioral_tmax.v'
>>> remap_path('skywater-src-nda/s8iom0s8/V0.1.0/oa/s8iom0s8/s8iom0s8_analog_pad/behavioral/verilog.v', 'skywater-src-nda', 'tmp')
'tmp/skywater-pdk/libraries/sky130_fd_io/V0.1.0/cells/analog_pad/sky130_fd_io__analog_pad.behavioral.v'
>>> remap_path('../skywater-src-nda/s8/V1.0.1/VirtuosoOA/libs/tech/csw_p/uni_verilog/verilog.v', '../skywater-src-nda', 'tmp')
'tmp/skywater-pdk/libraries/sky130_fd_pr_base/V1.0.1/tech/csw_p/uni_verilog/verilog.v'
>>> remap_path('skywater-src-nda/scs8ms/V0.0.0/verilog/scs8ms_a2BB2oi_2.v', 'skywater-src-nda', 'tmp')
'tmp/skywater-pdk/libraries/sky130_fd_sc_ms/V0.0.0/cells/a2bb2oi/sky130_fd_sc_ms__a2bb2oi_2.v'
>>> remap_path('/home/fullpath/Documents/skywater-src-nda/scs8hvl/V0.0.0/verilog/scs8hvl_pg_U_DF_P.v', 'skywater-src-nda', 'tmp')
'tmp/skywater-pdk/libraries/sky130_fd_sc_hvl/V0.0.0/cells/pg_u_df_p/sky130_fd_sc_hvl__pg_u_df_p.v'
>>> remap_path('../skywater-src-nda/scs8hs/V0.0.0/verilog/U_DF_P_R_NO_SLEEPB_pg.v', 'skywater-src-nda' ,'tmp')
'tmp/skywater-pdk/libraries/sky130_fd_sc_hs/V0.0.0/cells/u_df_p_r_no_sleepb_pg/sky130_fd_sc_hs__u_df_p_r_no_sleepb_pg.v'
>>> remap_path('../skywater-src-nda/s8/V1.2.1/VirtuosoOA/libs/tech/csw_p/verilog/verilog.v', 'skywater-src-nda' ,'tmp')
'tmp/skywater-pdk/libraries/sky130_fd_pr_base/V1.2.1/tech/csw_p/verilog/verilog.v'
>>> remap_path('../skywater-src-nda/scs8lp/V0.0.0/verilog/scs8lpa_U_DFB_SETDOM.v', 'skywater-src-nda' ,'tmp')
'tmp/skywater-pdk/libraries/sky130_fd_sc_lp/V0.0.0/cells/u_dfb_setdom/sky130_fd_sc_lp__u_dfb_setdom.v'
>>> remap_path('../skywater-src-nda/s8/V1.0.0/VirtuosoOA/libs/tech/short/verilog/verilog.v', 'skywater-src-nda' ,'tmp')
'tmp/skywater-pdk/libraries/sky130_fd_pr_base/V1.0.0/tech/short/verilog/verilog.v'
>>> remap_path('../skywater-src-nda/scs8hdll/V1.0.0/verilog/scs8hdll.v', 'skywater-src-nda' ,'tmp')
'tmp/skywater-pdk/libraries/sky130_fd_sc_hdll/V1.0.0/cells/sky130_fd_sc_hdll/sky130_fd_sc_hdll.v'
"""
global debug
old_path = old_path.replace('scs8lpa', 'scs8lp')
parent_dir = str(input_dir).split("/")[-1]
output_dir = str(output_dir)
lib, mod = mod_extract_from_path(old_path)
new_lib = convert_libname(lib)
try:
ver = "V" + ".".join([str(v) for v in version_extract_from_path(old_path)])
except TypeError:
ver = ""
if lib != None and ver != "" and lib != "???":
if mod == None:
mod = lib
rest, f_name= os.path.split(old_path)
if mod not in old_path:
# Letter case was changed - primitive declaration of definition
temp = mod
mod = mod.lower()
f_name = f_name.lower().replace(temp, mod)
if f_name == "verilog.v":
clean_mod = strip_strength(mod)
_, d_name = os.path.split(rest)
old_path = f"{output_dir}/skywater-pdk/libraries/{lib}/{ver}/cells/{clean_mod}/{lib}__{mod}.{d_name}.v"
else:
if f_name.find(lib) == -1:
f_name = lib + "__" + f_name
else:
#change lib-mod separator from _ to __
if f_name.find('_') != -1:
f_name = f_name[:len(lib)] + '__' + f_name[len(lib)+1:]
clean_mod = re.sub(r'_([0-9]{1,2}|m|lp|lp2)$', '', mod)
old_path = f"{output_dir}/skywater-pdk/libraries/{lib}/{ver}/cells/{clean_mod}/{f_name}"
else:
index = old_path.find(parent_dir) + len(parent_dir)
old_path = output_dir +"/skywater-pdk/libraries" + old_path[index:]
old_path = old_path.replace("/s8iom0s8/", "/sky130_fd_io/")
old_path = old_path.replace("/s8/", "/sky130_fd_pr_base/")
old_path = old_path.replace("/VirtuosoOA/libs", "")
old_path = old_path.replace("cds_", "")
if lib != None and new_lib != None:
new_path = old_path.replace(lib, new_lib)
else:
new_path = old_path
return new_path
def correct_negation(portname):
""" Replace '\w*?[BN]' with '\w*?_[BN]' which is the default symbolator notation.
Skips if portname is shorter then 2 chars
>>> correct_negation("IN")
'IN'
>>> correct_negation("N")
'N'
>>> correct_negation("GOOD_N")
'GOOD_N'
>>> correct_negation("SETB")
'SET_B'
>>> correct_negation("LSKN")
'LSK_N'
>>> correct_negation("VEN")
'VEN'
>>> correct_negation("CLKN")
'CLK_N'
>>> correct_negation("GATEN")
'GATE_N'
"""
portname = portname.replace(" ", "")
if len(portname) > 1 and portname[0] != 'V' and portname[-2:] not in ("_N", "_B", "IN"):
if portname[-1] == 'N':
portname = portname[:-1] + "_N"
elif portname[-1] == 'B':
portname = portname[:-1] + "_B"
return portname
def portstr_to_correct_port(port_str):
""" Takes string describing ports and returns list of tuples: 'type', 'name', 'width', 'old_name'.
>>> portstr_to_correct_port("input X")
[('input', 'X', '', 'X')]
>>> portstr_to_correct_port("input sleep")
[('input', 'SLEEP', '', 'sleep')]
>>> portstr_to_correct_port("input sleep, x, y")
[('input', 'SLEEP', '', 'sleep'), ('input', 'X', '', 'x'), ('input', 'Y', '', 'y')]
>>> portstr_to_correct_port("input sthn")
[('input', 'STH_N', '', 'sthn')]
>>> portstr_to_correct_port("output 5 x")
[('output', 'X', '5', 'x')]
>>> portstr_to_correct_port("input k,")
[('input', 'K', '', 'k')]
>>> portstr_to_correct_port("input k;")
[('input', 'K', '', 'k')]
>>> portstr_to_correct_port(" output Q; ")
[('output', 'Q', '', 'Q')]
>>> portstr_to_correct_port("input CK")
[('input', 'CLK', '', 'CK')]
"""
output = []
port_str = port_str.strip()
port_str = port_str.rstrip(";")
if port_str.find(",") != -1:
parts = port_str.split(" ")
parts = [x.strip() for x in parts if x != ""]
type = parts[0]
width = ""
names = "".join(parts[1:]).split(",")
for name in names:
if name != '':
if name in ('CK','ck'):
new_name = 'CLK'
else:
new_name = correct_negation(name.upper())
output.append((type, new_name, width, name))
else:
fields= port_str.lstrip().split(" ")
fields = [x for x in fields if x != ""]
if len(fields) == 3:
type = fields[0]
name = fields[2]
width = fields[1]
else:
type = fields[0]
name = fields[1]
width = ""
if name in ('CK','ck'):
new_name = 'CLK'
else:
new_name = correct_negation(name.upper())
output.append((type, new_name, width, name))
return output
def remove_all_comments(contents):
# remove multiblock '/* */' comments and '//*******...*/' lines
contents = re.sub(r"(?s)/+\*.*?\*/", "", contents)
# remove comments from line continuation
contents = re.sub(r"(?<=[;\)])\s*//.*?[\n]", "\n", contents)
contents = re.sub(r"(?s)(?<=[\n ])table\s*","table\n", contents)
# remove all commented lines, unless previous line starts with 'table'
contents = re.sub(r"(?s)(?<!table[\n])//.*?(?=[\n])", "", contents)
return contents
def indent_section(contents, match, indent_last = False):
match_string = contents[match.start():match.end()]
lines = match_string.split("\n")
new_string = lines[0]
if indent_last:
lines_to_indent = lines[1:]
else:
lines_to_indent = lines[1:-1]
for line in lines_to_indent:
new_string += "\n" + indent + line
if not indent_last:
new_string += "\n" + lines[-1]
return new_string
def indent_and_format_table(contents, match):
make_table = lambda line : "".join([x.center(field_width) if x not in (';',':') else x for x in line.strip().split()])
debug_print(match)
match_string = contents[match.start():match.end()]
lines = match_string.split("\n")
out = lines[0]
if lines[1].strip()[:2] == '//':
# First line is a comment
field_width = max([len(x) for x in re.split(r"[: ]", lines[1])]) + 3
out += "\n// " + make_table(lines[1][2:])
else:
field_width = 5
out += "\n" + indent + make_table(lines[1])
for line in lines[2:-1]:
out += "\n" + indent + make_table(line)
out += "\n" + lines[-1] + "\n"
return out
def basic_indent_fix(contents):
regexp_replace = lambda old, new : re.sub(old, new, contents)
rev_iter = lambda iter : [x for x in iter][::-1]
# remove whitespace around ';' on line ending
contents = regexp_replace(r"\s*;[ ]*", ";")
# don't allow multiple spaces
contents = regexp_replace(r"(?<!\n)[ \t\r\f\v]{2,}", " ")
# replace tabs with space
contents = regexp_replace(r"[\t]", " ")
# # remove all indent
contents = regexp_replace(r"(?<=[\n])\s+", "")
contents = regexp_replace(r"[ \t]*endmodule", "endmodule")
# remove trailing spaces
contents = regexp_replace(r"[ \t\v]*(?=$)", "")
# remove space before ','
contents = regexp_replace(r"[ ]*,", ",")
# don't allow newline after ','
contents = regexp_replace(r", *[\n]", ", ")
# don't allow 2 statements in one line
contents = regexp_replace(r"; *(?![\n])", ";\n")
# don't break strings
contents = regexp_replace(r"(?s)\\[\n] *", " ")
# remove empty lines
contents = regexp_replace(r"(?s)\n[ \t\v]*(?=[\n])", "\n")
# begin always in newline
contents = regexp_replace(r"(?<![\n])[ ]?begin", "\nbegin")
# add newline before initial
contents = regexp_replace(r"(?<=[\n])initial[\n]", "\ninitial\n")
# add newline before new 'ifdef'
contents = regexp_replace(r"(/s)(?<!`else)[\n]`ifdef", "\n\n`ifdef")
# add newline after '`endif'
contents = regexp_replace(r"`endif(?![\n](`e|, ))", "`endif\n")
# add newline before always
contents = regexp_replace(r"always @", "\nalways @")
# add newline after '^);
contents = regexp_replace(r"(?<=[\n])\);", ");\n")
# add newline if brackets contain newline
contents = regexp_replace(r"(?<=(= |\w{2}))\((?P<guts>.*?[^\);])[\n]", "(\n\g<guts>\n")
# add newline if module brackets contain newline
contents = regexp_replace(r"module (?P<mod_decl>.+?) \( *(?P<brac>[^\)\n]+)[\n]", "module \g<mod_decl> (\n\g<brac>")
# add newline between end and buildin decl
contents = regexp_replace(r"(?s)end\s*[\n](?=(wire|`endif|endmodule))", "end\n\n")
# all preprocessor directives should start on new line
contents = regexp_replace(r"(?<![\n])\s*(?P<directive>`\w*)", "\n\g<directive>")
# indent if
contents = regexp_replace(r"if\s+\((?P<guts>.*?)\)\s*[\n](?!(beg|if ))", "if (\g<guts>)\n ")
# indent else
contents = regexp_replace(r"(?<!`)else\s*[\n](?!begin)", "else \n ")
table_match = re.finditer(r"(?s)table.*?[\n]endtable", contents)
for match in rev_iter(table_match):
contents = contents[:match.start()] + indent_and_format_table(contents, match) + contents[match.end():]
linecontin_match = re.finditer(r"(?s)(?<=[\n])[^\n]*?\([\n].*?;", contents)
for match in rev_iter(linecontin_match):
contents = contents[:match.start()] + indent_section(contents, match, True) + contents[match.end():]
# indent everything inside specify
specify_match = re.finditer(r"(?s)specify\s+.*?[\n]endspecify", contents)
for match in rev_iter(specify_match):
contents = contents[:match.start()] + "\n" + indent_section(contents, match) + contents[match.end():]
contents = indent_beginend(contents)
# indent everything inside modules
modules_match = re.finditer(r"(?s)(?P<type>(module|primitive))\s+.*?[\n]end(?P=type)", contents)
for match in rev_iter(modules_match):
contents = contents[:match.start()] + "\n" + indent_section(contents, match) + contents[match.end():]
# unindent preprocessor directives and ');'
contents = regexp_replace(r"(?<=[\n])\s+(?=[`)])", "")
# additional nl before endmodule
contents = regexp_replace(r"endmodule", "\nendmodule")
return contents
def indent_lines(lines, start, end):
"""
>>> indent_lines(["a","b","c","d","e"], 1, 3)
['a', ' b', ' c', 'd', 'e']
"""
for x in range(start,end):
lines[x] = indent + lines[x]
return lines
def indent_beginend(contents):
"""
>>> indent_beginend("\\n always\\n begin\\nif blabla\\nbegin\\nsthsth\\nend\\nend")
'\\n always\\n begin\\n if blabla\\n begin\\n sthsth\\n end\\nend'
>>> indent_beginend("\\n always\\n begin : SMTH\\nif blabla\\nbegin\\nsthsth\\nend\\nend")
'\\n always\\n begin : SMTH\\n if blabla\\n begin\\n sthsth\\n end\\nend'
"""
lines = contents.split("\n")
matches = []
for ln_no, line in enumerate(lines):
stripped = line.split(":")[0].strip()
if stripped in ("begin", "end"):
matches.append((stripped, ln_no))
for i in reversed(range(len(matches)-1)):
if i == len(matches) - 1:
continue
if matches[i][0] != matches[i+1][0]:
lines = indent_lines(lines, matches[i][1]+1, matches[i+1][1])
if i < len(matches)-2:
matches = matches[:i] + (matches[i+2:])
else:
matches = matches[:i]
return "\n".join(lines)
def canonicalize_case(line, lib):
"""
>>> canonicalize_case("primitive scs8ls_pg_u_dfb(Q, S, R, CLK, D);", 'scs8hs')
'primitive scs8ls__pg_u_dfb (Q, S, R, CLK, D);'
>>> canonicalize_case("U_DL_P_NO_pg ( buf_Q, D_delayed, gate, notifier, vpwr, vgnd );", 'scs8hs')
'scs8hs__u_dl_p_no_pg ( buf_Q, D_delayed, gate, notifier, vpwr, vgnd );'
>>> canonicalize_case("U_MUX_2_1 ( mux_out, D, SCD, SCE );", 'scs8hs')
'scs8hs__u_mux_2_1 ( mux_out, D, SCD, SCE );'
>>> canonicalize_case("U_MUX_2_1_INV ( mux_out, D, SCD, SCE );", 'scs8hs')
'scs8hs__u_mux_2_1_inv ( mux_out, D, SCD, SCE );'
>>> canonicalize_case("module scs8hs_dlxtn_2 ( Q, D, GATEN, vpwr, vgnd );", 'scs8hs')
'module scs8hs__dlxtn_2 ( Q, D, GATEN, vpwr, vgnd );'
>>> canonicalize_case("primitive scs8hd_pg_U_VGND ( UDP_OUT, UDP_IN, VGND );", 'scs8hs')
'primitive scs8hd__pg_u_vgnd ( UDP_OUT, UDP_IN, VGND );'
>>> canonicalize_case("primitive U_MUX_2_1 (X, A0, A1, S);", 'scs8hs')
'primitive scs8hs__u_mux_2_1 (X, A0, A1, S);'
>>> canonicalize_case("primitive U_DL_P_NO_pg ( buf_Q, D_delayed, gate, notifier, vpwr, vgnd );", 'scs8hs')
'primitive scs8hs__u_dl_p_no_pg ( buf_Q, D_delayed, gate, notifier, vpwr, vgnd );'
>>> canonicalize_case("U_DL_P_NO_pg #0.0001 ( buf_Q, D_delayed, gate, notifier, vpwr, vgnd );", 'scs8hs')
'scs8hs__u_dl_p_no_pg #0.0001 ( buf_Q, D_delayed, gate, notifier, vpwr, vgnd );'
>>> canonicalize_case("primitive scs8hvl_pg_U_DF_P ( Q, D, CP );", 'scs8hvl')
'primitive scs8hvl__pg_u_df_p ( Q, D, CP );'
>>> canonicalize_case(" module short ( Q, D, CP );", 'scs8hvl')
' module scs8hvl__short ( Q, D, CP );'
>>> canonicalize_case("module scs8ls_bufinv_16 (\\n Q, D, CP );", 'scs8ls')
'module scs8ls__bufinv_16 (\\n Q, D, CP );'
"""
# Assert here is space between module name and '('
line = re.sub(r'(?<=\w{3})\(', ' (', line)
libname_search = re.search(fr"{all_lib_re}_", line)
nonlibmod_search = re.search(r"U_[A-Za-z0-9_]+? *(?=(\(|#))", line)
if libname_search is not None:
name_end = line.find(" ", libname_search.end())
mod = '_' + line[libname_search.end():name_end]
if not mod.islower():
mod = mod.lower()
return line[:libname_search.end()] + mod + line[name_end:]
elif nonlibmod_search is not None:
mod = line[nonlibmod_search.start():nonlibmod_search.end()]
if not mod.islower():
return line[:nonlibmod_search.start()] + lib + '__' + mod.lower() + line[nonlibmod_search.end():]
else:
mod = [x for x in line.split(" ") if x != ""][1]
mod_index = line.find(mod)
lib_in_name = mod.find('lib') != -1
if not lib_in_name:
old_mod = mod
mod = lib + '__' + mod
line = line.replace(old_mod, mod)
if not mod.islower():
return line[:mod_index] + mod.lower() + line[mod_index+len(mod):]
return line
def rename_modules(contents, lib):
new_lib_name = convert_libname(lib)
contents = contents.replace('scs8lpa', 'scs8lp')
if new_lib_name != None:
contents = contents.replace(lib + '__', new_lib_name + '__')
# change standard cells naming
contents = re.sub(r"scs8(?=[mhl][vdps]__)", "sky130_fd_sc_", contents)
return contents, new_lib_name
def main(verilog_file, out_file=None):
re_port_decl = re.compile(r'(input|output|inout) .*?(;|$)')
re_mod_def = re.compile(fr"^\s*(primitive|module)\s+{all_lib_re}_\w+.*?\(")
re_mod_def_cap = re.compile("^\s*(primitive|module) U_.*? ?\(")
re_mod_def_fd_pr = re.compile("^\s*module\s*\w+.*?\(")
re_mod_decl = re.compile(fr"^\s*{all_lib_re}_")
re_prim_decl = re.compile(fr"^\s*{all_lib_re}_\w+([A-Z0-9_]+[a-z_]*)?\s+(#[0-9\.]+\s)?\([\w$', ]+\).*?;")
re_prim_decl_cap = re.compile(fr"^\s*[A-Z1-9_]+([a-z_]*)?\s+(\#[0-9\.]*?\s+)?\([\w$', ]+\).*?\s?;")
with open(verilog_file, 'r') as in_f:
contents = in_f.read()
file_dir, filename = os.path.split(verilog_file)
contents = contents.replace('scs8lpa', 'scs8lp')
lib = lib_extract_from_path(verilog_file)
out = ""
ports_to_change = []
for ln_no, line in enumerate(contents.split("\n")):
mod_decl = re_mod_decl.search(line)
mod_def = re_mod_def.search(line)
mod_def_cap = re_mod_def_cap.search(line)
prim_decl_cap = re_prim_decl_cap.search(line)
prim_decl = re_prim_decl.search(line)
# Only this lib contains cell with no libname in name
if verilog_file.find('s8phirs_10r') != -1:
mod_def_fd_pr = re_mod_def_fd_pr.search(line)
all_regex = [mod_decl, mod_def, mod_def_cap, prim_decl, prim_decl_cap, mod_def_fd_pr]
else:
all_regex = [mod_decl, mod_def, mod_def_cap, prim_decl, prim_decl_cap]
# All letters in mod name should have the same case (all upper or all lower)
if any(all_regex):
new_line = canonicalize_case(line, lib)
if new_line != line:
debug_print(f"Line {ln_no+1}: Canonicalized!! Change:\n--\t| {line}\n++\t| {new_line}")
debug_print("m\t| mod_decl|mod_def|mod_def_cap|prim_decl|prim_decl_cap")
debug_print(f"\t| {mod_decl!=None}{' '*3}|{mod_def!=None}{' '*2}|{mod_def_cap!=None}{' '*8}|{prim_decl!=None}{' '*4}|{prim_decl_cap!=None}")
line = new_line
#if more then one statement in module/primitive def/decl line - split it
if line.count(";") > 1:
debug_print(f"Line {ln_no+1}: Line split!! Result:")
first_line = line.find(";") + 1
debug_print(f"\t| {line[:first_line]}\n\t| {line[first_line:]}")
out += line[:first_line] + "\n"
line = line[first_line:]
port_decl = re_port_decl.search(line)
# All ports names should be uppercase, negation should be consistent with symbolator requirements
if port_decl:
if line[:2]=="//" or re.search(r'["$%?=\\]', line):
debug_print(f"Line {ln_no+1} : Discarded as not being port decl:\n\t| {line}")
out += line + "\n"
continue
line = re.sub(r"(?<!^)\s*//.*", "", line)
port_decl_end = min(port_decl.end(), len(line))
if port_decl_end < port_decl.start():
continue
sep = line[port_decl_end-1]
old_decl = line[port_decl.start():port_decl_end ]
ports = portstr_to_correct_port(old_decl)
ports_to_change += [(x[1],x[3]) for x in ports if x[1] != ""]
if len(ports)==1:
port = ports[0]
new_decl = f"{port[0]} {port[2]+' ' if port[2]!='' else ''}{port[1]}{sep if sep in (',', ';') else ''}"
else:
assert ports[0][2]==''
new_decl = f"{ports[0][0]} {', '.join([x[1] for x in ports])}{sep if sep in (',', ';') else ''}"
if old_decl == new_decl:
pass
else:
debug_print(f"Line {ln_no+1} :Port decl changed!!")
debug_print(f"\t| '{old_decl}' >> '{new_decl}'")
line = line[:port_decl.start()] + new_decl + line[port_decl.end():]
out += line + "\n"
for new, old in ports_to_change:
if new==old:
continue
try:
out = re.sub(fr"(?<=\W){old}(?=\W)", new, out)
except re.error:
print(f"Regexp '(?<=\W){old}(?=\W)' failed on pair: old = {old}, new = {new}")
exit(1)
if lib is None:
lib = "X"*20
out, _ = rename_modules(out, lib)
out = remove_all_comments(out)
out = basic_indent_fix(out)
if lib != '???':
out = re.sub(rf"{lib}_(?=[^_])", f"{convert_libname(lib)}__", out)
out = out.replace('S8IOM0S8', 'SKY130_FD_IO')
out = Copyright_header + out
if out_file is not None:
dest_dir = os.path.split(out_file)[0]
if not os.path.isdir(dest_dir):
os.makedirs(dest_dir)
with open(out_file, 'w') as out_f:
out_f.write(out)
else:
pass
print(">>> Output:\n"+"_"*60+ "\n" + out)
if __name__=="__main__":
import doctest
fails, _ = doctest.testmod()
if fails>0:
exit(1)
else:
print("Tests Passed")
parser = argparse.ArgumentParser()
parser.add_argument(
"input",
help="The path to the source directory/file",
type=Path)
parser.add_argument(
"output",
help="The path to the output directory",
type=Path)
parser.add_argument(
"sourcetodests",
help="output file mapping input to ouput",
type=Path)
args = parser.parse_args()
if not os.path.isdir(args.input):
debug = True
main(str(args.input))
else:
files = sorted(args.input.rglob('*.v'))
for f in files:
f = str(f)
skipped = ['stubs', 'openfpga', 'sram', 's8fmlt', 'osu130']
if any(word in f for word in skipped):
continue
print(f)
out_f = remap_path(f, args.input, args.output)[:-2] + ".full.v"
main(f, out_f)
sourcetodests[f].append(out_f)
desttosource[out_f].append(f)
with open(args.sourcetodests, 'w') as srctodst:
json.dump(sourcetodests, srctodst, indent=2)
with open(args.sourcetodests.parent / Path("reverse-" + args.sourcetodests.name), 'w') as dsttosrc:
json.dump(desttosource, dsttosrc, indent=2)