Skip to content

Commit 79e3ecb

Browse files
committed
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>
1 parent df01afa commit 79e3ecb

File tree

9 files changed

+140
-39
lines changed

9 files changed

+140
-39
lines changed

boards/index.rst

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,28 @@
33
Supported Boards and Shields
44
############################
55

6-
If you are looking to add Zephyr support for a new board, please start with the
7-
:ref:`board_porting_guide`.
6+
This page lists all the boards and shields that are currently supported in Zephyr.
87

9-
When adding support documentation for a board, remember to use the template
10-
available under :zephyr_file:`doc/templates/board.tmpl`.
8+
If you are looking to add Zephyr support for a new board, please start with the
9+
:ref:`board_porting_guide`. When adding support documentation for a board, remember to use the
10+
template available under :zephyr_file:`doc/templates/board.tmpl`.
1111

12-
Shields are hardware add-ons that can be stacked on top of a board to add extra
13-
functionality. They are listed separately from boards, towards :ref:`the end of
14-
this page <boards-shields>`.
12+
Shields are hardware add-ons that can be stacked on top of a board to add extra functionality.
13+
Refer to the :ref:`shield_porting_guide` for more information on how to port a shield.
1514

1615
.. admonition:: Search Tips
1716
:class: dropdown
1817

19-
* Use the form below to filter the list of supported boards. If a field is left empty, it will
20-
not be used in the filtering process.
18+
* Use the form below to filter the list of supported boards and shields. If a field is left
19+
empty, it will not be used in the filtering process.
20+
21+
* Filtering by name and vendor is available for both boards and shields. The rest of the fields
22+
apply only to boards.
2123

22-
* A board must meet **all** criteria selected across different fields. For example, if you select
23-
both a vendor and an architecture, only boards that match both will be displayed. Within a
24-
single field, selecting multiple options (such as two architectures) will show boards matching
25-
**either** option.
24+
* A board/shield must meet **all** criteria selected across different fields. For example, if you
25+
select both a vendor and an architecture, only boards that match both will be displayed. Within
26+
a single field, selecting multiple options (such as two architectures) will show boards
27+
matching **either** option.
2628

