diff --git a/boards/bbc/microbit/doc/index.rst b/boards/bbc/microbit/doc/index.rst index 1d829d89a89ae..e7e9c49819ffe 100644 --- a/boards/bbc/microbit/doc/index.rst +++ b/boards/bbc/microbit/doc/index.rst @@ -13,13 +13,6 @@ magnetometer sensors, Bluetooth and USB connectivity, a display consisting of external battery pack. The device inputs and outputs are through five ring connectors that are part of the 23-pin edge connector. -* :abbr:`NVIC (Nested Vectored Interrupt Controller)` -* :abbr:`RTC (nRF RTC System Clock)` -* UART -* GPIO -* FLASH -* RADIO (Bluetooth Low Energy) - More information about the board can be found at the `microbit website`_. Hardware @@ -39,25 +32,7 @@ The micro:bit has the following physical features: Supported Features ================== -The bbc_microbit board configuration supports the following nRF51 -hardware features: - -+-----------+------------+----------------------+ -| Interface | Controller | Driver/Component | -+===========+============+======================+ -| NVIC | on-chip | nested vectored | -| | | interrupt controller | -+-----------+------------+----------------------+ -| RTC | on-chip | system clock | -+-----------+------------+----------------------+ -| UART | on-chip | serial port | -+-----------+------------+----------------------+ -| GPIO | on-chip | gpio | -+-----------+------------+----------------------+ -| FLASH | on-chip | flash | -+-----------+------------+----------------------+ -| RADIO | on-chip | Bluetooth | -+-----------+------------+----------------------+ +.. zephyr:board-supported-hw:: Programming and Debugging ************************* diff --git a/boards/bbc/microbit_v2/doc/index.rst b/boards/bbc/microbit_v2/doc/index.rst index 62c3cee2252c3..fad9f3d608fed 100644 --- a/boards/bbc/microbit_v2/doc/index.rst +++ b/boards/bbc/microbit_v2/doc/index.rst @@ -33,25 +33,7 @@ The micro:bit-v2 has the following physical features: Supported Features ================== -The bbc_microbit_v2 board configuration supports the following -hardware features: - -+-----------+------------+----------------------+ -| Interface | Controller | Driver/Component | -+===========+============+======================+ -| NVIC | on-chip | nested vectored | -| | | interrupt controller | -+-----------+------------+----------------------+ -| RTC | on-chip | system clock | -+-----------+------------+----------------------+ -| UART | on-chip | serial port | -+-----------+------------+----------------------+ -| GPIO | on-chip | gpio | -+-----------+------------+----------------------+ -| FLASH | on-chip | flash | -+-----------+------------+----------------------+ -| RADIO | on-chip | Bluetooth | -+-----------+------------+----------------------+ +.. zephyr:board-supported-hw:: Programming and Debugging ************************* diff --git a/doc/_extensions/zephyr/domain/__init__.py b/doc/_extensions/zephyr/domain/__init__.py index bc8957de01e4b..bc8981f3ea609 100644 --- a/doc/_extensions/zephyr/domain/__init__.py +++ b/doc/_extensions/zephyr/domain/__init__.py @@ -27,6 +27,7 @@ """ import json +import re import sys from collections.abc import Iterator from os import path @@ -65,6 +66,43 @@ TEMPLATES_DIR = Path(__file__).parent / "templates" RESOURCES_DIR = Path(__file__).parent / "static" +# Load and parse binding types from text file +BINDINGS_TXT_PATH = ZEPHYR_BASE / "dts" / "bindings" / "binding-types.txt" +ACRONYM_PATTERN = re.compile(r'([a-zA-Z0-9-]+)\s*\((.*?)\)') +BINDING_TYPE_TO_DOCUTILS_NODE = {} + + +def parse_text_with_acronyms(text): + """Parse text that may contain acronyms into a list of nodes.""" + result = nodes.inline() + last_end = 0 + + for match in ACRONYM_PATTERN.finditer(text): + # Add any text before the acronym + if match.start() > last_end: + result += nodes.Text(text[last_end : match.start()]) + + # Add the acronym + abbr, explanation = match.groups() + result += nodes.abbreviation(abbr, abbr, explanation=explanation) + last_end = match.end() + + # Add any remaining text + if last_end < len(text): + result += nodes.Text(text[last_end:]) + + return result + + +with open(BINDINGS_TXT_PATH) as f: + for line in f: + line = line.strip() + if not line or line.startswith('#'): + continue + + key, value = line.split('\t', 1) + BINDING_TYPE_TO_DOCUTILS_NODE[key] = parse_text_with_acronyms(value) + logger = logging.getLogger(__name__) @@ -685,6 +723,7 @@ def run(self): board_node = BoardNode(id=board_name) board_node["full_name"] = board["full_name"] board_node["vendor"] = vendors.get(board["vendor"], board["vendor"]) + board_node["supported_features"] = board["supported_features"] board_node["archs"] = board["archs"] board_node["socs"] = board["socs"] board_node["image"] = board["image"] @@ -716,6 +755,137 @@ def run(self): return [nodes.paragraph(text="Board catalog is only available in HTML.")] +class BoardSupportedHardwareDirective(SphinxDirective): + """A directive for showing the supported hardware features of a board.""" + + has_content = False + required_arguments = 0 + optional_arguments = 0 + + def run(self): + env = self.env + docname = env.docname + + matcher = NodeMatcher(BoardNode) + board_nodes = list(self.state.document.traverse(matcher)) + if not board_nodes: + logger.warning( + "board-supported-hw directive must be used in a board documentation page.", + location=(docname, self.lineno), + ) + return [] + + board_node = board_nodes[0] + supported_features = board_node["supported_features"] + result_nodes = [] + + paragraph = nodes.paragraph() + paragraph += nodes.Text("The ") + paragraph += nodes.literal(text=board_node["id"]) + paragraph += nodes.Text(" board supports the hardware features listed below.") + result_nodes.append(paragraph) + + if not env.app.config.zephyr_generate_hw_features: + note = nodes.admonition() + note += nodes.title(text="Note") + note["classes"].append("warning") + note += nodes.paragraph( + text="The list of supported hardware features was not generated. Run a full " + "documentation build for the required metadata to be available." + ) + result_nodes.append(note) + return result_nodes + + # Add the note before any tables + note = nodes.admonition() + note += nodes.title(text="Note") + note["classes"].append("note") + note += nodes.paragraph( + text="The tables below were automatically generated using information from the " + "Devicetree. They may not be fully representative of all the hardware features " + "supported by the board." + ) + result_nodes.append(note) + + for target, features in sorted(supported_features.items()): + if not features: + continue + + target_heading = nodes.section(ids=[f"{board_node['id']}-{target}-hw-features"]) + heading = nodes.title() + heading += nodes.literal(text=target) + heading += nodes.Text(" target") + target_heading += heading + result_nodes.append(target_heading) + + table = nodes.table(classes=["colwidths-given"]) + tgroup = nodes.tgroup(cols=3) + + tgroup += nodes.colspec(colwidth=20, classes=["col-1"]) + tgroup += nodes.colspec(colwidth=50) + tgroup += nodes.colspec(colwidth=30) + + thead = nodes.thead() + row = nodes.row() + headers = ["Type", "Description", "Compatible"] + for header in headers: + row += nodes.entry("", nodes.paragraph(text=header)) + thead += row + tgroup += thead + + tbody = nodes.tbody() + + def feature_sort_key(feature): + # Put "CPU" first. Later updates might also give priority to features + # like "sensor"s, for example. + if feature == "cpu": + return (0, feature) + return (1, feature) + + sorted_features = sorted(features.keys(), key=feature_sort_key) + + for feature in sorted_features: + items = list(features[feature].items()) + num_items = len(items) + + for i, (key, value) in enumerate(items): + row = nodes.row() + + # Add type column only for first row of a feature + if i == 0: + type_entry = nodes.entry(morerows=num_items - 1) + type_entry += nodes.paragraph( + "", + "", + BINDING_TYPE_TO_DOCUTILS_NODE.get( + feature, nodes.Text(feature) + ).deepcopy(), + ) + row += type_entry + + row += nodes.entry("", nodes.paragraph(text=value)) + + # Create compatible xref + xref = addnodes.pending_xref( + "", + refdomain="std", + reftype="dtcompatible", + reftarget=key, + refexplicit=False, + refwarn=True, + ) + xref += nodes.literal(text=key) + row += nodes.entry("", nodes.paragraph("", "", xref)) + + tbody += row + + tgroup += tbody + table += tgroup + result_nodes.append(table) + + return result_nodes + + class ZephyrDomain(Domain): """Zephyr domain""" @@ -734,6 +904,7 @@ class ZephyrDomain(Domain): "code-sample-category": CodeSampleCategoryDirective, "board-catalog": BoardCatalogDirective, "board": BoardDirective, + "board-supported-hw": BoardSupportedHardwareDirective, } object_types: dict[str, ObjType] = { diff --git a/doc/_extensions/zephyr/domain/templates/board-card.html b/doc/_extensions/zephyr/domain/templates/board-card.html index efc83cdcdabe2..0e4808b85a74f 100644 --- a/doc/_extensions/zephyr/domain/templates/board-card.html +++ b/doc/_extensions/zephyr/domain/templates/board-card.html @@ -15,7 +15,17 @@ data-arch="{{ board.archs | join(" ") }}" data-vendor="{{ board.vendor }}" data-socs="{{ board.socs | join(" ") }}" - data-supported-features="{{ board.supported_features | join(" ") }}" tabindex="0"> + data-supported-features=" + {%- set feature_types = [] -%} + {%- for target_features in board.supported_features.values() -%} + {%- for feature_type in target_features.keys() -%} + {%- if feature_type not in feature_types -%} + {%- set _ = feature_types.append(feature_type) -%} + {%- endif -%} + {%- endfor -%} + {%- endfor -%} + {{- feature_types|join(' ') -}} + " tabindex="0">