use consortium_cfg_common::config::VisibleCore;
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PeripheralKind {
I2c {
clock_hz: u32,
frequency: u32,
irq_number: u16,
irq_name: String,
},
Gpio,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PeripheralInstance {
pub owner: VisibleCore,
pub key: String,
pub base: u32,
pub kind: PeripheralKind,
}
#[derive(Debug, Clone)]
pub struct Peripherals {
pub hal_crate: String,
pub instances: Vec<PeripheralInstance>,
}
impl Peripherals {
pub fn initialize(&self, core: VisibleCore) -> Option<TokenStream> {
let owned: Vec<&PeripheralInstance> =
self.instances.iter().filter(|p| p.owner == core).collect();
if owned.is_empty() {
return None;
}
let hal: syn::Path =
syn::parse_str(&self.hal_crate).expect("hal_crate must be a valid path");
let mut fields = Vec::new();
let mut field_names = Vec::new();
let mut statics = Vec::new();
let mut handlers = Vec::new();
let mut nvic = Vec::new();
let mut constructs = Vec::new();
for instance in owned {
let field = ident(&instance.key);
field_names.push(field.clone());
let base = instance.base;
match &instance.kind {
PeripheralKind::I2c {
clock_hz,
frequency,
irq_number,
irq_name,
} => {
let state = ident(&format!("{}_STATE", instance.key.to_ascii_uppercase()));
let handler = ident(&format!("{irq_name}_IRQHandler"));
fields.push(quote! { pub #field: #hal::i2c::I2c });
statics.push(quote! {
#[cfg(target_arch = "arm")]
static #state: #hal::i2c::I2cState = #hal::i2c::I2cState::new();
});
handlers.push(quote! {
#[cfg(target_arch = "arm")]
#[unsafe(no_mangle)]
extern "C" fn #handler() {
#state.on_interrupt();
}
});
nvic.push(quote! {
unsafe {
#hal::hal::nvic::set_priority(#irq_number, 0x80u8);
#hal::hal::nvic::enable(#irq_number);
}
});
constructs.push(quote! {
let #field = unsafe {
#hal::i2c::I2c::new(#base as *mut (), #clock_hz, #frequency, &#state)
};
});
}
PeripheralKind::Gpio => {
fields.push(quote! { pub #field: #hal::gpio::Gpio });
constructs.push(quote! {
let #field = unsafe { #hal::gpio::Gpio::new(#base as *mut ()) };
});
}
}
}
Some(quote! {
#[cfg(target_arch = "arm")]
pub struct Peripherals {
#(#fields,)*
}
#(#statics)*
#(#handlers)*
#[cfg(target_arch = "arm")]
fn peripherals() -> Peripherals {
#(#nvic)*
#(#constructs)*
Peripherals {
#(#field_names),*
}
}
})
}
}
fn ident(raw: &str) -> Ident {
let mut out = String::with_capacity(raw.len());
for ch in raw.chars() {
if ch.is_ascii_alphanumeric() || ch == '_' {
out.push(ch);
} else {
out.push('_');
}
}
if out.is_empty() || out.starts_with(|ch: char| ch.is_ascii_digit()) {
out.insert(0, '_');
}
Ident::new(&out, Span::call_site())
}