| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright 2020 The SkyWater PDK Authors. |
| # |
| # Use of this source code is governed by the Apache 2.0 |
| # license that can be found in the LICENSE file or at |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| |
| import json |
| import os |
| import pprint |
| import re |
| import sys |
| import textwrap |
| |
| |
| copyright_header = """\ |
| // 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. |
| // |
| // SPDX-License-Identifier: Apache-2.0 |
| """ |
| |
| from skywater_pdk.utils import OrderedEnum |
| |
| class PortGroup(OrderedEnum): |
| DATA_IN_CHAR = 10 # A, A1, D, etc |
| DATA_IN_WORD = 11 # IN |
| |
| DATA_OUT_CHAR = 20 # X, Y, Q |
| DATA_OUT_WORD = 21 # OUT |
| |
| DATA_IO_CHAR = 30 # Inout? |
| DATA_IO_WORD = 31 # Inout? |
| |
| DATA_CONTROL = 40 # SET / RESET / etc |
| |
| SCAN_CHAIN = 44 # SCD, SCE, etc |
| |
| CLOCK = 50 # Clock |
| CLOCK_CONTROL = 51 # Clock enable |
| |
| POWER_CONTROL = 78 # SLEEP |
| POWER_OTHER = 79 # KAPWR |
| POWER_POSITIVE = 80 # VPWR |
| POWER_NEGATIVE = 81 # VGND |
| |
| @property |
| def type(self): |
| pg = self |
| if pg in (self.DATA_IN_CHAR, self.DATA_IN_WORD, |
| self.DATA_OUT_CHAR, self.DATA_OUT_WORD, |
| self.DATA_IO_CHAR, self.DATA_IO_WORD,): |
| return 'data' |
| if pg in (self.DATA_CONTROL,): |
| return 'control' |
| if pg in (self.CLOCK, self.CLOCK_CONTROL,): |
| return 'clocking' |
| if pg in (self.SCAN_CHAIN,): |
| return 'scanchain' |
| if pg in (self.POWER_CONTROL, self.POWER_OTHER, |
| self.POWER_POSITIVE, self.POWER_NEGATIVE,): |
| return 'power' |
| assert False, self |
| |
| @property |
| def desc(self): |
| pg = self |
| if pg in (self.DATA_IN_CHAR, self.DATA_IN_WORD, |
| self.DATA_OUT_CHAR, self.DATA_OUT_WORD, |
| self.DATA_IO_CHAR, self.DATA_IO_WORD,): |
| return 'Data Signals' |
| if pg in (self.DATA_CONTROL,): |
| return 'Control Signals' |
| if pg in (self.SCAN_CHAIN,): |
| return 'Scan Chain' |
| if pg in (self.CLOCK, self.CLOCK_CONTROL,): |
| return 'Clocking' |
| if pg in (self.POWER_CONTROL, self.POWER_OTHER, |
| self.POWER_POSITIVE, self.POWER_NEGATIVE,): |
| return 'Power' |
| assert False, self |
| |
| |
| @classmethod |
| def classify(cls, name, modname=None): |
| """ |
| |
| Data Input Signals |
| ++++++++++++++++++ |
| |
| >>> PortGroup.classify('A') |
| <PortGroup.DATA_IN_CHAR: 10> |
| >>> PortGroup.classify('A1') |
| <PortGroup.DATA_IN_CHAR: 10> |
| >>> PortGroup.classify('A1_N') |
| <PortGroup.DATA_IN_CHAR: 10> |
| >>> PortGroup.classify('A_N') |
| <PortGroup.DATA_IN_CHAR: 10> |
| |
| >>> PortGroup.classify('B') |
| <PortGroup.DATA_IN_CHAR: 10> |
| >>> PortGroup.classify('B1') |
| <PortGroup.DATA_IN_CHAR: 10> |
| >>> PortGroup.classify('B1_N') |
| <PortGroup.DATA_IN_CHAR: 10> |
| >>> PortGroup.classify('B_N') |
| <PortGroup.DATA_IN_CHAR: 10> |
| |
| >>> PortGroup.classify('J') |
| <PortGroup.DATA_IN_CHAR: 10> |
| >>> PortGroup.classify('K') |
| <PortGroup.DATA_IN_CHAR: 10> |
| |
| >>> PortGroup.classify('IN') |
| <PortGroup.DATA_IN_WORD: 11> |
| |
| >>> PortGroup.classify('D') |
| <PortGroup.DATA_IN_CHAR: 10> |
| >>> PortGroup.classify('D_N') |
| <PortGroup.DATA_IN_CHAR: 10> |
| |
| >>> PortGroup.classify('P0') |
| <PortGroup.DATA_IN_CHAR: 10> |
| >>> PortGroup.classify('N1') |
| <PortGroup.DATA_IN_CHAR: 10> |
| |
| >>> PortGroup.classify('CI') |
| <PortGroup.DATA_IN_WORD: 11> |
| >>> PortGroup.classify('CIN') |
| <PortGroup.DATA_IN_WORD: 11> |
| |
| SCD |
| Scan Chain Data |
| >>> PortGroup.classify('SCD') |
| <PortGroup.SCAN_CHAIN: 44> |
| |
| SCE |
| Scan Chain Enable |
| >>> PortGroup.classify('SCE') |
| <PortGroup.SCAN_CHAIN: 44> |
| |
| Data Output Signals |
| +++++++++++++++++++ |
| |
| >>> PortGroup.classify('X') |
| <PortGroup.DATA_OUT_CHAR: 20> |
| >>> PortGroup.classify('X_N') |
| <PortGroup.DATA_OUT_CHAR: 20> |
| |
| >>> PortGroup.classify('Y') |
| <PortGroup.DATA_OUT_CHAR: 20> |
| >>> PortGroup.classify('Y_N') |
| <PortGroup.DATA_OUT_CHAR: 20> |
| |
| >>> PortGroup.classify('Z') |
| <PortGroup.DATA_OUT_CHAR: 20> |
| >>> PortGroup.classify('Z_N') |
| <PortGroup.DATA_OUT_CHAR: 20> |
| |
| >>> PortGroup.classify('Q') |
| <PortGroup.DATA_OUT_CHAR: 20> |
| |
| >>> PortGroup.classify('OUT') |
| <PortGroup.DATA_OUT_WORD: 21> |
| |
| >>> PortGroup.classify('HI') |
| <PortGroup.DATA_OUT_WORD: 21> |
| >>> PortGroup.classify('LO') |
| <PortGroup.DATA_OUT_WORD: 21> |
| |
| >>> PortGroup.classify('COUT') |
| <PortGroup.DATA_OUT_WORD: 21> |
| |
| >>> PortGroup.classify('SUM') |
| <PortGroup.DATA_OUT_WORD: 21> |
| |
| Data Control Signals |
| ++++++++++++++++++++ |
| |
| >>> PortGroup.classify('SET') |
| <PortGroup.DATA_CONTROL: 40> |
| >>> PortGroup.classify('SET_N') |
| <PortGroup.DATA_CONTROL: 40> |
| >>> PortGroup.classify('SET_B') |
| <PortGroup.DATA_CONTROL: 40> |
| |
| >>> PortGroup.classify('S') |
| <PortGroup.DATA_CONTROL: 40> |
| >>> PortGroup.classify('S_N') |
| <PortGroup.DATA_CONTROL: 40> |
| >>> PortGroup.classify('S_B') |
| <PortGroup.DATA_CONTROL: 40> |
| |
| >>> PortGroup.classify('R') |
| <PortGroup.DATA_CONTROL: 40> |
| >>> PortGroup.classify('R_N') |
| <PortGroup.DATA_CONTROL: 40> |
| >>> PortGroup.classify('R_B') |
| <PortGroup.DATA_CONTROL: 40> |
| |
| >>> PortGroup.classify('RESET') |
| <PortGroup.DATA_CONTROL: 40> |
| >>> PortGroup.classify('RESET_N') |
| <PortGroup.DATA_CONTROL: 40> |
| >>> PortGroup.classify('RESET_B') |
| <PortGroup.DATA_CONTROL: 40> |
| |
| >>> PortGroup.classify('RESET') |
| <PortGroup.DATA_CONTROL: 40> |
| >>> PortGroup.classify('RESET_N') |
| <PortGroup.DATA_CONTROL: 40> |
| >>> PortGroup.classify('RESET_B') |
| <PortGroup.DATA_CONTROL: 40> |
| |
| Data Enable |
| >>> PortGroup.classify('DE') |
| <PortGroup.DATA_CONTROL: 40> |
| |
| Tristate Enable |
| >>> PortGroup.classify('TE_B') |
| <PortGroup.DATA_CONTROL: 40> |
| |
| Select lines on muxes |
| >>> PortGroup.classify('S0') |
| <PortGroup.DATA_CONTROL: 40> |
| >>> PortGroup.classify('S4') |
| <PortGroup.DATA_CONTROL: 40> |
| |
| Clock Signals |
| +++++++++++++ |
| |
| >>> PortGroup.classify('C') |
| <PortGroup.CLOCK: 50> |
| |
| >>> PortGroup.classify('CN') |
| <PortGroup.CLOCK: 50> |
| |
| >>> PortGroup.classify('C_N') |
| <PortGroup.CLOCK: 50> |
| |
| >>> PortGroup.classify('CLK') |
| <PortGroup.CLOCK: 50> |
| |
| >>> PortGroup.classify('GCLK') |
| <PortGroup.CLOCK: 50> |
| |
| >>> PortGroup.classify('CLK_N') |
| <PortGroup.CLOCK: 50> |
| |
| >>> PortGroup.classify('G') |
| <PortGroup.CLOCK: 50> |
| |
| >>> PortGroup.classify('GN') |
| <PortGroup.CLOCK: 50> |
| |
| >>> PortGroup.classify('G_N') |
| <PortGroup.CLOCK: 50> |
| |
| >>> PortGroup.classify('GATE') |
| <PortGroup.CLOCK: 50> |
| |
| >>> PortGroup.classify('GATE_N') |
| <PortGroup.CLOCK: 50> |
| |
| |
| Clock Control Signals |
| +++++++++++++++++++++ |
| |
| >>> PortGroup.classify('CLK_EN') |
| <PortGroup.CLOCK_CONTROL: 51> |
| |
| >>> PortGroup.classify('CE') |
| <PortGroup.CLOCK_CONTROL: 51> |
| |
| >>> PortGroup.classify('CEN') |
| <PortGroup.CLOCK_CONTROL: 51> |
| |
| >>> PortGroup.classify('GATE_EN') |
| <PortGroup.CLOCK_CONTROL: 51> |
| |
| >>> PortGroup.classify('GEN') |
| <PortGroup.CLOCK_CONTROL: 51> |
| |
| >>> PortGroup.classify('GE') |
| <PortGroup.CLOCK_CONTROL: 51> |
| |
| Positive Power Supplies |
| +++++++++++++++++++++++ |
| |
| >>> PortGroup.classify('VPWR') |
| <PortGroup.POWER_POSITIVE: 80> |
| |
| >>> PortGroup.classify('VPB') |
| <PortGroup.POWER_POSITIVE: 80> |
| |
| Negative Power Supplies |
| +++++++++++++++++++++++ |
| |
| >>> PortGroup.classify('VGND') |
| <PortGroup.POWER_NEGATIVE: 81> |
| |
| >>> PortGroup.classify('VNB') |
| <PortGroup.POWER_NEGATIVE: 81> |
| |
| >>> PortGroup.classify('DEST') |
| <PortGroup.POWER_OTHER: 79> |
| |
| Power Control Signals |
| +++++++++++++++++++++ |
| |
| >>> PortGroup.classify('SLEEP') |
| <PortGroup.POWER_CONTROL: 78> |
| |
| >>> PortGroup.classify('SLEEP_B') |
| <PortGroup.POWER_CONTROL: 78> |
| |
| >>> PortGroup.classify('SLEEP_N') |
| <PortGroup.POWER_CONTROL: 78> |
| |
| Other Power Suppliers |
| +++++++++++++++++++++ |
| |
| >>> PortGroup.classify('KAPWR') |
| <PortGroup.POWER_OTHER: 79> |
| |
| >>> PortGroup.classify('KAGND') |
| <PortGroup.POWER_OTHER: 79> |
| |
| >>> PortGroup.classify('DIODE') |
| <PortGroup.POWER_OTHER: 79> |
| |
| >>> PortGroup.classify('LOWLVPWRA') |
| <PortGroup.POWER_OTHER: 79> |
| |
| """ |
| # Override for csw module |
| if modname and 'csw' in modname: |
| if name == 'VPB': |
| return cls.POWER_POSITIVE |
| elif name == 'VNB': |
| return cls.POWER_NEGATIVE |
| elif name in ('S', 'D', 'GN', 'GP'): |
| return cls.POWER_OTHER |
| |
| name = name.upper() |
| if re.search('^[A-FJKPN][0-9]?(_[NB])?$', name): |
| if name not in ('C', 'C_N'): |
| return cls.DATA_IN_CHAR |
| |
| if re.search('^[XYZQ][0-9]?(_[NB])?$', name): |
| return cls.DATA_OUT_CHAR |
| |
| if re.search('^IN[0-9]?_?', name): |
| return cls.DATA_IN_WORD |
| if re.search('^CI[0-9]?_?', name): |
| return cls.DATA_IN_WORD |
| if re.search('^CIN[0-9]?_?', name): |
| return cls.DATA_IN_WORD |
| if re.search('^OUT[0-9]?_?', name): |
| return cls.DATA_OUT_WORD |
| if re.search('^COUT[0-9]?_?', name): |
| return cls.DATA_OUT_WORD |
| if re.search('^SUM[0-9]?_?', name): |
| return cls.DATA_OUT_WORD |
| if name in ('HI', 'LO'): |
| return cls.DATA_OUT_WORD |
| |
| if re.search('^S[0-9]+$', name): |
| return cls.DATA_CONTROL |
| |
| if re.search('^((SET)|(RESET)|[SR])(_[NB])?$', name): |
| return cls.DATA_CONTROL |
| |
| if re.search('^((T)|(TE))(_[NB])?$', name): |
| return cls.DATA_CONTROL |
| |
| if re.search('^(DE)(_[NB])?$', name): |
| return cls.DATA_CONTROL |
| |
| if re.search('^((CLK)|(GCLK)|(GATE))_EN$', name): |
| return cls.CLOCK_CONTROL |
| if re.search('^[CG]EN$', name): |
| return cls.CLOCK_CONTROL |
| if re.search('^[CG]E$', name): |
| return cls.CLOCK_CONTROL |
| if re.search('^((CLK)|(GCLK)|(GATE))$', name): |
| return cls.CLOCK |
| if re.search('^((CLK)|(GCLK)|(GATE))_N$', name): |
| return cls.CLOCK |
| if re.search('^[CG]_?N?$', name): |
| return cls.CLOCK |
| |
| if re.search('^SLEEP(_[NB])?$', name): |
| return cls.POWER_CONTROL |
| |
| if re.search('^VPWR', name): |
| return cls.POWER_POSITIVE |
| if re.search('^VPB$', name): |
| return cls.POWER_POSITIVE |
| if re.search('^VGND', name): |
| return cls.POWER_NEGATIVE |
| if re.search('^VNB$', name): |
| return cls.POWER_NEGATIVE |
| if name.startswith('DEST'): |
| return cls.POWER_OTHER |
| if 'DIODE' in name: |
| return cls.POWER_OTHER |
| if 'PWR' in name: |
| return cls.POWER_OTHER |
| if 'GND' in name: |
| return cls.POWER_OTHER |
| if 'SHORT' in name: |
| return cls.POWER_OTHER |
| |
| if 'MET' in name: |
| return cls.POWER_OTHER |
| if re.search('^[M][0-1]$', name): |
| return cls.POWER_OTHER |
| if 'SRC' == name: |
| return cls.DATA_IN_WORD |
| if 'DST' == name: |
| return cls.DATA_OUT_WORD |
| if 'NETA' == name: |
| return cls.DATA_IN_WORD |
| if 'NETB' == name: |
| return cls.DATA_OUT_WORD |
| |
| if name.startswith('SC'): |
| return cls.SCAN_CHAIN |
| if name.startswith('ASYNC'): |
| return cls.SCAN_CHAIN |
| |
| |
| |
| def write_verilog(define_data, outfile, drive=None): |
| with open(outfile, 'w') as f: |
| f.write(copyright_header) |
| f.write('\n') |
| f.write(include_header.format(define_data['fullname'])) |
| f.write('\n') |
| if not drive: |
| return |
| drive_name, drive_value = drive |
| |
| if not 'ports' in define_data: |
| return |
| |
| module_signal_defports = [] |
| module_signal_ports = [] |
| for pname, ptype in define_data['ports']['signal']: |
| module_signal_defports.append("{} {}, ".format(ptype, pname)) |
| module_signal_ports.append(pname) |
| |
| module_signal_defports = "".join(module_signal_defports) |
| assert module_signal_defports.endswith(", "), module_signal_defports |
| module_signal_defports = module_signal_defports[:-2] |
| module_signal_ports = ", ".join(module_signal_ports) |
| |
| module_power_defports = [] |
| module_power_ports = [] |
| for pname, ptype in define_data['ports']['power']: |
| module_power_defports.append(", {} {}".format('input', pname)) |
| module_power_ports.append(", {}".format(pname)) |
| module_power_defports = "".join(module_power_defports) |
| module_power_ports = "".join(module_power_ports) |
| |
| library_name = "{} {}".format( |
| define_data['library']['name'].upper(), define_data['library']['type']) |
| |
| f.write(module_header.format( |
| module_base_name = define_data['fullname'], |
| cell_name = define_data['name'], |
| library_name = library_name, |
| drive_name = drive_name, |
| drive_value = drive_value, |
| description = define_data.get('description', ''), |
| module_signal_defports = module_signal_defports, |
| module_signal_ports = module_signal_ports, |
| module_power_defports = module_power_defports, |
| module_power_ports = module_power_ports, |
| )) |
| |
| |
| def echo_file(fname): |
| with open(fname) as f: |
| sys.stdout.write('\n') |
| sys.stdout.write('File: ') |
| sys.stdout.write(fname) |
| sys.stdout.write('\n') |
| sys.stdout.write('------\n') |
| sys.stdout.write(f.read()) |
| sys.stdout.write('------\n') |
| sys.stdout.flush() |
| |
| |
| def drive_strengths(basename, cellpath): |
| drives = [] |
| for f in os.listdir(cellpath): |
| if not f.endswith('.gds'): |
| continue |
| f = f.split('.', 1)[0] |
| |
| libname, modname = f.split('__', 1) |
| drive = modname.replace(basename, '') |
| if not drive: |
| continue |
| drives.append(drive) |
| return drives |
| |
| def wrap(s): |
| return "\n".join(textwrap.wrap(s, initial_indent=' * ', subsequent_indent=' * ')) |
| |
| |
| |
| def write_verilog_header(f, define_data): |
| f.write(copyright_header) |
| f.write('\n') |
| f.write(f"/**\n") |
| f.write(wrap(f"{define_data['name']}: {define_data['description']}")) |
| f.write('\n') |
| f.write(f" */\n") |
| f.write('(* blackbox *)\n') |
| f.write(f"module {define_data['library']}__{define_data['name']} (\n") |
| |
| |
| def write_blackbox(cellpath, define_data): |
| outpath = os.path.join(cellpath, f"{define_data['library']}__{define_data['name']}.blackbox.v") |
| assert not os.path.exists(outpath), outpath |
| print("Creating", outpath) |
| with open(outpath, "w+") as f: |
| write_verilog_header(f, define_data) |
| |
| maxlen = {} |
| maxlen['pname'] = max([0]+[len(p[1]) for p in define_data['ports'] if p[0] == 'signal']) |
| maxlen['pdir'] = max([0]+[len(p[2]) for p in define_data['ports'] if p[0] == 'signal']) |
| maxlen['ptype'] = max([0]+[len(p[3]) for p in define_data['ports'] if p[0] == 'signal']) |
| |
| for pclass, pname, pdir, ptype in define_data['ports']: |
| if pclass != 'signal': |
| continue |
| |
| pname = pname.ljust(maxlen['pname']) |
| |
| if maxlen['pdir']: |
| pdir = pdir.ljust(maxlen['pdir'])+' ' |
| else: |
| pdir = '' |
| |
| if maxlen['ptype']: |
| ptype = ptype.ljust(maxlen['ptype'])+ ' ' |
| else: |
| ptype = '' |
| |
| f.write(f" {pdir}{ptype}{pname},\n") |
| f.seek(f.tell()-2) |
| f.write("\n);\n") |
| |
| maxlen['pname'] = max([0]+[len(p[0]) for p in define_data['parameters']]) |
| maxlen['ptype'] = max([0]+[len(p[1]) for p in define_data['parameters']]) |
| if maxlen['pname']: |
| f.write('\n // Parameters\n') |
| for pname, ptype in define_data['parameters']: |
| pname = pname.ljust(maxlen['pname']) |
| if maxlen['ptype']: |
| ptype = ptype.ljust(maxlen['ptype'])+ ' ' |
| else: |
| ptype = '' |
| f.write(f' parameter {ptype}{pname};\n') |
| |
| maxlen['pname'] = max([0]+[len(p[1]) for p in define_data['ports'] if p[0] == 'power']) |
| maxlen['ptype'] = max([0]+[len(p[3]) for p in define_data['ports'] if p[0] == 'power']) |
| if maxlen['pname']: |
| f.write('\n // Voltage supply signals\n') |
| for pclass, pname, pdir, ptype in define_data['ports']: |
| if pclass != 'power': |
| continue |
| assert ptype, (pclass, pname, pdir, ptype) |
| |
| pname = pname.ljust(maxlen['pname']) |
| ptype = ptype.ljust(maxlen['ptype']) |
| |
| f.write(f' {ptype} {pname};\n') |
| |
| f.write('endmodule\n') |
| |
| |
| def write_blackbox_pp(cellpath, define_data): |
| outpath = os.path.join(cellpath, f"{define_data['library']}__{define_data['name']}.pp.blackbox.v") |
| assert not os.path.exists(outpath), outpath |
| print("Creating", outpath) |
| with open(outpath, "w+") as f: |
| write_verilog_header(f, define_data) |
| |
| maxlen = {} |
| maxlen['pname'] = max([0]+[len(p[1]) for p in define_data['ports']]) |
| maxlen['pdir'] = max([0]+[len(p[2]) for p in define_data['ports']]) |
| maxlen['ptype'] = max([0]+[len(p[3]) for p in define_data['ports'] if p[0] == 'signal']) |
| |
| for pclass, pname, pdir, ptype in define_data['ports']: |
| if pclass == 'power': |
| ptype = '' |
| |
| pname = pname.ljust(maxlen['pname']) |
| |
| if maxlen['pdir']: |
| pdir = pdir.ljust(maxlen['pdir'])+' ' |
| else: |
| pdir = '' |
| |
| if maxlen['ptype']: |
| ptype = ptype.ljust(maxlen['ptype'])+ ' ' |
| else: |
| ptype = '' |
| |
| f.write(f" {pdir}{ptype}{pname},\n") |
| f.seek(f.tell()-2) |
| f.write("\n);\n") |
| |
| maxlen['pname'] = max([0]+[len(p[0]) for p in define_data['parameters']]) |
| maxlen['ptype'] = max([0]+[len(p[1]) for p in define_data['parameters']]) |
| if maxlen['pname']: |
| f.write('\n // Parameters\n') |
| for pname, ptype in define_data['parameters']: |
| pname = pname.ljust(maxlen['pname']) |
| if maxlen['ptype']: |
| ptype = ptype.ljust(maxlen['ptype'])+ ' ' |
| else: |
| ptype = '' |
| f.write(f' parameter {ptype}{pname};\n') |
| f.write('endmodule\n') |
| |
| |
| def write_symbol_pp(cellpath, define_data): |
| outpath = os.path.join(cellpath, f"{define_data['library']}__{define_data['name']}.pp.symbol.v") |
| assert not os.path.exists(outpath), outpath |
| |
| ports = [] |
| for pclass, pname, pdir, ptype in define_data['ports']: |
| pg = PortGroup.classify(pname, define_data['name']) |
| ports.append((pg, pclass, pname, pdir, ptype)) |
| |
| ports.sort() |
| |
| pports = [None,] |
| while len(ports) > 0: |
| if pports[-1] and pports[-1][0].type != ports[0][0].type: |
| pports.append(None) |
| pports.append(ports.pop(0)) |
| |
| for i, p in enumerate(pports): |
| if not p: |
| np = pports[i+1] |
| print('//# {{'+np[0].type+'|'+np[0].desc+'}}') |
| continue |
| print(p) |
| |
| return |
| |
| |
| print("Creating", outpath) |
| with open(outpath, "w+") as f: |
| write_verilog_header(f, define_data) |
| |
| maxlen = {} |
| maxlen['pname'] = max([0]+[len(p[1]) for p in define_data['ports']]) |
| maxlen['pdir'] = max([0]+[len(p[2]) for p in define_data['ports']]) |
| maxlen['ptype'] = max([0]+[len(p[3]) for p in define_data['ports'] if p[0] == 'signal']) |
| |
| for pclass, pname, pdir, ptype in define_data['ports']: |
| if pclass == 'power': |
| ptype = '' |
| |
| pname = pname.ljust(maxlen['pname']) |
| |
| if maxlen['pdir']: |
| pdir = pdir.ljust(maxlen['pdir'])+' ' |
| else: |
| pdir = '' |
| |
| if maxlen['ptype']: |
| ptype = ptype.ljust(maxlen['ptype'])+ ' ' |
| else: |
| ptype = '' |
| |
| f.write(f" {pdir}{ptype}{pname},\n") |
| f.seek(f.tell()-2) |
| f.write("\n);\n") |
| |
| maxlen['pname'] = max([0]+[len(p[0]) for p in define_data['parameters']]) |
| maxlen['ptype'] = max([0]+[len(p[1]) for p in define_data['parameters']]) |
| if maxlen['pname']: |
| f.write('\n // Parameters\n') |
| for pname, ptype in define_data['parameters']: |
| pname = pname.ljust(maxlen['pname']) |
| if maxlen['ptype']: |
| ptype = ptype.ljust(maxlen['ptype'])+ ' ' |
| else: |
| ptype = '' |
| f.write(f' parameter {ptype}{pname};\n') |
| f.write('endmodule\n') |
| |
| |
| |
| def process(cellpath): |
| print() |
| print(cellpath) |
| define_json = os.path.join(cellpath, 'definition.json') |
| if not os.path.exists(define_json): |
| print("No definition.json in", cellpath) |
| return |
| assert os.path.exists(define_json), define_json |
| define_data = json.load(open(define_json)) |
| pprint.pprint(define_data) |
| |
| write_blackbox(cellpath, define_data) |
| write_blackbox_pp(cellpath, define_data) |
| |
| write_symbol_pp(cellpath, define_data) |
| pprint.pprint(drive_strengths(define_data['name'], cellpath)) |
| |
| for pclass, pname, pdir, ptype in define_data['ports']: |
| print(pname, PortGroup.classify(pname, define_data['name'])) |
| |
| return |
| drives = define_data.get('drives', []) |
| for d in drives: |
| assert len(d) == 1, d |
| drive_name = list(d.keys())[0] |
| drive_value = list(d.values())[0] |
| if drive_name == 'units': |
| pass |
| elif drive_name == 'lp_variant': |
| if drive_value == 0: |
| drive_value = 'lp' |
| else: |
| drive_value = 'lp'+str(drive_value+1) |
| elif drive_name == "minimum": |
| assert drive_value is None |
| drive_value = "m" |
| else: |
| raise TypeError("Unknown drive:"+repr(d)) |
| |
| dvfile = os.path.join(cellpath, "{}_{}.v".format(define_data['fullname'], drive_value)) |
| write_verilog(define_data, dvfile, list(d.items())[0]) |
| echo_file(dvfile) |
| |
| if not drives: |
| outfile = os.path.join(cellpath, "{}.v".format(define_data['fullname'])) |
| write_verilog(define_data, outfile) |
| echo_file(outfile) |
| |
| |
| def main(args): |
| for a in args: |
| process(a) |
| |
| |
| |
| if __name__ == "__main__": |
| import doctest |
| fails, _ = doctest.testmod() |
| if fails>0: |
| sys.exit() |
| sys.exit(main(sys.argv[1:])) |