Browse Source

doc: boards: catalog: add shields to board catalog

Populate board catalog with shields as well and have them show up in the
boards/index page.

Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
pull/90404/head
Benjamin Cabé 2 months ago committed by Benjamin Cabé
parent
commit
79e3ecb7a9
  1. 39
      boards/index.rst
  2. 8
      boards/shields/index.rst
  3. 2
      doc/_extensions/zephyr/domain/__init__.py
  4. 46
      doc/_extensions/zephyr/domain/static/css/board-catalog.css
  5. 24
      doc/_extensions/zephyr/domain/static/js/board-catalog.js
  6. 14
      doc/_extensions/zephyr/domain/templates/board-catalog.html
  7. 25
      doc/_extensions/zephyr/domain/templates/shield-card.html
  8. 19
      doc/_scripts/gen_boards_catalog.py
  9. 2
      doc/hardware/porting/shields.rst

39
boards/index.rst

@ -3,26 +3,28 @@
Supported Boards and Shields Supported Boards and Shields
############################ ############################
If you are looking to add Zephyr support for a new board, please start with the This page lists all the boards and shields that are currently supported in Zephyr.
:ref:`board_porting_guide`.
When adding support documentation for a board, remember to use the template If you are looking to add Zephyr support for a new board, please start with the
available under :zephyr_file:`doc/templates/board.tmpl`. :ref:`board_porting_guide`. When adding support documentation for a board, remember to use the
template available under :zephyr_file:`doc/templates/board.tmpl`.
Shields are hardware add-ons that can be stacked on top of a board to add extra Shields are hardware add-ons that can be stacked on top of a board to add extra functionality.
functionality. They are listed separately from boards, towards :ref:`the end of Refer to the :ref:`shield_porting_guide` for more information on how to port a shield.
this page <boards-shields>`.
.. admonition:: Search Tips .. admonition:: Search Tips
:class: dropdown :class: dropdown
* Use the form below to filter the list of supported boards. If a field is left empty, it will * Use the form below to filter the list of supported boards and shields. If a field is left
not be used in the filtering process. empty, it will not be used in the filtering process.
* Filtering by name and vendor is available for both boards and shields. The rest of the fields
apply only to boards.
* A board must meet **all** criteria selected across different fields. For example, if you select * A board/shield must meet **all** criteria selected across different fields. For example, if you
both a vendor and an architecture, only boards that match both will be displayed. Within a select both a vendor and an architecture, only boards that match both will be displayed. Within
single field, selecting multiple options (such as two architectures) will show boards matching a single field, selecting multiple options (such as two architectures) will show boards
**either** option. matching **either** option.
* The list of supported hardware features for each board is automatically generated using * The list of supported hardware features for each board is automatically generated using
information from the Devicetree. It may not be reflecting the full list of supported features information from the Devicetree. It may not be reflecting the full list of supported features
@ -40,14 +42,3 @@ this page <boards-shields>`.
*/index */index
.. zephyr:board-catalog:: .. zephyr:board-catalog::
.. _boards-shields:
Shields
#######
.. toctree::
:maxdepth: 1
:glob:
shields/**/*

8
boards/shields/index.rst

@ -0,0 +1,8 @@
Shields
#######
.. toctree::
:maxdepth: 1
:glob:
**/*

2
doc/_extensions/zephyr/domain/__init__.py

@ -757,6 +757,7 @@ class BoardCatalogDirective(SphinxDirective):
"board-catalog.html", "board-catalog.html",
{ {
"boards": domain_data["boards"], "boards": domain_data["boards"],
"shields": domain_data["shields"],
"vendors": domain_data["vendors"], "vendors": domain_data["vendors"],
"socs": domain_data["socs"], "socs": domain_data["socs"],
"hw_features_present": self.env.app.config.zephyr_generate_hw_features, "hw_features_present": self.env.app.config.zephyr_generate_hw_features,
@ -1381,6 +1382,7 @@ def load_board_catalog_into_domain(app: Sphinx) -> None:
hw_features_vendor_filter=app.config.zephyr_hw_features_vendor_filter, hw_features_vendor_filter=app.config.zephyr_hw_features_vendor_filter,
) )
app.env.domaindata["zephyr"]["boards"] = board_catalog["boards"] app.env.domaindata["zephyr"]["boards"] = board_catalog["boards"]
app.env.domaindata["zephyr"]["shields"] = board_catalog["shields"]
app.env.domaindata["zephyr"]["vendors"] = board_catalog["vendors"] app.env.domaindata["zephyr"]["vendors"] = board_catalog["vendors"]
app.env.domaindata["zephyr"]["socs"] = board_catalog["socs"] app.env.domaindata["zephyr"]["socs"] = board_catalog["socs"]
app.env.domaindata["zephyr"]["runners"] = board_catalog["runners"] app.env.domaindata["zephyr"]["runners"] = board_catalog["runners"]

46
doc/_extensions/zephyr/domain/static/css/board-catalog.css

@ -146,6 +146,7 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
transition: transform 0.3s ease; transition: transform 0.3s ease;
position: relative;
} }
.board-card:hover, .board-card:hover,
@ -155,6 +156,34 @@
text-decoration: none; text-decoration: none;
} }
.board-card.shield::before {
content: "";
position: absolute;
top: 0;
right: 0;
border-style: solid;
border-width: 0 48px 48px 0;
border-color: transparent var(--admonition-note-title-background-color) transparent transparent;
border-radius: 0 8px 0 0;
}
.board-card.shield::after {
rotate: 45deg;
content: "SHIELD";
position: absolute;
top: 7px;
right: 6px;
color: var(--admonition-note-title-color);
font-size: 11px;
font-weight: 600;
z-index: 1;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.board-card .picture { .board-card .picture {
width: auto; width: auto;
height: auto; height: auto;
@ -247,6 +276,23 @@
max-width: none; max-width: none;
} }
#catalog.compact .board-card.shield::before {
display: none; /* Remove the S prefix */
}
#catalog.compact .board-card.shield::after {
content: "Shield";
color: var(--admonition-note-color);
background-color: var(--admonition-note-background-color);
padding: 2px 10px 2px 10px;
border-radius: 30px;
font-size: 0.8em;
}
#catalog.compact .board-card.shield {
padding: 4px 0;
}
#catalog.compact .board-card .vendor, #catalog.compact .board-card .vendor,
#catalog.compact .board-card .picture { #catalog.compact .board-card .picture {
display: none; display: none;

24
doc/_extensions/zephyr/domain/static/js/board-catalog.js

@ -119,7 +119,7 @@ function setupHWCapabilitiesField() {
const datalist = document.getElementById('tag-list'); const datalist = document.getElementById('tag-list');
const tagCounts = Array.from(document.querySelectorAll('.board-card')).reduce((acc, board) => { const tagCounts = Array.from(document.querySelectorAll('.board-card')).reduce((acc, board) => {
board.getAttribute('data-supported-features').split(' ').forEach(tag => { (board.getAttribute('data-supported-features') || '').split(' ').forEach(tag => {
acc[tag] = (acc[tag] || 0) + 1; acc[tag] = (acc[tag] || 0) + 1;
}); });
return acc; return acc;
@ -254,12 +254,14 @@ function resetForm() {
} }
function updateBoardCount() { function updateBoardCount() {
const boards = document.getElementsByClassName("board-card"); const boards = Array.from(document.getElementsByClassName("board-card"));
const visibleBoards = Array.from(boards).filter( const visible = boards.filter(board => !board.classList.contains("hidden"));
(board) => !board.classList.contains("hidden") const shields = boards.filter(board => board.classList.contains("shield"));
).length; const visibleShields = visible.filter(board => board.classList.contains("shield"));
const totalBoards = boards.length;
document.getElementById("nb-matches").textContent = `Showing ${visibleBoards} of ${totalBoards}`; document.getElementById("nb-matches").textContent =
`Showing ${visible.length - visibleShields.length} of ${boards.length - shields.length} boards,`
+ ` ${visibleShields.length} of ${shields.length} shields`;
} }
function filterBoards() { function filterBoards() {
@ -281,10 +283,10 @@ function filterBoards() {
Array.from(boards).forEach(function (board) { Array.from(boards).forEach(function (board) {
const boardName = board.getAttribute("data-name").toLowerCase(); const boardName = board.getAttribute("data-name").toLowerCase();
const boardArchs = board.getAttribute("data-arch").split(" "); const boardArchs = (board.getAttribute("data-arch") || "").split(" ").filter(Boolean);
const boardVendor = board.getAttribute("data-vendor"); const boardVendor = board.getAttribute("data-vendor") || "";
const boardSocs = board.getAttribute("data-socs").split(" "); const boardSocs = (board.getAttribute("data-socs") || "").split(" ").filter(Boolean);
const boardSupportedFeatures = board.getAttribute("data-supported-features").split(" "); const boardSupportedFeatures = (board.getAttribute("data-supported-features") || "").split(" ").filter(Boolean);
let matches = true; let matches = true;

14
doc/_extensions/zephyr/domain/templates/board-catalog.html

@ -3,12 +3,12 @@
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
#} #}
<form class="filter-form" aria-label="Filter boards by name, architecture, and vendor"> <form class="filter-form" aria-label="Filter boards & shields by name, architecture, and vendor">
<div class="form-group" style="flex-basis: 100%"> <div class="form-group" style="flex-basis: 100%">
<label for="name">Name</label> <label for="name">Name</label>
<input type="text" id="name" <input type="text" id="name"
placeholder='Name (or partial name) of the board, e.g. "reel board", "nucleo", &hellip;' placeholder='Name (or partial name) of the board/shield, e.g. "reel board", "nucleo", &hellip;'
oninput="filterBoards()" /> oninput="filterBoards()" />
</div> </div>
@ -35,9 +35,11 @@
<div class="select-container"> <div class="select-container">
<select id="vendor"> <select id="vendor">
<option value="" disabled selected>Select a vendor</option> <option value="" disabled selected>Select a vendor</option>
{# Only show those vendors that have actual boards in the catalog. {# Only show those vendors that have actual boards or shields in the catalog.
Note: as sorting per vendor name is not feasible in Jinja, the option list is sorted in the JavaScript code later #} Note: as sorting per vendor name is not feasible in Jinja, the option list is sorted in the JavaScript code later #}
{% for vendor in (boards | items | map(attribute='1.vendor') | unique ) -%} {% set board_vendors = boards | items | map(attribute='1.vendor') | unique | list %}
{% set shield_vendors = shields | items | map(attribute='1.vendor') | unique | list %}
{% for vendor in (board_vendors + shield_vendors) | unique -%}
<option value="{{ vendor }}">{{ vendors[vendor] }}</option> <option value="{{ vendor }}">{{ vendors[vendor] }}</option>
{% endfor %} {% endfor %}
</select> </select>
@ -98,6 +100,10 @@
{% for board_name, board in boards | items | sort(attribute='1.full_name') -%} {% for board_name, board in boards | items | sort(attribute='1.full_name') -%}
{% include "board-card.html" %} {% include "board-card.html" %}
{% endfor %} {% endfor %}
{% for shield_name, shield in shields | items | sort(attribute='1.full_name') -%}
{% include "shield-card.html" %}
{% endfor %}
</div> </div>
<script> <script>

25
doc/_extensions/zephyr/domain/templates/shield-card.html

@ -0,0 +1,25 @@
{#
Copyright (c) 2024-2025, The Linux Foundation.
SPDX-License-Identifier: Apache-2.0
#}
<a
class="board-card shield"
{% if shield.doc_page -%}
href="../{{ shield.doc_page | replace(".rst", ".html") }}"
{% else -%}
href="#"
{% endif -%}
aria-label="Open the documentation page for {{ shield.full_name }}"
data-name="{{ shield.full_name}}"
data-vendor="{{ shield.vendor }}"
tabindex="0">
<div class="vendor">{{ vendors[shield.vendor] }}</div>
{% if shield.image -%}
<img alt="A picture of the {{ shield.full_name }} shield"
src="../_images/{{ shield.image.split('/')[-1] }}" class="picture" />
{% else -%}
<div class="no-picture fa fa-microchip picture"></div>
{% endif -%}
<div class="board-name">{{ shield.full_name }}</div>
</a>

19
doc/_scripts/gen_boards_catalog.py

@ -12,6 +12,7 @@ from pathlib import Path
import list_boards import list_boards
import list_hardware import list_hardware
import list_shields
import yaml import yaml
import zephyr_module import zephyr_module
from gen_devicetree_rest import VndLookup from gen_devicetree_rest import VndLookup
@ -256,8 +257,10 @@ def get_catalog(generate_hw_features=False, hw_features_vendor_filter=None):
) )
boards = list_boards.find_v2_boards(args_find_boards) boards = list_boards.find_v2_boards(args_find_boards)
shields = list_shields.find_shields(args_find_boards)
systems = list_hardware.find_v2_systems(args_find_boards) systems = list_hardware.find_v2_systems(args_find_boards)
board_catalog = {} board_catalog = {}
shield_catalog = {}
board_devicetrees = {} board_devicetrees = {}
board_runners = {} board_runners = {}
@ -400,8 +403,24 @@ def get_catalog(generate_hw_features=False, hw_features_vendor_filter=None):
"commands": runner.capabilities().commands, "commands": runner.capabilities().commands,
} }
for shield in shields:
doc_page = guess_doc_page(shield)
if doc_page and doc_page.is_relative_to(ZEPHYR_BASE):
doc_page_path = doc_page.relative_to(ZEPHYR_BASE).as_posix()
else:
doc_page_path = None
shield_catalog[shield.name] = {
"name": shield.name,
"full_name": shield.full_name or shield.name,
"vendor": shield.vendor or "others",
"doc_page": doc_page_path,
"image": guess_image(shield),
}
return { return {
"boards": board_catalog, "boards": board_catalog,
"shields": shield_catalog,
"vendors": {**vnd_lookup.vnd2vendor, "others": "Other/Unknown"}, "vendors": {**vnd_lookup.vnd2vendor, "others": "Other/Unknown"},
"socs": socs_hierarchy, "socs": socs_hierarchy,
"runners": available_runners, "runners": available_runners,

2
doc/hardware/porting/shields.rst

@ -8,6 +8,8 @@ to extend its features and services for easier and modularized prototyping.
In Zephyr, the shield feature provides Zephyr-formatted shield In Zephyr, the shield feature provides Zephyr-formatted shield
descriptions for easier compatibility with applications. descriptions for easier compatibility with applications.
.. _shield_porting_guide:
Shield porting and configuration Shield porting and configuration
******************************** ********************************

Loading…
Cancel
Save