Documentation guidelines#

A brief set-of-rules for the documentation.

Note

The old wiki uses dokuwiki. When importing text from there, consider the automated options that are provided in this page to convert it to reST.

Importing from DokuWiki to Sphinx#

Use the following command to import a DokuWiki page (old wiki.analog.com):

pandoc imported.txt -f dokuwiki -t rst --columns=80 -s -o imported.rst --list-tables

The list-tables parameter requires pandoc-types >= 1.23, included in any recent pandoc release; if it is not an option, you shall remove it and export in the grid table format (see Tables for more information).

After converting, update it to better conform with the guidelines below, and make sure to use our directives and roles, for example, the Git role.

To speed thing up, combine with wget:

wikifile=resources/eval/user-guides/adrv9009/adrv9009
outfile=output.rst

wget -O - https://wiki.analog.com/$wikifile?do=export_raw --no-verbose | \
  pandoc -f dokuwiki -t rst --columns=80 -s -o $outfile --list-tables

Also, consider recording macros in your favorite text editor to automate repetitive steps.

Indentation#

Directives are indented with 3 space, which is Sphinx’s default. At code directives, the code keeps its original indentation (e.g. 2 spaces for Verilog code), but is offset by 3 spaces at the beginning of every line, to instruct Sphinx the beginning and end of the code directive.

Table of contents#

The relation between pages are created with the toctree directive, which allows to generate the table of contents and navigation bars.

Each page may have only one toctree, since they are the equivalent of the volumes of a book, and it does not make sense to have multiple “volumes” at the repository level.

The only exception is the ADI System Level Documentation repository, which indeed contains various types of documentation (eval-boards, university program, Linux drivers, etc.).

The toctree directive has the following format:

.. toctree::
   :maxdepth: <depth>

   Custom title <path/to/page>

For pages with shorter titles, such as libraries, the label is inherited from the page itself, for example:

.. toctree::

   library/axi_dmac/index
   library/spi_engine/index

And for pages with long titles, such as projects, overwrite the full title with a custom title, e.g:

.. toctree::

   AD7616-SDZ <projects/ad7616_sdz/index>

This way, only “AD7616-SDZ” will be displayed in the page navigation bar instead of “AD7616-SDZ HDL project”.

Also, it is recommended to wrap the toctree in a “Contents” section:

Contents
========

.. toctree::

   some_page

For extensive documentation with different topics, it makes sense to filter the toctree based on the current topic/toctree title. This is possible by setting the environment variable ADOC_FILTER_TOCTREE to 1. Alternatively, setting filter_toctree on conf.py has higher precedence than ADOC_FILTER_TOCTREE. And is supposed to be used alongside the topic field at lut.py to preserve high level links for each topic.

Enable this environment variable only on the release build, since writing pages with it enabled may be obnoxious and confusing prior the final structure/location of them.

Versioning#

To avoid having the version set in multiple places or having to tweak conf.py to obtain it from somewhere else, continuous integration can set the environment variable ADOC_DOC_VERSION to set the version value.

Still, the version value on conf.py has higher precedence, and ADOC_DOC_VERSION will be ignored if the variable is already set.

The CI, in general, should set ADOC_DOC_VERSION as the current checkout branch in the pipeline (e.g. main, v1.0.0).

Tip

If creating a branch or PR output, consider using GitHub short reference ${{ github.ref_name }}.

If both environment variable and version on conf.py are unset, it defaults to an empty string.

Also, set ADOC_TARGET_DEPTH to match the final destination depth, for example, if the target directory is:

  • ./: 0 or unset

  • ./v2.2: 1

  • ./prs/1234: 2

  • ./staging/user/branch: 3

Exporting to PDF#

The whole documentation can be exported to a PDF document for a more compact format. This is done by setting the environment variable called ADOC_MEDIA_PRINT to 1 (default it’s 0) and building the documentation using this command:

user@analog:~/doctools/docs$ sphinx-build -b pdf .  _build/pdfbuild

In the output folder, you’ll find a PDF document named after the repository (e.g. Doctools.pdf). This document includes an auto-generated cover, followed by the remaining pages. Note that an HTML build of the documentation is not required for the PDF build.

Warning

The enviroment variable ADOC_MEDIA_PRINT should be set to 0 when building the HTML pages of documentation. If not set, some components of the pages may not render properly.

Local references#

References to labels have the format :ref:`context topic`, e.g. :ref:`role git` renders as Git role.

Labels are created for any content with the syntax (dot-dot underscore<label>two-dots):

.. _context topic:

Hint

Add labels to any content that may be linked, locally or externally.

References to docs have the format :doc:`path/to/doc`, e.g. :doc:`docs_guidelines` for docs_guidelines.rst.

Attention

Do not break reference roles between lines! Even though Sphinx allows breaking line inside the reference role, it makes pattern matching hard.

Prefer hyphen separation - over undeline _ for the “title” section, and always lower case, for example my_code control-interface instead of MY_CODE Control_Interface.

Numbered references#

References can be numbered by using numref, for example, “see Figure 1”.

To use this feature, enable on the conf.py:

numfig = True

To customize the format, set numfig_format:

numfig_format = {'figure': 'Figure %s',
                 'table': 'Table %s',
                 'code-block': 'Listing %s',
                 'section': 'Section %s'}

Tip

By default enumeration is global, so if the toctree is not numbered to divide the pages in numbered sections (e.g. Figure 3.4.4), the numbering will “propagate” across page which may be counter-intuitive.

To have the numbering reset at every page, add numfig_per_doc = True to conf.py.

External references#

External references to other Sphinx documentation are created using the built-in sphinx.ext.intersphinx extension.

To setup in-organization references read the section below, and for third-party docs, the section that follows.

For either, to create a reference to a label, use:
:external+inv:ref:`label`, where inv is a mapped source, for example, :external+hdl:ref:`spi_engine control-interface`.
To create a reference to a doc, use:
:external+inv:doc:`label`, where inv is a mapped source, for example, :external+hdl:doc:`library/spi_engine/index`.

As the other roles, it is possible to customize the text, e.g. :external+hdl:ref:`Custom text <spi_engine control-interface>`.

Tip

Pay attention to the log output, since a warnings is thrown for each reference not found.

External references work with any kind of references, such as ref, doc, envvar, token, term, numref and keyword.

For references to labels it is possible to use the short form:
:ref-inv:`label` (equivalent to :external+inv:ref:`label`), but is discouraged.

Note

Sphinx 8 allows the syntax :ref:`<inv>:label`, which allows local references to have higher precedence than external refs, useful for generating custom docs like user guides. However, since some users may require Sphinx 7, use the former syntax, and let adoc patch it when necessary.

To show all links of an InterSphinx mapping file, use the built-in tool:

python3 -m sphinx.ext.intersphinx https://analogdevicesinc.github.io/hdl/objects.inv

In organization reference#

To create references to Sphinx docs inside the organization add the repositories of interest to the conf.py file with the following format:

interref_repos = [external...]

For example:

interref_repos = ['hdl', 'no-OS', 'pyadi-iio/main']

Notice that in the example main suffixes pyadi-iio, this means that will look for the build at path main of this repo instead of at root. This can be used to target a specific version, if the target repository stores multiple, for example, v1.1, more about that Versioned.

Tip

For even more freedom, you can setup with an explicit path as an Outside organization Sphinx reference.

It is possible to customize the target URL with the interref_uri config or ADOC_INTERREF_URI environment variables. The default value is https://analogdevicesinc.github.io/ and can be set to a local path like ../../.

Beyond the main target dictated by interref_uri, by setting the config interref_local as true, a secondary target is inferred foreseeing a local copy of the target external documentation alongside the current repository:

/data/work
├─my-repo-0/doc/sources
│
├─my-repo-1/docs
│
└─my-repo-2/doc

The correct relative paths are resolved looking into the lut.py.

Outside organization Sphinx reference#

To create references to third-party Sphinx documentations, add the mappings to to the conf.py file with the following format:

intersphinx_mapping = {
    '<name>': ('<path/to/external>', None)
}

For example:

intersphinx_mapping = {
    'sphinx': ('https://www.sphinx-doc.org/en/master', None)
}

Text width#

Each line must be less than 80 columns wide. You can use the fold command to break the lines of the imported text while respecting word-breaks:

cat imported.txt | fold -sw 80 > imported.rst

The header divider “---” shall be either 80 characters wide or end at the title character, that means, this is also valid:

My title
========

Tables#

Prefer list-tables and imported csv-tables (using the file option), because they are faster to create, easier to maintain and the 80 column-width rule can be respected with list-tables.

Only use grid tables if strictly necessary, since they are hard to update.

Lists#

Unordered lists use * or - and ordered lists #..

Child items must be aligned with the first letter of the parent item, that means, 2 spaces for unordered list and 3 spaces for ordered lists, for example:

#. Parent ordered item.

   * Child unordeded item.

     #. Child ordered item.
     #. Child ordered item.

Renders as:

  1. Parent numbered item.

    • Child unordered item.

      1. Child ordered item.

      2. Child ordered item.

Code#

Prefer code-blocks to code directives, because code-blocks have more options, such as showing line numbers and emphasizing lines.

For example,

.. code-block:: python
   :linenos:
   :emphasize-lines: 2

   def hello_world():
       string = "Hello world"
       print(string)

Renders as

1def hello_world():
2    string = "Hello world"
3    print(string)

Images#

Prefer the SVG format for images, and save it as Optimized SVG in inkscape to use less space.

Store them in a hierarchically, do not use images subdirectories. The idea is to have simpler relative paths, for example, e.g.:

.. image:: ad2234_sdz_schematic.svg

Instead of over complicated paths like:

.. image:: ../../project/images/ad2234_sdz/ad2234_sdz_schematic.svg

In general, this avoids dangling artifacts and keeps the documentation simple.

Git Large File Storage#

Where applicable, Git Large File Storage (LFS) is used to replace large files with text pointers inside Git, reducing cloning time.

To setup, install from your package manager and init:

apt install git-lfs
git lfs install

The files that will use Git LFS are tracked at .gitattributes, to add new files use a pattern at the repo root, for example:

git lfs track *.jpg

Or edit .gitattributes directly.

Third-party directives and roles#

Third-party tools are used to expand Sphinx functionality, if you haven’t already, do:

pip install -r requirements.txt

Custom directives and roles#

To expand Sphinx functionality beyond existing tools, custom directives and roles have been written, which are located in the docs/extensions folder. Extensions are straight forward to create, if some functionality is missing, consider requesting or creating one.

Note

Link-like roles use the :role:`text <link>` synthax, like external links, but without the undescore in the end.

Color role#

To print text in red or green, use :red:`text` and :green:`text`.

Clear content directive#

A simple directive to clear the content, forcing any following content to be moved below any preceding content. It is useful when working with images aligned/float left/right and wants to ensure the next section does not also gets “squashed”.

.. clear-content::
   :side: [both,left,right]
   :break:

It can clear content to it’s left, right or both sides. By default, it clear both sides.

With the break option, it will break the page when generating a PDF (behaves similar to LaTeX cleardoublepage).

Shell directive#

The shell directive allows to embed shell code in a standard way.

.. shell:: [bash,sh,zsh,ps1]
   :user: <user>
   :group: <group>
   :caption: <caption>
   :show-user:
   :no-path:

   /path_absolute
   ~path_relative_to_home
   $command
    output

That means, each line is prefixed by character to:

  • $: bash commands.

  • (one space): command output.

  • #: bash comments

  • /: set absolute working directory (cygpath-formatted for ps1).

  • ~: set relative to “home” working directory (cygpath-formatted for ps1).

Anything that does not match the previous characters will default to output print, but please be careful, since you may accidentally mark a working directory or command, if not identing the output by one space.

The bash type defaults to bash, user to user, group to analog and the working directory as “doesn’t matter” (hidden), so, for example:

.. shell::
   :caption: iio_reg help

   $iio_reg -h
    Usage:

    iio_reg <device> <register> [<value>]

Renders as:

iio_reg help#
user@analog:~$
iio_reg -h
Usage:

iio_reg <device> <register> [<value>]

Insight

To make it super easy for the user to copy only the command, the current directory and output cannot be selected.

To show the user and user group, add the :show-user: flag.

For Windows, set bash type as ps1 (PowerShell), for example:

.. shell:: ps1
   :user: Analog

   /e/MyData
   $cd ~/Documents
   $ls
    Mode  LastWriteTime      Name
    ----  -------------      ----
    d---- 6/14/2024 10:30 AM ImportantFiles
    d---- 6/14/2024 10:30 AM LessImportantFiles
   $cd ..\Other\Folder
   $echo HelloWindows
    HelloWindows

Renders as:

E:\MyData>
cd ~/Documents
C:\Users\Analog\Documents>
ls
Mode  LastWriteTime      Name
----  -------------      ----
d---- 6/14/2024 10:30 AM ImportantFiles
d---- 6/14/2024 10:30 AM LessImportantFiles
C:\Users\Analog\Documents>
cd ..\Other\Folder
C:\Users\Analog\Other\Folder>
echo HelloWindows
HelloWindows

To make things more interesting, basic $cd commands change the working directory accordingly, for example:

.. shell::

   $cd /sys/bus/iio/devices/
   $ls
    iio:device0  iio:device3  iio:device2  iio:device3  iio:device4  iio:device5  iio:device6
   $cd iio:device3
   $ls -al
    total 0
    drwxr-xr-x 3 root root     0 May 16 14:21 .
    -rw-rw-rw- 1 root root  4096 May 16 14:22 calibrate
    -rw-rw-rw- 1 root root  4096 May 16 14:22 calibrate_frm_en

Renders as:

~$
cd /sys/bus/iio/devices/
/sys/bus/iio/devices$
ls
iio:device0  iio:device3  iio:device2  iio:device3  iio:device4  iio:device5  iio:device6
/sys/bus/iio/devices$
cd iio:device3
/sys/bus/iio/devices/iio:device3$
ls -al
total 0
drwxr-xr-x 3 root root     0 May 16 14:21 .
-rw-rw-rw- 1 root root  4096 May 16 14:22 calibrate
-rw-rw-rw- 1 root root  4096 May 16 14:22 calibrate_frm_en

Finally, be mindful of the command legibility, break long commands and sugar coat with indent:

.. shell::

   # Write the file to the storage devices
   $time sudo dd \
   $  if=2021-07-28-ADI-Kuiper-full.img \
   $  of=/dev/mmcblk0 \
   $  bs=4194304
    [sudo] password for user:
    0+60640 records in 0+60640 records out 7948206080 bytes (7.9 GB) copied, 571.766 s, 13.9 MB/s
    real 7m54.11s user 0.29s sys 8.94s

Renders to:

~$
# Write the file to the storage device
~$
time sudo dd \
  if=2021-07-28-ADI-Kuiper-full.img \
  of=/dev/mmcblk0 \
  bs=4194304
[sudo] password for user:
0+60640 records in 0+60640 records out 7948206080 bytes (7.9 GB) copied, 571.766 s, 13.9 MB/s
real 7m54.11s user 0.29s sys 8.94s

HDL build status directive#

The HDL build status directive gets information from a markdown formatted status table (output.md) and generates a table with the build statuses.

The directive syntax is:

.. hdl-build-status::
   :file: <build_status_file>

The :path: option is optional, in the sense that if it’s not provided, no table is generated. If provided, but the build status file does not exist, an error is thrown.

Note

The :path: option is meant to be “filled” during a CI procedure.

HDL parameters directive#

The HDL parameters directive gets information parsed from IP-XACT (component.xml) library and generates a table with the IP parameters.

Note

The IP-XACT files are generated by Vivado during the library build and not by the documentation tooling.

The directive syntax is:

.. hdl-parameters::
   :path: <ip_path>

   * - <parameter>
     - <description>

For example:

.. hdl-parameters::
   :path: library/spi_engine/spi_engine_interconnect

   * - DATA_WIDTH
     - Data width of the parallel SDI/SDO data interfaces.
   * - NUM_OF_SDI
     - Number of SDI lines on the physical SPI interface.

Descriptions in the directive have higher precedence than in the component.xml file.

The :path: option is optional, and should not be included if the documentation file path matches the component.xml hierarchically.

HDL interface directive#

The HDL interfaces directive gets information parsed from component.xml library and generates tables with the IP interfaces, both buses and ports.

Note

The component.xml files are generated by Vivado during the library build and not by the documentation tooling.

The directive syntax is:

.. hdl-interfaces::
   :path: <ip_path>

   * - <port/bus>
     - <description>

For example:

.. hdl-interfaces::
   :path: library/spi_engine/spi_engine_interconnect

Descriptions in the directive have higher precedence than in the component.xml file. You can provide description to a port or a bus, but not for a bus port. Ports/buses that are consecutive are squashed into a single instance to avoid repetition, for example:

{data_tx_12_p, data_tx_23_p} -> data_tx_*_p
{data_tx_12, data_tx_23} -> data_tx_*
{adc_data_i0, adc_data_i0} -> adc_data_i*
{adc_data_q0, adc_data_q0} -> adc_data_q*
{rx_phy2, rx_phy4} -> rx_phy*

To provide a description to the squashed signals/buses, write, for example, data_tx_* once instead of the original name of all.

Warning

Do not create new IP with signals named as _phy*, it was added for legacy puporses, instead suffix with _*, e.g. mysignal_phy_4.

The :path: option is optional, and should not be included if the documentation file path matches the component.xml hierarchically.

HDL component diagram directive#

The HDL component diagram directive gets information parsed from component.xml library and generates a component diagram for the IP with buses and ports information.

Note

The component.xml files are generated by Vivado during the library build and not by the documentation tooling.

