mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-30 19:22:31 +00:00 
			
		
		
		
	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
					
				
					 9 changed files with 331 additions and 130 deletions
				
			
		
							
								
								
									
										1
									
								
								docs/.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								docs/.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,6 +1,5 @@ | |||
| /build/ | ||||
| /source/cmd | ||||
| /source/cell | ||||
| /source/generated | ||||
| /source/_images/**/*.log | ||||
| /source/_images/**/*.aux | ||||
|  |  | |||
							
								
								
									
										5
									
								
								docs/source/cell/gate_other.rst
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								docs/source/cell/gate_other.rst
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| .. autocellgroup:: gate_other | ||||
|    :caption: Other gate-level cells | ||||
|    :members: | ||||
|    :source: | ||||
|    :linenos: | ||||
							
								
								
									
										5
									
								
								docs/source/cell/word_other.rst
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								docs/source/cell/word_other.rst
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| .. autocellgroup:: word_other | ||||
|    :caption: Other word-level cells | ||||
|    :members: | ||||
|    :source: | ||||
|    :linenos: | ||||
							
								
								
									
										50
									
								
								docs/source/cell/word_unary.rst
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								docs/source/cell/word_unary.rst
									
										
									
									
									
										Normal file
									
								
							|  | @ -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:: | ||||
|    :caption: Gate-level cells | ||||
|    :maxdepth: 1 | ||||
|    :glob: | ||||
|    :maxdepth: 2 | ||||
| 
 | ||||
|    /cell/gate_* | ||||
|    /cell/gate_other | ||||
|  |  | |||
|  | @ -2,8 +2,8 @@ Word-level cells | |||
| ---------------- | ||||
| 
 | ||||
| .. toctree:: | ||||
|    :caption: Word-level cells | ||||
|    :maxdepth: 1 | ||||
|    :maxdepth: 2 | ||||
|    :glob: | ||||
| 
 | ||||
|    /cell/word_* | ||||
|    /cell/word_unary | ||||
|    /cell/word_other | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ logger = logging.getLogger(__name__) | |||
| 
 | ||||
