mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 11:42:30 +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/ | /build/ | ||||||
| /source/cmd | /source/cmd | ||||||
| /source/cell |  | ||||||
| /source/generated | /source/generated | ||||||
| /source/_images/**/*.log | /source/_images/**/*.log | ||||||
| /source/_images/**/*.aux | /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:: | .. 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 | ||||||
|  | @ -35,21 +35,25 @@ class YosysCell: | ||||||
|     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,10 +64,8 @@ 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 | ||||||
|  | @ -74,75 +76,51 @@ class YosysCellDocumenter(Documenter): | ||||||
|         isattr: bool, |         isattr: bool, | ||||||
|         parent: Any |         parent: Any | ||||||
|     ) -> bool: |     ) -> 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: |     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, | ||||||
|  |  | ||||||
|  | @ -16,6 +16,15 @@ 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,47 +967,52 @@ 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; | ||||||
|  | 			auto ct = it.second.second; | ||||||
|  | 			json.name(ch.name.c_str()); json.begin_object(); | ||||||
| 			json.name("title"); json.value(ch.title); | 			json.name("title"); json.value(ch.title); | ||||||
| 			json.name("ports"); json.value(ch.ports); | 			json.name("ports"); json.value(ch.ports); | ||||||
| 			json.name("source"); json.value(ch.source); | 			json.name("source"); json.value(ch.source); | ||||||
| 			json.name("desc"); json.value(ch.desc); | 			json.name("desc"); json.value(ch.desc); | ||||||
| 			json.name("code"); json.value(ch.code); | 			json.name("code"); json.value(ch.code); | ||||||
| 				json.name("inputs"); json.begin_array(); | 			vector<string> inputs, outputs; | ||||||
| 			for (auto &input : ct.inputs) | 			for (auto &input : ct.inputs) | ||||||
| 					json.value(input.c_str()); | 				inputs.push_back(input.str()); | ||||||
| 				json.end_array(); | 			json.name("inputs"); json.value(inputs); | ||||||
| 				json.name("outputs"); json.begin_array(); |  | ||||||
| 			for (auto &output : ct.outputs) | 			for (auto &output : ct.outputs) | ||||||
| 					json.value(output.c_str()); | 				outputs.push_back(output.str()); | ||||||
| 				json.end_array(); | 			json.name("outputs"); json.value(outputs); | ||||||
| 			dict<string, bool> prop_dict = { | 			dict<string, bool> prop_dict = { | ||||||
| 				{"is_evaluable", ct.is_evaluable}, | 				{"is_evaluable", ct.is_evaluable}, | ||||||
| 				{"is_combinatorial", ct.is_combinatorial}, | 				{"is_combinatorial", ct.is_combinatorial}, | ||||||
|  | @ -1015,10 +1021,7 @@ struct HelpPass : public Pass { | ||||||
| 			json.name("properties"); json.value(prop_dict); | 			json.name("properties"); json.value(prop_dict); | ||||||
| 			json.end_object(); | 			json.end_object(); | ||||||
| 		} | 		} | ||||||
| 			json.end_array(); |  | ||||||
| 		json.end_object(); | 		json.end_object(); | ||||||
| 		} |  | ||||||
| 		json.end_array(); |  | ||||||
| 
 | 
 | ||||||
| 		json.end_object(); | 		json.end_object(); | ||||||
| 		return raise_error; | 		return raise_error; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue