blob: ff90d34bf8848ec4920ee2c0bb8bbff2c45238a7 [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
''' This is a prototype of cell documentation generation script.
'''
import csv
import json
import os
import sys
import argparse
import pathlib
import glob
import subprocess
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
verbose = False
readme_template ="""\
{header}
**{description}**
*This is a stub of cell description file*
- **Cell name**: {name}
- **Type**: {deftype}
- **Verilog name**: {verilog_name}
- **Library**: {library}
- **Inputs**: {inputs}
- **Outputs**: {outputs}
{subheader_sym}
.. list-table::
* - .. figure:: {symbol1}
-
- .. figure:: {symbol2}
{subheader_sch}
.. figure:: {schematic}
:align: center
{subheader_gds}
"""
figure_template ="""
.. figure:: {fig}
:align: center
:width: 50%
{name}
"""
def write_readme(cellpath, define_data):
''' Generates README for a given cell.
Args:
cellpath - path to a cell [str of pathlib.Path]
define_data - cell data from json [dic]
'''
outpath = os.path.join(cellpath, 'README.rst')
prefix = define_data['file_prefix']
header = f':cell:`{prefix}`'
subheader_sym = header + ' symbols'
subheader_sch = header + ' schematic'
subheader_gds = header + ' GDSII layouts'
header += '\n' + '=' * len(header)
subheader_sym += '\n' + '-' * len(subheader_sym)
subheader_sch += '\n' + '-' * len(subheader_sch)
subheader_gds += '\n' + '-' * len(subheader_gds)
symbol1 = prefix + '.symbol.svg'
symbol2 = prefix + '.pp.symbol.svg'
schematic = prefix + '.schematic.svg'
inputs = []
outputs = []
for p in define_data['ports']:
try:
if p[0]=='signal' and p[2]=='input':
inputs.append(p[1])
if p[0]=='signal' and p[2]=='output':
outputs.append(p[1])
except:
pass
gdssvg = []
svglist = list(pathlib.Path(cellpath).glob('*.svg'))
for s in svglist:
gdsfile = pathlib.Path(os.path.join(cellpath, s.stem +'.gds'))
if gdsfile.is_file():
gdssvg.append(s)
with open(outpath, 'w') as f:
f.write (readme_template.format (
header = header,
subheader_sym = subheader_sym,
subheader_sch = subheader_sch,
subheader_gds = subheader_gds,
description = define_data['description'].rstrip('.'),
name = ':cell:`' + prefix +'`',
deftype = define_data['type'],
verilog_name = define_data['verilog_name'],
library = define_data['library'],
inputs = f'{len(inputs)} (' + ', '.join(inputs) + ')',
outputs = f'{len(outputs)} (' + ', '.join(outputs) + ')',
symbol1 = symbol1,
symbol2 = symbol2,
schematic = schematic,
))
for gs in sorted(gdssvg):
f.write (figure_template.format (
fig = gs.name,
name = gs.stem
))
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]
'''
if verbose:
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
# --- Sphinx extension wrapper ----------------
def GenerateCellReadme (app, cellpath):
print (f'GenerateCellReadme: generating files for {cellpath}')
path = pathlib.Path(cellpath).expanduser()
parts = path.parts[1:] if path.is_absolute() else path.parts
paths = pathlib.Path(path.root).glob(str(pathlib.Path("").joinpath(*parts)))
paths = list(paths)
cell_dirs = [d.resolve() for d in paths if d.is_dir()]
errors = 0
for d in cell_dirs:
try:
process(d)
except (AssertionError, FileNotFoundError, ChildProcessError) as ex:
print (f'GenerateCellReadme: {type(ex).__name__}')
print (f'{ex.args}')
errors +=1
print (f'GenerateCellReadme: {len(cell_dirs)} files processed, {errors} errors.')
def setup(app):
app.add_event("cells_generate_readme")
app.connect('cells_generate_readme', GenerateCellReadme)
return {
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}
# ----------------------------------------------
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())