Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sphinx-svdomain

sphinx-svdomain is a Sphinx extension that adds a SystemVerilog domain for documenting hardware APIs.

It supports hand-written domain directives and an experimental source-driven autodoc flow.

Status: alpha. The manual domain is intended to be usable for the supported object types below. Autodoc is useful, but still limited to the extracted declaration forms listed in this README.

Installation

sphinx-svdomain requires Python 3.12 or newer and Sphinx 9.1.x.

Install the current alpha into the Python environment used by Sphinx after the release has been published to PyPI. Either select the exact prerelease or allow prereleases:

pip install sphinx-svdomain==0.1.0-alpha.2
pip install --pre sphinx-svdomain

To install an unpublished local checkout instead:

pip install .

Source-driven autodoc uses pyslang>=11 as its default backend. It is installed automatically as a normal package dependency.

When developing this repository, use uv:

uv sync --group test --group docs

Sphinx Configuration

Enable the extension in conf.py:

extensions = ["sphinx_svdomain"]

No extra configuration is required for the default manual and source-driven documentation flows.

The default autodoc backend is pyslang; most projects do not need to set sv_autodoc_backend.

Manual Documentation

Manual directives are the stable core of the extension. They register SystemVerilog objects with Sphinx so they appear in generated HTML, search results, indexes, and objects.inv.

Module Example

.. sv:module:: uart_rx

   UART receiver.

   .. sv:parameter:: DATA_WIDTH
      :type: int
      :default: 8

      Width of the received data.

   .. sv:localparam:: COUNTER_WIDTH
      :type: int
      :default: 4

      Width of the internal counter.

   .. sv:typedef:: state_t
      :type: logic [1:0]

      Receiver state encoding.

   .. sv:port:: clk
      :direction: input
      :type: logic

   .. sv:port:: rx
      :direction: input
      :type: logic

   .. sv:port:: data
      :direction: output
      :type: logic [:sv:param:`DATA_WIDTH` - 1:0]

   .. sv:function:: parity
      :lifetime: automatic
      :type: logic
      :arguments: input logic [DATA_WIDTH-1:0] value

      Calculate parity for one data word.

The :sv:mod:`uart_rx` module samples :sv:port:`uart_rx.rx`.
The data width is configured by :sv:param:`uart_rx.DATA_WIDTH`.
Parity is calculated by :sv:func:`uart_rx.parity`.

Function content can use normal RST fields such as :param value: and :returns:. Autodoc emits those fields from @param and @return tags.

Nested declarations inherit the surrounding scope. In the example above, DATA_WIDTH is registered as uart_rx.DATA_WIDTH, and data is registered as uart_rx.data.

Package Example

.. sv:package:: uart_cfg

   Shared UART configuration declarations.

   .. sv:parameter:: DEFAULT_DATA_WIDTH
      :type: int
      :default: 8

   .. sv:typedef:: byte_t
      :type: logic [7:0]

   .. sv:enum:: parity_t
      :type: logic [1:0]

      .. sv:enumerator:: NONE

         No parity bit.

      .. sv:enumerator:: EVEN

         Even parity.

   .. sv:struct:: config_t
      :type: packed

      .. sv:field:: data_width
         :type: int unsigned

      .. sv:field:: parity
         :type: :sv:enum:`parity_t`

The byte type is :sv:type:`uart_cfg::byte_t`.
The parity field is :sv:field:`uart_cfg::config_t.parity`.

Package-scoped names use ::, so byte_t inside uart_cfg is registered as uart_cfg::byte_t. Members of enum, struct, and union types use ., so the field above is registered as uart_cfg::config_t.parity.

Interface Example

.. sv:interface:: uart_line_if

   UART serial line interface.

   .. sv:parameter:: DATA_WIDTH
      :type: int
      :default: 8

   .. sv:typedef:: payload_t
      :type: logic [:sv:param:`DATA_WIDTH` - 1:0]

   .. sv:port:: rx_line
      :direction: input
      :type: logic

   .. sv:port:: tx_line
      :direction: output
      :type: logic

Directives And Roles

Supported manual directives:

Object Directive Reference role
Interface .. sv:interface:: name :sv:intf:
Module .. sv:module:: name :sv:mod:
Package .. sv:package:: name :sv:pkg:
Parameter .. sv:parameter:: name :sv:param:
Localparam .. sv:localparam:: name :sv:lparam:
Port .. sv:port:: name :sv:port:
Typedef .. sv:typedef:: name :sv:type:
Enum .. sv:enum:: name :sv:enum: and :sv:type:
Enumerator .. sv:enumerator:: name :sv:enumval:
Struct .. sv:struct:: name :sv:struct: and :sv:type:
Union .. sv:union:: name :sv:union: and :sv:type:
Field .. sv:field:: name :sv:field:
Function .. sv:function:: name :sv:func:

The generic Sphinx :any: role also resolves SystemVerilog objects using the same lookup rules.

Supported directive options:

  • sv:parameter and sv:localparam: :type:, :unpacked-dimensions:, :default:
  • sv:port: :direction:, :type:, :unpacked-dimensions:
  • sv:typedef, sv:enum, sv:struct, sv:union: :type:, :unpacked-dimensions:
  • sv:enumerator: :value:
  • sv:field: :type:, :unpacked-dimensions:
  • sv:function: :lifetime:, :type:, :arguments:

Manual object directives also inherit Sphinx's standard object options, including :no-index:, :no-index-entry:, :no-contents-entry:, and :no-typesetting:.

Expression-bearing options can contain inline RST roles when an expression needs manual links. This applies to :type:, :unpacked-dimensions:, :default:, :value:, and :arguments: where those options are supported:

.. sv:port:: data
   :direction: output
   :type: logic [:sv:param:`DATA_WIDTH` - 1:0]

.. sv:field:: payload
   :type: :sv:type:`uart_cfg::byte_t`

The :direction: and :lifetime: options are rendered as plain text.

Names And Cross-References

Canonical names use :: for package-style namespaces and . for members:

  • uart_cfg::DEFAULT_DATA_WIDTH
  • uart_cfg::byte_t
  • uart_cfg::parity_t.NONE
  • uart_cfg::config_t.parity
  • uart_line_if.rx_line
  • uart_rx.DATA_WIDTH
  • uart_rx.data
  • uart_rx.parity

Inside module, interface, package, enum, struct, and union content, references can use scope-relative names:

.. sv:module:: uart_rx

   .. sv:parameter:: DATA_WIDTH
      :type: int

   :sv:param:`DATA_WIDTH` controls :sv:port:`data`.

Outside a scope, use the fully qualified name unless the short name is unique:

The receiver output is :sv:port:`uart_rx.data`.
The shared byte type is :sv:type:`uart_cfg::byte_t`.

If an unqualified reference is ambiguous, Sphinx emits a warning and leaves the reference unresolved.

HTML anchor IDs are generated by Sphinx from the canonical object name. The same canonical names are exported in objects.inv for intersphinx.

Autodoc

Autodoc reads SystemVerilog source and renders the extracted declarations through the same domain directives used for manual documentation.

Document every supported top-level declaration in one file:

.. sv:autofile:: rtl/uart_rx.sv

Document one top-level declaration from a source file:

.. sv:autopackage:: uart_cfg
   :source: rtl/uart_cfg.sv

.. sv:autointerface:: uart_line_if
   :source: rtl/uart_line_if.sv

.. sv:automodule:: uart_rx
   :source: rtl/uart_rx.sv

Source paths are resolved by Sphinx relative to the document containing the autodoc directive.

Autodoc does not require the design to elaborate. Unresolved imports or package references can remain as source text in generated signatures. Syntax problems are reported as Sphinx warnings, and the affected file is skipped. Non-error parser diagnostics, such as style warnings, do not prevent declaration extraction.

Autodoc-generated :type: text links known parameters, localparams, and types automatically. For example, logic [DATA_WIDTH-1:0] links DATA_WIDTH to the generated parameter object when that parameter is known.

Source Comments

Autodoc extracts Doxygen-style comments into declaration descriptions.

Leading block comments attach to the next supported declaration:

/**
 * UART receiver.
 *
 * Receives serial data and emits parallel words.
 */
module uart_rx;
endmodule

Leading line comments work too:

/// UART receiver.
///
/// Receives serial data and emits parallel words.
module uart_rx;
endmodule

Trailing member comments attach to declarations on the same line:

typedef struct packed {
  logic [7:0] payload; ///< Payload byte.
} packet_t;

