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 singleton: String,
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 parameters = 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 singleton = ident(&instance.singleton);
parameters.push(quote! {
#field: #hal::Peri<'static, #hal::peripherals::#singleton>
});
match &instance.kind {
PeripheralKind::I2c {
clock_hz,
frequency,
irq_number,
irq_name,
} => {
let handler = ident(&format!("{irq_name}_IRQHandler"));
fields.push(quote! { pub #field: #hal::i2c::I2c });
handlers.push(quote! {
#[cfg(target_arch = "arm")]
#[unsafe(no_mangle)]
extern "C" fn #handler() {
#hal::i2c::on_interrupt::<#hal::peripherals::#singleton>();
}
});
nvic.push(quote! {
unsafe {
#hal::hal::nvic::set_priority(#irq_number, 0x80u8);
#hal::hal::nvic::enable(#irq_number);
}
});
constructs.push(quote! {
let #field = #hal::i2c::I2c::new(#field, #clock_hz, #frequency);
});
}
PeripheralKind::Gpio => {
fields.push(quote! { pub #field: #hal::gpio::Gpio });
constructs.push(quote! {
let #field = #hal::gpio::Gpio::new(#field);
});
}
}
}
Some(quote! {
#[cfg(target_arch = "arm")]
pub struct Peripherals {
#(#fields,)*
}
#(#handlers)*
#[cfg(target_arch = "arm")]
fn peripherals(#(#parameters),*) -> Peripherals {
#(#nvic)*
#(#constructs)*
Peripherals {
#(#field_names),*
}
}
})
}
pub fn arguments(&self, core: VisibleCore) -> Vec<Ident> {
self.instances
.iter()
.filter(|p| p.owner == core)
.map(|p| ident(&p.singleton))
.collect()
}
}
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())
}