2729
* The list of supported hardware features for each board is automatically generated using
2830
information from the Devicetree. It may not be reflecting the full list of supported features
@@ -40,14 +42,3 @@ this page <boards-shields>`.
4042
*/index
4143

4244
.. zephyr:board-catalog::
43-
44-
.. _boards-shields:
45-
46-
Shields
47-
#######
48-
49-
.. toctree::
50-
:maxdepth: 1
51-
:glob:
52-
53-
shields/**/*

boards/shields/index.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Shields
2+
#######
3+
4+
.. toctree::
5+
:maxdepth: 1
6+
:glob:
7+
8+
**/*

doc/_extensions/zephyr/domain/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,7 @@ def run(self):
757757
"board-catalog.html",
758758
{
759759
"boards": domain_data["boards"],
760+
"shields": domain_data["shields"],
760761
"vendors": domain_data["vendors"],
761762
"socs": domain_data["socs"],
762763
"hw_features_present": self.env.app.config.zephyr_generate_hw_features,
@@ -1381,6 +1382,7 @@ def load_board_catalog_into_domain(app: Sphinx) -> None:
13811382
hw_features_vendor_filter=app.config.zephyr_hw_features_vendor_filter,
13821383
)
13831384
app.env.domaindata["zephyr"]["boards"] = board_catalog["boards"]
1385+
app.env.domaindata["zephyr"]["shields"] = board_catalog["shields"]
13841386
app.env.domaindata["zephyr"]["vendors"] = board_catalog["vendors"]
13851387
app.env.domaindata["zephyr"]["socs"] = board_catalog["socs"]
13861388
app.env.domaindata["zephyr"]["runners"] = board_catalog["runners"]

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
flex-direction: column;
147147
align-items: center;
148148
transition: transform 0.3s ease;
149+
position: relative;
149150
}
150151

151152
.board-card:hover,
@@ -155,6 +156,34 @@
155156
text-decoration: none;
156157
}
157158

159+
.board-card.shield::before {
160+
content: "";
161+
position: absolute;
162+
top: 0;
163+
right: 0;
164+
border-style: solid;
165+
border-width: 0 48px 48px 0;
166+
border-color: transparent var(--admonition-note-title-background-color) transparent transparent;
167+
border-radius: 0 8px 0 0;
168+
}
169+
170+
.board-card.shield::after {
171+
rotate: 45deg;
172+
content: "SHIELD";
173+
position: absolute;
174+
top: 7px;
175+
right: 6px;
176+
color: var(--admonition-note-title-color);
177+
font-size: 11px;
178+
font-weight: 600;
179+
z-index: 1;
180+
width: 24px;
181+
height: 24px;
182+
display: flex;
183+
align-items: center;
184+
justify-content: center;
185+
}
186+
158187
.board-card .picture {
159188
width: auto;
160189
height: auto;
@@ -247,6 +276,23 @@
247276
max-width: none;
248277
}
249278

279+
#catalog.compact .board-card.shield::before {
280+
display: none; /* Remove the S prefix */
281+
}
282+
283+
#catalog.compact .board-card.shield::after {
284+
content: "Shield";
285+
color: var(--admonition-note-color);
286+
background-color: var(--admonition-note-background-color);
287+
padding: 2px 10px 2px 10px;
288+
border-radius: 30px;
289+
font-size: 0.8em;
290+
}
291+
292+
#catalog.compact .board-card.shield {
293+
padding: 4px 0;
294+
}
295+
250296
#catalog.compact .board-card .vendor,
251297
#catalog.compact .board-card .picture {
252298
display: none;

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

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ function setupHWCapabilitiesField() {
119119
const datalist = document.getElementById('tag-list');
120120

121121
const tagCounts = Array.from(document.querySelectorAll('.board-card')).reduce((acc, board) => {
122-
board.getAttribute('data-supported-features').split(' ').forEach(tag => {
122+
(board.getAttribute('data-supported-features') || '').split(' ').forEach(tag => {
123123
acc[tag] = (acc[tag] || 0) + 1;
124124
});
125125
return acc;
@@ -254,12 +254,14 @@ function resetForm() {
254254
}
255255

256256
function updateBoardCount() {
257-
const boards = document.getElementsByClassName("board-card");
258-
const visibleBoards = Array.from(boards).filter(
259-
(board) => !board.classList.contains("hidden")
260-
).length;
261-
const totalBoards = boards.length;
262-
document.getElementById("nb-matches").textContent = `Showing ${visibleBoards} of ${totalBoards}`;
257+
const boards = Array.from(document.getElementsByClassName("board-card"));
258+
const visible = boards.filter(board => !board.classList.contains("hidden"));
259+
const shields = boards.filter(board => board.classList.contains("shield"));
260+
const visibleShields = visible.filter(board => board.classList.contains("shield"));
261+
262+
document.getElementById("nb-matches").textContent =
263+
`Showing ${visible.length - visibleShields.length} of ${boards.length - shields.length} boards,`
264+
+ ` ${visibleShields.length} of ${shields.length} shields`;
263265
}
264266

265267
function filterBoards() {
@@ -281,10 +283,10 @@ function filterBoards() {
281283

282284
Array.from(boards).forEach(function (board) {
283285
const boardName = board.getAttribute("data-name").toLowerCase();
284-
const boardArchs = board.getAttribute("data-arch").split(" ");
285-
const boardVendor = board.getAttribute("data-vendor");
286-
const boardSocs = board.getAttribute("data-socs").split(" ");
287-
const boardSupportedFeatures = board.getAttribute("data-supported-features").split(" ");
286+
const boardArchs = (board.getAttribute("data-arch") || "").split(" ").filter(Boolean);
287+
const boardVendor = board.getAttribute("data-vendor") || "";
288+
const boardSocs = (board.getAttribute("data-socs") || "").split(" ").filter(Boolean);
289+
const boardSupportedFeatures = (board.getAttribute("data-supported-features") || "").split(" ").filter(Boolean);
288290

289291
let matches = true;
290292

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
SPDX-License-Identifier: Apache-2.0
44
#}
55

6-
<form class="filter-form" aria-label="Filter boards by name, architecture, and vendor">
6+
<form class="filter-form" aria-label="Filter boards & shields by name, architecture, and vendor">
77

88
<div class="form-group" style="flex-basis: 100%">
99
<label for="name">Name</label>
1010
<input type="text" id="name"
11-
placeholder='Name (or partial name) of the board, e.g. "reel board", "nucleo", &hellip;'
11+
placeholder='Name (or partial name) of the board/shield, e.g. "reel board", "nucleo", &hellip;'
1212
oninput="filterBoards()" />
1313
</div>
1414

@@ -35,9 +35,11 @@
3535
<div class="select-container">
3636
<select id="vendor">
3737
<option value="" disabled selected>Select a vendor</option>
38-
{# Only show those vendors that have actual boards in the catalog.
38+
{# Only show those vendors that have actual boards or shields in the catalog.
3939
Note: as sorting per vendor name is not feasible in Jinja, the option list is sorted in the JavaScript code later #}
40-
{% for vendor in (boards | items | map(attribute='1.vendor') | unique ) -%}
40+
{% set board_vendors = boards | items | map(attribute='1.vendor') | unique | list %}
41+
{% set shield_vendors = shields | items | map(attribute='1.vendor') | unique | list %}
42+
{% for vendor in (board_vendors + shield_vendors) | unique -%}
4143
<option value="{{ vendor }}">{{ vendors[vendor] }}</option>
4244
{% endfor %}
4345
</select>
@@ -98,6 +100,10 @@
98100
{% for board_name, board in boards | items | sort(attribute='1.full_name') -%}
99101
{% include "board-card.html" %}
100102
{% endfor %}
103+
104+
{% for shield_name, shield in shields | items | sort(attribute='1.full_name') -%}
105+
{% include "shield-card.html" %}
106+
{% endfor %}
101107
</div>
102108

103109
<script>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{#
2+
Copyright (c) 2024-2025, The Linux Foundation.
3+
SPDX-License-Identifier: Apache-2.0
4+
#}
5+
6+
<a
7+
class="board-card shield"
8+
{% if shield.doc_page -%}
9+
href="../{{ shield.doc_page | replace(".rst", ".html") }}"
10+
{% else -%}
11+
href="#"
12+
{% endif -%}
13+
aria-label="Open the documentation page for {{ shield.full_name }}"
14+
data-name="{{ shield.full_name}}"
15+
data-vendor="{{ shield.vendor }}"
16+
tabindex="0">
17+
<div class="vendor">{{ vendors[shield.vendor] }}</div>
18+
{% if shield.image -%}
19+
<img alt="A picture of the {{ shield.full_name }} shield"
20+
src="../_images/{{ shield.image.split('/')[-1] }}" class="picture" />
21+
{% else -%}
22+
<div class="no-picture fa fa-microchip picture"></div>
23+
{% endif -%}
24+
<div class="board-name">{{ shield.full_name }}</div>
25+
</a>

doc/_scripts/gen_boards_catalog.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import list_boards
1414
import list_hardware
15+
import list_shields
1516
import yaml
1617
import zephyr_module
1718
from gen_devicetree_rest import VndLookup
@@ -256,8 +257,10 @@ def get_catalog(generate_hw_features=False, hw_features_vendor_filter=None):
256257
)
257258

258259
boards = list_boards.find_v2_boards(args_find_boards)
260+
shields = list_shields.find_shields(args_find_boards)
259261
systems = list_hardware.find_v2_systems(args_find_boards)
260262
board_catalog = {}
263+
shield_catalog = {}
261264
board_devicetrees = {}
262265
board_runners = {}
263266

@@ -400,8 +403,24 @@ def get_catalog(generate_hw_features=False, hw_features_vendor_filter=None):
400403
"commands": runner.capabilities().commands,
401404
}
402405

406+
for shield in shields:
407+
doc_page = guess_doc_page(shield)
408+
if doc_page and doc_page.is_relative_to(ZEPHYR_BASE):
409+
doc_page_path = doc_page.relative_to(ZEPHYR_BASE).as_posix()
410+
else:
411+
doc_page_path = None
412+
413+
shield_catalog[shield.name] = {
414+
"name": shield.name,
415+
"full_name": shield.full_name or shield.name,
416+
"vendor": shield.vendor or "others",
417+
"doc_page": doc_page_path,
418+
"image": guess_image(shield),
419+
}
420+
403421
return {
404422
"boards": board_catalog,
423+
"shields": shield_catalog,
405424
"vendors": {**vnd_lookup.vnd2vendor, "others": "Other/Unknown"},
406425
"socs": socs_hierarchy,
407426
"runners": available_runners,

doc/hardware/porting/shields.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ to extend its features and services for easier and modularized prototyping.
88
In Zephyr, the shield feature provides Zephyr-formatted shield
99
descriptions for easier compatibility with applications.
1010

11+
.. _shield_porting_guide:
12+
1113
Shield porting and configuration
1214
********************************
1315

0 commit comments

Comments
 (0)