From dd987d7da94eb80089e6fdd729700a2073c4431e Mon Sep 17 00:00:00 2001 From: zackees Date: Sun, 5 Apr 2026 20:11:56 -0700 Subject: [PATCH] feat(rp2040): wire up RP2040/RP2350 build orchestrator Replace the stub orchestrator that returned "Rp2040Cores package not wired up" with a real implementation following the STM32 pattern: ARM GCC toolchain + arduino-pico cores + generic ArmCompiler/ArmLinker. Simplify mcu_config.rs to reuse ArmMcuConfig instead of the redundant Rp2040McuConfig type. --- crates/fbuild-build/src/rp2040/mcu_config.rs | 82 +++++------ .../fbuild-build/src/rp2040/orchestrator.rs | 134 +++++++++++++++++- 2 files changed, 161 insertions(+), 55 deletions(-) diff --git a/crates/fbuild-build/src/rp2040/mcu_config.rs b/crates/fbuild-build/src/rp2040/mcu_config.rs index ef171c6e..62abe3a4 100644 --- a/crates/fbuild-build/src/rp2040/mcu_config.rs +++ b/crates/fbuild-build/src/rp2040/mcu_config.rs @@ -1,61 +1,17 @@ //! Data-driven RP2040/RP2350 MCU configuration from embedded JSON. - -use std::collections::HashMap; +//! +//! Maps RP2040/RP2350 MCU names to the appropriate ARM Cortex-M configuration. +//! RP2040 uses Cortex-M0+, RP2350 uses Cortex-M33. use fbuild_core::Result; -use serde::Deserialize; -use crate::compiler::{CompilerFlags, McuConfig, ObjcopyConfig, ProfileFlags}; -use crate::esp32::mcu_config::DefineEntry; +use crate::generic_arm::ArmMcuConfig; const RP2040_JSON: &str = include_str!("configs/rp2040.json"); const RP2350_JSON: &str = include_str!("configs/rp2350.json"); -/// RP2040/RP2350 MCU configuration parsed from JSON. -#[derive(Debug, Clone, Deserialize)] -pub struct Rp2040McuConfig { - pub name: String, - #[serde(default)] - pub description: String, - pub architecture: String, - pub compiler_flags: CompilerFlags, - pub linker_flags: Vec, - pub linker_libs: Vec, - pub objcopy: ObjcopyConfig, - pub profiles: HashMap, - #[serde(default)] - pub defines: Vec, -} - -impl Rp2040McuConfig { - pub fn defines_map(&self) -> HashMap { - let mut map = HashMap::new(); - for entry in &self.defines { - match entry { - DefineEntry::Simple(name) => { - map.insert(name.clone(), "1".to_string()); - } - DefineEntry::KeyValue(name, value) => { - map.insert(name.clone(), value.clone()); - } - } - } - map - } -} - -impl McuConfig for Rp2040McuConfig { - fn compiler_flags(&self) -> &CompilerFlags { - &self.compiler_flags - } - - fn get_profile(&self, name: &str) -> Option<&ProfileFlags> { - self.profiles.get(name) - } -} - /// Load MCU configuration for a specific RP2040/RP2350 MCU. -pub fn get_rp2040_config_for_mcu(mcu: &str) -> Result { +pub fn get_rp2040_config_for_mcu(mcu: &str) -> Result { let json = match mcu { "rp2040" => RP2040_JSON, "rp2350" => RP2350_JSON, @@ -82,16 +38,44 @@ mod tests { fn test_load_rp2040_config() { let config = get_rp2040_config_for_mcu("rp2040").unwrap(); assert_eq!(config.name, "RP2040"); + assert_eq!(config.architecture, "arm-cortex-m0plus"); } #[test] fn test_load_rp2350_config() { let config = get_rp2040_config_for_mcu("rp2350").unwrap(); assert_eq!(config.name, "RP2350"); + assert_eq!(config.architecture, "arm-cortex-m33"); } #[test] fn test_unsupported_mcu() { assert!(get_rp2040_config_for_mcu("rp9999").is_err()); } + + #[test] + fn test_rp2040_compiler_flags() { + let config = get_rp2040_config_for_mcu("rp2040").unwrap(); + assert!(config + .compiler_flags + .common + .contains(&"-mcpu=cortex-m0plus".to_string())); + assert!(config + .compiler_flags + .common + .contains(&"-mthumb".to_string())); + } + + #[test] + fn test_rp2350_has_fpu_flags() { + let config = get_rp2040_config_for_mcu("rp2350").unwrap(); + assert!(config + .compiler_flags + .common + .contains(&"-mfloat-abi=softfp".to_string())); + assert!(config + .compiler_flags + .common + .contains(&"-mfpu=fpv5-sp-d16".to_string())); + } } diff --git a/crates/fbuild-build/src/rp2040/orchestrator.rs b/crates/fbuild-build/src/rp2040/orchestrator.rs index 2680ab71..016b7df7 100644 --- a/crates/fbuild-build/src/rp2040/orchestrator.rs +++ b/crates/fbuild-build/src/rp2040/orchestrator.rs @@ -1,10 +1,26 @@ -//! RP2040/RP2350 build orchestrator. +//! RP2040/RP2350 build orchestrator — wires together config, packages, compiler, linker. +//! +//! Build phases: +//! 1. Parse platformio.ini +//! 2. Load board config (rpipico, rpipico2, etc.) +//! 3. Ensure ARM GCC toolchain +//! 4. Ensure RP2040 cores (arduino-pico by earlephilhower) +//! 5. Setup build directories +//! 6. Scan source files +//! 7. Compile core + variant sources +//! 8. Compile sketch sources +//! 9. Link (with linker script from variant dir) +//! 10. Convert to binary + report size use std::path::Path; +use std::time::Instant; use fbuild_core::{Platform, Result}; -use crate::BuildOrchestrator; +use crate::compile_database::TargetArchitecture; +use crate::generic_arm::{ArmCompiler, ArmLinker}; +use crate::pipeline; +use crate::{BuildOrchestrator, BuildParams, BuildResult, SourceScanner}; /// RP2040 platform build orchestrator. pub struct Rp2040Orchestrator; @@ -14,10 +30,116 @@ impl BuildOrchestrator for Rp2040Orchestrator { Platform::RaspberryPi } - fn build(&self, _params: &crate::BuildParams) -> Result { - Err(fbuild_core::FbuildError::BuildFailed( - "RP2040 platform build not yet available: Rp2040Cores package not wired up".into(), - )) + fn build(&self, params: &BuildParams) -> Result { + let start = Instant::now(); + + // 1-2. Parse config, load board, setup build dirs, resolve src dir, collect flags + let mut ctx = pipeline::BuildContext::new( + ¶ms.project_dir, + ¶ms.env_name, + params.clean, + params.profile, + params.log_sender.clone(), + )?; + + // 3. Ensure ARM GCC toolchain + let toolchain = fbuild_packages::toolchain::ArmToolchain::new(¶ms.project_dir); + let toolchain_dir = fbuild_packages::Package::ensure_installed(&toolchain)?; + tracing::info!("arm-gcc toolchain at {}", toolchain_dir.display()); + + use fbuild_packages::Toolchain; + pipeline::log_toolchain_version( + &toolchain.get_gcc_path(), + "arm-none-eabi-gcc", + &mut ctx.build_log, + ); + + // 4. Ensure RP2040 cores (arduino-pico by earlephilhower) + let framework = fbuild_packages::library::Rp2040Cores::new(¶ms.project_dir); + let framework_dir = fbuild_packages::Package::ensure_installed(&framework)?; + tracing::info!("RP2040 cores at {}", framework_dir.display()); + + // 5. Scan sources (core + variant) + let core_dir = framework.get_core_dir(&ctx.board.core); + let variant_dir = framework.get_variant_dir(&ctx.board.variant); + + let scanner = SourceScanner::new(&ctx.src_dir, &ctx.src_build_dir); + let sources = scanner.scan_all(Some(&core_dir), Some(&variant_dir))?; + + tracing::info!( + "sources: {} sketch, {} core, {} variant", + sources.sketch_sources.len(), + sources.core_sources.len(), + sources.variant_sources.len(), + ); + + // 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 include_dirs = ctx.board.get_include_paths(&framework_dir); + include_dirs.push(ctx.src_dir.clone()); + pipeline::discover_project_includes(¶ms.project_dir, &mut include_dirs); + // Toolchain sysroot includes + include_dirs.extend(toolchain.get_include_dirs()); + // Pico SDK includes + let pico_sdk_dir = framework.get_pico_sdk_dir(); + let pico_sdk_src = pico_sdk_dir.join("src"); + if pico_sdk_src.exists() { + // Common headers (pico.h, pico/types.h, etc.) + let common_inc = pico_sdk_src + .join("common") + .join("pico_base_headers") + .join("include"); + if common_inc.exists() { + include_dirs.push(common_inc); + } + // Board headers + let boards_inc = pico_sdk_src.join("boards").join("include"); + if boards_inc.exists() { + include_dirs.push(boards_inc); + } + } + + let compiler = ArmCompiler::new( + toolchain.get_gcc_path(), + toolchain.get_gxx_path(), + &ctx.board.mcu, + &ctx.board.f_cpu, + defines, + include_dirs, + mcu_config.clone(), + params.profile, + params.verbose, + ); + + // 7. Create linker (linker script from variant dir) + let linker_script = framework.get_linker_script(&ctx.board.variant); + let linker = ArmLinker::new( + toolchain.get_gcc_path(), + toolchain.get_ar_path(), + toolchain.get_objcopy_path(), + toolchain.get_size_path(), + linker_script, + mcu_config, + params.profile, + ctx.board.max_flash, + ctx.board.max_ram, + params.verbose, + ); + + // 8. Run shared sequential build pipeline + pipeline::run_sequential_build( + &compiler, + &linker, + ctx, + params, + &sources, + TargetArchitecture::Arm, + "RP2040", + start, + ) } }