| #!/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()) |
| |