| # cell signature | ||||
| cell_ext_sig_re = re.compile( | ||||
|     r'''^ (?:([^:\s]+):)?        # explicit file name | ||||
|     r'''^ ([^:\s]+::)?           # optional group or file name | ||||
|           ([\w$._]+?)            # module name | ||||
|           (?:\.([\w_]+))?        # optional: thing name | ||||
|           (::[\w_]+)?            #           attribute | ||||
|  | @ -25,7 +25,7 @@ cell_ext_sig_re = re.compile( | |||
| 
 | ||||
| @dataclass | ||||
| class YosysCell: | ||||
|     cell: str | ||||
|     name: str | ||||
|     title: str | ||||
|     ports: str | ||||
|     source: str | ||||
|  | @ -35,21 +35,25 @@ class YosysCell: | |||
|     outputs: list[str] | ||||
|     properties: dict[str, bool] | ||||
|      | ||||
| class YosysCellDocumenter(Documenter): | ||||
|     objtype = 'cell' | ||||
|     object: YosysCell | ||||
| class YosysCellGroupDocumenter(Documenter): | ||||
|     objtype = 'cellgroup' | ||||
|     priority = 10 | ||||
|     object: tuple[str, list[str]] | ||||
|     lib_key = 'groups' | ||||
| 
 | ||||
|     option_spec = { | ||||
|         'caption': autodoc.annotation_option, | ||||
|         'members': autodoc.members_option, | ||||
|         'source': 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 | ||||
|     def cell_lib(self) -> dict[str, YosysCell]: | ||||
|     def cell_lib(self) -> dict[str, list[str] | dict[str]]: | ||||
|         if not 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: | ||||
|                 with open(self.config.cells_json, "r") as f: | ||||
|                     cells_obj = json.loads(f.read()) | ||||
|  | @ -60,10 +64,8 @@ class YosysCellDocumenter(Documenter): | |||
|                     subtype = 'cell_lib' | ||||
|                 ) | ||||
|             else: | ||||
|                 for group in cells_obj.get("groups", []): | ||||
|                     for cell in group.get("cells", []): | ||||
|                         yosysCell = YosysCell(**cell) | ||||
|                         self.__cell_lib[yosysCell.cell] = yosysCell | ||||
|                 for (name, obj) in cells_obj.get(self.lib_key, {}).items(): | ||||
|                     self.__cell_lib[name] = obj | ||||
|         return self.__cell_lib | ||||
|      | ||||
|     @classmethod | ||||
|  | @ -74,75 +76,51 @@ class YosysCellDocumenter(Documenter): | |||
|         isattr: bool, | ||||
|         parent: Any | ||||
|     ) -> bool: | ||||
|         sourcename = str(member).split(":")[0] | ||||
|         if not sourcename.endswith(".v"): | ||||
|             return False | ||||
|         if membername == "__source": | ||||
|             return False | ||||
|         return False | ||||
| 
 | ||||
|     def parse_name(self) -> bool: | ||||
|         try: | ||||
|             matched = cell_ext_sig_re.match(self.name) | ||||
|             path, 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.objpath = [path] | ||||
|         self.attribute = attribute | ||||
|         self.fullname = ((self.modname) + (thing or '')) | ||||
| 
 | ||||
|         if not self.options.caption: | ||||
|             self.content_indent = '' | ||||
|         self.fullname = self.modname = self.name | ||||
|         return True | ||||
|      | ||||
|     def import_object(self, raiseerror: bool = False) -> bool: | ||||
|         # get cell | ||||
|         try: | ||||
|             self.object = self.cell_lib[self.modname] | ||||
|             self.object = (self.modname, self.cell_lib[self.modname]) | ||||
|         except KeyError: | ||||
|             if raiseerror: | ||||
|                 raise | ||||
|             return False | ||||
| 
 | ||||
|         self.real_modname = self.modname or '' | ||||
|         self.real_modname = self.modname | ||||
|         return True | ||||
|      | ||||
|     def get_sourcename(self) -> str: | ||||
|         return self.object.source.split(":")[0] | ||||
|         return self.env.doc2path(self.env.docname) | ||||
|      | ||||
|     def format_name(self) -> str: | ||||
|         return self.object.cell | ||||
|         return self.options.caption or '' | ||||
| 
 | ||||
|     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: | ||||
|         domain = getattr(self, 'domain', self.objtype) | ||||
|         directive = getattr(self, 'directivetype', 'def') | ||||
|         domain = getattr(self, 'domain', 'cell') | ||||
|         directive = getattr(self, 'directivetype', 'group') | ||||
|         name = self.format_name() | ||||
|         sourcename = self.get_sourcename() | ||||
|         cell = self.object | ||||
|         cell_list = self.object | ||||
| 
 | ||||
|         # cell definition | ||||
|         self.add_line(f'.. {domain}:{directive}:: {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) | ||||
|         self.add_line(f'.. {domain}:{directive}:: {sig}', sourcename) | ||||
|         self.add_line(f'   :caption: {name}', 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) | ||||
| 
 | ||||
|         # groups have no native content | ||||
|         # add additional content (e.g. from document), if present | ||||
|         if more_content: | ||||
|             for line, src in zip(more_content.data, more_content.items): | ||||
|  | @ -161,14 +139,25 @@ class YosysCellDocumenter(Documenter): | |||
|     ) -> tuple[bool, list[tuple[str, Any]]]: | ||||
|         ret: list[tuple[str, str]] = [] | ||||
| 
 | ||||
|         if self.options.source: | ||||
|             ret.append(('__source', self.real_modname)) | ||||
|         if want_all: | ||||
|             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 | ||||
| 
 | ||||
|     def document_members(self, all_members: bool = False) -> None: | ||||
|         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 | ||||
|         members_check_module, members = self.get_object_members(want_all) | ||||
| 
 | ||||
|  | @ -184,7 +173,7 @@ class YosysCellDocumenter(Documenter): | |||
|             classes.sort(key=lambda cls: cls.priority) | ||||
|             # give explicitly separated module name, so that members | ||||
|             # 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) | ||||
|             memberdocumenters.append((documenter, isattr)) | ||||
| 
 | ||||
|  | @ -247,6 +236,101 @@ class YosysCellDocumenter(Documenter): | |||
|         # document members, if possible | ||||
|         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): | ||||
|     objtype = 'cellsource' | ||||
|     priority = 20 | ||||
|  | @ -273,7 +357,7 @@ class YosysCellSourceDocumenter(YosysCellDocumenter): | |||
|         cell = self.object | ||||
| 
 | ||||
