[rp2] Limit pin validation to GPIO 0-29 on RP2350A boards (#18102)

This commit is contained in:
J. Nick Koston
2026-08-06 08:55:24 -05:00
committed by GitHub
parent ba86685361
commit 73689f8d8b
3 changed files with 183 additions and 64 deletions
+26 -60
View File
@@ -133,9 +133,7 @@ RP2_BOARD_PINS = {
"RX": 1,
"SCK": 22,
"SCL": 3,
"SCL1": 31,
"SDA": 2,
"SDA1": 31,
"SS": 13,
"TX": 0,
},
@@ -146,9 +144,7 @@ RP2_BOARD_PINS = {
"RX": 1,
"SCK": 22,
"SCL": 3,
"SCL1": 31,
"SDA": 2,
"SDA1": 31,
"SS": 21,
"TX": 0,
},
@@ -464,9 +460,7 @@ RP2_BOARD_PINS = {
"RX": 13,
"SCK": 18,
"SCL": 21,
"SCL1": 31,
"SDA": 20,
"SDA1": 31,
"SS": 17,
"TX": 12,
},
@@ -477,9 +471,7 @@ RP2_BOARD_PINS = {
"RX": 13,
"SCK": 18,
"SCL": 21,
"SCL1": 31,
"SDA": 20,
"SDA1": 31,
"SS": 17,
"TX": 12,
},
@@ -509,14 +501,10 @@ RP2_BOARD_PINS = {
"LED": 29,
"MISO": 20,
"MOSI": 19,
"RX": 31,
"SCK": 22,
"SCL": 17,
"SCL1": 31,
"SDA": 16,
"SDA1": 31,
"SS": 21,
"TX": 31,
},
"cytron_maker_nano_rp2040": {
"LED": 2,
@@ -954,31 +942,15 @@ RP2_BOARD_PINS = {
"TX": 0,
},
"pimoroni_plasma2040": {"LED": 16, "SCL": 21, "SDA": 20},
"pimoroni_plasma2350": {
"LED": 16,
"MISO": 31,
"MOSI": 31,
"RX": 31,
"SCK": 31,
"SCL": 21,
"SCL1": 31,
"SDA": 20,
"SDA1": 31,
"SS": 31,
"TX": 31,
},
"pimoroni_plasma2350": {"LED": 16, "SCL": 21, "SDA": 20},
"pimoroni_plasma2350w": {
"LED": 16,
"MISO": 24,
"MOSI": 24,
"RX": 31,
"SCK": 29,
"SCL": 21,
"SCL1": 31,
"SDA": 20,
"SDA1": 31,
"SS": 25,
"TX": 31,
},
"pimoroni_servo2040": {"LED": 18, "SCL": 21, "SDA": 20},
"pimoroni_tiny2040": {
@@ -1221,9 +1193,7 @@ RP2_BOARD_PINS = {
"RX": 19,
"SCK": 14,
"SCL": 21,
"SCL1": 31,
"SDA": 20,
"SDA1": 31,
"SS": 13,
"TX": 18,
},
@@ -1269,9 +1239,7 @@ RP2_BOARD_PINS = {
"RX": 1,
"SCK": 22,
"SCL": 17,
"SCL1": 31,
"SDA": 16,
"SDA1": 31,
"SS": 21,
"TX": 0,
},
@@ -1294,9 +1262,7 @@ RP2_BOARD_PINS = {
"RX": 1,
"SCK": 2,
"SCL": 7,
"SCL1": 31,
"SDA": 6,
"SDA1": 31,
"SS": 9,
"TX": 0,
},
@@ -1621,12 +1587,12 @@ BOARDS = {
"adafruit_feather_rp2350_adalogger": {
"name": "Adafruit Feather RP2350 Adalogger",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"adafruit_feather_rp2350_hstx": {
"name": "Adafruit Feather RP2350 HSTX",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"adafruit_feather_scorpio": {
"name": "Adafruit Feather RP2040 SCORPIO",
@@ -1796,17 +1762,17 @@ BOARDS = {
"challenger_2350_bconnect": {
"name": "iLabs Challenger 2350 BConnect",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"challenger_2350_nbiot": {
"name": "iLabs Challenger 2350 NB-IoT",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"challenger_2350_wifi6_ble5": {
"name": "iLabs Challenger 2350 WiFi/BLE",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"challenger_nb_2040_wifi": {
"name": "iLabs Challenger NB 2040 WiFi",
@@ -1821,7 +1787,7 @@ BOARDS = {
"cytron_iriv_io_controller": {
"name": "Cytron IRIV IO Controller",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"cytron_maker_nano_rp2040": {
"name": "Cytron Maker Nano RP2040",
@@ -1841,7 +1807,7 @@ BOARDS = {
"cytron_motion_2350_pro": {
"name": "Cytron Motion 2350 Pro",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"datanoisetv_picoadk": {
"name": "DatanoiseTV PicoADK",
@@ -1851,7 +1817,7 @@ BOARDS = {
"datanoisetv_picoadk_v2": {
"name": "DatanoiseTV PicoADK v2",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"degz_suibo": {
"name": "Degz Robotics Suibo RP2040",
@@ -1906,7 +1872,7 @@ BOARDS = {
"ilabs_cpico_2350": {
"name": "iLabs CPico 2350",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"ilabs_rpico32": {
"name": "iLabs RPICO32",
@@ -2028,12 +1994,12 @@ BOARDS = {
"pimoroni_plasma2350": {
"name": "Pimoroni Plasma2350",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"pimoroni_plasma2350w": {
"name": "Pimoroni Plasma2350W",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
"wifi": True,
},
"pimoroni_servo2040": {
@@ -2049,7 +2015,7 @@ BOARDS = {
"pimoroni_tiny2350": {
"name": "Pimoroni Tiny2350",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"pintronix_pinmax": {
"name": "Pintronix PinMax",
@@ -2079,12 +2045,12 @@ BOARDS = {
"rpipico2": {
"name": "Raspberry Pi Pico 2",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"rpipico2w": {
"name": "Raspberry Pi Pico 2W",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
"wifi": True,
"max_virtual_pin": 64,
},
@@ -2118,7 +2084,7 @@ BOARDS = {
"seeed_xiao_rp2350": {
"name": "Seeed XIAO RP2350",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"silicognition_rp2040_shim": {
"name": "Silicognition RP2040-Shim",
@@ -2144,7 +2110,7 @@ BOARDS = {
"solderparty_rp2350_stamp": {
"name": "Solder Party RP2350 Stamp",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"solderparty_rp2350_stamp_xl": {
"name": "Solder Party RP2350 Stamp XL",
@@ -2154,7 +2120,7 @@ BOARDS = {
"sparkfun_iotnode_lorawanrp2350": {
"name": "SparkFun IoT Node LoRaWAN",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"sparkfun_iotredboard_rp2350": {
"name": "SparkFun IoT RedBoard RP2350",
@@ -2175,7 +2141,7 @@ BOARDS = {
"sparkfun_promicrorp2350": {
"name": "SparkFun ProMicro RP2350",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"sparkfun_thingplusrp2040": {
"name": "SparkFun Thing Plus RP2040",
@@ -2185,7 +2151,7 @@ BOARDS = {
"sparkfun_thingplusrp2350": {
"name": "SparkFun Thing Plus RP2350",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
"wifi": True,
"max_virtual_pin": 64,
},
@@ -2266,7 +2232,7 @@ BOARDS = {
"waveshare_rp2350_lcd_0_96": {
"name": "Waveshare RP2350 LCD 0.96",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"waveshare_rp2350_pizero": {
"name": "Waveshare RP2350 PiZero",
@@ -2276,12 +2242,12 @@ BOARDS = {
"waveshare_rp2350_plus": {
"name": "Waveshare RP2350 Plus",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"waveshare_rp2350_zero": {
"name": "Waveshare RP2350 Zero",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"waveshare_rp2350b_plus_w": {
"name": "Waveshare RP2350B Plus W",
@@ -2302,7 +2268,7 @@ BOARDS = {
"wiznet_5100s_evb_pico2": {
"name": "WIZnet W5100S-EVB-Pico2",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"wiznet_5500_evb_pico": {
"name": "WIZnet W5500-EVB-Pico",
@@ -2312,7 +2278,7 @@ BOARDS = {
"wiznet_5500_evb_pico2": {
"name": "WIZnet W5500-EVB-Pico2",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"wiznet_55rp20_evb_pico": {
"name": "WIZnet W55RP20-EVB-Pico",
@@ -2327,7 +2293,7 @@ BOARDS = {
"wiznet_6300_evb_pico2": {
"name": "WIZnet W6300-EVB-Pico2",
"mcu": "rp2350",
"max_pin": 47,
"max_pin": 29,
},
"wiznet_wizfi360_evb_pico": {
"name": "WIZnet WizFi360-EVB-Pico",
+52 -2
View File
@@ -34,11 +34,17 @@ CYW43_GPIO_COUNT = 3
# Max GPIO pin per MCU (hardware specs from datasheets)
MCU_MAX_PIN = {
"rp2040": 29, # GPIO 0-29
"rp2350": 47, # GPIO 0-47 (RP2350A)
"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).
RP2350A_MAX_PIN = 29
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+)")
RP2350A_MENU_PLACEHOLDER = "__PICO_RP2350A"
def parse_variant_pins(variant_dir: Path) -> dict[str, int]:
@@ -56,6 +62,40 @@ 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).
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.
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.
"""
header = variant_dir / "pins_arduino.h"
match = (
RP2350A_DEFINE_RE.search(header.read_text(encoding="utf-8"))
if header.exists()
else None
)
if match is None:
raise ValueError(
f"{header}: no PICO_RP2350A define found; cannot classify the "
"RP2350 die (A exposes GPIO 0-29, B exposes GPIO 0-47)"
)
value = match.group(1)
if value == RP2350A_MENU_PLACEHOLDER:
return False
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
def load_boards(arduino_pico_path: Path) -> tuple[dict, dict]:
"""Load all board definitions and return (board_pins, boards) dicts."""
json_dir = arduino_pico_path / "tools" / "json"
@@ -64,6 +104,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] = {}
for json_file in sorted(json_dir.glob("*.json")):
board_name = json_file.stem
@@ -81,10 +122,19 @@ def load_boards(arduino_pico_path: Path) -> tuple[dict, dict]:
extra_flags = build.get("extra_flags", "")
has_wifi = "PICO_CYW43_SUPPORTED=1" in extra_flags
max_pin = MCU_MAX_PIN.get(mcu, DEFAULT_MAX_PIN)
if mcu == "rp2350":
if variant not in variant_rp2350a_cache:
variant_rp2350a_cache[variant] = parse_variant_is_rp2350a(
variants_dir / variant
)
if variant_rp2350a_cache[variant]:
max_pin = RP2350A_MAX_PIN
board_entry: dict = {
"name": display_name,
"mcu": mcu,
"max_pin": MCU_MAX_PIN.get(mcu, DEFAULT_MAX_PIN),
"max_pin": max_pin,
}
if has_wifi:
board_entry["wifi"] = True
@@ -158,19 +158,122 @@ def test_load_basic_board(arduino_pico: Path) -> None:
def test_load_rp2350_board(arduino_pico: Path) -> None:
"""The Pico 2 uses the RP2350A die, which only exposes GPIO 0-29."""
_add_board(
arduino_pico,
"rpipico2",
mcu="rp2350",
vendor="Raspberry Pi",
name="Pico 2",
pins_header=PICO_PINS_HEADER,
pins_header="#define PICO_RP2350A 1\n" + PICO_PINS_HEADER,
)
_, boards = load_boards(arduino_pico)
assert boards["rpipico2"]["mcu"] == "rp2350"
assert boards["rpipico2"]["max_pin"] == 47
assert boards["rpipico2"]["max_pin"] == 29
def test_rp2350_missing_die_define_raises(arduino_pico: Path) -> None:
"""A variant without PICO_RP2350A cannot be classified; fail loudly."""
_add_board(
arduino_pico,
"no_die_define",
mcu="rp2350",
pins_header=PICO_PINS_HEADER,
)
with pytest.raises(ValueError, match="no PICO_RP2350A define"):
load_boards(arduino_pico)
def test_rp2350_unrecognized_die_define_raises(arduino_pico: Path) -> None:
"""An unparseable PICO_RP2350A value must not silently widen to B-die."""
_add_board(
arduino_pico,
"hex_die_define",
mcu="rp2350",
pins_header="#define PICO_RP2350A 0x1\n" + PICO_PINS_HEADER,
)
with pytest.raises(ValueError, match="unrecognized PICO_RP2350A value"):
load_boards(arduino_pico)
def test_rp2350a_parenthesized_die_define(arduino_pico: Path) -> None:
"""Literal forms like (1u) classify the same as bare 1."""
_add_board(
arduino_pico,
"paren_die",
mcu="rp2350",
pins_header="#define PICO_RP2350A (1u)\n" + PICO_PINS_HEADER,
)
_, boards = load_boards(arduino_pico)
assert boards["paren_die"]["max_pin"] == 29
def test_rp2350b_board_keeps_max_pin_47(arduino_pico: Path) -> None:
"""A variant declaring the RP2350B die keeps the full GPIO 0-47 range.
The define uses extra whitespace, matching real variant headers.
"""
_add_board(
arduino_pico,
"weact_rp2350b",
mcu="rp2350",
pins_header="#define PICO_RP2350A 0 // RP2350B\n" + PICO_PINS_HEADER,
)
_, boards = load_boards(arduino_pico)
assert boards["weact_rp2350b"]["max_pin"] == 47
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."""
_add_board(
arduino_pico,
"generic_rp2350",
mcu="rp2350",
pins_header="#define PICO_RP2350A __PICO_RP2350A\n" + PICO_PINS_HEADER,
)
_, boards = load_boards(arduino_pico)
assert boards["generic_rp2350"]["max_pin"] == 47
def test_rp2350a_pins_above_29_filtered(arduino_pico: Path) -> None:
"""Pin defines beyond the A-die range are dropped from the pin map."""
header = textwrap.dedent("""\
#define PICO_RP2350A 1
#define PIN_LED (25u)
#define PIN_SPI0_MISO (40u)
""")
_add_board(arduino_pico, "a_die", mcu="rp2350", pins_header=header)
board_pins, _ = load_boards(arduino_pico)
assert board_pins["a_die"]["LED"] == 25
assert "MISO" not in board_pins["a_die"]
def test_rp2350a_board_keeps_cyw43_virtual_pins(arduino_pico: Path) -> None:
"""A-die narrowing must not filter CYW43 virtual pins (64-66)."""
_add_board(
arduino_pico,
"rpipico2w",
mcu="rp2350",
pins_header="#define PICO_RP2350A 1\n" + PICOW_PINS_HEADER,
)
board_pins, boards = load_boards(arduino_pico)
assert boards["rpipico2w"]["max_pin"] == 29
assert boards["rpipico2w"]["max_virtual_pin"] == 64
assert board_pins["rpipico2w"]["LED"] == 64
def test_cyw43_board_has_max_virtual_pin(arduino_pico: Path) -> None: