blob: 7c1586a8be67d03c00bf51fa1c8d00294981ca18 [file] [log] [blame]
#!/usr/bin/env python3
import csv
from pathlib import Path
import argparse
from collections import defaultdict
import shlex
import re
from termcolor import colored
import sys
from prettytable import PrettyTable
RE_SUBCKT = re.compile(r'\.subckt\s+(?P<subcktname>[^ ]+)\s?(?P<nodenames>.*)')
# RE_MODEL = re.compile(r'^[\.]*model\s+(?P<modelname>[^ ]+)\s+(?P<modeltype>[^ ]+)')
RE_MODEL_END = re.compile(r'^[\s\n]*ends(?P<end>.*)(\n|$)')
RE_SUBCKT_END = re.compile(r'^.ends')
RE_GROUP = re.compile(r'[*\s]*?(?P<group>[^*\n]+?)[*\s]*(\n|$)')
RE_MODE_LINE = re.compile(r'(?P<modeid>\d+)\s*:\s*type=\s*(?P<modetype>.*)(\n|$)')
RE_INLINE_SUBCKT_MODEL = re.compile(r'^(?P<subcktname>[^ ]+)\s*\((?P<nodenames>.*)\)\s*(?P<modelname>[^ ]+)\s*(?P<parameters>.*)($|\n)')
RE_MODEL = re.compile(r'[\.]?model')
def order_parameters(paramname):
priority = {
'subckt': 0,
'nodes': 1,
'model': 2,
'type': 3,
'mode': 4,
'modetype': 5,
'lmin': 6,
'lmax': 7,
'wmin': 8,
'wmax': 9,
'level': 10,
'tnom': 11,
'version': 12,
'tox': 13,
'toxm': 14,
}
if paramname in priority:
return (priority[paramname], paramname)
return (1000, paramname)
def pm3_to_csv(lines):
result = []
params = {}
params['subckt'] = {}
params['model'] = {}
params['mode'] = {}
scopetype = None
def produce_line():
finparams = {}
finparams.update(params['subckt'])
finparams.update(params['model'])
finparams.update(params['mode'])
if len(finparams) > 0:
result.append(finparams)
scopetype = None
for line in lines:
try:
if not line.strip():
continue
line = line.strip()
elements = shlex.split(line, posix=False)
if line.startswith('*'):
continue
m = RE_MODE_LINE.match(line)
if m:
if scopetype == 'mode':
produce_line()
params['mode'] = {}
params['mode']['mode'] = m.group('modeid')
params['mode']['modetype'] = m.group('modetype')
scopetype = 'mode'
continue
ind = 0
def parse_parameters():
if scopetype is None:
return
ind = 0
prevkey = None
while ind < len(elements):
if not elements[ind].strip():
ind += 1
elif elements[ind].endswith('='):
if scopetype is None:
print(colored(line, 'magenta'), file=sys.stderr)
ind += 2
continue
params[scopetype][elements[ind][:-1]] = elements[ind+1]
prevkey = elements[ind][:-1]
ind += 2
elif '=' in elements[ind]:
res = elements[ind].split('=')
params[scopetype][res[0]] = res[1]
prevkey = res[0]
ind += 1
elif prevkey is not None:
params[scopetype][prevkey] += elements[ind]
ind += 1
else:
print(colored(line, 'cyan'), file=sys.stderr)
raise Exception(line)
if elements[0] == '.subckt':
produce_line()
params['subckt'] = {}
params['model'] = {}
params['mode'] = {}
params['subckt']['subckt'] = elements[1]
nodelist = []
ind = 2
for element in elements[2:]:
if '=' in element:
break
element = element.replace('(', '')
element = element.replace(')', '')
nodelist.append(element)
ind += 1
params['subckt']['nodes'] = ','.join(nodelist)
scopetype = 'subckt'
elements = elements[ind:]
parse_parameters()
elif RE_MODEL.match(elements[0]):
if scopetype != 'subckt':
produce_line()
params['model'] = {}
params['mode'] = {}
params['model']['model'] = elements[1]
params['model']['type'] = elements[2]
scopetype = 'model'
if len(elements) >=4 and elements[3] == '{':
continue
elements = elements[3:]
parse_parameters()
elif elements[0].startswith('+'):
elements[0] = elements[0][1:]
parse_parameters()
elif elements[0] == 'parameters':
elements = elements[1:]
parse_parameters()
else:
m = RE_INLINE_SUBCKT_MODEL.match(line)
if m:
if scopetype is not None:
produce_line()
params['subckt'] = {}
params['model'] = {}
params['mode'] = {}
scopetype = 'subckt'
params['subckt']['subckt'] = m.group('subcktname')
params['subckt']['nodes'] = ','.join(m.group('nodenames').split(' '))
params['model']['model'] = m.group('modelname')
params['model']['type'] = params['subckt']['subckt']
elements = m.group('parameters').split()
parse_parameters()
else:
print(colored(line, 'yellow'), file=sys.stderr)
except:
print(colored(line, 'red'), file=sys.stderr)
raise
produce_line()
parameters = set()
for entry in result:
parameters.update(entry.keys())
parameters = sorted(parameters, key=order_parameters)
csvcontent = [parameters]
for entry in result:
row = []
for param in parameters:
if param in entry:
row.append(entry[param])
else:
row.append('N/A')
csvcontent.append(row)
return csvcontent
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
"input_dir",
help="The path to the directory containing skywater-pdk",
type=Path)
parser.add_argument(
"--dry-run",
help="Do not perform actions on filesystem, only print",
action="store_true")
args = parser.parse_args()
files = sorted(args.input_dir.rglob('*.pm3'))
allfilesnum = len(files)
parameters = []
# file model parameter value
filesdata = defaultdict(lambda: defaultdict(dict))
for num, filename in enumerate(files):
with open(filename, 'r') as data:
print(f'[{num:05d}/{allfilesnum:05d}]: {filename}')
print(f'[{num:05d}/{allfilesnum:05d}]: {filename}', file=sys.stderr)
# parameters across many entries
lines = []
for line in data.readlines():
lines.append(line)
result = pm3_to_csv(lines)
if len(result) > 1:
with open(filename.with_suffix('.csv'), 'w', newline='') as csvfile:
writer = csv.writer(csvfile, delimiter=';')
writer.writerows(result)
with open(filename.with_suffix('.table'), 'w') as tablefile:
t = PrettyTable(result[0])
t.align = 'r'
t.border = False
for line in result[1:]:
t.add_row(line)
tablefile.write(str(t))