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
75 changes: 74 additions & 1 deletion crates/fbuild-build-mcu/src/ch32v/orchestrator.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! CH32V build orchestrator — wires together config, packages, compiler, linker.
//! CH32V build orchestrator — wires together config, packages, compiler, linker.
//!
//! Build phases:
//! 1. Parse platformio.ini
Expand Down Expand Up @@ -122,6 +122,12 @@ impl BuildOrchestrator for Ch32vOrchestrator {
let mut defines = ctx.board.get_defines();
defines.extend(mcu_config.defines_map());
defines.insert(system_series.clone(), "1".to_string());
let (sysclk_name, sysclk_value) = sysclk_define(
&series,
&ctx.board.f_cpu,
ctx.board.clock_source.as_deref().unwrap_or("hsi+pll"),
)?;
defines.insert(sysclk_name, sysclk_value);
// CH32V cores use `#include VARIANT_H` — define it from the variant dir
if let Some(vh) = resolve_variant_h(&variant_dir, ctx.board.variant_h.as_deref()) {
defines.insert("VARIANT_H".to_string(), format!("\\\"{}\\\"", vh));
Expand Down Expand Up @@ -260,6 +266,52 @@ fn validate_ch32v_framework(framework: Option<&str>) -> fbuild_core::Result<()>
}
}

fn sysclk_define(
series: &str,
f_cpu: &str,
clock_source: &str,
) -> fbuild_core::Result<(String, String)> {
let hz = f_cpu.trim_end_matches('L').parse::<u64>().map_err(|_| {
fbuild_core::FbuildError::ConfigError(format!(
"ch32v: invalid f_cpu `{f_cpu}` for {series}"
))
})?;
let mhz = hz / 1_000_000;
if mhz == 0 || hz % 1_000_000 != 0 {
return Err(fbuild_core::FbuildError::ConfigError(format!(
"ch32v: unsupported f_cpu `{f_cpu}` for {series}; expected a whole MHz value"
)));
}
let supported: &[u64] = match series {
"ch32v003" | "ch32v006" => &[24, 48],
"ch32v103" => &[48, 56, 72],
"ch32v203" | "ch32v208" | "ch32v303" | "ch32v307" => &[48, 56, 72, 96, 120, 144],
"ch32x035" => &[8, 12, 16, 24, 48],
"ch32l103" => &[48, 72, 96],
_ => &[],
};
if !supported.contains(&mhz) {
return Err(fbuild_core::FbuildError::ConfigError(format!(
"ch32v: unsupported f_cpu `{f_cpu}` for {series}; supported values: {supported:?} MHz"
)));
}
let source = match clock_source.trim().to_ascii_lowercase().as_str() {
"hsi" | "hsi+pll" => "HSI",
"hse" | "hse+pll" => "HSE",
other => {
return Err(fbuild_core::FbuildError::ConfigError(format!(
"ch32v: unsupported clock_source `{other}` for {series}; expected hsi, hsi+pll, hse, or hse+pll"
)));
}
};
let unit = if matches!(series, "ch32v003" | "ch32v006") && source == "HSI" {
"MHZ"
} else {
"MHz"
};
Ok((format!("SYSCLK_FREQ_{mhz}{unit}_{source}"), hz.to_string()))
}

/// Recursively add subdirectories that contain .h files as include paths.
fn discover_header_subdirs(dir: &Path, include_dirs: &mut Vec<PathBuf>) {
if let Ok(entries) = std::fs::read_dir(dir) {
Expand Down Expand Up @@ -435,6 +487,27 @@ mod tests {
assert!(error.to_string().contains("1108"));
}

#[test]
fn test_sysclk_define_uses_series_spelling_and_clock_source() {
assert_eq!(
sysclk_define("ch32v203", "144000000L", "hsi+pll").unwrap(),
(
"SYSCLK_FREQ_144MHz_HSI".to_string(),
"144000000".to_string()
)
);
assert_eq!(
sysclk_define("ch32v003", "48000000L", "hsi+pll").unwrap(),
("SYSCLK_FREQ_48MHZ_HSI".to_string(), "48000000".to_string())
);
}

#[test]
fn test_sysclk_define_rejects_unsupported_frequency() {
let error = sysclk_define("ch32v203", "8000000L", "hsi").unwrap_err();
assert!(error.to_string().contains("supported values"));
}

#[test]
fn test_series_to_system_dir() {
// CH32V series: last digit replaced with 'x'
Expand Down
6 changes: 6 additions & 0 deletions crates/fbuild-config/src/board/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ fn flatten_board_entry(entry: &serde_json::Value, board_id: &str) -> HashMap<Str
} else if let Some(f_cpu_str) = build.and_then(|b| b.get("f_cpu")).and_then(|v| v.as_str()) {
d.insert("f_cpu".into(), f_cpu_str.to_string());
}
if let Some(clock_source) = build
.and_then(|b| b.get("clock_source"))
.and_then(|v| v.as_str())
{
d.insert("clock_source".into(), clock_source.to_string());
}

// ram/rom: enriched top-level wins; fall back to PIO `upload.maximum_ram_size`
// and `upload.maximum_size` respectively.
Expand Down
3 changes: 3 additions & 0 deletions crates/fbuild-config/src/board/loaders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ impl BoardConfig {
name,
mcu,
f_cpu: get("f_cpu").unwrap_or_else(|| "16000000L".to_string()),
clock_source: get("clock_source"),
board: get("board")
.or_else(|| props.get("board").cloned())
.unwrap_or_else(|| board_id_to_board_define(board_id)),
Expand Down Expand Up @@ -319,6 +320,8 @@ impl BoardConfig {
name: get("name", board_id),
mcu: get("mcu", "unknown"),
f_cpu: get("f_cpu", "16000000L"),
clock_source: Some(get("clock_source", ""))
.filter(|source| !source.is_empty()),
board: get("board", &board_id_to_board_define(board_id)),
core: get("core", "arduino"),
variant: get("variant", "standard"),
Expand Down
3 changes: 3 additions & 0 deletions crates/fbuild-config/src/board/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ pub struct BoardConfig {
pub name: String,
pub mcu: String,
pub f_cpu: String,
/// Clock source used by the MCU startup code (for example `hsi+pll`).
#[serde(default)]
pub clock_source: Option<String>,
pub board: String,
pub core: String,
pub variant: String,
Expand Down
Loading