Documentation generation#

Automatic documentation generation of hardware designs is one of the main purposes of having a machine-readable project description format along with a library providing a Document Object Model (DOM). Some of the features to be implemented on top of pyVHDLModelUtils are the following:

Integration with Sphinx#

Sphinx is the de facto static site generator (SSG) used for building documentation of Python projects. It was originally created for Python, and it has support for documenting software projects in several languages. Since many open source EDA projects and CLIs are written in Python, Sphinx is probably the most used SSG. It’s used by SymbiFlow, GHDL, VUnit, pyVHDLModel/pyVHDLParser, cocotb, TerosHDL, Boolector, edalize/fusesoc, Wishbone (FOSSi), SpinalHDL, tsfpga…

Additional features can be added to Sphinx through extensions. For instance, intersphinx allows cross-referencing content across sites, as if they were local references. That is really handy for building knowledge as a community.

The default plaintext markup language used by Sphinx is reStructuredText. Other markup languages, such as Markdown, can be imported through extensions, however, not all cross-reference features are available when using those.

Overall, there are four approaches for adding content with custom processing to a site built with Sphinx:

  • Execute before Sphinx:

    • html_static_path: content in the static paths is copied to the output’s _static directory, overriding existing sources with the same name. This is typically used for adding images and/or customising the CSS.

    • html_extra_path: content in extra paths is copied as-is to the output directory, overriding existing sources with the same name. This can be used for adding static content generated with a different generator.

  • Built into Sphinx:

    • exec: a generic directive (such as the ones proposed in stackoverflow.com/a/18143318) allows executing arbitrary Python code which generates reStructuredText output through print statements.

    • Ad-hoc directive: as explained in Developing extensions for Sphinx, there are several objects whose API can be used when writing extensions: Application, Environment, Builder and Config. Those allow fine-grained integration into Sphinx’s internals, potentially bypassing the reStructuredText parsing layer. This is the end-goal for tightly integrated and customised functionality.

exec directive#

The generic exec extension (see doc/exec.py) is based on stackoverflow.com/a/18143318. It allows executing arbitrary Python code which prints reStructuredText output.

Setup is done by adding exec to the extensions variable of the conf.py. Depending on the location of exec.py, it might be necessary to add sys.path.insert(0, abspath(".")). Other than that, users (developers of the arbitrary Python code) don’t need to know internals of Sphinx, but just print regular reStructuredText statements.

However, a main caveat of the current implementation is that Sphinx will never fail. That is, even if the arbitrary code fails, the Sphinx build is reported as successful. That’s because errors are shown as an admonition instead of making the build fail. As a result, manual inspection of the output is required (desirable).

Moreover, currently no context is passed to the Python code. Therefore, it is not possible to know where it belongs in the hierarchy of the document. This is a limitation for generating headers and other context dependent statements.

Note

Should you want to help improve the implementation of this directive, let us know!

Lists and tables#

This section showcases a naive approach for documenting VHDL design units using pyGHDL.dom. It is based on exec directive and the sphinx module of pyVHDLModelUtils.

First, initDesign needs to be executed, in order to provide the lists of sources and VHDL library names.

Note

Currently, there is no specific JSON/YAML format supported for this task. Find work in progress in section Core.

Listing 1 Loading design sources.#
.. exec::
   from pyVHDLModelUtils.sphinx import initDesign
   initDesign(
     '..',
     AXI4 = ["AXI4Stream/src/*.vhd"],
     fpconv = ["fpconv/*.vhd"]
   )

The output of initDesign is a NOTE containing the result of parsing the sources with pyGHDL.dom. If a failure was produced, an admonition of type ERROR is shown instead.

Note

Output of initDesign:

  • AXI4

    • AXI4Stream/src/fifo.vhd

      • [NOT IMPLEMENTED] Array_Subtype_Definition

    • AXI4Stream/src/axis_buffer.vhd

  • fpconv

    • fpconv/tb.vhd

Then, printDocumentationOf allows generating the documentation of libraries and/or design units. By default, the content is shown where the directive was called. In case of failure, an admonition of type ERROR is shown.

Listing 2 Printing a summary of the content.#
.. exec::
   from pyVHDLModelUtils.sphinx import printDocumentationOf
   printDocumentationOf()

Design content:

  • Library AXI4 [2 entities]

    • Entity: fifo

      • Architecture: arch

    • Entity: axis_buffer

      • Architecture: arch

At the moment, two different styles are supported for printing the documentation of entities.

List style:

Listing 3 Printing the documentation of a unit (style ‘rst:list’).#
.. exec::
   from pyVHDLModelUtils.sphinx import printDocumentationOf
   printDocumentationOf(["AXI4.axis_buffer"])

