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">
{{ vendors[board.vendor] }}
{% if board.image -%} A picture of the {{ board.full_name }} board 1 and lines[1] == ""): + first_line = lines[0] + # Check for the first period + period_index = first_line.find(".") + # If there's a period, return up to the period; otherwise, return the full line + return first_line[: period_index + 1] if period_index != -1 else first_line + + # Case 2: Multiple contiguous lines, treat as a block + block = " ".join(lines) + period_index = block.find(".") + # If there's a period, return up to the period; otherwise, return the full block + return block[: period_index + 1] if period_index != -1 else block + + @classmethod + def get_cached_description(cls, node): + """Get the cached description for a devicetree node. + + Args: + node: A devicetree node object with matching_compat and description attributes. + + Returns: + The cached description for the node's compatible, creating it if needed. + """ + return cls._compat_description_cache.setdefault( + node.matching_compat, + cls.get_first_sentence(node.description) + ) + + def guess_file_from_patterns(directory, patterns, name, extensions): for pattern in patterns: for ext in extensions: @@ -197,13 +250,10 @@ def get_catalog(generate_hw_features=False): doc_page = guess_doc_page(board) supported_features = {} - targets = set() # Use pre-gathered build info and DTS files if board.name in board_devicetrees: for board_target, edt in board_devicetrees[board.name].items(): - targets.add(board_target) - okay_nodes = [ node for node in edt.nodes @@ -218,13 +268,13 @@ def get_catalog(generate_hw_features=False): if binding_path.is_relative_to(ZEPHYR_BINDINGS) else "misc" ) - target_features.setdefault(binding_type, set()).add(node.matching_compat) - + description = DeviceTreeUtils.get_cached_description(node) + target_features.setdefault(binding_type, {}).setdefault( + node.matching_compat, description + ) - # for now we do the union of all supported features for all of board's targets but - # in the future it's likely the catalog will be organized so that the list of - # supported features is also available per target. - supported_features.update(target_features) + # Store features for this specific target + supported_features[board_target] = target_features # Grab all the twister files for this board and use them to figure out all the archs it # supports. @@ -246,7 +296,6 @@ def get_catalog(generate_hw_features=False): "archs": list(archs), "socs": list(socs), "supported_features": supported_features, - "targets": list(targets), "image": guess_image(board), } diff --git a/doc/_static/css/custom.css b/doc/_static/css/custom.css index 5671d6a052055..0f21fc944de2f 100644 --- a/doc/_static/css/custom.css +++ b/doc/_static/css/custom.css @@ -1190,3 +1190,12 @@ li>a.code-sample-link.reference.internal.current { text-overflow: ellipsis; max-width:80%; } + +#supported-features>div>table>tbody>tr>td>p { + white-space: normal; +} + +#supported-features td { + background-color: var(--table-row-odd-background-color); + border-left-width: 1px; +} diff --git a/doc/contribute/documentation/guidelines.rst b/doc/contribute/documentation/guidelines.rst index 8cba07080cc03..891d29e27e55e 100644 --- a/doc/contribute/documentation/guidelines.rst +++ b/doc/contribute/documentation/guidelines.rst @@ -1231,6 +1231,20 @@ Boards This directive is used to generate a catalog of Zephyr-supported boards that can be used to quickly browse the list of all supported boards and filter them according to various criteria. +.. rst:directive:: .. zephyr:board-supported-hw:: + + This directive is used to show supported hardware features for all the targets of the board + documented in the current page. The tables are automatically generated based on the board's + Devicetree. + + The directive must be used in a document that also contains a :rst:dir:`zephyr:board` directive, + as it relies on the board information to generate the table. + + .. note:: + + This directive requires that the documentation is built with hardware features generation enabled + (``zephyr_generate_hw_features`` config option set to ``True``). If disabled, a warning message + will be shown instead of the hardware features tables. References ********** diff --git a/doc/templates/board.tmpl b/doc/templates/board.tmpl index 96201094eed29..d17c936a67a37 100644 --- a/doc/templates/board.tmpl +++ b/doc/templates/board.tmpl @@ -17,7 +17,8 @@ Hardware Supported Features ================== -[List of supported features and level of support in Zephyr] + +.. zephyr:board-supported-hw:: Connections and IOs =================== diff --git a/dts/bindings/binding-types.txt b/dts/bindings/binding-types.txt new file mode 100644 index 0000000000000..49eb269555413 --- /dev/null +++ b/dts/bindings/binding-types.txt @@ -0,0 +1,136 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 The Linux Foundation +# SPDX-License-Identifier: Apache-2.0 +# +# Devicetree binding types registry. +# +# This file contains the mapping of binding types (folders directly under this +# file) to their descriptions and provides a definition for each acronym used. +# +# The contents of this file are parsed during documentation generation. +# +# Anything that starts with a '#' is treated as a comment and ignored. +# Non-empty lines should be in this format: +# +# + +# zephyr-keep-sorted-start +acpi ACPI (Advanced Configuration and Power Interface) +adc ADC (Analog to Digital Converter) +alh ALH (Audio Link Hub) +arc ARC architecture +arm ARM architecture +audio Audio +auxdisplay Auxiliary Display +base Base +battery Battery +bluetooth Bluetooth +cache Cache +can CAN (Controller Area Network) +charger Charger +clock Clock control +comparator Comparator +coredump Core dump +counter Counter +cpu CPU (Central Processing Unit) +crypto Cryptographic accelerator +dac DAC (Digital to Analog Converter) +dai DAI (Digital Audio Interface) +debug Debug +dfpmcch DFPMCCH +dfpmccu DFPMCCU +disk Disk +display Display +dma DMA (Direct Memory Access) +dsa DSA (Distributed Switch Architecture) +edac EDAC (Error Detection and Correction) +espi ESPI (Enhanced Serial Peripheral Interface) +ethernet Ethernet +firmware Firmware +flash_controller Flash controller +fpga FPGA (Field Programmable Gate Array) +fs File system +fuel-gauge Fuel gauge +gnss GNSS (Global Navigation Satellite System) +gpio GPIO (General Purpose Input/Output) & Headers +haptics Haptics +hda HDA (High Definition Audio) +hdlc_rcp_if IEEE 802.15.4 HDLC RCP interface +hwinfo Hardware information +hwspinlock Hardware spinlock +i2c I2C (Inter-Integrated Circuit) +i2s I2S (Inter-Integrated Circuit Sound) +i3c I3C (Improved Inter-Integrated Circuit) +ieee802154 IEEE 802.15.4 +iio IIO (Industrial I/O) +input Input +interrupt-controller Interrupt controller +ipc IPC (Inter-Processor Communication) +ipm IPM (Inter-Processor Mailbox) +kscan Keyscan +led LED (Light Emitting Diode) +led_strip LED (Light Emitting Diode) strip +lora LoRa +mbox Mailbox +mdio MDIO (Management Data Input/Output) +memory-controllers Memory controller +memory-window Memory window +mfd Multi-Function Device +mhu Mailbox Handling Unit +mipi-dbi Mobile Industry Processor Interface Display Bus Interface +mipi-dsi Mobile Industry Processor Interface Display Serial Interface +misc Miscellaneous +mm Memory management +mmc MMC (MultiMedia Card) +mmu_mpu MMU (Memory Management Unit) / MPU (Memory Protection Unit) +modem Modem +mspi Multi-bit SPI (Serial Peripheral Interface) +mtd MTD (Memory Technology Device) +net Networking +options Options +ospi OCTOSPI (Octal Serial Peripheral Interface) +pcie PCIe (Peripheral Component Interconnect Express) +peci PECI (Platform Environment Control Interface) +phy PHY +pinctrl Pin control +pm_cpu_ops Power management CPU operations +power Power management +power-domain Power domain +ppc PPC architecture +ps2 PS/2 (Personal System/2) +pwm PWM (Pulse Width Modulation) +qspi QSPI (Quad Serial Peripheral Interface) +regulator Regulator +reserved-memory Reserved memory +reset Reset controller +retained_mem Retained memory +retention Retention +riscv RISC-V architecture +rng RNG (Random Number Generator) +rtc RTC (Real Time Clock) +sd SD (Secure Digital) +sdhc SDHC (Secure Digital High Capacity) +sensor Sensors +serial Serial controller +shi SHI (Serial Host Interface) +sip_svc Service in Platform +smbus SMbus (System Management Bus) +sound Sound +spi SPI (Serial Peripheral Interface) +sram SRAM (Static Random Access Memory) +stepper Stepper +syscon System controller +tach Tachometer +tcpc USB Type-C Port Controller +test Test +timer Timer +timestamp Timestamp +usb USB (Universal Serial Bus) +usb-c USB (Universal Serial Bus) Type-C +video Video +virtualization Virtualization +w1 1-Wire +watchdog Watchdog +wifi Wi-Fi +xen Xen +xspi xSPI (Expanded Serial Peripheral Interface) +# zephyr-keep-sorted-stop