The directive syntax is:

.. hdl-component-diagram::
   :path: <ip_path>

For example:

.. hdl-component-diagram::
   :path: library/spi_engine/spi_engine_interconnect

The :path: option is optional, and should not be included if the documentation file path matches the component.xml hierarchically.

Note

This directive replaces the deprecated symbolator directive.

HDL regmap directive#

The HDL regmap directive gets information from docs/regmap/adi_regmap_*.txt files and generates tables with the register maps.

The directive syntax is:

.. hdl-regmap::
   :name: <regmap_name>
   :no-type-info:

For example:

.. hdl-regmap::
   :name: DMAC

Note

The register map name is the title-tool, the value above ENDTITLE in the source file.

This directive does not support content for descriptions, since the source file already have proper descriptions.

The :name: option is required, because the title tool does not match the IP name and one single docs/regmap/adi_regmap_*.txt file can have more than one register map. The :no-type-info: option is optional, and should not be included if it is in the main IP documentation page. It appends an auxiliary table explaining the register access types.

Collapsible directive#

The collapsible directive creates a collapsible/dropdown/”HTML details”.

The directive syntax is:

.. collapsible:: <label>

   <content>

For example:

.. collapsible:: Python code example.

   .. code:: python

      print("Hello World!")

Renders as:

print("Hello World!")

Notice how you can use any Sphinx syntax, even nest other directives.

Video directive#

The video directive creates a embedded video. Currently, direct MP4 and youtube embed links are supported, but could be easily expanded to support third-party services.

The directive syntax is:

.. video:: <url>

   <caption>

Always add a caption to the video, since a PDF output won’t contain the embed video, but a link to it.

For example:

.. video:: http://ftp.fau.de/fosdem/2015/devroom-software_defined_radio/iiosdr.mp4

   **Linux Industrial IO framework** - Lars-Peter Clausen, Analog Devices Inc

Renders as:

Linux Industrial IO framework - Lars-Peter Clausen, Analog Devices Inc

Video

Linux Industrial IO framework - Lars-Peter Clausen, Analog Devices Inc

http://ftp.fau.de/fosdem/2015/devroom-software_defined_radio/iiosdr.mp4

And:

.. video:: https://www.youtube.com/watch?v=p_VntEwUe24

   **LibIIO - A Library for Interfacing with Linux IIO Devices** - Dan Nechita, Analog Devices Inc

Renders as:

LibIIO - A Library for Interfacing with Linux IIO Devices - Dan Nechita, Analog Devices Inc

Video

LibIIO - A Library for Interfacing with Linux IIO Devices - Dan Nechita, Analog Devices Inc

https://www.youtube.com/watch?v=p_VntEwUe24

ESD warning directive#

The ESD warning directive creates a ESD warning, for example:

.. esd-warning::

Renders as:

All the products described on this page include ESD (electrostatic discharge) sensitive devices. Electrostatic charges as high as 4000V readily accumulate on the human body or test equipment and can discharge without detection. Although the boards feature ESD protection circuitry, permanent damage may occur on devices subjected to high-energy electrostatic discharges. Therefore, proper ESD precautions are recommended to avoid performance degradation or loss of functionality. This includes removing static charge on external equipment, cables, or antennas before connecting to the device.

Global options for directives#

Set hide_collapsible_content to True to hide the collapsibles by default.

Set monolithic to True prefix paths with repos/<repo>. This is meant for the System Top Documentation repository only.

Common sections#

HDL common sections#

The More information and Support sections that are present in the HDL project documentation, are actually separate pages inserted as links. They’re located at hdl/projects/common/more_information.rst and /support.rst, and cannot be referenced here because they don’t have an ID at the beginning of the page, so not to have warnings when the documentation is rendered that they’re not included in any toctree.

They are inserted like this:

.. include:: ../common/more_information.rst

.. include:: ../common/support.rst

And will be rendered as sections of the page.

Dynamic elements#

Dynamic elements refer to sections of the generated webpage that updates when loaded online from a source of truth, in general, doctools/*.json files; it uses a concept similar to “react components”.

These *.json files are generated when export_metadata is true in the conf.py. From the JavaScript side, it fetches from {content_root}[../versioned]/../doctools/[versioned]/metadata.json.

Note

path version is present and set if latest exists at {content_root}/../doctools and the stored version can be extracted.

The dynamic elements are:

  • The navigation bar at the top is updated using the repotoc entry in doctools/metadata.json.

  • A banner at the top is present/updated when the banner entry in doctools/metadata.json exists.