Entity axis_buffer from Library AXI4:

  • Generics:

    • data_width : integer := 32

    • fifo_depth : integer := 0

  • Ports:

    • s_axis_clk : in std_logic

    • s_axis_rstn : in std_logic

    • s_axis_rdy : out std_logic

    • s_axis_data : in std_logic_vector(data_width - 1 downto 0)

    • s_axis_valid : in std_logic

    • s_axis_strb : in std_logic_vector((data_width / 8) - 1 downto 0)

    • s_axis_last : in std_logic

    • m_axis_clk : in std_logic

    • m_axis_rstn : in std_logic

    • m_axis_valid : out std_logic

    • m_axis_data : out std_logic_vector(data_width - 1 downto 0)

    • m_axis_rdy : in std_logic

    • m_axis_strb : out std_logic_vector((data_width / 8) - 1 downto 0)

    • m_axis_last : out std_logic

  • Architectures:

    • arch

Table style:

Listing 4 Printing the documentation of a unit (style ‘rst:table’).#
.. exec::
   from pyVHDLModelUtils.sphinx import printDocumentationOf
   printDocumentationOf(
     ["AXI4.axis_buffer"],
     'rst:table'
   )
Table 1 AXI4.axis_buffer Generics#

Identifiers

Type

Default

data_width

integer

32

fifo_depth

integer

0

Table 2 AXI4.axis_buffer Ports#

Identifiers

Mode

Type

Default

s_axis_clk

in

std_logic

s_axis_rstn

in

std_logic

s_axis_rdy

out

std_logic

s_axis_data

in

std_logic_vector(data_width - 1 downto 0)

s_axis_valid

in

std_logic

s_axis_strb

in

std_logic_vector((data_width / 8) - 1 downto 0)

s_axis_last

in

std_logic

m_axis_clk

in

std_logic

m_axis_rstn

in

std_logic

m_axis_valid

out

std_logic

m_axis_data

out

std_logic_vector(data_width - 1 downto 0)

m_axis_rdy

in

std_logic

m_axis_strb

out

std_logic_vector((data_width / 8) - 1 downto 0)

m_axis_last

out

std_logic

Table 3 AXI4.axis_buffer Architectures#

Identifier

Number of statements

arch

11

Note

This is a demo for showcasing the capabilities of pyGHDL.dom and pyVHDLModel. Should you want to help improve the implementation for it to be more usable in practice, let us know!

VHDL Domain#

gh:Paebbels/sphinxcontrib-vhdldomain is work in progress for adding a VHDL language domain to Sphinx. That is, a set of nestable directives resembling the architecture of pyVHDLModel. The purpose is twofold:

  • Allow a better integration of the content into Sphinx, rather than generating reStructuredText output from arbitrary Python functions.

  • Allow users to specify a pyVHDLModel project by handwriting directives in reStructuredText sources, by either pointing to individual files or explicitly describing all the items.

See Paebbels/sphinxcontrib-vhdldomain#4.

There is also gh:CESNET/sphinx-vhdl, which uses a custom basic parser (CESNET/sphinx-vhdl: src/sphinxvhdl/autodoc.py) and multiple custom Sphinx directives (CESNET/sphinx-vhdl: src/sphinxvhdl/vhdl.py).

Diagrams#

Both GHDL and Yosys allow generating diagrams of synthesised designs.

  • ghdl synth --out=dot generates a Graphviz DOT diagram of the netlist AST.

  • gh:ghdl/ghdl-yosys-plugin allows using GHDL as a frontend for Yosys.

  • Yosys’s show command allows generating a Graphviz DOT diagram and compiling it to a graphics file (say SVG).

    • Optionally, command aigmap can map the logic to and/nand gates only, before generating the diagram.

    • Alternatively, gh:nturley/netlistsvg allows generating SVG schematics from Yosys’ JSON netlist output.

By combining those tools, diagrams of a given VHDL design can be generated as follows:

~# yosys -p 'ghdl --std=08 design.vhd -e primary_unit secondary_unit; prep; write_json netlist.json'
~# netlistsvg netlist.json -o netlist.svg
~# convert netlist.svg netlist.png

Important

There is an Sphinx extension named sphinxcontrib-hdl-diagrams, which wraps Yosys and (optionally) netlistsvg in a directive. That allows including diagrams in the docs without manually calling yosys and netlistsvg. For instance:

.. hdl-diagram:: file.v
   :type: netlistsvg
   :module: name
   :flatten:

However, since sphinxcontrib-hdl-diagrams depends on combining the WASM version of Yosys and netlistsvg (which is JavaScript), it does not support VHDL yet. There is work in progress for using the extension with “natively” installed tools, as well as supporting VHDL and mixed-language designs. See SymbiFlow/sphinxcontrib-hdl-diagrams#65, SymbiFlow/sphinxcontrib-hdl-diagrams#72 and SymbiFlow/sphinxcontrib-hdl-diagrams#73.

References#