Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/check-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ jobs:
run: |
soldr cargo test -p fbuild-build --test it renesas_acceptance:: -- --ignored --nocapture

- name: Build Pico boot2 and default firmware
shell: bash
env:
FBUILD_DEV_MODE: "1"
run: |
soldr cargo test -p fbuild-build --test it rp2040_acceptance:: -- --ignored --nocapture

- name: Setup uv for legacy source package install
uses: astral-sh/setup-uv@v3

Expand Down
3 changes: 3 additions & 0 deletions crates/fbuild-build-arm/src/rp2040/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ RP2040/RP2350 platform build support (Raspberry Pi Pico).

Uses the generic ARM compiler/linker from `generic_arm/` with RP2040-specific
MCU configurations and the RP2040 Arduino cores framework.

`board_props.rs` applies Arduino-Pico board properties and resolves the variant
from the selected framework while preserving explicit project overrides.
133 changes: 133 additions & 0 deletions crates/fbuild-build-arm/src/rp2040/board_props.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//! Arduino-Pico board properties and framework-specific variant selection.

use std::collections::HashMap;
use std::path::Path;

pub(super) fn rp_board_defines(
board: &fbuild_config::BoardConfig,
mcu: &crate::generic_arm::ArmMcuConfig,
) -> HashMap<String, String> {
// MCU values are defaults; board/project flags are more specific. In
// particular, USB identity aliases must not replace explicit USBD_* flags.
let mut defines = mcu.defines_map();
defines.extend(board.get_defines());
defines
}

/// User variant overrides take precedence over framework metadata. The board
/// manifest may describe another core (e.g. Mbed), so use the selected Arduino
/// framework's variant when it is available.
pub(super) fn resolve_variant(
declared: &str,
props: &Option<HashMap<String, String>>,
overrides: &HashMap<String, String>,
) -> String {
if overrides.contains_key("variant") {
return declared.to_owned();
}
props
.as_ref()
.and_then(|props| props.get("variant"))
.filter(|variant| !variant.is_empty())
.map_or_else(|| declared.to_owned(), Clone::clone)
}

