[rp2] Record the RP2350 die on generated board entries (#18305)

This commit is contained in:
Jesse Hills
2026-08-12 01:56:05 +00:00
committed by GitHub
parent 3f5b8139f3
commit 3349046c5d
4 changed files with 175 additions and 20 deletions
+4
View File
@@ -14,6 +14,10 @@ RP2_BOARD_PINS = {
{%- endfor %}
}
# RP2350 boards carry a {{ rp2350_die_key | repr }} key holding the die letter:
# 'A' for the RP2350A (GPIO 0-29, 5 ADC channels), 'B' for the RP2350B
# (GPIO 0-47, 9 ADC channels), and None when the die is a build-time menu
# choice and so is not known here. The key is absent on non-RP2350 boards.
BOARDS = {
{%- for name, info in boards %}
{{ name | repr }}: {
+48
View File
@@ -1533,6 +1533,10 @@ RP2_BOARD_PINS = {
},
}
# RP2350 boards carry a 'die' key holding the die letter:
# 'A' for the RP2350A (GPIO 0-29, 5 ADC channels), 'B' for the RP2350B
# (GPIO 0-47, 9 ADC channels), and None when the die is a build-time menu
# choice and so is not known here. The key is absent on non-RP2350 boards.
BOARDS = {
"0xcb_helios": {
"name": "0xCB Helios",
@@ -1548,6 +1552,7 @@ BOARDS = {
"name": "MyMakers RP2350B",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
},
"MyRP_bot": {
"name": "MyMakers RP2040",
@@ -1588,11 +1593,13 @@ BOARDS = {
"name": "Adafruit Feather RP2350 Adalogger",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"adafruit_feather_rp2350_hstx": {
"name": "Adafruit Feather RP2350 HSTX",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"adafruit_feather_scorpio": {
"name": "Adafruit Feather RP2040 SCORPIO",
@@ -1618,6 +1625,7 @@ BOARDS = {
"name": "Adafruit Fruit Jam RP2350",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
},
"adafruit_itsybitsy": {
"name": "Adafruit ItsyBitsy RP2040",
@@ -1643,6 +1651,7 @@ BOARDS = {
"name": "Adafruit Metro RP2350",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
},
"adafruit_qtpy": {
"name": "Adafruit QT Py RP2040",
@@ -1763,16 +1772,19 @@ BOARDS = {
"name": "iLabs Challenger 2350 BConnect",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"challenger_2350_nbiot": {
"name": "iLabs Challenger 2350 NB-IoT",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"challenger_2350_wifi6_ble5": {
"name": "iLabs Challenger 2350 WiFi/BLE",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"challenger_nb_2040_wifi": {
"name": "iLabs Challenger NB 2040 WiFi",
@@ -1788,6 +1800,7 @@ BOARDS = {
"name": "Cytron IRIV IO Controller",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"cytron_maker_nano_rp2040": {
"name": "Cytron Maker Nano RP2040",
@@ -1808,6 +1821,7 @@ BOARDS = {
"name": "Cytron Motion 2350 Pro",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"datanoisetv_picoadk": {
"name": "DatanoiseTV PicoADK",
@@ -1818,6 +1832,7 @@ BOARDS = {
"name": "DatanoiseTV PicoADK v2",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"degz_suibo": {
"name": "Degz Robotics Suibo RP2040",
@@ -1863,6 +1878,7 @@ BOARDS = {
"name": "Generic RP2350",
"mcu": "rp2350",
"max_pin": 47,
"die": None,
},
"groundstudio_marble_pico": {
"name": "GroundStudio Marble Pico",
@@ -1873,6 +1889,7 @@ BOARDS = {
"name": "iLabs CPico 2350",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"ilabs_rpico32": {
"name": "iLabs RPICO32",
@@ -1888,6 +1905,7 @@ BOARDS = {
"name": "Architeuthis Flux Jumperless V5",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
},
"melopero_cookie_rp2040": {
"name": "Melopero Cookie RP2040",
@@ -1928,16 +1946,19 @@ BOARDS = {
"name": "Olimex Pico2BB48",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
},
"olimex_pico2xl": {
"name": "Olimex Pico2XL",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
},
"olimex_pico2xxl": {
"name": "Olimex Pico2XXL",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
},
"olimex_rp2040pico30": {
"name": "Olimex RP2040-Pico30",
@@ -1963,6 +1984,7 @@ BOARDS = {
"name": "Pimoroni Explorer",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
},
"pimoroni_pga2040": {
"name": "Pimoroni PGA2040",
@@ -1973,16 +1995,19 @@ BOARDS = {
"name": "Pimoroni PGA2350",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
},
"pimoroni_pico_plus_2": {
"name": "Pimoroni PicoPlus2",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
},
"pimoroni_pico_plus_2w": {
"name": "Pimoroni PicoPlus2W",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
"wifi": True,
"max_virtual_pin": 64,
},
@@ -1995,11 +2020,13 @@ BOARDS = {
"name": "Pimoroni Plasma2350",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"pimoroni_plasma2350w": {
"name": "Pimoroni Plasma2350W",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
"wifi": True,
},
"pimoroni_servo2040": {
@@ -2016,6 +2043,7 @@ BOARDS = {
"name": "Pimoroni Tiny2350",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"pintronix_pinmax": {
"name": "Pintronix PinMax",
@@ -2046,11 +2074,13 @@ BOARDS = {
"name": "Raspberry Pi Pico 2",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"rpipico2w": {
"name": "Raspberry Pi Pico 2W",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
"wifi": True,
"max_virtual_pin": 64,
},
@@ -2085,6 +2115,7 @@ BOARDS = {
"name": "Seeed XIAO RP2350",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"silicognition_rp2040_shim": {
"name": "Silicognition RP2040-Shim",
@@ -2100,6 +2131,7 @@ BOARDS = {
"name": "Soldered Electronics NULA RP2350",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
"wifi": True,
},
"solderparty_rp2040_stamp": {
@@ -2111,21 +2143,25 @@ BOARDS = {
"name": "Solder Party RP2350 Stamp",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"solderparty_rp2350_stamp_xl": {
"name": "Solder Party RP2350 Stamp XL",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
},
"sparkfun_iotnode_lorawanrp2350": {
"name": "SparkFun IoT Node LoRaWAN",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"sparkfun_iotredboard_rp2350": {
"name": "SparkFun IoT RedBoard RP2350",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
"wifi": True,
},
"sparkfun_micromodrp2040": {
@@ -2142,6 +2178,7 @@ BOARDS = {
"name": "SparkFun ProMicro RP2350",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"sparkfun_thingplusrp2040": {
"name": "SparkFun Thing Plus RP2040",
@@ -2152,6 +2189,7 @@ BOARDS = {
"name": "SparkFun Thing Plus RP2350",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
"wifi": True,
"max_virtual_pin": 64,
},
@@ -2159,6 +2197,7 @@ BOARDS = {
"name": "SparkFun XRP Controller",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
"wifi": True,
"max_virtual_pin": 64,
},
@@ -2233,32 +2272,38 @@ BOARDS = {
"name": "Waveshare RP2350 LCD 0.96",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"waveshare_rp2350_pizero": {
"name": "Waveshare RP2350 PiZero",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
},
"waveshare_rp2350_plus": {
"name": "Waveshare RP2350 Plus",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"waveshare_rp2350_zero": {
"name": "Waveshare RP2350 Zero",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"waveshare_rp2350b_plus_w": {
"name": "Waveshare RP2350B Plus W",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
"wifi": True,
},
"weact_rp2350b": {
"name": "WeAct Studio RP2350B Core Board",
"mcu": "rp2350",
"max_pin": 47,
"die": "B",
},
"wiznet_5100s_evb_pico": {
"name": "WIZnet W5100S-EVB-Pico",
@@ -2269,6 +2314,7 @@ BOARDS = {
"name": "WIZnet W5100S-EVB-Pico2",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"wiznet_5500_evb_pico": {
"name": "WIZnet W5500-EVB-Pico",
@@ -2279,6 +2325,7 @@ BOARDS = {
"name": "WIZnet W5500-EVB-Pico2",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"wiznet_55rp20_evb_pico": {
"name": "WIZnet W55RP20-EVB-Pico",
@@ -2294,6 +2341,7 @@ BOARDS = {
"name": "WIZnet W6300-EVB-Pico2",
"mcu": "rp2350",
"max_pin": 29,
"die": "A",
},
"wiznet_wizfi360_evb_pico": {
"name": "WIZnet WizFi360-EVB-Pico",
+42 -18
View File
@@ -37,13 +37,23 @@ MCU_MAX_PIN = {
"rp2350": 47, # GPIO 0-47 (RP2350B; A-die boards are narrowed to 29 below)
}
DEFAULT_MAX_PIN = 29
# The RP2350 comes in two die variants: RP2350A exposes GPIO 0-29, RP2350B
# GPIO 0-47. Variant headers declare the die via PICO_RP2350A (1 = A, 0 = B).
# The RP2350 currently comes in two die variants: RP2350A exposes GPIO 0-29,
# RP2350B GPIO 0-47. Variant headers declare the die via PICO_RP2350A
# (1 = A, 0 = B).
RP2350_DIE_A = "A"
RP2350_DIE_B = "B"
RP2350A_MAX_PIN = 29
# Key recording the die letter on RP2350 board entries. Holds a letter rather
# than a bool so a future die can be named instead of forced into "not A".
RP2350_DIE_KEY = "die"
PIN_DEFINE_RE = re.compile(r"#define\s+PIN_(\w+)\s+\((\d+)u\)")
# Accepts the literal forms seen in these headers: 1, (1), 1u, (1u)
RP2350A_DEFINE_RE = re.compile(r"#define\s+PICO_RP2350A\s+(\S+)")
# Only PICO_RP2350A exists today. A define for any other die letter means the
# A/B assumption below no longer holds. The trailing \b keeps this from
# matching unrelated names such as PICO_RP2350_A2_SUPPORTED.
OTHER_DIE_DEFINE_RE = re.compile(r"#define\s+PICO_RP2350(?!A\b)([B-Z])\b")
RP2350A_MENU_PLACEHOLDER = "__PICO_RP2350A"
@@ -62,23 +72,30 @@ def parse_variant_pins(variant_dir: Path) -> dict[str, int]:
return pins
def parse_variant_is_rp2350a(variant_dir: Path) -> bool:
"""Return True if the variant declares an RP2350A die (GPIO 0-29 only).
def parse_variant_rp2350_die(variant_dir: Path) -> str | None:
"""Return the RP2350 die letter the variant declares, or None if unknown.
Generic boards leave the die a build-time menu choice (PICO_RP2350A is set
to a __PICO_RP2350A placeholder rather than a literal); those return False
so they keep the permissive B-die pin range.
to a __PICO_RP2350A placeholder rather than a literal); those return None,
meaning the die is genuinely unknown at code generation time. They keep the
permissive B-die pin range, but that is a fallback and must not be recorded
as a known die.
A missing or unrecognized define raises: silently treating it as B-die
would widen pin validation back to GPIO 47 on A-die boards, so a framework
bump that changes the header format must fail loudly here instead.
bump that changes the header format must fail loudly here instead. The same
goes for a die beyond A and B: PICO_RP2350A is a yes/no answer about the A
die, so "not A" can only be read as B while A and B are the whole family.
"""
header = variant_dir / "pins_arduino.h"
match = (
RP2350A_DEFINE_RE.search(header.read_text(encoding="utf-8"))
if header.exists()
else None
)
text = header.read_text(encoding="utf-8") if header.exists() else ""
if other_die := OTHER_DIE_DEFINE_RE.search(text):
raise ValueError(
f"{header}: found a PICO_RP2350{other_die.group(1)} define; the "
"RP2350 gained a die beyond A and B, so PICO_RP2350A being 0 no "
"longer means the B die"
)
match = RP2350A_DEFINE_RE.search(text)
if match is None:
raise ValueError(
f"{header}: no PICO_RP2350A define found; cannot classify the "
@@ -86,14 +103,14 @@ def parse_variant_is_rp2350a(variant_dir: Path) -> bool:
)
value = match.group(1)
if value == RP2350A_MENU_PLACEHOLDER:
return False
return None
literal = value.strip("()u")
if not literal.isdigit():
raise ValueError(
f"{header}: unrecognized PICO_RP2350A value {value!r}; cannot "
"classify the RP2350 die (A exposes GPIO 0-29, B exposes GPIO 0-47)"
)
return int(literal) == 1
return RP2350_DIE_A if int(literal) == 1 else RP2350_DIE_B
def load_boards(arduino_pico_path: Path) -> tuple[dict, dict]:
@@ -104,7 +121,7 @@ def load_boards(arduino_pico_path: Path) -> tuple[dict, dict]:
board_pins = {}
boards = {}
variant_pins_cache: dict[str, dict[str, int]] = {}
variant_rp2350a_cache: dict[str, bool] = {}
variant_die_cache: dict[str, str | None] = {}
for json_file in sorted(json_dir.glob("*.json")):
board_name = json_file.stem
@@ -123,12 +140,14 @@ def load_boards(arduino_pico_path: Path) -> tuple[dict, dict]:
has_wifi = "PICO_CYW43_SUPPORTED=1" in extra_flags
max_pin = MCU_MAX_PIN.get(mcu, DEFAULT_MAX_PIN)
die: str | None = None
if mcu == "rp2350":
if variant not in variant_rp2350a_cache:
variant_rp2350a_cache[variant] = parse_variant_is_rp2350a(
if variant not in variant_die_cache:
variant_die_cache[variant] = parse_variant_rp2350_die(
variants_dir / variant
)
if variant_rp2350a_cache[variant]:
die = variant_die_cache[variant]
if die == RP2350_DIE_A:
max_pin = RP2350A_MAX_PIN
board_entry: dict = {
@@ -136,6 +155,10 @@ def load_boards(arduino_pico_path: Path) -> tuple[dict, dict]:
"mcu": mcu,
"max_pin": max_pin,
}
if mcu == "rp2350":
# Recorded explicitly because max_pin cannot express the die:
# 29 also means RP2040, and 47 also means "die not known yet".
board_entry[RP2350_DIE_KEY] = die
if has_wifi:
board_entry["wifi"] = True
boards[board_name] = board_entry
@@ -218,6 +241,7 @@ def generate(arduino_pico_path: Path) -> str:
cyw43_gpio_offset=CYW43_GPIO_OFFSET,
cyw43_max_gpio=CYW43_GPIO_OFFSET + CYW43_GPIO_COUNT - 1,
default_max_pin=DEFAULT_MAX_PIN,
rp2350_die_key=RP2350_DIE_KEY,
board_pins=sorted(board_pins.items()),
boards=sorted(boards.items()),
)
@@ -8,7 +8,11 @@ import textwrap
import pytest
from esphome.components.rp2.generate_boards import load_boards, parse_variant_pins
from esphome.components.rp2.generate_boards import (
generate,
load_boards,
parse_variant_pins,
)
PICO_PINS_HEADER = textwrap.dedent("""\
#pragma once
@@ -151,6 +155,8 @@ def test_load_basic_board(arduino_pico: Path) -> None:
assert boards["rpipico"]["name"] == "Raspberry Pi Pico"
assert boards["rpipico"]["mcu"] == "rp2040"
assert boards["rpipico"]["max_pin"] == 29
# The die key only applies to the RP2350, which ships as more than one die
assert "die" not in boards["rpipico"]
assert "rpipico" in board_pins
assert board_pins["rpipico"]["LED"] == 25
@@ -172,6 +178,7 @@ def test_load_rp2350_board(arduino_pico: Path) -> None:
assert boards["rpipico2"]["mcu"] == "rp2350"
assert boards["rpipico2"]["max_pin"] == 29
assert boards["rpipico2"]["die"] == "A"
def test_rp2350_missing_die_define_raises(arduino_pico: Path) -> None:
@@ -200,6 +207,35 @@ def test_rp2350_unrecognized_die_define_raises(arduino_pico: Path) -> None:
load_boards(arduino_pico)
def test_rp2350_unknown_die_define_raises(arduino_pico: Path) -> None:
"""A third die breaks the "not A means B" reading, so stop rather than guess."""
_add_board(
arduino_pico,
"future_die",
mcu="rp2350",
pins_header="#define PICO_RP2350A 0\n#define PICO_RP2350C 1\n"
+ PICO_PINS_HEADER,
)
with pytest.raises(ValueError, match="found a PICO_RP2350C define"):
load_boards(arduino_pico)
def test_rp2350_silicon_revision_define_ignored(arduino_pico: Path) -> None:
"""PICO_RP2350_A2_SUPPORTED is a silicon revision, not a die letter."""
_add_board(
arduino_pico,
"revision_define",
mcu="rp2350",
pins_header="#define PICO_RP2350A 1\n#define PICO_RP2350_A2_SUPPORTED 1\n"
+ PICO_PINS_HEADER,
)
_, boards = load_boards(arduino_pico)
assert boards["revision_define"]["die"] == "A"
def test_rp2350a_parenthesized_die_define(arduino_pico: Path) -> None:
"""Literal forms like (1u) classify the same as bare 1."""
_add_board(
@@ -212,6 +248,7 @@ def test_rp2350a_parenthesized_die_define(arduino_pico: Path) -> None:
_, boards = load_boards(arduino_pico)
assert boards["paren_die"]["max_pin"] == 29
assert boards["paren_die"]["die"] == "A"
def test_rp2350b_board_keeps_max_pin_47(arduino_pico: Path) -> None:
@@ -229,10 +266,15 @@ def test_rp2350b_board_keeps_max_pin_47(arduino_pico: Path) -> None:
_, boards = load_boards(arduino_pico)
assert boards["weact_rp2350b"]["max_pin"] == 47
assert boards["weact_rp2350b"]["die"] == "B"
def test_rp2350_menu_selectable_die_keeps_max_pin_47(arduino_pico: Path) -> None:
"""Generic boards leave the die a build-time choice; stay permissive."""
"""Generic boards leave the die a build-time choice; stay permissive.
The permissive range is a fallback, so the die must be recorded as unknown
rather than as the B die.
"""
_add_board(
arduino_pico,
"generic_rp2350",
@@ -243,6 +285,43 @@ def test_rp2350_menu_selectable_die_keeps_max_pin_47(arduino_pico: Path) -> None
_, boards = load_boards(arduino_pico)
assert boards["generic_rp2350"]["max_pin"] == 47
assert boards["generic_rp2350"]["die"] is None
def test_generated_output_records_die(arduino_pico: Path) -> None:
"""The rendered boards.py carries the die on every RP2350 entry."""
_add_board(
arduino_pico,
"rpipico",
pins_header=PICO_PINS_HEADER,
)
_add_board(
arduino_pico,
"a_die",
mcu="rp2350",
pins_header="#define PICO_RP2350A 1\n" + PICO_PINS_HEADER,
)
_add_board(
arduino_pico,
"b_die",
mcu="rp2350",
pins_header="#define PICO_RP2350A 0\n" + PICO_PINS_HEADER,
)
_add_board(
arduino_pico,
"menu_die",
mcu="rp2350",
pins_header="#define PICO_RP2350A __PICO_RP2350A\n" + PICO_PINS_HEADER,
)
namespace: dict = {}
exec(compile(generate(arduino_pico), "boards.py", "exec"), namespace)
boards = namespace["BOARDS"]
assert boards["a_die"]["die"] == "A"
assert boards["b_die"]["die"] == "B"
assert boards["menu_die"]["die"] is None
assert "die" not in boards["rpipico"]
def test_rp2350a_pins_above_29_filtered(arduino_pico: Path) -> None: