use super::ir::*;
use anyhow::{Context, Result, bail};
use std::path::Path;
pub fn yaml_to_chip(text: &str) -> Result<RpChip> {
serde_yaml::from_str(text).context("parse RP chip YAML")
}
pub fn yaml_to_board(text: &str) -> Result<RpBoard> {
serde_yaml::from_str(text).context("parse RP board YAML")
}
pub fn load_chip_db(name: &str) -> Result<RpChip> {
let text = rlvgl_chips_rp2040::chip_yaml(name)
.with_context(|| format!("no RP chip '{name}' in db"))?;
yaml_to_chip(text)
}
pub fn load_board_db(name: &str) -> Result<RpBoard> {
let text = rlvgl_chips_rp2040::board_yaml(name)
.with_context(|| format!("no RP board '{name}' in db"))?;
yaml_to_board(text)
}
pub fn load_chip_file(path: &Path) -> Result<RpChip> {
let text = std::fs::read_to_string(path).with_context(|| format!("read {}", path.display()))?;
yaml_to_chip(&text)
}
pub fn load_board_file(path: &Path) -> Result<RpBoard> {
let text = std::fs::read_to_string(path).with_context(|| format!("read {}", path.display()))?;
yaml_to_board(&text)
}
pub fn merge(chip: RpChip, board: RpBoard) -> Result<RpIr> {
if !board.chip.eq_ignore_ascii_case(&chip.name) {
bail!(
"board chip '{}' does not match chip name '{}'",
board.chip,
chip.name
);
}
validate_pins(&chip, &board)?;
let clocks = build_clocks(&chip);
let pins = board.pins.clone();
Ok(RpIr {
chip,
board,
clocks,
pins,
})
}
fn build_clocks(chip: &RpChip) -> RpClocksConfig {
let find_domain = |name: &str| -> u32 {
chip.clock_tree
.clk_domains
.iter()
.find(|d| d.name == name)
.map(|d| d.default_hz)
.unwrap_or(0)
};
RpClocksConfig {
sys_hz: find_domain("clk_sys"),
ref_hz: find_domain("clk_ref"),
peri_hz: find_domain("clk_peri"),
usb_hz: find_domain("clk_usb"),
xosc_hz: chip.clock_tree.xosc_hz,
}
}
fn validate_pins(chip: &RpChip, board: &RpBoard) -> Result<()> {
let mut labels_seen = std::collections::HashSet::new();
let mut gpios_seen = std::collections::HashSet::new();
for pin in &board.pins {
if pin.gpio >= chip.gpio_count {
bail!(
"gpio {} out of range (chip has {} GPIOs)",
pin.gpio,
chip.gpio_count
);
}
if !gpios_seen.insert(pin.gpio) {
bail!("gpio {} assigned more than once", pin.gpio);
}
if let Some(periph) = &pin.peripheral {
if !chip.peripherals.contains_key(periph.as_str()) {
bail!(
"gpio {} references peripheral '{}' not in chip",
pin.gpio,
periph
);
}
if let Some(role) = &pin.role {
validate_funcsel(chip, pin.gpio, periph, role, &pin.signal)?;
}
}
if let Some(label) = &pin.label {
if !labels_seen.insert(label.clone()) {
bail!("duplicate label '{}'", label);
}
}
}
Ok(())
}
fn validate_funcsel(
chip: &RpChip,
gpio: u8,
_peripheral: &str,
_role: &str,
signal: &str,
) -> Result<()> {
let funcsel_pin = chip
.funcsel
.iter()
.find(|f| f.gpio == gpio)
.with_context(|| format!("gpio {} not found in chip funcsel table", gpio))?;
let has_match = funcsel_pin
.funcs
.iter()
.any(|f| f.func.eq_ignore_ascii_case(signal));
if !has_match {
bail!(
"gpio {} has no funcsel entry matching signal '{}'",
gpio,
signal
);
}
Ok(())
}