pub(super) fn apply_rp_board_props(
board_props: &Option<HashMap<String, String>>,
framework_dir: &Path,
defines: &mut HashMap<String, String>,
) {
let Some(props) = board_props.as_ref() else {
return;
};
for key in [
"usbpwr",
"usbstack_flags",
"variantdefines",
"led",
"libpicowdefs",
"wificc",
"sdfatdefines",
] {
if let Some(flags) = props.get(key) {
let expanded =
flags.replace("{runtime.platform.path}", &framework_dir.to_string_lossy());
let tokens = fbuild_core::shell_split::split(&expanded);
defines.extend(fbuild_core::compiler_flags::parse_define_flags(&tokens));
}
}
if let Some(value) = props.get("usb_manufacturer") {
defines.insert("USB_MANUFACTURER".to_string(), value.clone());
}
if let Some(value) = props.get("usb_product") {
defines.insert("USB_PRODUCT".to_string(), value.clone());
}
for (property, define) in [
("flash_total", "PICO_FLASH_SIZE_BYTES"),
("fs_start", "FS_START"),
("fs_end", "FS_END"),
] {
if let Some(value) = props.get(property) {
defines.insert(define.to_string(), value.clone());
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn explicit_usb_aliases_override_rp_mcu_defaults() {
for id in ["pico", "rpipico2"] {
let overrides = HashMap::from([(
"extra_flags".to_owned(),
"-DUSBD_VID=TEST_VENDOR -DUSBD_PID=TEST_PRODUCT".to_owned(),
)]);
let board = fbuild_config::BoardConfig::from_board_id(id, &overrides).unwrap();
let mcu =
super::super::mcu_config::get_rp2040_config_for_mcu(&board.mcu.to_lowercase())
.unwrap();
let defines = rp_board_defines(&board, &mcu);
assert_eq!(
defines.get("USBD_VID").map(String::as_str),
Some("TEST_VENDOR"),
"{id}"
);
assert_eq!(
defines.get("USBD_PID").map(String::as_str),
Some("TEST_PRODUCT"),
"{id}"
);
let defaults = fbuild_config::BoardConfig::from_board_id(id, &HashMap::new()).unwrap();
let defines = rp_board_defines(&defaults, &mcu);
assert_eq!(defines.get("USBD_VID").map(String::as_str), Some("USB_VID"));
assert_eq!(defines.get("USBD_PID").map(String::as_str), Some("USB_PID"));
}
}

#[test]
fn resolves_framework_variant_and_preserves_explicit_overrides() {
let tmp = tempfile::tempdir().unwrap();
let path = tmp.path().join("boards.txt");
std::fs::write(
&path,
"fixture.name=Test board\nfixture.build.variant=framework_variant\n",
)
.unwrap();
let props = crate::arduino_props::load_board_props_with_default_menus(&path, "fixture");
assert_eq!(
resolve_variant("manifest_variant", &props, &HashMap::new()),
"framework_variant"
);
let overrides = HashMap::from([("variant".to_owned(), "custom_variant".to_owned())]);
assert_eq!(
resolve_variant("custom_variant", &props, &overrides),
"custom_variant"
);
assert_eq!(
resolve_variant("manifest_variant", &None, &HashMap::new()),
"manifest_variant"
);
}
}
3 changes: 3 additions & 0 deletions crates/fbuild-build-arm/src/rp2040/configs/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# RP2040 MCU Configs

USB device macros `USBD_VID` and `USBD_PID` alias the registry-supplied
`USB_VID` and `USB_PID`; these configs contain no board USB identity values.

JSON configuration files for Raspberry Pi RP2040/RP2350 MCU families.

- `rp2040.json` - RP2040 (Cortex-M0+, no FPU)
Expand Down
4 changes: 3 additions & 1 deletion crates/fbuild-build-arm/src/rp2040/configs/rp2040.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
},

"defines": [
["ARDUINO", "10808"]
["ARDUINO", "10808"],
["USBD_VID", "USB_VID"],
["USBD_PID", "USB_PID"]
Comment thread
zackees marked this conversation as resolved.
]
}
4 changes: 3 additions & 1 deletion crates/fbuild-build-arm/src/rp2040/configs/rp2350.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
},

