mirror of https://github.com/YosysHQ/yosys.git
Docs: Preliminary autocellgroup usage
Remove `/source/cell` from .gitignore. Add a few initial cell pages. Add YosysCellGroup documenter and cell:group directive. Update Documenters to use nested json. Better nested tocs for group.module.source layout.
This commit is contained in:
parent
5a4a4191af
commit
b127ac07f8
|
@ -1,6 +1,5 @@
|
||||||
/build/
|
/build/
|
||||||
/source/cmd
|
/source/cmd
|
||||||
/source/cell
|
|
||||||
/source/generated
|
/source/generated
|
||||||
/source/_images/**/*.log
|
/source/_images/**/*.log
|
||||||
/source/_images/**/*.aux
|
/source/_images/**/*.aux
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
.. autocellgroup:: gate_other
|
||||||
|
:caption: Other gate-level cells
|
||||||
|
:members:
|
||||||
|
:source:
|
||||||
|
:linenos:
|
|
@ -0,0 +1,5 @@
|
||||||
|
.. autocellgroup:: word_other
|
||||||
|
:caption: Other word-level cells
|
||||||
|
:members:
|
||||||
|
:source:
|
||||||
|
:linenos:
|
|
@ -0,0 +1,50 @@
|
||||||
|
.. role:: verilog(code)
|
||||||
|
:language: Verilog
|
||||||
|
|
||||||
|
Unary operators
|
||||||
|
---------------
|
||||||
|
|
||||||
|
All unary RTL cells have one input port ``A`` and one output port ``Y``. They
|
||||||
|
also have the following parameters:
|
||||||
|
|
||||||
|
``A_SIGNED``
|
||||||
|
Set to a non-zero value if the input ``A`` is signed and therefore should be
|
||||||
|
sign-extended when needed.
|
||||||
|
|
||||||
|
``A_WIDTH``
|
||||||
|
The width of the input port ``A``.
|
||||||
|
|
||||||
|
``Y_WIDTH``
|
||||||
|
The width of the output port ``Y``.
|
||||||
|
|
||||||
|
.. table:: Cell types for unary operators with their corresponding Verilog expressions.
|
||||||
|
|
||||||
|
================== ==============
|
||||||
|
Verilog Cell Type
|
||||||
|
================== ==============
|
||||||
|
:verilog:`Y = ~A` `$not`
|
||||||
|
:verilog:`Y = +A` `$pos`
|
||||||
|
:verilog:`Y = -A` `$neg`
|
||||||
|
:verilog:`Y = &A` `$reduce_and`
|
||||||
|
:verilog:`Y = |A` `$reduce_or`
|
||||||
|
:verilog:`Y = ^A` `$reduce_xor`
|
||||||
|
:verilog:`Y = ~^A` `$reduce_xnor`
|
||||||
|
:verilog:`Y = |A` `$reduce_bool`
|
||||||
|
:verilog:`Y = !A` `$logic_not`
|
||||||
|
================== ==============
|
||||||
|
|
||||||
|
For the unary cells that output a logical value (`$reduce_and`, `$reduce_or`,
|
||||||
|
`$reduce_xor`, `$reduce_xnor`, `$reduce_bool`, `$logic_not`), when the
|
||||||
|
``Y_WIDTH`` parameter is greater than 1, the output is zero-extended, and only
|
||||||
|
the least significant bit varies.
|
||||||
|
|
||||||
|
Note that `$reduce_or` and `$reduce_bool` actually represent the same logic
|
||||||
|
function. But the HDL frontends generate them in different situations. A
|
||||||
|
`$reduce_or` cell is generated when the prefix ``|`` operator is being used. A
|
||||||
|
`$reduce_bool` cell is generated when a bit vector is used as a condition in an
|
||||||
|
``if``-statement or ``?:``-expression.
|
||||||
|
|
||||||
|
.. autocellgroup:: unary
|
||||||
|
:members:
|
||||||
|
:source:
|
||||||
|
:linenos:
|
|
@ -3,7 +3,6 @@ Gate-level cells
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:caption: Gate-level cells
|
:caption: Gate-level cells
|
||||||
:maxdepth: 1
|
:maxdepth: 2
|
||||||
:glob:
|
|
||||||
|
|
||||||
/cell/gate_*
|
/cell/gate_other
|
||||||
|
|
|
@ -2,8 +2,8 @@ Word-level cells
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:caption: Word-level cells
|
:maxdepth: 2
|
||||||
:maxdepth: 1
|
|
||||||
:glob:
|
:glob:
|
||||||
|
|
||||||
/cell/word_*
|
/cell/word_unary
|
||||||
|
/cell/word_other
|
||||||
|
|
|
@ -16,7 +16,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# cell signature
|
# cell signature
|
||||||
cell_ext_sig_re = re.compile(
|
cell_ext_sig_re = re.compile(
|
||||||
r'''^ (?:([^:\s]+):)? # explicit file name
|
r'''^ ([^:\s]+::)? # optional group or file name
|
||||||
([\w$._]+?) # module name
|
([\w$._]+?) # module name
|
||||||
(?:\.([\w_]+))? # optional: thing name
|
(?:\.([\w_]+))? # optional: thing name
|
||||||
(::[\w_]+)? # attribute
|
(::[\w_]+)? # attribute
|
||||||
|
@ -25,7 +25,7 @@ cell_ext_sig_re = re.compile(
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class YosysCell:
|
class YosysCell:
|
||||||
cell: str
|
name: str
|
||||||
title: str
|
title: str
|
||||||
ports: str
|
ports: str
|
||||||
source: str
|
source: str
|
||||||
|
@ -34,22 +34,26 @@ class YosysCell:
|
||||||
inputs: list[str]
|
inputs: list[str]
|
||||||
outputs: list[str]
|
outputs: list[str]
|
||||||
properties: dict[str, bool]
|
properties: dict[str, bool]
|
||||||
|
|
||||||
class YosysCellDocumenter(Documenter):
|
class YosysCellGroupDocumenter(Documenter):
|
||||||
objtype = 'cell'
|
objtype = 'cellgroup'
|
||||||
object: YosysCell
|
priority = 10
|
||||||
|
object: tuple[str, list[str]]
|
||||||
|
lib_key = 'groups'
|
||||||
|
|
||||||
option_spec = {
|
option_spec = {
|
||||||
|
'caption': autodoc.annotation_option,
|
||||||
|
'members': autodoc.members_option,
|
||||||
'source': autodoc.bool_option,
|
'source': autodoc.bool_option,
|
||||||
'linenos': autodoc.bool_option,
|
'linenos': autodoc.bool_option,
|
||||||
}
|
}
|
||||||
|
|
||||||
__cell_lib: dict[str, YosysCell] | None = None
|
__cell_lib: dict[str, list[str] | dict[str]] | None = None
|
||||||
@property
|
@property
|
||||||
def cell_lib(self) -> dict[str, YosysCell]:
|
def cell_lib(self) -> dict[str, list[str] | dict[str]]:
|
||||||
if not self.__cell_lib:
|
if not self.__cell_lib:
|
||||||
self.__cell_lib = {}
|
self.__cell_lib = {}
|
||||||
cells_obj: dict[str, list[dict[str, list[dict[str]]]]]
|
cells_obj: dict[str, dict[str, list[str] | dict[str]]]
|
||||||
try:
|
try:
|
||||||
with open(self.config.cells_json, "r") as f:
|
with open(self.config.cells_json, "r") as f:
|
||||||
cells_obj = json.loads(f.read())
|
cells_obj = json.loads(f.read())
|
||||||
|
@ -60,12 +64,10 @@ class YosysCellDocumenter(Documenter):
|
||||||
subtype = 'cell_lib'
|
subtype = 'cell_lib'
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
for group in cells_obj.get("groups", []):
|
for (name, obj) in cells_obj.get(self.lib_key, {}).items():
|
||||||
for cell in group.get("cells", []):
|
self.__cell_lib[name] = obj
|
||||||
yosysCell = YosysCell(**cell)
|
|
||||||
self.__cell_lib[yosysCell.cell] = yosysCell
|
|
||||||
return self.__cell_lib
|
return self.__cell_lib
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_document_member(
|
def can_document_member(
|
||||||
cls,
|
cls,
|
||||||
|
@ -74,75 +76,51 @@ class YosysCellDocumenter(Documenter):
|
||||||
isattr: bool,
|
isattr: bool,
|
||||||
parent: Any
|
parent: Any
|
||||||
) -> bool:
|
) -> bool:
|
||||||
sourcename = str(member).split(":")[0]
|
return False
|
||||||
if not sourcename.endswith(".v"):
|
|
||||||
return False
|
|
||||||
if membername == "__source":
|
|
||||||
return False
|
|
||||||
|
|
||||||
def parse_name(self) -> bool:
|
def parse_name(self) -> bool:
|
||||||
try:
|
if not self.options.caption:
|
||||||
matched = cell_ext_sig_re.match(self.name)
|
self.content_indent = ''
|
||||||
path, modname, thing, attribute = matched.groups()
|
self.fullname = self.modname = self.name
|
||||||
except AttributeError:
|
|
||||||
logger.warning(('invalid signature for auto%s (%r)') % (self.objtype, self.name),
|
|
||||||
type='cellref')
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.modname = modname
|
|
||||||
self.objpath = [path]
|
|
||||||
self.attribute = attribute
|
|
||||||
self.fullname = ((self.modname) + (thing or ''))
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def import_object(self, raiseerror: bool = False) -> bool:
|
def import_object(self, raiseerror: bool = False) -> bool:
|
||||||
# get cell
|
# get cell
|
||||||
try:
|
try:
|
||||||
self.object = self.cell_lib[self.modname]
|
self.object = (self.modname, self.cell_lib[self.modname])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
if raiseerror:
|
||||||
|
raise
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.real_modname = self.modname or ''
|
self.real_modname = self.modname
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_sourcename(self) -> str:
|
def get_sourcename(self) -> str:
|
||||||
return self.object.source.split(":")[0]
|
return self.env.doc2path(self.env.docname)
|
||||||
|
|
||||||
def format_name(self) -> str:
|
def format_name(self) -> str:
|
||||||
return self.object.cell
|
return self.options.caption or ''
|
||||||
|
|
||||||
def format_signature(self, **kwargs: Any) -> str:
|
def format_signature(self, **kwargs: Any) -> str:
|
||||||
return f"{self.object.cell} {self.object.ports}"
|
return self.modname
|
||||||
|
|
||||||
def add_directive_header(self, sig: str) -> None:
|
def add_directive_header(self, sig: str) -> None:
|
||||||
domain = getattr(self, 'domain', self.objtype)
|
domain = getattr(self, 'domain', 'cell')
|
||||||
directive = getattr(self, 'directivetype', 'def')
|
directive = getattr(self, 'directivetype', 'group')
|
||||||
name = self.format_name()
|
name = self.format_name()
|
||||||
sourcename = self.get_sourcename()
|
sourcename = self.get_sourcename()
|
||||||
cell = self.object
|
cell_list = self.object
|
||||||
|
|
||||||
# cell definition
|
# cell definition
|
||||||
self.add_line(f'.. {domain}:{directive}:: {name}', sourcename)
|
self.add_line(f'.. {domain}:{directive}:: {sig}', sourcename)
|
||||||
|
self.add_line(f' :caption: {name}', sourcename)
|
||||||
# options
|
|
||||||
opt_attrs = ["title", ]
|
|
||||||
for attr in opt_attrs:
|
|
||||||
val = getattr(cell, attr, None)
|
|
||||||
if val:
|
|
||||||
self.add_line(f' :{attr}: {val}', sourcename)
|
|
||||||
|
|
||||||
if self.options.noindex:
|
if self.options.noindex:
|
||||||
self.add_line(' :noindex:', sourcename)
|
self.add_line(' :noindex:', sourcename)
|
||||||
|
|
||||||
def add_content(self, more_content: Any | None) -> None:
|
def add_content(self, more_content: Any | None) -> None:
|
||||||
# set sourcename and add content from attribute documentation
|
# groups have no native content
|
||||||
sourcename = self.get_sourcename()
|
|
||||||
startline = int(self.object.source.split(":")[1])
|
|
||||||
|
|
||||||
for i, line in enumerate(self.object.desc.splitlines(), startline):
|
|
||||||
self.add_line(line, sourcename, i)
|
|
||||||
|
|
||||||
# add additional content (e.g. from document), if present
|
# add additional content (e.g. from document), if present
|
||||||
if more_content:
|
if more_content:
|
||||||
for line, src in zip(more_content.data, more_content.items):
|
for line, src in zip(more_content.data, more_content.items):
|
||||||
|
@ -161,14 +139,25 @@ class YosysCellDocumenter(Documenter):
|
||||||
) -> tuple[bool, list[tuple[str, Any]]]:
|
) -> tuple[bool, list[tuple[str, Any]]]:
|
||||||
ret: list[tuple[str, str]] = []
|
ret: list[tuple[str, str]] = []
|
||||||
|
|
||||||
if self.options.source:
|
if want_all:
|
||||||
ret.append(('__source', self.real_modname))
|
for member in self.object[1]:
|
||||||
|
ret.append((member, self.modname))
|
||||||
|
else:
|
||||||
|
memberlist = self.options.members or []
|
||||||
|
for name in memberlist:
|
||||||
|
if name in self.object:
|
||||||
|
ret.append((name, self.modname))
|
||||||
|
else:
|
||||||
|
logger.warning(('unknown module mentioned in :members: option: '
|
||||||
|
f'group {self.modname}, module {name}'),
|
||||||
|
type='cellref')
|
||||||
|
|
||||||
return False, ret
|
return False, ret
|
||||||
|
|
||||||
def document_members(self, all_members: bool = False) -> None:
|
def document_members(self, all_members: bool = False) -> None:
|
||||||
want_all = (all_members or
|
want_all = (all_members or
|
||||||
self.options.inherited_members)
|
self.options.inherited_members or
|
||||||
|
self.options.members is autodoc.ALL)
|
||||||
# find out which members are documentable
|
# find out which members are documentable
|
||||||
members_check_module, members = self.get_object_members(want_all)
|
members_check_module, members = self.get_object_members(want_all)
|
||||||
|
|
||||||
|
@ -184,7 +173,7 @@ class YosysCellDocumenter(Documenter):
|
||||||
classes.sort(key=lambda cls: cls.priority)
|
classes.sort(key=lambda cls: cls.priority)
|
||||||
# give explicitly separated module name, so that members
|
# give explicitly separated module name, so that members
|
||||||
# of inner classes can be documented
|
# of inner classes can be documented
|
||||||
full_mname = self.real_modname + '::' + mname
|
full_mname = self.format_signature() + '::' + mname
|
||||||
documenter = classes[-1](self.directive, full_mname, self.indent)
|
documenter = classes[-1](self.directive, full_mname, self.indent)
|
||||||
memberdocumenters.append((documenter, isattr))
|
memberdocumenters.append((documenter, isattr))
|
||||||
|
|
||||||
|
@ -247,6 +236,101 @@ class YosysCellDocumenter(Documenter):
|
||||||
# document members, if possible
|
# document members, if possible
|
||||||
self.document_members(all_members)
|
self.document_members(all_members)
|
||||||
|
|
||||||
|
class YosysCellDocumenter(YosysCellGroupDocumenter):
|
||||||
|
objtype = 'cell'
|
||||||
|
priority = 15
|
||||||
|
object: YosysCell
|
||||||
|
lib_key = 'cells'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def can_document_member(
|
||||||
|
cls,
|
||||||
|
member: Any,
|
||||||
|
membername: str,
|
||||||
|
isattr: bool,
|
||||||
|
parent: Any
|
||||||
|
) -> bool:
|
||||||
|
if membername == "__source":
|
||||||
|
return False
|
||||||
|
if not membername.startswith('$'):
|
||||||
|
return False
|
||||||
|
return isinstance(parent, YosysCellGroupDocumenter)
|
||||||
|
|
||||||
|
def parse_name(self) -> bool:
|
||||||
|
try:
|
||||||
|
matched = cell_ext_sig_re.match(self.name)
|
||||||
|
group, modname, thing, attribute = matched.groups()
|
||||||
|
except AttributeError:
|
||||||
|
logger.warning(('invalid signature for auto%s (%r)') % (self.objtype, self.name),
|
||||||
|
type='cellref')
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.modname = modname
|
||||||
|
self.groupname = group or ''
|
||||||
|
self.attribute = attribute or ''
|
||||||
|
self.fullname = ((self.modname) + (thing or ''))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def import_object(self, raiseerror: bool = False) -> bool:
|
||||||
|
if super().import_object(raiseerror):
|
||||||
|
self.object = YosysCell(self.modname, **self.object[1])
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_sourcename(self) -> str:
|
||||||
|
return self.object.source.split(":")[0]
|
||||||
|
|
||||||
|
def format_name(self) -> str:
|
||||||
|
return self.object.name
|
||||||
|
|
||||||
|
def format_signature(self, **kwargs: Any) -> str:
|
||||||
|
return self.groupname + self.fullname + self.attribute
|
||||||
|
|
||||||
|
def add_directive_header(self, sig: str) -> None:
|
||||||
|
domain = getattr(self, 'domain', self.objtype)
|
||||||
|
directive = getattr(self, 'directivetype', 'def')
|
||||||
|
name = self.format_name()
|
||||||
|
sourcename = self.get_sourcename()
|
||||||
|
cell = self.object
|
||||||
|
|
||||||
|
# cell definition
|
||||||
|
self.add_line(f'.. {domain}:{directive}:: {sig}', sourcename)
|
||||||
|
|
||||||
|
# options
|
||||||
|
opt_attrs = ["title", ]
|
||||||
|
for attr in opt_attrs:
|
||||||
|
val = getattr(cell, attr, None)
|
||||||
|
if val:
|
||||||
|
self.add_line(f' :{attr}: {val}', sourcename)
|
||||||
|
|
||||||
|
if self.options.noindex:
|
||||||
|
self.add_line(' :noindex:', sourcename)
|
||||||
|
|
||||||
|
def add_content(self, more_content: Any | None) -> None:
|
||||||
|
# set sourcename and add content from attribute documentation
|
||||||
|
sourcename = self.get_sourcename()
|
||||||
|
startline = int(self.object.source.split(":")[1])
|
||||||
|
|
||||||
|
for i, line in enumerate(self.object.desc.splitlines(), startline):
|
||||||
|
self.add_line(line, sourcename, i)
|
||||||
|
|
||||||
|
# add additional content (e.g. from document), if present
|
||||||
|
if more_content:
|
||||||
|
for line, src in zip(more_content.data, more_content.items):
|
||||||
|
self.add_line(line, src[0], src[1])
|
||||||
|
|
||||||
|
def get_object_members(
|
||||||
|
self,
|
||||||
|
want_all: bool
|
||||||
|
) -> tuple[bool, list[tuple[str, Any]]]:
|
||||||
|
ret: list[tuple[str, str]] = []
|
||||||
|
|
||||||
|
if self.options.source:
|
||||||
|
ret.append(('__source', self.real_modname))
|
||||||
|
|
||||||
|
return False, ret
|
||||||
|
|
||||||
class YosysCellSourceDocumenter(YosysCellDocumenter):
|
class YosysCellSourceDocumenter(YosysCellDocumenter):
|
||||||
objtype = 'cellsource'
|
objtype = 'cellsource'
|
||||||
priority = 20
|
priority = 20
|
||||||
|
@ -273,7 +357,7 @@ class YosysCellSourceDocumenter(YosysCellDocumenter):
|
||||||
cell = self.object
|
cell = self.object
|
||||||
|
|
||||||
# cell definition
|
# cell definition
|
||||||
self.add_line(f'.. {domain}:{directive}:: {name}', sourcename)
|
self.add_line(f'.. {domain}:{directive}:: {sig}', sourcename)
|
||||||
|
|
||||||
if self.options.linenos:
|
if self.options.linenos:
|
||||||
self.add_line(f' :source: {cell.source.split(":")[0]}', sourcename)
|
self.add_line(f' :source: {cell.source.split(":")[0]}', sourcename)
|
||||||
|
@ -312,6 +396,7 @@ def setup(app: Sphinx) -> dict[str, Any]:
|
||||||
app.setup_extension('sphinx.ext.autodoc')
|
app.setup_extension('sphinx.ext.autodoc')
|
||||||
app.add_autodocumenter(YosysCellDocumenter)
|
app.add_autodocumenter(YosysCellDocumenter)
|
||||||
app.add_autodocumenter(YosysCellSourceDocumenter)
|
app.add_autodocumenter(YosysCellSourceDocumenter)
|
||||||
|
app.add_autodocumenter(YosysCellGroupDocumenter)
|
||||||
return {
|
return {
|
||||||
'version': '1',
|
'version': '1',
|
||||||
'parallel_read_safe': True,
|
'parallel_read_safe': True,
|
||||||
|
|
|
@ -15,7 +15,16 @@ from sphinx.directives.code import container_wrapper
|
||||||
from sphinx.util.nodes import make_refnode
|
from sphinx.util.nodes import make_refnode
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
|
|
||||||
class TocNode(ObjectDescription):
|
class TocNode(ObjectDescription):
|
||||||
|
def add_target_and_index(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
sig: str,
|
||||||
|
signode: addnodes.desc_signature
|
||||||
|
) -> None:
|
||||||
|
idx = ".".join(name.split("::"))
|
||||||
|
signode['ids'].append(idx)
|
||||||
|
|
||||||
def _object_hierarchy_parts(self, sig_node: addnodes.desc_signature) -> tuple[str, ...]:
|
def _object_hierarchy_parts(self, sig_node: addnodes.desc_signature) -> tuple[str, ...]:
|
||||||
if 'fullname' not in sig_node:
|
if 'fullname' not in sig_node:
|
||||||
return ()
|
return ()
|
||||||
|
@ -34,17 +43,13 @@ class TocNode(ObjectDescription):
|
||||||
|
|
||||||
config = self.env.app.config
|
config = self.env.app.config
|
||||||
objtype = sig_node.parent.get('objtype')
|
objtype = sig_node.parent.get('objtype')
|
||||||
if config.add_function_parentheses and objtype in {'function', 'method'}:
|
|
||||||
parens = '()'
|
|
||||||
else:
|
|
||||||
parens = ''
|
|
||||||
*parents, name = sig_node['_toc_parts']
|
*parents, name = sig_node['_toc_parts']
|
||||||
if config.toc_object_entries_show_parents == 'domain':
|
if config.toc_object_entries_show_parents == 'domain':
|
||||||
return sig_node.get('fullname', name) + parens
|
return sig_node.get('tocname', name)
|
||||||
if config.toc_object_entries_show_parents == 'hide':
|
if config.toc_object_entries_show_parents == 'hide':
|
||||||
return name + parens
|
return name
|
||||||
if config.toc_object_entries_show_parents == 'all':
|
if config.toc_object_entries_show_parents == 'all':
|
||||||
return '.'.join(parents + [name + parens])
|
return '.'.join(parents + [name])
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
class CommandNode(TocNode):
|
class CommandNode(TocNode):
|
||||||
|
@ -59,11 +64,10 @@ class CommandNode(TocNode):
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_signature(self, sig, signode: addnodes.desc_signature):
|
def handle_signature(self, sig, signode: addnodes.desc_signature):
|
||||||
fullname = sig
|
signode['fullname'] = sig
|
||||||
signode['fullname'] = fullname
|
|
||||||
signode += addnodes.desc_addname(text="yosys> help ")
|
signode += addnodes.desc_addname(text="yosys> help ")
|
||||||
signode += addnodes.desc_name(text=sig)
|
signode += addnodes.desc_name(text=sig)
|
||||||
return fullname
|
return signode['fullname']
|
||||||
|
|
||||||
def add_target_and_index(self, name_cls, sig, signode):
|
def add_target_and_index(self, name_cls, sig, signode):
|
||||||
signode['ids'].append(type(self).name + '-' + sig)
|
signode['ids'].append(type(self).name + '-' + sig)
|
||||||
|
@ -82,11 +86,47 @@ class CommandNode(TocNode):
|
||||||
type(self).name + '-' + sig,
|
type(self).name + '-' + sig,
|
||||||
0))
|
0))
|
||||||
|
|
||||||
class CellNode(CommandNode):
|
class CellNode(TocNode):
|
||||||
"""A custom node that describes an internal cell."""
|
"""A custom node that describes an internal cell."""
|
||||||
|
|
||||||
name = 'cell'
|
name = 'cell'
|
||||||
|
|
||||||
|
option_spec = {
|
||||||
|
'title': directives.unchanged,
|
||||||
|
'ports': directives.unchanged,
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_signature(self, sig: str, signode: addnodes.desc_signature):
|
||||||
|
signode['fullname'] = sig
|
||||||
|
signode['tocname'] = tocname = sig.split('::')[-1]
|
||||||
|
signode += addnodes.desc_addname(text="yosys> help ")
|
||||||
|
signode += addnodes.desc_name(text=tocname)
|
||||||
|
return signode['fullname']
|
||||||
|
|
||||||
|
def add_target_and_index(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
sig: str,
|
||||||
|
signode: addnodes.desc_signature
|
||||||
|
) -> None:
|
||||||
|
idx = ".".join(name.split("::"))
|
||||||
|
signode['ids'].append(idx)
|
||||||
|
if 'noindex' not in self.options:
|
||||||
|
tocname: str = signode.get('tocname', name)
|
||||||
|
tagmap = self.env.domaindata[self.domain]['obj2tag']
|
||||||
|
tagmap[name] = list(self.options.get('tags', '').split(' '))
|
||||||
|
title: str = self.options.get('title', sig)
|
||||||
|
titlemap = self.env.domaindata[self.domain]['obj2title']
|
||||||
|
titlemap[name] = title
|
||||||
|
objs = self.env.domaindata[self.domain]['objects']
|
||||||
|
# (name, sig, typ, docname, anchor, prio)
|
||||||
|
objs.append((name,
|
||||||
|
tocname,
|
||||||
|
title,
|
||||||
|
self.env.docname,
|
||||||
|
idx,
|
||||||
|
0))
|
||||||
|
|
||||||
class CellSourceNode(TocNode):
|
class CellSourceNode(TocNode):
|
||||||
"""A custom code block for including cell source."""
|
"""A custom code block for including cell source."""
|
||||||
|
|
||||||
|
@ -104,21 +144,12 @@ class CellSourceNode(TocNode):
|
||||||
signode: addnodes.desc_signature
|
signode: addnodes.desc_signature
|
||||||
) -> str:
|
) -> str:
|
||||||
language = self.options.get('language')
|
language = self.options.get('language')
|
||||||
fullname = sig + "::" + language
|
signode['fullname'] = sig
|
||||||
signode['fullname'] = fullname
|
signode['tocname'] = f"{sig.split('::')[-2]} {language}"
|
||||||
signode += addnodes.desc_name(text="Simulation model")
|
signode += addnodes.desc_name(text="Simulation model")
|
||||||
signode += addnodes.desc_sig_space()
|
signode += addnodes.desc_sig_space()
|
||||||
signode += addnodes.desc_addname(text=f'({language})')
|
signode += addnodes.desc_addname(text=f'({language})')
|
||||||
return fullname
|
return signode['fullname']
|
||||||
|
|
||||||
def add_target_and_index(
|
|
||||||
self,
|
|
||||||
name: str,
|
|
||||||
sig: str,
|
|
||||||
signode: addnodes.desc_signature
|
|
||||||
) -> None:
|
|
||||||
idx = f'{".".join(self.name.split(":"))}.{sig}'
|
|
||||||
signode['ids'].append(idx)
|
|
||||||
|
|
||||||
def run(self) -> list[Node]:
|
def run(self) -> list[Node]:
|
||||||
"""Override run to parse content as a code block"""
|
"""Override run to parse content as a code block"""
|
||||||
|
@ -192,6 +223,29 @@ class CellSourceNode(TocNode):
|
||||||
|
|
||||||
return [self.indexnode, node, literal]
|
return [self.indexnode, node, literal]
|
||||||
|
|
||||||
|
class CellGroupNode(TocNode):
|
||||||
|
name = 'cellgroup'
|
||||||
|
|
||||||
|
option_spec = {
|
||||||
|
'caption': directives.unchanged,
|
||||||
|
}
|
||||||
|
|
||||||
|
def add_target_and_index(self, name: str, sig: str, signode: addnodes.desc_signature) -> None:
|
||||||
|
if self.options.get('caption', ''):
|
||||||
|
super().add_target_and_index(name, sig, signode)
|
||||||
|
|
||||||
|
def handle_signature(
|
||||||
|
self,
|
||||||
|
sig,
|
||||||
|
signode: addnodes.desc_signature
|
||||||
|
) -> str:
|
||||||
|
signode['fullname'] = fullname = sig
|
||||||
|
caption = self.options.get("caption", fullname)
|
||||||
|
if caption:
|
||||||
|
signode['tocname'] = caption
|
||||||
|
signode += addnodes.desc_name(text=caption)
|
||||||
|
return fullname
|
||||||
|
|
||||||
class TagIndex(Index):
|
class TagIndex(Index):
|
||||||
"""A custom directive that creates a tag matrix."""
|
"""A custom directive that creates a tag matrix."""
|
||||||
|
|
||||||
|
@ -368,6 +422,7 @@ class CellDomain(CommandDomain):
|
||||||
directives = {
|
directives = {
|
||||||
'def': CellNode,
|
'def': CellNode,
|
||||||
'source': CellSourceNode,
|
'source': CellSourceNode,
|
||||||
|
'group': CellGroupNode,
|
||||||
}
|
}
|
||||||
|
|
||||||
indices = {
|
indices = {
|
||||||
|
|
|
@ -958,7 +958,8 @@ struct HelpPass : public Pass {
|
||||||
json.entry("version", "Yosys internal cells");
|
json.entry("version", "Yosys internal cells");
|
||||||
json.entry("generator", yosys_version_str);
|
json.entry("generator", yosys_version_str);
|
||||||
|
|
||||||
dict<string, dict<string, pair<SimHelper, CellType>>> groups;
|
dict<string, vector<string>> groups;
|
||||||
|
dict<string, pair<SimHelper, CellType>> cells;
|
||||||
|
|
||||||
// iterate over cells
|
// iterate over cells
|
||||||
bool raise_error = false;
|
bool raise_error = false;
|
||||||
|
@ -966,59 +967,61 @@ struct HelpPass : public Pass {
|
||||||
auto name = it.first.str();
|
auto name = it.first.str();
|
||||||
if (cell_help_messages.contains(name)) {
|
if (cell_help_messages.contains(name)) {
|
||||||
auto cell_help = cell_help_messages.get(name);
|
auto cell_help = cell_help_messages.get(name);
|
||||||
dict<string, pair<SimHelper, CellType>> *cell_group;
|
|
||||||
if (groups.count(cell_help.group) != 0) {
|
if (groups.count(cell_help.group) != 0) {
|
||||||
cell_group = &groups.at(cell_help.group);
|
auto group_cells = &groups.at(cell_help.group);
|
||||||
|
group_cells->push_back(name);
|
||||||
} else {
|
} else {
|
||||||
cell_group = new dict<string, pair<SimHelper, CellType>>();
|
auto group_cells = new vector<string>(1, name);
|
||||||
groups.emplace(cell_help.group, *cell_group);
|
groups.emplace(cell_help.group, *group_cells);
|
||||||
}
|
}
|
||||||
auto cell_pair = pair<SimHelper, CellType>(cell_help, it.second);
|
auto cell_pair = pair<SimHelper, CellType>(cell_help, it.second);
|
||||||
cell_group->emplace(name, cell_pair);
|
cells.emplace(name, cell_pair);
|
||||||
} else {
|
} else {
|
||||||
log("ERROR: Missing cell help for cell '%s'.\n", name.c_str());
|
log("ERROR: Missing cell help for cell '%s'.\n", name.c_str());
|
||||||
raise_error |= true;
|
raise_error |= true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto &it : cell_help_messages.cell_help) {
|
||||||
|
if (cells.count(it.first) == 0) {
|
||||||
|
log_warning("Found cell model '%s' without matching cell type.\n", it.first.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// write to json
|
// write to json
|
||||||
json.name("groups");
|
json.name("groups"); json.begin_object();
|
||||||
json.begin_array();
|
|
||||||
groups.sort();
|
groups.sort();
|
||||||
for (auto &it : groups) {
|
for (auto &it : groups) {
|
||||||
json.begin_object();
|
json.name(it.first.c_str()); json.value(it.second);
|
||||||
json.name("group"); json.value(it.first.c_str());
|
}
|
||||||
json.name("cells"); json.begin_array();
|
json.end_object();
|
||||||
for (auto &it2 : it.second) {
|
|
||||||
auto ch = it2.second.first;
|
json.name("cells"); json.begin_object();
|
||||||
auto ct = it2.second.second;
|
cells.sort();
|
||||||
json.begin_object();
|
for (auto &it : cells) {
|
||||||
json.name("cell"); json.value(ch.name);
|
auto ch = it.second.first;
|
||||||
json.name("title"); json.value(ch.title);
|
auto ct = it.second.second;
|
||||||
json.name("ports"); json.value(ch.ports);
|
json.name(ch.name.c_str()); json.begin_object();
|
||||||
json.name("source"); json.value(ch.source);
|
json.name("title"); json.value(ch.title);
|
||||||
json.name("desc"); json.value(ch.desc);
|
json.name("ports"); json.value(ch.ports);
|
||||||
json.name("code"); json.value(ch.code);
|
json.name("source"); json.value(ch.source);
|
||||||
json.name("inputs"); json.begin_array();
|
json.name("desc"); json.value(ch.desc);
|
||||||
for (auto &input : ct.inputs)
|
json.name("code"); json.value(ch.code);
|
||||||
json.value(input.c_str());
|
vector<string> inputs, outputs;
|
||||||
json.end_array();
|
for (auto &input : ct.inputs)
|
||||||
json.name("outputs"); json.begin_array();
|
inputs.push_back(input.str());
|
||||||
for (auto &output : ct.outputs)
|
json.name("inputs"); json.value(inputs);
|
||||||
json.value(output.c_str());
|
for (auto &output : ct.outputs)
|
||||||
json.end_array();
|
outputs.push_back(output.str());
|
||||||
dict<string, bool> prop_dict = {
|
json.name("outputs"); json.value(outputs);
|
||||||
{"is_evaluable", ct.is_evaluable},
|
dict<string, bool> prop_dict = {
|
||||||
{"is_combinatorial", ct.is_combinatorial},
|
{"is_evaluable", ct.is_evaluable},
|
||||||
{"is_synthesizable", ct.is_synthesizable},
|
{"is_combinatorial", ct.is_combinatorial},
|
||||||
};
|
{"is_synthesizable", ct.is_synthesizable},
|
||||||
json.name("properties"); json.value(prop_dict);
|
};
|
||||||
json.end_object();
|
json.name("properties"); json.value(prop_dict);
|
||||||
}
|
|
||||||
json.end_array();
|
|
||||||
json.end_object();
|
json.end_object();
|
||||||
}
|
}
|
||||||
json.end_array();
|
json.end_object();
|
||||||
|
|
||||||
json.end_object();
|
json.end_object();
|
||||||
return raise_error;
|
return raise_error;
|
||||||
|
|
Loading…
Reference in New Issue