Supported markers are /**, /*!, ///, and //!. Trailing member comments use ///< or //!<. Decorative comments beginning with four or more slashes are treated as ordinary comments rather than documentation.

Supported tags:

  • @brief TEXT: short description for the current declaration
  • @details TEXT: additional description for the current declaration
  • @param NAME TEXT: description for a parameter or localparam, or for a function argument when attached to a function
  • @port NAME TEXT: description for a port
  • @field NAME TEXT: description for a struct or union field
  • @enumval NAME TEXT: description for an enum value
  • @return TEXT: description of a function's return value

Example:

/**
 * UART receiver.
 *
 * @param DATA_WIDTH Width of the received data.
 * @port clk Clock input.
 * @port rx Serial input.
 */
module uart_rx #(
  parameter int DATA_WIDTH = 8
) (
  input logic clk,
  input logic rx
);
endmodule

Comment text is interpreted as reStructuredText after extraction, so explicit SV roles can be used in comments:

/// Receives data from :sv:port:`uart_rx.rx`.
module uart_rx;
endmodule

Rendered Comment Captures

Source comments become normal Sphinx object descriptions.

For example, this source comment:

/**
 * UART block documented from SystemVerilog comments.
 *
 * @param DATA_WIDTH Width of the received data.
 * @port clk Clock input.
 * @port rx Serial receive input.
 */
module autodoc_uart #(
  parameter int DATA_WIDTH = autodoc_pkg::AUTO_DATA_WIDTH
) (
  input logic clk,
  input logic rx,
  output logic [DATA_WIDTH-1:0] data ///< Parallel receive data.
);
endmodule

is rendered as module, parameter, and port documentation:

Rendered autodoc module comments

Trailing field comments are rendered on nested field objects:

typedef union packed {
  logic [DATA_WIDTH-1:0] raw; ///< Raw sampled payload.
  autodoc_pkg::auto_byte_t byte_view; ///< Byte-sized view.
} sample_u;

Rendered autodoc field comments

Supported SystemVerilog Subset

Manual documentation supports the object types listed in Directives And Roles.

Autodoc currently extracts:

  • modules, interfaces, and packages
  • ANSI module/interface parameters and ports
  • non-ANSI body port declarations
  • parameter and localparam declarations
  • typedefs
  • typedef enum, struct, and union declarations
  • enum values and struct/union fields
  • module-, interface-, and package-scoped functions with ANSI arguments
  • unpacked declarator dimensions and explicit enum values
  • supported documentation comments and member tags

Known limitations:

  • tasks and non-ANSI function argument declarations are not implemented yet
  • classes, programs, modports, clocking blocks, covergroups, assertions, properties, sequences, constraints, macros, UDPs, and instances are not documented as first-class objects yet
  • autodoc does not elaborate designs or resolve imported symbols
  • autodoc does not recursively discover a directory; list source files with sv:autofile or generate those directives in the consuming project
  • unsupported source declarations are ignored by the current extractor
  • source-location diagnostics do not always point to the exact unsupported declaration
  • there is no custom SystemVerilog index page yet; objects still appear in Sphinx's general index, search, and inventory

Repository Layout

  • sphinx_svdomain/: extension implementation
  • sphinx_svdomain/backends/: source parser integration
  • test/: pytest suite
  • test/doc_gen/: example Sphinx project used as an integration test

Development

Install all development dependencies:

uv sync --group test --group docs

Run the test suite:

uv run --group test pytest

Build the example documentation with warnings treated as errors:

uv run --group docs --group test sphinx-build -E -W --keep-going test/doc_gen test/doc_gen/_build/html

Build the package:

uv build

Most users do not need to configure the autodoc parser. The default is selected automatically. For extension development, the internal JSON autodoc backend is used for deterministic frontend tests and can be selected explicitly:

sv_autodoc_backend = "json"

Recommended release checks:

uv run --group test pytest
uv run --group docs --group test sphinx-build -E -W --keep-going test/doc_gen test/doc_gen/_build/html
uv build

Versioning

The project uses Semantic Versioning 2.0.0 for release tags and release names:

v0.1.0-alpha.1
v0.1.0-beta.1
v0.1.0-rc.1
v0.1.0

Python package tooling uses PEP 440 and normalizes SemVer prereleases in built artifacts. For example, the SemVer release 0.1.0-alpha.1 is accepted by the source metadata and is normalized by Python packaging tools as 0.1.0a1 in wheel filenames and package metadata.

License

sphinx-svdomain is distributed under the GNU LGPL version 2.1 or later. See LICENSE.

Releases

Packages

Contributors

Languages