| #!/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. |
| |
| |
| import re |
| import os |
| import argparse |
| from pathlib import Path |
| from common import convert_libname, lib_extract_from_path, files |
| from verilog2simple import print_cont |
| from verilog2full import Copyright_header |
| |
| debug = False |
| |
| debug_print = lambda x: print(x) if debug else 0 |
| |
| def main(in_file, out_file = None): |
| |
| with open(in_file, 'r') as in_f: |
| contents = in_f.read() |
| |
| if contents.find('endmodule') == -1 and contents.find('endprimitive') == -1: |
| return |
| lib = lib_extract_from_path(in_file) |
| |
| # user defined primitives to modules |
| contents = re.sub(r"primitive ", "module ", contents) |
| contents = re.sub(r"endprimitive", "endmodule", contents) |
| # # remove alias modules |
| # contents = re.sub(r"(?s)module\s*\w*alias\(.*[\n]endmodule", "", contents) |
| # contents = re.sub(r"(?<!^)\s*\\.*?[\n]", "", contents) |
| |
| re_mod_def_libname = re.compile(fr"\s*module\s*{lib}__") |
| |
| contents = re.sub(r'(?<=,) *(?P<type>(input|outpu|inout))', '\n \g<type>', contents) |
| |
| lines = contents.split("\n") |
| in_module = False |
| in_module_header = False |
| in_port_def = False |
| ports_decl = [] |
| module_sep = re.compile(r"[\t ]*,[\t\n ]*") |
| module_regexp = re.compile(r"^\W*module.*?") |
| port_def_regexp = re.compile(r"^\s*(,\s*)?(inout|input|output) ") |
| output = "" |
| for line in lines: |
| module_def = module_regexp.search(line) |
| module_end = line.find("endmodule") != -1 |
| port_def = port_def_regexp.search(line) |
| if port_def: |
| p = re.sub(r"(,|;|\);)$", "", line.strip()) |
| ports_decl.append(p) |
| debug_print(f"# port found: '{p}'") |
| if any([module_end, |
| port_def, |
| module_def]): |
| output += line + "\n" |
| # hack- remove 'addr' input definition from s8fmlt files |
| # this input is not defined in module header |
| output = re.sub(r"[\n].* addr;", "", output) |
| # output = re.sub(r"\^\\n", "", output) |
| # strip empty ifdef/else/endif |
| output = re.sub(r"[`]ifdef.*[\n]([`]else\W*(//.*)?[\n])?[`]endif\W*(//.*)?[\n]", "", output) |
| # move commas from begining of line to end of previous |
| output = re.sub(r"(?s)[\n],", r",\n", output) |
| #move closing bracket to end of line |
| output = re.sub(r"\s*[\n]\s*\);", ");\n", output) |
| # remove empty modules definition |
| output = re.sub(r"(?s)\(\* blackbox \*\)\s*[\n]module\s*[a-z0-9_]*\s\(\s*\);\s*[\n]\s*endmodule\s*[\n]", "", output) |
| |
| # split some weird ports_decl |
| temp = ports_decl |
| ports_decl = [] |
| for x in temp: |
| if len(re.findall(r'(input|output|inout)', x)) > 1: |
| ports_decl.extend([y.strip() for y in x.split(',')]) |
| else: |
| ports_decl.append(x) |
| |
| port_names = [] |
| for x in ports_decl : |
| x = re.sub(r',? ?(input|output|inout) ([\[\]:0-9]+)?', '', x) |
| if x.find(',') != -1: |
| for name in x.split(','): |
| port_names.append(name.strip()) |
| else: |
| port_names.append(x.strip()) |
| |
| # assumes one module per file - which is now asserted by splitting |
| new_output = '\n(* blackbox *)\n' |
| new_output += re.search(r'module\s*\w*', output)[0] |
| new_output += ' (\n' |
| for x in port_names[:-1]: |
| new_output += ' ' + x + ',\n' |
| new_output += ' ' + port_names[-1] + ');\n' |
| for x in ports_decl: |
| new_output += ' ' + x + ';\n' |
| new_output += 'endmodule\n' |
| |
| |
| |
| output = Copyright_header + new_output |
| |
| if out_file is not None: |
| with open(out_file, 'w') as out_f: |
| out_f.write(output) |
| else: |
| print(">>> Output:") |
| print_cont(output) |
| |
| def create_mod_header(module_header): |
| module_header = [x.replace("\t", "") for x in module_header if x.strip() != ""] |
| return ",\n ".join(module_header) |
| |
| |
| if __name__ == "__main__": |
| |
| import doctest |
| fails, _ = doctest.testmod() |
| if fails>0: |
| exit() |
| else: |
| print("Tests Passed") |
| parser = argparse.ArgumentParser() |
| parser.add_argument( |
| "input", |
| help="The path to the source directory/file. If file is passed it will run in debug mode with no output file" + |
| "If dir is passed it will find '.full.v' files generate '.simple.v' in the same directory", |
| type=Path) |
| args = parser.parse_args() |
| if not os.path.isdir(args.input): |
| debug = True |
| main(str(args.input)) |
| else: |
| files = args.input.rglob('*.full.v') |
| for f in files: |
| f = str(f) |
| print(f) |
| main(f, f[:-7] + ".blackbox.v") |
| |
| |