|         # cell definition | ||||
|         self.add_line(f'.. {domain}:{directive}:: {name}', sourcename) | ||||
|         self.add_line(f'.. {domain}:{directive}:: {sig}', sourcename) | ||||
| 
 | ||||
|         if self.options.linenos: | ||||
|             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.add_autodocumenter(YosysCellDocumenter) | ||||
|     app.add_autodocumenter(YosysCellSourceDocumenter) | ||||
|     app.add_autodocumenter(YosysCellGroupDocumenter) | ||||
|     return { | ||||
|         'version': '1', | ||||
|         'parallel_read_safe': True, | ||||
|  |  | |||
|  | @ -16,6 +16,15 @@ from sphinx.util.nodes import make_refnode | |||
| from sphinx import addnodes | ||||
| 
 | ||||
| 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, ...]: | ||||
|         if 'fullname' not in sig_node: | ||||
|             return () | ||||
|  | @ -34,17 +43,13 @@ class TocNode(ObjectDescription): | |||
| 
 | ||||
|         config = self.env.app.config | ||||
|         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'] | ||||
|         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': | ||||
|             return name + parens | ||||
|             return name | ||||
|         if config.toc_object_entries_show_parents == 'all': | ||||
|             return '.'.join(parents + [name + parens]) | ||||
|             return '.'.join(parents + [name]) | ||||
|         return '' | ||||
| 
 | ||||
| class CommandNode(TocNode): | ||||
|  | @ -59,11 +64,10 @@ class CommandNode(TocNode): | |||
|     } | ||||
| 
 | ||||
|     def handle_signature(self, sig, signode: addnodes.desc_signature): | ||||
|         fullname = sig | ||||
|         signode['fullname'] = fullname | ||||
|         signode['fullname'] = sig | ||||
|         signode += addnodes.desc_addname(text="yosys> help ") | ||||
|         signode += addnodes.desc_name(text=sig) | ||||
|         return fullname | ||||
|         return signode['fullname'] | ||||
| 
 | ||||
|     def add_target_and_index(self, name_cls, sig, signode): | ||||
|         signode['ids'].append(type(self).name + '-' + sig) | ||||
|  | @ -82,11 +86,47 @@ class CommandNode(TocNode): | |||
|                          type(self).name + '-' + sig, | ||||
|                          0)) | ||||
| 
 | ||||
