blob: 3a9978abb6bda109f18b3246b1f4fc4249fd623d [file] [log] [blame]
#!/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 hashlib
import json
import os
import pprint
import re
import sys
import traceback
from skywater_pdk import base, corners, drives
import hdlparse.verilog_parser as vlog
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
"""
vlog_ex = vlog.VerilogExtractor()
IMPORTANT = [
'cell.json',
'full.v',
'specify.v',
'gds',
'cdl',
]
IGNORE = [
re.compile('README.rst$'),
re.compile('metadata.json$'),
re.compile('wrap.json'),
re.compile('wrap.lib'),
]
ALLOW_ERRORS = [
re.compile('/pg_u_'),
re.compile('fill'),
re.compile('tap'),
re.compile('lpflow_'),
]
def should_ignore(f, x=IGNORE):
"""
>>> should_ignore('README.rst')
True
>>> should_ignore('metadata.json')
True
>>> should_ignore('asdfasdfasdf/README.rst')
True
>>> should_ignore('/home/tim/gob/foss-eda-tools/skywater-pdk-scratch/skywater-pdk/libraries/sky130_fd_sc_hd/v0.0.1/cells/README.rst')
True
>>> should_ignore('/home/tim/gob/foss-eda-tools/skywater-pdk-scratch/skywater-pdk/libraries/sky130_fd_sc_hd/v0.0.1/cells/XXXX')
False
"""
for i in x:
if i.search(f):
return True
return False
def get_description(cellpath):
readme_fn = os.path.join(cellpath, 'README.rst')
if not os.path.exists(readme_fn):
return ''
readme = open(readme_fn).read()
desc = """\
Description
***********
"""
logic = """\
Logic
*****
"""
assert desc in readme, readme
assert logic in readme, readme
_, readme = readme.split(desc, 1)
readme, _ = readme.split(logic, 1)
return readme.strip()
def process(cellpath):
assert os.path.exists(cellpath), cellpath
assert os.path.isdir(cellpath), cellpath
files = [
(f, os.path.abspath(os.path.join(cellpath, f)))
for f in os.listdir(cellpath)]
files.sort()
dcell, fname = base.parse_pathname(cellpath)
assert isinstance(dcell, base.Cell), (cellpath, dcell, fname)
assert fname is None, (cellpath, dcell, fname)
extensions = set()
dcorners = set()
ddrives = set()
checksums = {}
errors = []
for fname, fpath in files:
print("Processing:", fname)
if should_ignore(fpath):
continue
try:
fcell, fextra, fext = base.parse_filename(fpath)
except Exception as e:
traceback.print_exc()
errors.append(e)
assert isinstance(fcell, base.Cell), (fpath, fcell, fextra, ext)
if fext in IMPORTANT:
checksums[fname] = hashlib.sha1(open(fpath, 'rb').read()).hexdigest()
extensions.add(fext)
assert fcell.library == dcell.library, (fcell, dcell)
if not fextra:
continue
try:
fcorner = corners.parse_filename(fextra)
except Exception as e:
traceback.print_exc()
errors.append(e)
dcorners.add(fcorner)
assert fcell.name.startswith(dcell.name), (fcell, dcell)
if dcell.name != fcell.name:
try:
fdrive = fcell.name[len(dcell.name):]
ddrives.add(drives.parse_drive(fdrive))
except Exception as e:
traceback.print_exc()
errors.append(e)
basepath = cellpath.split("libraries", 1)[0]
cellrelpath = os.path.relpath(cellpath, basepath)
print(cellrelpath)
metadata = dcell.to_dict()
metadata['fullname'] = dcell.fullname
metadata['description'] = get_description(cellpath)
if 'blackbox.v' in extensions:
bbv_fname = os.path.join(cellpath, "{}.blackbox.v".format(dcell.fullname))
assert os.path.exists(bbv_fname), bbv_fname
o = vlog_ex.extract_objects(bbv_fname)
assert len(o) == 1, o
o = o[0]
assert dcell.fullname in o.name, (dcell.fullname, o)
assert not o.generics, (dcell.fullname, o)
#metadata['all_ports'] = [(p.name, p.mode, p.data_type) for p in o.ports]
if 'full.v' in extensions:
full_fname = os.path.join(cellpath, "{}.full.v".format(dcell.fullname))
assert os.path.exists(full_fname), full_fname
o = vlog_ex.extract_objects(full_fname)
if not o:
simple_fname = os.path.join(cellpath, "{}.simple.v".format(dcell.fullname))
assert os.path.exists(simple_fname), simple_fname
o = vlog_ex.extract_objects(simple_fname)
assert len(o) == 1, o
o = o[0]
assert dcell.fullname in o.name, (dcell.fullname, o)
assert not o.generics, (dcell.fullname, o)
non_pwr = []
pwr = []
current_list = non_pwr
p = list(o.ports)
while len(p) > 0:
a = p.pop(0)
if a.name == 'ifdef':
assert len(p) > 2, p
pg_pin = p.pop(0)
assert 'SC_USE_PG_PIN' == pg_pin.name, pg_pin
current_list = pwr
continue
elif a.name == 'endif':
assert len(p) == 0, p
break
else:
current_list.append((a.name, a.mode))
metadata['ports'] = {
'signal': non_pwr,
'power': pwr,
}
extensions.add('metadata.json')
assert checksums
metadata['files'] = checksums
if dcorners:
metadata['corners'] = [d.to_dict() for d in sorted(dcorners)]
else:
errors.append('Missing corners for: {}\n'.format(cellpath))
assert extensions
metadata['extensions'] = list(sorted(extensions))
if ddrives:
metadata['drives'] = [d.to_dict() for d in sorted(ddrives)]
# Save the metadata file.
with open(os.path.join(cellpath, 'metadata.json'), 'w') as f:
json.dump(metadata, f, sort_keys=True, indent=" ")
# Create verilog files for each drive strength
print()
print()
print(dcell.name)
print("-"*75)
pprint.pprint(metadata)
if errors:
raise ValueError("\n".join(str(e) for e in errors))
def main(args):
for a in args:
print()
print()
p = os.path.abspath(a)
if should_ignore(p):
continue
try:
process(p)
except Exception as e:
if not should_ignore(p, ALLOW_ERRORS):
raise
print("Failed to process ignorable:", p)
traceback.print_exc()
if __name__ == "__main__":
import doctest
doctest.testmod()
sys.exit(main(sys.argv[1:]))