Adding tool to generate blackbox verilog output.
diff --git a/scripts/python-skywater-pdk/generate_verilog_blackbox.py b/scripts/python-skywater-pdk/generate_verilog_blackbox.py
new file mode 100755
index 0000000..82d7257
--- /dev/null
+++ b/scripts/python-skywater-pdk/generate_verilog_blackbox.py
@@ -0,0 +1,780 @@
+#!/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:]))
diff --git a/scripts/python-skywater-pdk/skywater_pdk/utils.py b/scripts/python-skywater-pdk/skywater_pdk/utils.py
index cc8bfc0..e3f2736 100644
--- a/scripts/python-skywater-pdk/skywater_pdk/utils.py
+++ b/scripts/python-skywater-pdk/skywater_pdk/utils.py
@@ -200,6 +200,41 @@
return hash(self._name_)
+class OrderedEnum(Flag):
+ def __ge__(self, other):
+ if other is None:
+ return True
+ if self.__class__ is other.__class__:
+ return self.value >= other.value
+ return NotImplemented
+ def __gt__(self, other):
+ if other is None:
+ return True
+ if self.__class__ is other.__class__:
+ return self.value > other.value
+ return NotImplemented
+ def __le__(self, other):
+ if other is None:
+ return False
+ if self.__class__ is other.__class__:
+ return self.value <= other.value
+ return NotImplemented
+ def __lt__(self, other):
+ if other is None:
+ return False
+ if self.__class__ is other.__class__:
+ return self.value < other.value
+ return NotImplemented
+ def __eq__(self, other):
+ if other is None:
+ return False
+ if self.__class__ is other.__class__:
+ return self.value == other.value
+ return NotImplemented
+ def __hash__(self):
+ return hash(self._name_)
+
+
if __name__ == "__main__":
import doctest
doctest.testmod()