| class CellNode(CommandNode): | ||||
| class CellNode(TocNode): | ||||
|     """A custom node that describes an internal 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): | ||||
|     """A custom code block for including cell source.""" | ||||
| 
 | ||||
|  | @ -104,21 +144,12 @@ class CellSourceNode(TocNode): | |||
|         signode: addnodes.desc_signature | ||||
|     ) -> str: | ||||
|         language = self.options.get('language') | ||||
|         fullname = sig + "::" + language | ||||
|         signode['fullname'] = fullname | ||||
|         signode['fullname'] = sig | ||||
|         signode['tocname'] = f"{sig.split('::')[-2]} {language}" | ||||
|         signode += addnodes.desc_name(text="Simulation model") | ||||
|         signode += addnodes.desc_sig_space() | ||||
|         signode += addnodes.desc_addname(text=f'({language})') | ||||
|         return 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) | ||||
|         return signode['fullname'] | ||||
| 
 | ||||
|     def run(self) -> list[Node]: | ||||
|         """Override run to parse content as a code block""" | ||||
|  | @ -192,6 +223,29 @@ class CellSourceNode(TocNode): | |||
| 
 | ||||
|         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): | ||||
|     """A custom directive that creates a tag matrix.""" | ||||
|      | ||||
|  | @ -368,6 +422,7 @@ class CellDomain(CommandDomain): | |||
|     directives = { | ||||
|         'def': CellNode, | ||||
|         'source': CellSourceNode, | ||||
|         'group': CellGroupNode, | ||||
|     } | ||||
| 
 | ||||
|     indices = { | ||||
|  |  | |||
|  | @ -958,7 +958,8 @@ struct HelpPass : public Pass { | |||
| 		json.entry("version", "Yosys internal cells"); | ||||
| 		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
 | ||||
| 		bool raise_error = false; | ||||
|  | @ -966,59 +967,61 @@ struct HelpPass : public Pass { | |||
| 			auto name = it.first.str(); | ||||
| 			if (cell_help_messages.contains(name)) { | ||||
| 				auto cell_help = cell_help_messages.get(name); | ||||
| 				dict<string, pair<SimHelper, CellType>> *cell_group; | ||||
| 				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 { | ||||
| 					cell_group = new dict<string, pair<SimHelper, CellType>>(); | ||||
| 					groups.emplace(cell_help.group, *cell_group); | ||||
| 					auto group_cells = new vector<string>(1, name); | ||||
| 					groups.emplace(cell_help.group, *group_cells); | ||||
| 				} | ||||
| 				auto cell_pair = pair<SimHelper, CellType>(cell_help, it.second); | ||||
| 				cell_group->emplace(name, cell_pair); | ||||
| 				cells.emplace(name, cell_pair); | ||||
| 			} else { | ||||
| 				log("ERROR: Missing cell help for cell '%s'.\n", name.c_str()); | ||||
| 				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
 | ||||
| 		json.name("groups"); | ||||
| 		json.begin_array(); | ||||
| 		json.name("groups"); json.begin_object(); | ||||
| 		groups.sort(); | ||||
| 		for (auto &it : groups) { | ||||
| 			json.begin_object(); | ||||
| 			json.name("group"); json.value(it.first.c_str()); | ||||
| 			json.name("cells"); json.begin_array(); | ||||
| 			for (auto &it2 : it.second) { | ||||
| 				auto ch = it2.second.first; | ||||
| 				auto ct = it2.second.second; | ||||
| 				json.begin_object(); | ||||
| 				json.name("cell"); json.value(ch.name); | ||||
| 				json.name("title"); json.value(ch.title); | ||||
| 				json.name("ports"); json.value(ch.ports); | ||||
| 				json.name("source"); json.value(ch.source); | ||||
| 				json.name("desc"); json.value(ch.desc); | ||||
| 				json.name("code"); json.value(ch.code); | ||||
| 				json.name("inputs"); json.begin_array(); | ||||
| 				for (auto &input : ct.inputs) | ||||
| 					json.value(input.c_str()); | ||||
| 				json.end_array(); | ||||
| 				json.name("outputs"); json.begin_array(); | ||||
| 				for (auto &output : ct.outputs) | ||||
| 					json.value(output.c_str()); | ||||
| 				json.end_array(); | ||||
| 				dict<string, bool> prop_dict = { | ||||
| 					{"is_evaluable", ct.is_evaluable}, | ||||
| 					{"is_combinatorial", ct.is_combinatorial}, | ||||
| 					{"is_synthesizable", ct.is_synthesizable}, | ||||
| 				}; | ||||
| 				json.name("properties"); json.value(prop_dict); | ||||
| 				json.end_object(); | ||||
| 			} | ||||
| 			json.end_array(); | ||||
| 			json.name(it.first.c_str()); json.value(it.second); | ||||
| 		} | ||||
| 		json.end_object(); | ||||
| 
 | ||||
| 		json.name("cells"); json.begin_object(); | ||||
| 		cells.sort(); | ||||
| 		for (auto &it : cells) { | ||||
| 			auto ch = it.second.first; | ||||
| 			auto ct = it.second.second; | ||||
| 			json.name(ch.name.c_str()); json.begin_object(); | ||||
| 			json.name("title"); json.value(ch.title); | ||||
| 			json.name("ports"); json.value(ch.ports); | ||||
| 			json.name("source"); json.value(ch.source); | ||||
| 			json.name("desc"); json.value(ch.desc); | ||||
| 			json.name("code"); json.value(ch.code); | ||||
| 			vector<string> inputs, outputs; | ||||
| 			for (auto &input : ct.inputs) | ||||
| 				inputs.push_back(input.str()); | ||||
| 			json.name("inputs"); json.value(inputs); | ||||
| 			for (auto &output : ct.outputs) | ||||
| 				outputs.push_back(output.str()); | ||||
| 			json.name("outputs"); json.value(outputs); | ||||
| 			dict<string, bool> prop_dict = { | ||||
| 				{"is_evaluable", ct.is_evaluable}, | ||||
| 				{"is_combinatorial", ct.is_combinatorial}, | ||||
| 				{"is_synthesizable", ct.is_synthesizable}, | ||||
| 			}; | ||||
| 			json.name("properties"); json.value(prop_dict); | ||||
| 			json.end_object(); | ||||
| 		} | ||||
| 		json.end_array(); | ||||
| 		json.end_object(); | ||||
| 
 | ||||
| 		json.end_object(); | ||||
| 		return raise_error; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue