use std::fs;
use std::fs::File;
use std::io::Write;
use svd_parser::{
parse,
svd::{Cpu, Device},
};
fn main() {
let current_dir = std::env::current_dir().expect("Failed to get current directory");
println!("Scanning directory: {:?}", current_dir);
let svd_files: Vec<_> = fs::read_dir(¤t_dir)
.expect("Failed to read directory")
.filter_map(|entry| {
let entry = entry.expect("Failed to get directory entry");
let path = entry.path();
if let Some(extension) = path.extension() {
if extension == "svd" {
return Some(path);
}
}
None
})
.collect();
if svd_files.is_empty() {
eprintln!("No SVD files found. Please place SVD files in the project directory.");
std::process::exit(1);
}
let known_exceptions = [
"MemManage_Handler",
"BusFault_Handler",
"UsageFault_Handler",
"SVCall_Handler",
"DebugMon_Handler",
"PendSV_Handler",
"SysTick_Handler",
];
let defined_handlers = [
"Reset_Handler",
"NMI_Handler",
"HardFault_Handler",
"Default_Handler",
];
for path in svd_files {
let svd_content = fs::read_to_string(&path).expect("Failed to read SVD file");
let device: Device = parse(&svd_content).expect("Failed to parse SVD file");
let system_exceptions: Vec<String> = match device.cpu {
Some(Cpu { name, .. }) => match name.as_str() {
"CM0" | "CM0+" => vec![
"Some(Reset_Handler)".to_string(),
"Some(NMI_Handler)".to_string(),
"Some(HardFault_Handler)".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"Some(SVCall_Handler)".to_string(),
"None".to_string(),
"None".to_string(),
"Some(PendSV_Handler)".to_string(),
"Some(SysTick_Handler)".to_string(),
],
"CM3" | "CM4" | "CM7" => vec![
"Some(Reset_Handler)".to_string(),
"Some(NMI_Handler)".to_string(),
"Some(HardFault_Handler)".to_string(),
"Some(MemManage_Handler)".to_string(),
"Some(BusFault_Handler)".to_string(),
"Some(UsageFault_Handler)".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"Some(SVCall_Handler)".to_string(),
"None".to_string(),
"None".to_string(),
"Some(PendSV_Handler)".to_string(),
"Some(SysTick_Handler)".to_string(),
],
_ => vec![
"Some(Reset_Handler)".to_string(),
"Some(NMI_Handler)".to_string(),
"Some(HardFault_Handler)".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"Some(SVCall_Handler)".to_string(),
"None".to_string(),
"None".to_string(),
"Some(PendSV_Handler)".to_string(),
"Some(SysTick_Handler)".to_string(),
],
},
None => vec![
"Some(Reset_Handler)".to_string(),
"Some(NMI_Handler)".to_string(),
"Some(HardFault_Handler)".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"None".to_string(),
"Some(SVCall_Handler)".to_string(),
"None".to_string(),
"None".to_string(),
"Some(PendSV_Handler)".to_string(),
"Some(SysTick_Handler)".to_string(),
],
};
let mut interrupts = Vec::new();
for peripheral in &device.peripherals {
for interrupt in &peripheral.interrupt {
interrupts.push(interrupt.clone());
}
}
let max_interrupt_number = interrupts
.iter()
.map(|interrupt| interrupt.value as usize)
.max()
.unwrap_or(0);
let mut vector_table: Vec<String> = vec!["None".to_string(); max_interrupt_number + 1];
for interrupt in &interrupts {
vector_table[interrupt.value as usize] = format!("Some({}_Handler)", interrupt.name);
}
let mut full_vector_table = system_exceptions;
full_vector_table.extend(vector_table.into_iter());
let mut handler_names: Vec<String> = full_vector_table
.iter()
.filter_map(|entry| {
if entry.starts_with("Some(") && entry.ends_with(")") {
let inside = &entry[5..entry.len() - 1];
if inside.ends_with("_Handler") {
Some(inside.to_string())
} else {
None
}
} else {
None
}
})
.collect();
let mut unique_handlers = handler_names.clone();
unique_handlers.sort();
unique_handlers.dedup();
let (mut handlers_to_define, mut handlers_to_declare): (Vec<_>, Vec<_>) =
unique_handlers
.into_iter()
.partition(|h| defined_handlers.contains(&h.as_str()));
if defined_handlers.contains(&"Default_Handler")
&& !handlers_to_define.contains(&"Default_Handler".to_string())
{
handlers_to_define.push("Default_Handler".to_string());
}
let mut exceptions_to_declare = Vec::new();
let mut irqs_to_declare = Vec::new();
for h in handlers_to_declare {
if known_exceptions.contains(&h.as_str()) {
exceptions_to_declare.push(h);
} else {
irqs_to_declare.push(h);
}
}
let mut top_definitions = String::new();
for handler in &handlers_to_define {
top_definitions.push_str(&format!(
"#[no_mangle]\nextern \"C\" fn {}() {{ loop {{}} }}\n",
handler
));
}
top_definitions.push('\n');
let mut extern_block = String::from("extern \"C\" {\n");
for handler in &exceptions_to_declare {
extern_block.push_str(&format!(" fn {}();\n", handler));
}
for handler in &irqs_to_declare {
extern_block.push_str(&format!(" fn {}();\n", handler));
}
extern_block.push_str("}\n\n");
let mut vector_table_string = String::new();
vector_table_string.push_str("#[used]\n");
vector_table_string.push_str("#[link_section = \".isr_vector\"]\n");
vector_table_string.push_str("static VECTOR_TABLE: [Option<unsafe extern \"C\" fn()>; ");
vector_table_string.push_str(&full_vector_table.len().to_string());
vector_table_string.push_str("] = [\n");
for entry in &full_vector_table {
vector_table_string.push_str(&format!(" {},\n", entry));
}
vector_table_string.push_str("];\n");
let mut final_vector_file_content = String::new();
final_vector_file_content.push_str(&top_definitions);
final_vector_file_content.push_str(&extern_block);
final_vector_file_content.push_str(&vector_table_string);
let file_stem = path
.file_stem()
.unwrap_or_default()
.to_str()
.unwrap_or("unknown");
let vector_output_path = format!("vector_{}.txt", file_stem);
let mut vector_file =
File::create(&vector_output_path).expect("Failed to create vector output file");
vector_file
.write_all(final_vector_file_content.as_bytes())
.expect("Failed to write to vector output file");
println!("Generated {}", vector_output_path);
let mut device_entries = String::new();
for exception in &known_exceptions {
device_entries.push_str(&format!(
"PROVIDE({} = Default_Handler);\n",
exception
));
}
for interrupt in &irqs_to_declare {
device_entries.push_str(&format!(
"PROVIDE({} = Default_Handler);\n",
interrupt
));
}
let device_output_path = format!("device_{}.x", file_stem);
let mut device_file =
File::create(&device_output_path).expect("Failed to create device output file");
device_file
.write_all(device_entries.as_bytes())
.expect("Failed to write to device output file");
println!("Generated {}", device_output_path);
}
}