Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added netlistsvg wrapper/generator #247

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions scripts/python-skywater-pdk/skywater_pdk/cell/generate/netlist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#!/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 csv
import json
import os
import sys
import argparse
import pathlib
import glob
import subprocess
import re

def outfile(cellpath, define_data, ftype='', extra='', exists=False):
''' Determines output file path and name.

Args:
cellpath - path to a cell [str of pathlib.Path]
define_data - cell definition data [dic]
ftype - file type suffix [str]
extra - extra suffix [str]
exist - optional check if file exists [bool or None]

Returns:
outpath - output file namepath [str]
'''

fname = define_data['name'].lower().replace('$', '_')
if ftype:
ftype = '.'+ftype
outpath = os.path.join(cellpath, f'{define_data["file_prefix"]}{extra}{ftype}.svg')
if exists is None:
pass
elif not exists:
#assert not os.path.exists(outpath), "Refusing to overwrite existing file:"+outpath
print("Creating", outpath)
elif exists:
assert os.path.exists(outpath), "Missing required:"+outpath
return outpath

def patch_netlist_json (infile, outfile):
''' Patches yosys generated netlist json
to workaround netlistsvg compability issues.
Currently covered:
- convert unsupported pin direction 'inout' to 'output'
- convert unsupported High-Z connection ("z") to none ("x")

Args:
infile - input file [str of pathlib.Path]
outfile - patched file [str of pathlib.Path]
'''

block_header = '\s*"\w*"\:\s*\{' #eg. "connections": {
unsupported_dir = '\s*"direction":\s*"inout"' #eg. "direction": "input"
unsupported_con = '\s*"\w*":\s*\[\s*"z"\s*\]' #eg. "A": [ "z" ]
json = ''
current_block = None

with open(str(infile),'r') as f:
for line in f:
if re.match (block_header, line):
current_block = line.partition(':')[0].strip(' "\n')
if re.match (unsupported_dir, line):
line = re.sub ( ':\s*"inout"', ': "output"', line)
if current_block =='connections' and re.match (unsupported_con, line):
line = re.sub ( '\[\s*"z"\s*\]', '[ "x" ]' , line, flags=re.IGNORECASE)
json +=line
with open(str(outfile),'w') as f:
f.write(json)


def write_netlistsvg(cellpath, define_data, json_patching = False):
''' Generates netlistsvg for a given cell.

Args:
cellpath - path to a cell [str of pathlib.Path]
define_data - cell definition data [dic]
json_patching - filter netlist for potential netlistsvg compablity issues
'''

netlist_json = os.path.join(cellpath, define_data['file_prefix']+'.json')
if not os.path.exists(netlist_json):
print("No netlist in", cellpath)
assert os.path.exists(netlist_json), netlist_json

if json_patching: #correct netlistsvg parsing issues
netlist_json_fix = os.path.join(cellpath, define_data['file_prefix']+'.patched.json')
patch_netlist_json (netlist_json, netlist_json_fix)
netlist_json = netlist_json_fix

outpath = outfile(cellpath, define_data, 'schematic')
if subprocess.call(['netlistsvg', netlist_json, '-o', outpath]):
if json_patching:
os.remove(netlist_json_fix)
raise ChildProcessError("netlistsvg execution failed")

if json_patching:
pass
os.remove(netlist_json_fix)

def process(cellpath):
''' Processes cell indicated by path.
Opens cell definiton and calls further processing

Args:
cellpath - path to a cell [str of pathlib.Path]
'''

print()
print(cellpath)
define_json = os.path.join(cellpath, 'definition.json')
if not os.path.exists(define_json):
print("No definition.json in", cellpath)
assert os.path.exists(define_json), define_json
define_data = json.load(open(define_json))

if define_data['type'] == 'cell':
try:
write_netlistsvg(cellpath, define_data)
except ChildProcessError:
print ("Netlistsvg error, retrying with json patch...")
write_netlistsvg(cellpath, define_data, json_patching=True)

return


def main():
''' Generates netlistsvg schematic from cell netlist.'''

prereq_txt = 'prerequisities:\n netlistsvg'
output_txt = 'output:\n generates [cell_prefix].schematic.svg'
allcellpath = '../../../libraries/*/latest/cells/*'

parser = argparse.ArgumentParser(
description = main.__doc__,
epilog = prereq_txt +'\n\n'+ output_txt,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
"--all_libs",
help="process all cells in "+allcellpath,
action="store_true")
parser.add_argument(
"cell_dir",
help="path to the cell directory",
type=pathlib.Path,
nargs="*")

args = parser.parse_args()

if args.all_libs:
path = pathlib.Path(allcellpath).expanduser()
parts = path.parts[1:] if path.is_absolute() else path.parts
paths = pathlib.Path(path.root).glob(str(pathlib.Path("").joinpath(*parts)))
args.cell_dir = list(paths)

cell_dirs = [d.resolve() for d in args.cell_dir if d.is_dir()]

errors = 0
for d in cell_dirs:
try:
process(d)
except KeyboardInterrupt:
sys.exit(1)
except (AssertionError, FileNotFoundError, ChildProcessError) as ex:
print (f'Error: {type(ex).__name__}')
print (f'{ex.args}')
errors +=1
print (f'\n{len(cell_dirs)} files processed, {errors} errors.')
return 0 if errors else 1

if __name__ == "__main__":
sys.exit(main())

134 changes: 134 additions & 0 deletions scripts/python-skywater-pdk/skywater_pdk/netlistsvg-generate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#!/usr/bin/env python3
wgryncewicz marked this conversation as resolved.
Show resolved Hide resolved
# -*- 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 csv
import json
import os
import sys
import argparse
import pathlib
import glob
import subprocess

def outfile(cellpath, define_data, ftype='', extra='', exists=False):
''' Determines output file path and name.

Args:
cellpath - path to a cell [str of pathlib.Path]
define_data - cell definition data [dic]
ftype - file type suffix [str]
extra - extra suffix [str]
exist - optional check if file exists [bool or None]

Returns:
outpath - output file namepath [str]
'''

fname = define_data['name'].lower().replace('$', '_')
if ftype:
ftype = '.'+ftype
outpath = os.path.join(cellpath, f'{define_data["file_prefix"]}{extra}{ftype}.svg')
if exists is None:
pass
elif not exists:
#assert not os.path.exists(outpath), "Refusing to overwrite existing file:"+outpath
print("Creating", outpath)
elif exists:
assert os.path.exists(outpath), "Missing required:"+outpath
return outpath


def write_netlistsvg(cellpath, define_data):
''' Generates netlistsvg for a given cell.

Args:
cellpath - path to a cell [str of pathlib.Path]
define_data - cell definition data [dic]
'''

netlist_json = os.path.join(cellpath, define_data['file_prefix']+'.json')
if not os.path.exists(netlist_json):
print("No netlist in", cellpath)
assert os.path.exists(netlist_json), netlist_json
outpath = outfile(cellpath, define_data, 'schematic')
if subprocess.call(['netlistsvg', netlist_json, '-o', outpath]):
raise ChildProcessError("netlistsvg execution failed")

def process(cellpath):
''' Processes cell indicated by path.
Opens cell definiton and calls further processing

Args:
cellpath - path to a cell [str of pathlib.Path]
'''

print()
print(cellpath)
define_json = os.path.join(cellpath, 'definition.json')
if not os.path.exists(define_json):
print("No definition.json in", cellpath)
assert os.path.exists(define_json), define_json
define_data = json.load(open(define_json))

if define_data['type'] == 'cell':
write_netlistsvg(cellpath, define_data)

return


def main():
''' Generates netlistsvg schematic from cell netlist.'''

prereq_txt = 'prerequisities:\n netlistsvg'
output_txt = 'output:\n generates [cell_prefix].schematic.svg'
allcellpath = '../../../libraries/*/latest/cells/*'

parser = argparse.ArgumentParser(
description = main.__doc__,
epilog = prereq_txt +'\n\n'+ output_txt,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
"--all_libs",
help="process all cells in "+allcellpath,
action="store_true")
parser.add_argument(
"cell_dir",
help="path to the cell directory",
type=pathlib.Path,
nargs="*")

args = parser.parse_args()

if args.all_libs:
path = pathlib.Path(allcellpath).expanduser()
parts = path.parts[1:] if path.is_absolute() else path.parts
paths = pathlib.Path(path.root).glob(str(pathlib.Path("").joinpath(*parts)))
args.cell_dir = list(paths)

cell_dirs = [d.resolve() for d in args.cell_dir if d.is_dir()]

errors = 0
for d in cell_dirs:
try:
process(d)
except KeyboardInterrupt:
sys.exit(1)
except (AssertionError, FileNotFoundError, ChildProcessError) as ex:
print (f'Error: {type(ex).__name__}')
print (f'{ex.args}')
errors +=1
print (f'\n{len(cell_dirs)} files processed, {errors} errors.')
return 0 if errors else 1

if __name__ == "__main__":
sys.exit(main())