Proto of cell README generator
This commit is contained in:
parent
ca58d58c07
commit
0ca0b2968e
|
@ -0,0 +1,200 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2020 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
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import pprint
|
||||||
|
import sys
|
||||||
|
import textwrap
|
||||||
|
from docutils import nodes
|
||||||
|
from docutils.parsers.rst import Directive
|
||||||
|
from docutils.statemachine import ViewList
|
||||||
|
from sphinx.util.nodes import nested_parse_with_titles
|
||||||
|
|
||||||
|
from typing import Tuple, List, Dict
|
||||||
|
|
||||||
|
# using a list-table here to allow for easier line breaks in description
|
||||||
|
rst_header_line_char = '-'
|
||||||
|
rst_header = 'List of cells in :lib:`{libname}`'
|
||||||
|
rst_template ="""\
|
||||||
|
{header_line}
|
||||||
|
{header_underline}
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Cell name
|
||||||
|
- Description
|
||||||
|
- Type
|
||||||
|
- Verilog name
|
||||||
|
{cell_list}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
cell_template = """\
|
||||||
|
* - {cell_name}
|
||||||
|
- {description}
|
||||||
|
- {type}
|
||||||
|
- {verilog_name}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def collect(library_dir) -> Tuple[str, List[str]]:
|
||||||
|
"""Collect the available definitions for cells in a library
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
library_dir: str or pathlib.Path
|
||||||
|
Path to a library.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
lib : str
|
||||||
|
Library name
|
||||||
|
|
||||||
|
cells : list of pathlib.Path
|
||||||
|
definition files for cells in the library.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(library_dir, pathlib.Path):
|
||||||
|
library_dir = pathlib.Path(library_dir)
|
||||||
|
|
||||||
|
libname = None
|
||||||
|
cells = set()
|
||||||
|
|
||||||
|
for p in library_dir.rglob("definition.json"):
|
||||||
|
if not p.is_file():
|
||||||
|
continue
|
||||||
|
cells.add(p)
|
||||||
|
if libname is None:
|
||||||
|
with open(str(p), "r") as sample_json:
|
||||||
|
sample_def = json.load(sample_json)
|
||||||
|
libname = sample_def['library']
|
||||||
|
|
||||||
|
assert len(libname) > 0
|
||||||
|
cells = list(sorted(cells))
|
||||||
|
return libname, cells
|
||||||
|
|
||||||
|
|
||||||
|
def generate_rst(library_dir, library_name, cells):
|
||||||
|
"""Generate the RST paragraph containing basic information about cells
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
library_dir: str or pathlib.Path
|
||||||
|
Path to a library.
|
||||||
|
|
||||||
|
library_name: str
|
||||||
|
Name of the library
|
||||||
|
|
||||||
|
cells: list of pathlib.Path
|
||||||
|
List of paths to JSON description files
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
paragraph: str
|
||||||
|
Generated paragraph
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(library_dir, pathlib.Path):
|
||||||
|
library_dir = pathlib.Path(library_dir)
|
||||||
|
|
||||||
|
paragraph = ""
|
||||||
|
cell_list = ""
|
||||||
|
|
||||||
|
for cell in cells:
|
||||||
|
with open(str(cell), "r") as c:
|
||||||
|
cell_json = json.load(c)
|
||||||
|
cell_list = cell_list + cell_template.format(
|
||||||
|
cell_name = cell_json['name'],
|
||||||
|
#description = cell_json['description'].replace("\n", "\n "),
|
||||||
|
description = textwrap.indent(cell_json['description'], ' ').lstrip(),
|
||||||
|
type = cell_json['type'],
|
||||||
|
verilog_name = cell_json['verilog_name']
|
||||||
|
)
|
||||||
|
|
||||||
|
header = rst_header.format(libname = library_name)
|
||||||
|
paragraph = rst_template.format(
|
||||||
|
header_line = header,
|
||||||
|
header_underline = rst_header_line_char * len(header),
|
||||||
|
cell_list = cell_list
|
||||||
|
)
|
||||||
|
return paragraph
|
||||||
|
|
||||||
|
# --- Sphinx extension wrapper ---
|
||||||
|
|
||||||
|
class CellList(Directive):
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
env = self.state.document.settings.env
|
||||||
|
dirname = env.docname.rpartition('/')[0]
|
||||||
|
libname, cells = collect(dirname)
|
||||||
|
paragraph = generate_rst(dirname, libname, cells)
|
||||||
|
# parse rst string to docutils nodes
|
||||||
|
rst = ViewList()
|
||||||
|
for i,line in enumerate(paragraph.split('\n')):
|
||||||
|
rst.append(line, libname+"-cell-list.rst", i+1)
|
||||||
|
node = nodes.section()
|
||||||
|
node.document = self.state.document
|
||||||
|
nested_parse_with_titles(self.state, rst, node)
|
||||||
|
return node.children
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
app.add_directive("cell_list", CellList)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'version': '0.1',
|
||||||
|
'parallel_read_safe': True,
|
||||||
|
'parallel_write_safe': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- stand alone, command line operation ---
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"library_dir",
|
||||||
|
help="Path to the library.",
|
||||||
|
type=pathlib.Path,
|
||||||
|
nargs=1)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
lib = args.library_dir[0]
|
||||||
|
|
||||||
|
print(f"Analysing {lib}")
|
||||||
|
libname, cells = collect(lib)
|
||||||
|
print(f"Library name: {libname}, found {len(cells)} cells")
|
||||||
|
paragraph = generate_rst(lib, libname, cells)
|
||||||
|
library_dir = pathlib.Path(lib)
|
||||||
|
cell_list_file = pathlib.Path(library_dir, "cell-list.rst")
|
||||||
|
try:
|
||||||
|
with(open(str(cell_list_file), "w")) as c:
|
||||||
|
c.write(paragraph)
|
||||||
|
print(f'Generated {cell_list_file}')
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"ERROR: Failed to create {str(cell_list_file)}", file=sys.stderr)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
#!/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
|
||||||
|
|
||||||
|
''' This is a prototype of cell documentation generation script.
|
||||||
|
WORK IN PROGRESS
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
import csv
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import pathlib
|
||||||
|
import glob
|
||||||
|
import subprocess
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
def write_readme(cellpath, define_data):
|
||||||
|
''' Generates README for a given cell.
|
||||||
|
'''
|
||||||
|
netlist_json = os.path.join(cellpath, define_data['file_prefix']+'.json')
|
||||||
|
assert os.path.exists(netlist_json), netlist_json
|
||||||
|
outpath = os.path.join(cellpath, 'README.rst')
|
||||||
|
|
||||||
|
header = define_data['name'] + ' cell description'
|
||||||
|
headline = '-' * len(header)
|
||||||
|
|
||||||
|
prefix = define_data['file_prefix']
|
||||||
|
|
||||||
|
sym1 = prefix + '.symbol.svg'
|
||||||
|
sym2 = prefix + '.pp.symbol.svg'
|
||||||
|
sche = prefix + '.schematic.svg'
|
||||||
|
|
||||||
|
|
||||||
|
with open(outpath, 'w') as f:
|
||||||
|
f.write (f'{header}\n')
|
||||||
|
f.write (f'{headline}\n')
|
||||||
|
f.write ('\nThis is a stub of cell descrition file.\n\n')
|
||||||
|
|
||||||
|
f.write (f" * Name: {define_data['name']}\n")
|
||||||
|
f.write (f" * Type: {define_data['type']}\n")
|
||||||
|
f.write (f" * Verilog name: {define_data['verilog_name']}\n")
|
||||||
|
desc = textwrap.indent(define_data['description'], ' ').lstrip(),
|
||||||
|
f.write (f" * Description: {desc}\n")
|
||||||
|
|
||||||
|
f.write ('\nSome sample images:\n')
|
||||||
|
|
||||||
|
f.write (f'\n.. image:: {sym1}\n :align: center\n :alt: Symbol\n')
|
||||||
|
f.write (f'\n.. image:: {sym2}\n :align: center\n :alt: SymbolPP\n')
|
||||||
|
f.write (f'\n.. image:: {sche}\n :align: center\n :alt: Schematic\n')
|
||||||
|
|
||||||
|
|
||||||
|
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_readme(cellpath, define_data)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
''' Generates README.rst for cell.'''
|
||||||
|
|
||||||
|
prereq_txt = ''
|
||||||
|
output_txt = 'output:\n generates README.rst'
|
||||||
|
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())
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
#!/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
|
||||||
|
|
||||||
|
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())
|
||||||
|
|
Loading…
Reference in New Issue