google / open-source-pdks

Index of the fully open source process design kits (PDKs) maintained by Google.
https://open-source-pdks.rtfd.io
Apache License 2.0
84 stars 8 forks source link

Include the sphinx "roles" used to improve PDK documentation to enable sharing between PDKs #10

Closed mithro closed 2 years ago

mithro commented 2 years ago

The SkyWater PDK defines a bunch of "rst roles" in it's sphinx conf.py file. These include things like;

Which are used like this in the SkyWater PDK documentation;

:lib:`sky130_fd_io` - SKY130 IO and periphery cells (SkyWater Provided)

and like follows;

Libraries in the SKY130 PDK are named using the following scheme;

  :lib_process:`<Process name>` _ :lib_src:`<Library Source Abbreviation>` _ :lib_type:`<Library Type Abbreviation>` [_ :lib_name:`<Library Name>`]

Which results in the following colorization in the PDK documentation; image

In the future, it would also be good if it resulted in these nodes also being clickable links to useful things.

This is useful for documenting all the PDKs which follow our naming process. We should move it into this repository and make it shared.

This is done with the following Python code;

import re
from docutils.parsers.rst import directives, roles, nodes

LIB_REGEX = re.compile('sky130_(?P<lib_src>[^_\s]*)_(?P<lib_type>[^_\s]*)(_(?P<lib_name>[^_\s]*))?')
CELL_REGEX = re.compile('sky130_(?P<lib_src>[^_\s]*)_(?P<lib_type>[^_\s]*)(_(?P<lib_name>[^_\s]*))?__(?P<cell_name>[^\s]*)')

def lib_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
    """Library name which gets colorized."""
    m = LIB_REGEX.match(text)
    if not m:
        msg = inliner.reporter.error("Malformed library name of "+repr(text), line=lineno)
        prb = inliner.problematic(rawtext, rawtext, msg)
        return [prb], [msg]
    app = inliner.document.settings.env.app

    #lib_process_role = roles.role('lib_src', inliner.language, lineno, inliner.reporter)
    #lib_src_role = roles.role('lib_src', inliner.language, lineno, inliner.reporter)
    #lib_type_role = roles.role('lib_src', inliner.language, lineno, inliner.reporter)
    #lib_name_role = roles.role('lib_src', inliner.language, lineno, inliner.reporter)
    lib_process = 'sky130'
    lib_src = m.group('lib_src')
    lib_type = m.group('lib_type')
    lib_name = m.group('lib_name')

    r = [
        nodes.inline(lib_process, lib_process, classes=['lib-process']),
        nodes.inline('_', '_', options=options),
        nodes.inline(lib_src, lib_src, classes=['lib-src']),
        nodes.inline('_', '_', options=options),
        nodes.inline(lib_type, lib_type, classes=['lib-type']),
    ]
    if lib_name:
        r.append(nodes.inline('_', '_', options=options))
        r.append(nodes.inline(lib_name, lib_name, classes=['lib-name']))

    return r, []

def cell_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
    """Cell name which gets colorized."""
    m = CELL_REGEX.match(text)
    if not m:
        msg = inliner.reporter.error("Malformed cell name of "+repr(text), line=lineno)
        prb = inliner.problematic(rawtext, rawtext, msg)
        return [prb], [msg]
    app = inliner.document.settings.env.app

    #lib_process_role = roles.role('lib_src', inliner.language, lineno, inliner.reporter)
    #lib_src_role = roles.role('lib_src', inliner.language, lineno, inliner.reporter)
    #lib_type_role = roles.role('lib_src', inliner.language, lineno, inliner.reporter)
    #lib_name_role = roles.role('lib_src', inliner.language, lineno, inliner.reporter)
    lib_process = 'sky130'
    lib_src = m.group('lib_src')
    lib_type = m.group('lib_type')
    lib_name = m.group('lib_name')
    cell_name = m.group('cell_name')

    r = [
        nodes.inline(lib_process, lib_process, classes=['lib-process']),
        nodes.inline('_', '_', options=options),
        nodes.inline(lib_src, lib_src, classes=['lib-src']),
        nodes.inline('_', '_', options=options),
        nodes.inline(lib_type, lib_type, classes=['lib-type']),
    ]
    if lib_name:
        r.append(nodes.inline('_', '_', options=options))
        r.append(nodes.inline(lib_name, lib_name, classes=['lib-name']))
    r.append(nodes.inline('__', '__', options=options))
    r.append(nodes.inline(cell_name, cell_name, classes=['cell-name']))

    return r, []

def add_role(app, new_role_name):
    options = {
        'class': directives.class_option(new_role_name),
    }
    role = roles.CustomRole(new_role_name, roles.generic_custom_role, options, "")
    app.add_role(new_role_name, role)

def setup(app):
    app.add_css_file('extra.css')
    add_role(app, 'cell_name')
    add_role(app, 'lib_process')
    add_role(app, 'lib_src')
    add_role(app, 'lib_type')
    add_role(app, 'lib_name')
    add_role(app, 'drc_rule')
    add_role(app, 'drc_tag')
    add_role(app, 'drc_flag')
    add_role(app, 'layer')

    app.add_role('lib', lib_role)
    app.add_role('cell', cell_role)
    app.add_role('model', cell_role)