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
82 changes: 33 additions & 49 deletions crates/fbuild-build/src/rp2040/mcu_config.rs
Original file line number Diff line number Diff line change
@@ -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<String>,
pub linker_libs: Vec<String>,
pub objcopy: ObjcopyConfig,
pub profiles: HashMap<String, ProfileFlags>,
#[serde(default)]
pub defines: Vec<DefineEntry>,
}

impl Rp2040McuConfig {
pub fn defines_map(&self) -> HashMap<String, String> {
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<Rp2040McuConfig> {
pub fn get_rp2040_config_for_mcu(mcu: &str) -> Result<ArmMcuConfig> {
let json = match mcu {
"rp2040" => RP2040_JSON,
"rp2350" => RP2350_JSON,
Expand All @@ -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()));
}
}
134 changes: 128 additions & 6 deletions crates/fbuild-build/src/rp2040/orchestrator.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,10 +30,116 @@ impl BuildOrchestrator for Rp2040Orchestrator {
Platform::RaspberryPi
}

fn build(&self, _params: &crate::BuildParams) -> Result<crate::BuildResult> {
Err(fbuild_core::FbuildError::BuildFailed(
"RP2040 platform build not yet available: Rp2040Cores package not wired up".into(),
))
fn build(&self, params: &BuildParams) -> Result<BuildResult> {
let start = Instant::now();

// 1-2. Parse config, load board, setup build dirs, resolve src dir, collect flags
let mut ctx = pipeline::BuildContext::new(
&params.project_dir,
&params.env_name,
params.clean,
params.profile,
params.log_sender.clone(),
)?;

// 3. Ensure ARM GCC toolchain
let toolchain = fbuild_packages::toolchain::ArmToolchain::new(&params.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(&params.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(&params.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,
)
}
}

Expand Down
Loading