"defines": [
["ARDUINO", "10808"]
["ARDUINO", "10808"],
["USBD_VID", "USB_VID"],
["USBD_PID", "USB_PID"]
]
}
9 changes: 9 additions & 0 deletions crates/fbuild-build-arm/src/rp2040/mcu_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ pub fn get_rp2040_config_for_mcu(mcu: &str) -> Result<ArmMcuConfig> {
mod tests {
use super::*;

#[test]
fn usb_device_defines_alias_the_registry_compile_identity() {
for mcu in ["rp2040", "rp2350"] {
let defines = get_rp2040_config_for_mcu(mcu).unwrap().defines_map();
assert_eq!(defines.get("USBD_VID").map(String::as_str), Some("USB_VID"));
assert_eq!(defines.get("USBD_PID").map(String::as_str), Some("USB_PID"));
}
}

#[test]
fn test_load_rp2040_config() {
let config = get_rp2040_config_for_mcu("rp2040").unwrap();
Expand Down
1 change: 1 addition & 0 deletions crates/fbuild-build-arm/src/rp2040/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! RP2040/RP2350 platform build support (Raspberry Pi Pico, etc.)

mod bluetooth_libs;
mod board_props;
pub mod mcu_config;
pub mod orchestrator;
mod uf2;
Expand Down
88 changes: 29 additions & 59 deletions crates/fbuild-build-arm/src/rp2040/orchestrator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
//! 9. Link (with linker script from variant dir)
//! 10. Convert to binary + report size

use super::board_props::{apply_rp_board_props, resolve_variant, rp_board_defines};
use std::collections::{BTreeMap, HashMap};
use std::path::{Path, PathBuf};
use std::time::Instant;
Expand All @@ -35,6 +36,8 @@ use crate::{BuildOrchestrator, BuildParams, BuildResult, SourceScanner};

use super::uf2::convert_elf_to_uf2;

const RP_DEFINE_POLICY: &str = "platform-sdfatdefaults-board-defines-v2";

/// RP2040 platform build orchestrator.
pub struct Rp2040Orchestrator;

Expand All @@ -55,6 +58,7 @@ struct Rp2040FingerprintMetadata {
board_extra_flags: Option<String>,
board_upload_protocol: Option<String>,
board_menu_overrides: BTreeMap<String, String>,
board_defines: BTreeMap<String, String>,
platform: String,
max_flash: Option<u64>,
max_ram: Option<u64>,
Expand Down Expand Up @@ -199,12 +203,21 @@ impl BuildOrchestrator for Rp2040Orchestrator {
&framework.get_boards_txt(),
&board_id,
&board_menu_overrides,
);
)
.or_else(|| {
crate::arduino_props::load_board_props_with_menu_overrides(
&framework.get_boards_txt(),
ctx.board.registry_board_id(),
&board_menu_overrides,
)
});
ctx.board.variant =
resolve_variant(&ctx.board.variant, &board_props, &board_menu_overrides);
let build_dir = ctx.build_dir.clone();
let metadata_hash = stable_hash_with_build_config(
&Rp2040FingerprintMetadata {
version: BUILD_FINGERPRINT_VERSION,
platform_define_policy: "platform-sdfatdefaults-v1",
platform_define_policy: RP_DEFINE_POLICY,
framework_library_linkage: "local-and-framework-archive-v2",
env_name: params.env_name.clone(),
profile: profile_label(params.profile).to_string(),
Expand All @@ -217,6 +230,7 @@ impl BuildOrchestrator for Rp2040Orchestrator {
board_extra_flags: ctx.board.extra_flags.clone(),
board_upload_protocol: ctx.board.upload_protocol.clone(),
board_menu_overrides: board_menu_overrides.into_iter().collect(),
board_defines: ctx.board.get_defines().into_iter().collect(),
platform: "raspberrypi".to_string(),
max_flash: ctx.board.max_flash,
max_ram: ctx.board.max_ram,
Expand Down Expand Up @@ -292,8 +306,7 @@ impl BuildOrchestrator for Rp2040Orchestrator {
// 6. Build include dirs + compiler
let mcu_config =
super::mcu_config::get_rp2040_config_for_mcu(&ctx.board.mcu.to_lowercase())?;
let mut defines = ctx.board.get_defines();
defines.extend(mcu_config.defines_map());
let mut defines = rp_board_defines(&ctx.board, &mcu_config);
if let Some(max_flash) = ctx.board.max_flash {
defines.insert("PICO_FLASH_SIZE_BYTES".to_string(), max_flash.to_string());
}
Expand Down Expand Up @@ -693,49 +706,6 @@ fn add_define_file(define_file: &Path, defines: &mut HashMap<String, String>) {
apply_define_flags(&flags, defines);
}

fn apply_rp_board_props(
board_props: &Option<HashMap<String, String>>,
framework_dir: &Path,
defines: &mut HashMap<String, String>,
) {
let Some(props) = board_props.as_ref() else {
return;
};
for key in [
"usbvid",
"usbpid",
"usbpwr",
"usbstack_flags",
"variantdefines",
"led",
"libpicowdefs",
"wificc",
"sdfatdefines",
] {
if let Some(flags) = props.get(key) {
let expanded =
flags.replace("{runtime.platform.path}", &framework_dir.to_string_lossy());
let tokens = fbuild_core::shell_split::split(&expanded);
apply_define_flags(&tokens, defines);
}
}
if let Some(value) = props.get("usb_manufacturer") {
defines.insert("USB_MANUFACTURER".to_string(), value.clone());
}
if let Some(value) = props.get("usb_product") {
defines.insert("USB_PRODUCT".to_string(), value.clone());
}
for (property, define) in [
("flash_total", "PICO_FLASH_SIZE_BYTES"),
("fs_start", "FS_START"),
("fs_end", "FS_END"),
] {
if let Some(value) = props.get(property) {
defines.insert(define.to_string(), value.clone());
}
}
}

/// Apply Arduino-Pico's framework compiler definitions, with selected board
/// properties overriding platform defaults. SdFat's public-copy setting lives
/// in platform.txt, not boards.txt, and is required to compile bundled SDFS.
Expand Down Expand Up @@ -800,15 +770,7 @@ fn add_rp_board_includes(
}

fn apply_define_flags(flags: &[String], defines: &mut HashMap<String, String>) {
for flag in flags {
if let Some(def) = flag.strip_prefix("-D") {
if let Some((key, val)) = def.split_once('=') {
defines.insert(key.to_string(), val.trim().to_string());
} else {
defines.insert(def.trim().to_string(), "1".to_string());
}
}
}
defines.extend(fbuild_core::compiler_flags::parse_define_flags(flags));
}

fn rp_family(mcu: &str) -> &'static str {
Expand Down Expand Up @@ -1062,6 +1024,14 @@ mod tests {
assert_policy_invalidates_legacy_firmware_cache("platform_define_policy", None);
}

#[test]
fn board_define_precedence_invalidates_previous_firmware_cache() {
assert_policy_invalidates_legacy_firmware_cache(
"platform_define_policy",
Some("platform-sdfatdefaults-v1"),
);
}

#[test]
fn framework_archive_policy_invalidates_legacy_firmware_cache() {
assert_policy_invalidates_legacy_firmware_cache("framework_library_linkage", None);
Expand All @@ -1083,7 +1053,7 @@ mod tests {

let metadata = Rp2040FingerprintMetadata {
version: BUILD_FINGERPRINT_VERSION,
platform_define_policy: "platform-sdfatdefaults-v1",
platform_define_policy: RP_DEFINE_POLICY,
framework_library_linkage: "local-and-framework-archive-v2",
env_name: "pico".into(),
profile: "release".into(),
Expand All @@ -1096,6 +1066,7 @@ mod tests {
board_extra_flags: None,
board_upload_protocol: None,
board_menu_overrides: BTreeMap::new(),
board_defines: BTreeMap::new(),
platform: "raspberrypi".into(),
max_flash: None,
max_ram: None,
Expand Down Expand Up @@ -1339,8 +1310,7 @@ mod tests {
board_id,
);
let mcu = super::super::mcu_config::get_rp2040_config_for_mcu(&board.mcu).unwrap();
let mut defines = board.get_defines();
defines.extend(mcu.defines_map());
let mut defines = rp_board_defines(&board, &mcu);
add_rp_manifest_defines(&framework, &board.mcu, &mut defines);
apply_rp_board_props(&props, &framework, &mut defines);
apply_rp_platform_defines(&framework, &props, &mut defines).unwrap();
Expand Down
4 changes: 4 additions & 0 deletions crates/fbuild-build/tests/it/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ interrupt handler (including an LTO build), plus ClearCore vendor syscall linkag
`renesas_acceptance` builds UNO R4 firmware using `String` and `IPAddress`
and verifies that the supported default core supplied both source files.
Run with `soldr cargo test -p fbuild-build --test it renesas_acceptance:: -- --ignored`.

`rp2040_acceptance` builds a real default `pico` project, including boot2
assembly and USB string definitions. Run it with
`soldr cargo test -p fbuild-build --test it rp2040_acceptance:: -- --ignored`.
1 change: 1 addition & 0 deletions crates/fbuild-build/tests/it/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod lite_scons_acceptance;
mod nxplpc_build_flags;
mod nxplpc_core_compile_commands;
mod renesas_acceptance;
mod rp2040_acceptance;
mod samd_acceptance;
mod stm32_acceptance;
mod teensy30_acceptance;
Expand Down
Loading
Loading