Parse interrupts on demand for each chip instead of upfront.

This commit is contained in:
Dario Nieuwenhuis 2023-11-17 23:42:07 +01:00
parent fbb8f77326
commit 221d24f6f8
3 changed files with 367 additions and 291 deletions

View File

@ -67,7 +67,7 @@ pub struct Chip {
pub struct ChipGroup {
chip_names: Vec<String>,
xml: xml::Mcu,
ips: HashMap<String, xml::Ip>,
pub ips: HashMap<String, xml::Ip>,
pins: HashMap<String, xml::Pin>,
family: Option<String>,
line: Option<String>,
@ -810,73 +810,9 @@ fn process_core(
chip_af: Option<&HashMap<String, Vec<stm32_data_serde::chip::core::peripheral::Pin>>>,
dma_channels: &dma::DmaChannels,
) -> stm32_data_serde::chip::Core {
let real_core_name = corename(core_xml);
let core_name = corename(core_xml);
let defines = h.get_defines(&core_name);
let core_name = if !h.interrupts.contains_key(&real_core_name) || !h.defines.contains_key(&real_core_name) {
"all"
} else {
&real_core_name
};
// C header defines for this core.
let defines = h.defines.get(core_name).unwrap();
// Interrupts!
let want_nvic_name = {
// Most chips have a single NVIC, named "NVIC"
let mut want_nvic_name = "NVIC";
// Exception 1: Multicore: NVIC1 is the first core, NVIC2 is the second. We have to pick the right one.
if ["H745", "H747", "H755", "H757", "WL54", "WL55"].contains(&&chip_name[5..9]) {
if core_name == "cm7" {
want_nvic_name = "NVIC1";
} else {
want_nvic_name = "NVIC2"
}
}
if &chip_name[5..8] == "WL5" {
if core_name == "cm4" {
want_nvic_name = "NVIC1";
} else {
want_nvic_name = "NVIC2"
}
}
// Exception 2: TrustZone: NVIC1 is Secure mode, NVIC2 is NonSecure mode. For now, we pick the NonSecure one.
if ["L5", "U5"].contains(&&chip_name[5..7]) {
want_nvic_name = "NVIC2"
}
if ["H56", "H57", "WBA"].contains(&&chip_name[5..8]) {
want_nvic_name = "NVIC2"
}
want_nvic_name
};
let chip_nvic = group
.ips
.values()
.find(|x| x.name == want_nvic_name)
.ok_or_else(|| format!("couldn't find nvic. chip_name={chip_name} want_nvic_name={want_nvic_name}"))
.unwrap();
// With the current data sources, this value is always either 2 or 4, and never resolves to None
let nvic_priority_bits = defines.0.get("__NVIC_PRIO_BITS").map(|bits| *bits as u8);
let mut header_irqs = h.interrupts.get(core_name).unwrap().clone();
let chip_irqs = chip_interrupts
.0
.get(&(chip_nvic.name.clone(), chip_nvic.version.clone()))
.unwrap();
// F100xE MISC_REMAP remaps some DMA IRQs, so ST decided to give two names
// to the same IRQ number.
if chip_name.starts_with("STM32F100") {
header_irqs.remove("DMA2_Channel4_5");
}
let mut interrupts: Vec<_> = header_irqs
.iter()
.map(|(k, v)| stm32_data_serde::chip::core::Interrupt {
name: k.clone(),
number: *v,
})
.collect();
interrupts.sort_unstable_by_key(|x| x.number);
let mut peri_kinds = HashMap::new();
peri_kinds.insert("UID".to_string(), "UID".to_string());
for ip in group.ips.values() {
@ -1067,41 +1003,6 @@ fn process_core(
// sort pins to avoid diff for c pins
p.pins.sort_by_key(|x| (x.pin.clone(), x.signal.clone()));
if let Some(peri_irqs) = chip_irqs.get(&pname) {
use stm32_data_serde::chip::core::peripheral::Interrupt;
//filter by available, because some are conditioned on <Die>
static EQUIVALENT_IRQS: &[(&str, &[&str])] = &[
("HASH_RNG", &["RNG"]),
("USB_HP_CAN_TX", &["CAN_TX"]),
("USB_LP_CAN_RX0", &["CAN_RX0"]),
];
let mut irqs: Vec<_> = peri_irqs
.iter()
.filter_map(|i| {
if header_irqs.contains_key(&i.interrupt) {
return Some(i.clone());
}
if let Some((_, eq_irqs)) = EQUIVALENT_IRQS.iter().find(|(irq, _)| irq == &i.interrupt) {
for eq_irq in *eq_irqs {
if header_irqs.contains_key(*eq_irq) {
return Some(Interrupt {
signal: i.signal.clone(),
interrupt: eq_irq.to_string(),
});
}
}
}
None
})
.collect();
irqs.sort_by_key(|x| (x.signal.clone(), x.interrupt.clone()));
irqs.dedup_by_key(|x| (x.signal.clone(), x.interrupt.clone()));
p.interrupts = Some(irqs);
}
peripherals.insert(p.name.clone(), p);
}
if let Ok(extra_f) = std::fs::read(format!("data/extra/family/{}.yaml", group.family.as_ref().unwrap())) {
@ -1189,13 +1090,18 @@ fn process_core(
p.dma_channels = chs;
}
}
stm32_data_serde::chip::Core {
name: real_core_name.clone(),
let mut core = stm32_data_serde::chip::Core {
name: core_name.clone(),
peripherals,
nvic_priority_bits,
interrupts,
nvic_priority_bits: None,
interrupts: vec![],
dma_channels: core_dma_channels,
}
};
chip_interrupts.process(&mut core, chip_name, h, group);
core
}
fn process_chip(

View File

@ -196,6 +196,26 @@ pub struct ParsedHeader {
}
impl ParsedHeader {
/// Get C header defines for this core.
pub fn get_defines(&self, core_name: &str) -> &Defines {
let core_name = if !self.interrupts.contains_key(core_name) || !self.defines.contains_key(core_name) {
"all"
} else {
core_name
};
self.defines.get(core_name).unwrap()
}
/// Get interrupts for this core.
pub fn get_interrupts(&self, core_name: &str) -> &HashMap<String, u8> {
let core_name = if !self.interrupts.contains_key(core_name) || !self.defines.contains_key(core_name) {
"all"
} else {
core_name
};
self.interrupts.get(core_name).unwrap()
}
fn parse(f: impl AsRef<std::path::Path>) -> anyhow::Result<Self> {
let mut irqs = HashMap::<String, HashMap<String, u8>>::new();
let mut defines = HashMap::<String, Defines>::new();

View File

@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet};
use log::*;
use crate::chips::ChipGroup;
use crate::regex;
mod xml {
@ -35,14 +36,14 @@ mod xml {
}
#[derive(Debug)]
pub struct ChipInterrupts(
// (chip name, chip version), (signal name, [interrupt])
pub HashMap<(String, String), HashMap<String, Vec<stm32_data_serde::chip::core::peripheral::Interrupt>>>,
);
pub struct ChipInterrupts {
// (nvic name, nvic version) => [cursed unparsed interrupt string]
irqs: HashMap<(String, String), Vec<String>>,
}
impl ChipInterrupts {
pub fn parse() -> anyhow::Result<Self> {
let mut chip_interrupts = HashMap::new();
let mut irqs = HashMap::new();
let mut files: Vec<_> = glob::glob("sources/cubedb/mcu/IP/NVIC*_Modes.xml")?
.map(Result::unwrap)
@ -54,211 +55,329 @@ impl ChipInterrupts {
trace!("parsing {f:?}");
let file = std::fs::read_to_string(&f)?;
let parsed: xml::Ip = quick_xml::de::from_str(&file)?;
let mut chip_signals = HashMap::<_, Vec<_>>::new();
for irq in parsed
let strings: Vec<_> = parsed
.ref_parameters
.into_iter()
.filter(|param| param.name == "IRQn")
.flat_map(|param| param.possible_values)
// F3 can remap USB IRQs, ignore them.
.filter(|irq| !parsed.version.starts_with("STM32F3") || !irq.comment.contains("remap"))
.map(|irq| irq.value)
.collect();
irqs.insert((parsed.name, parsed.version), strings);
}
Ok(Self { irqs })
}
pub(crate) fn process(
&self,
core: &mut stm32_data_serde::chip::Core,
chip_name: &str,
h: &crate::header::ParsedHeader,
group: &ChipGroup,
) {
// =================== Populate nvic_priority_bits
// With the current data sources, this value is always either 2 or 4, and never resolves to None
let header_defines = h.get_defines(&core.name);
core.nvic_priority_bits = header_defines.0.get("__NVIC_PRIO_BITS").map(|bits| *bits as u8);
// =================== Populate core interrupts
let mut header_irqs = h.get_interrupts(&core.name).clone();
// F100xE MISC_REMAP remaps some DMA IRQs, so ST decided to give two names
// to the same IRQ number.
if chip_name.starts_with("STM32F100") {
header_irqs.remove("DMA2_Channel4_5");
}
core.interrupts = header_irqs
.iter()
.map(|(k, v)| stm32_data_serde::chip::core::Interrupt {
name: k.clone(),
number: *v,
})
.collect();
core.interrupts.sort_unstable_by_key(|x| x.number);
// =================== Populate peripheral interrupts
let want_nvic_name = pick_nvic(chip_name, &core.name);
let chip_nvic = group
.ips
.values()
.find(|x| x.name == want_nvic_name)
.ok_or_else(|| format!("couldn't find nvic. chip_name={chip_name} want_nvic_name={want_nvic_name}"))
.unwrap();
let nvic_strings = self
.irqs
.get(&(chip_nvic.name.clone(), chip_nvic.version.clone()))
.unwrap();
let mut chip_signals = HashMap::<_, Vec<_>>::new();
for nvic_string in nvic_strings {
trace!(" irq={nvic_string:?}");
let parts = {
let mut iter = nvic_string.split(':');
let parts = [(); 5].map(|_| iter.next().unwrap());
assert!(iter.next().is_none());
parts
};
let mut name = parts[0].strip_suffix("_IRQn").unwrap().to_string();
// Fix typo in STM32Lxx and L083 devices
let contains_rng = || parts[2..].iter().flat_map(|x| x.split(',')).any(|x| x == "RNG");
if name == "AES_RNG_LPUART1" && !contains_rng() {
name = "AES_LPUART1".to_string()
}
// More typos
let name = name.replace("USAR11", "USART11");
// Flags.
// Y
// unknown, it's in all of them
// H3, nHS
// ???
// 2V, 3V, nV, 2V1
// unknown, it has to do with the fact the irq is shared among N peripehrals
// DMA, DMAL0, DMAF0, DMAL0_DMAMUX, DMAF0_DMAMUX
// special format for DMA
// DFSDM
// special format for DFSDM
// EXTI
// special format for EXTI
let flags: Vec<_> = parts[1].split(',').collect();
// F100xE MISC_REMAP remaps some DMA IRQs, so ST decided to give two names
// to the same IRQ number.
if chip_nvic.version == "STM32F100E" && name == "DMA2_Channel4_5" {
continue;
}
//not supported
if name == "LSECSSD" {
continue;
}
let mut interrupt_signals = HashSet::<(String, String)>::new();
if [
"NonMaskableInt",
"HardFault",
"MemoryManagement",
"BusFault",
"UsageFault",
"SVCall",
"DebugMonitor",
"PendSV",
"SysTick",
]
.contains(&name.as_str())
{
trace!(" irq={irq:?}");
let parts = {
let mut iter = irq.value.split(':');
let parts = [(); 5].map(|_| iter.next().unwrap());
assert!(iter.next().is_none());
parts
// pass
} else if flags
.iter()
.map(|flag| ["DMA", "DMAL0", "DMAF0", "DMAL0_DMAMUX", "DMAF0_DMAMUX"].contains(flag))
.any(std::convert::identity)
{
let mut dmas_iter = parts[3].split(',');
let mut chans_iter = parts[4].split(';');
for (dma, chan) in std::iter::zip(&mut dmas_iter, &mut chans_iter) {
let range = {
let mut ch = chan.split(',');
let ch_from: usize = ch.next().unwrap().parse().unwrap();
let ch_to = match ch.next() {
Some(ch_to) => ch_to.parse().unwrap(),
None => ch_from,
};
assert!(ch.next().is_none());
ch_from..=ch_to
};
for ch in range {
interrupt_signals.insert((dma.to_string(), format!("CH{ch}")));
}
}
assert!(dmas_iter.next().is_none());
assert!(chans_iter.next().is_none());
} else if name == "DMAMUX1" || name == "DMAMUX1_S" || name == "DMAMUX_OVR" || name == "DMAMUX1_OVR" {
interrupt_signals.insert(("DMAMUX1".to_string(), "OVR".to_string()));
} else if name == "DMAMUX2_OVR" {
interrupt_signals.insert(("DMAMUX2".to_string(), "OVR".to_string()));
} else if flags.contains(&"DMAMUX") {
panic!("should've been handled above");
} else if flags.contains(&"EXTI") {
for signal in parts[2].split(',') {
interrupt_signals.insert(("EXTI".to_string(), signal.to_string()));
}
} else if name == "FLASH" {
interrupt_signals.insert(("FLASH".to_string(), "GLOBAL".to_string()));
} else if name == "CRS" {
interrupt_signals.insert(("RCC".to_string(), "CRS".to_string()));
} else if name == "RCC" {
interrupt_signals.insert(("RCC".to_string(), "GLOBAL".to_string()));
} else {
if parts[2].is_empty() {
continue;
}
let peri_names: Vec<_> = parts[2].split(',').map(ToString::to_string).collect();
let name2 = {
if name == "USBWakeUp" || name == "USBWakeUp_RMP" {
"USB_WKUP"
} else {
name.strip_suffix("_S").unwrap_or(&name)
}
};
let mut name = parts[0].strip_suffix("_IRQn").unwrap().to_string();
// Fix typo in STM32Lxx and L083 devices
let contains_rng = || parts[2..].iter().flat_map(|x| x.split(',')).any(|x| x == "RNG");
if name == "AES_RNG_LPUART1" && !contains_rng() {
name = "AES_LPUART1".to_string()
}
// More typos
let name = name.replace("USAR11", "USART11");
// Flags.
// Y
// unknown, it's in all of them
// H3, nHS
// ???
// 2V, 3V, nV, 2V1
// unknown, it has to do with the fact the irq is shared among N peripehrals
// DMA, DMAL0, DMAF0, DMAL0_DMAMUX, DMAF0_DMAMUX
// special format for DMA
// DFSDM
// special format for DFSDM
// EXTI
// special format for EXTI
let flags: Vec<_> = parts[1].split(',').collect();
// F100xE MISC_REMAP remaps some DMA IRQs, so ST decided to give two names
// to the same IRQ number.
if parsed.version == "STM32F100E" && name == "DMA2_Channel4_5" {
continue;
}
// F3 can remap USB IRQs, ignore them.
if parsed.version.starts_with("STM32F3") && irq.comment.contains("remap") {
continue;
}
// not supported
if name == "LSECSSD" {
continue;
}
let mut interrupt_signals = HashSet::<(String, String)>::new();
if [
"NonMaskableInt",
"HardFault",
"MemoryManagement",
"BusFault",
"UsageFault",
"SVCall",
"DebugMonitor",
"PendSV",
"SysTick",
]
.contains(&name.as_str())
{
// pass
} else if flags
let mut peri_signals: HashMap<_, _> = peri_names
.iter()
.map(|flag| ["DMA", "DMAL0", "DMAF0", "DMAL0_DMAMUX", "DMAF0_DMAMUX"].contains(flag))
.any(std::convert::identity)
{
let mut dmas_iter = parts[3].split(',');
let mut chans_iter = parts[4].split(';');
for (dma, chan) in std::iter::zip(&mut dmas_iter, &mut chans_iter) {
let range = {
let mut ch = chan.split(',');
let ch_from: usize = ch.next().unwrap().parse().unwrap();
let ch_to = match ch.next() {
Some(ch_to) => ch_to.parse().unwrap(),
None => ch_from,
};
assert!(ch.next().is_none());
ch_from..=ch_to
};
for ch in range {
interrupt_signals.insert((dma.to_string(), format!("CH{ch}")));
}
}
assert!(dmas_iter.next().is_none());
assert!(chans_iter.next().is_none());
} else if name == "DMAMUX1" || name == "DMAMUX1_S" || name == "DMAMUX_OVR" || name == "DMAMUX1_OVR" {
interrupt_signals.insert(("DMAMUX1".to_string(), "OVR".to_string()));
} else if name == "DMAMUX2_OVR" {
interrupt_signals.insert(("DMAMUX2".to_string(), "OVR".to_string()));
} else if flags.contains(&"DMAMUX") {
panic!("should've been handled above");
} else if flags.contains(&"EXTI") {
for signal in parts[2].split(',') {
interrupt_signals.insert(("EXTI".to_string(), signal.to_string()));
}
} else if name == "FLASH" {
interrupt_signals.insert(("FLASH".to_string(), "GLOBAL".to_string()));
} else if name == "CRS" {
interrupt_signals.insert(("RCC".to_string(), "CRS".to_string()));
} else if name == "RCC" {
interrupt_signals.insert(("RCC".to_string(), "GLOBAL".to_string()));
} else {
if parts[2].is_empty() {
continue;
}
.map(|name| (name.clone(), Vec::<String>::new()))
.collect();
let peri_names: Vec<_> = parts[2].split(',').map(ToString::to_string).collect();
let mut curr_peris = Vec::new();
if peri_names.len() == 1 {
curr_peris = peri_names.clone();
}
let name2 = {
if name == "USBWakeUp" || name == "USBWakeUp_RMP" {
"USB_WKUP"
// Parse IRQ interrupt_signals from the IRQ name.
for part in tokenize_name(name2) {
let part = {
if part == "TAMPER" {
"TAMP".to_string()
} else {
name.strip_suffix("_S").unwrap_or(&name)
part
}
};
let mut peri_signals: HashMap<_, _> = peri_names
.iter()
.map(|name| (name.clone(), Vec::<String>::new()))
.collect();
let mut curr_peris = Vec::new();
if peri_names.len() == 1 {
curr_peris = peri_names.clone();
}
// Parse IRQ interrupt_signals from the IRQ name.
for part in tokenize_name(name2) {
let part = {
if part == "TAMPER" {
"TAMP".to_string()
} else {
part
}
};
if part == "LSECSS" {
interrupt_signals.insert(("RCC".to_string(), "LSECSS".to_string()));
} else if part == "CSS" {
interrupt_signals.insert(("RCC".to_string(), "CSS".to_string()));
} else if part == "LSE" {
interrupt_signals.insert(("RCC".to_string(), "LSE".to_string()));
} else if part == "CRS" {
interrupt_signals.insert(("RCC".to_string(), "CRS".to_string()));
if part == "LSECSS" {
interrupt_signals.insert(("RCC".to_string(), "LSECSS".to_string()));
} else if part == "CSS" {
interrupt_signals.insert(("RCC".to_string(), "CSS".to_string()));
} else if part == "LSE" {
interrupt_signals.insert(("RCC".to_string(), "LSE".to_string()));
} else if part == "CRS" {
interrupt_signals.insert(("RCC".to_string(), "CRS".to_string()));
} else {
let pp = match_peris(&peri_names, &part);
trace!(" part={part}, pp={pp:?}");
if !pp.is_empty() {
curr_peris = pp;
} else {
let pp = match_peris(&peri_names, &part);
trace!(" part={part}, pp={pp:?}");
if !pp.is_empty() {
curr_peris = pp;
} else {
assert!(!curr_peris.is_empty());
for p in &curr_peris {
peri_signals.entry(p.clone()).or_default().push(part.clone());
}
assert!(!curr_peris.is_empty());
for p in &curr_peris {
peri_signals.entry(p.clone()).or_default().push(part.clone());
}
}
}
for (p, mut ss) in peri_signals.into_iter() {
let known = valid_signals(&p);
// If we have no interrupt_signals for the peri, assume it's "global" so assign it all known ones
if ss.is_empty() {
if p.starts_with("COMP") {
ss = vec!["WKUP".to_string()];
} else {
ss = known.clone();
}
}
for s in ss {
if !known.contains(&s.clone()) {
panic!("Unknown signal {s} for peri {p}, known={known:?}, parts={parts:?}");
}
interrupt_signals.insert((p.clone(), s));
}
}
}
for (p, s) in interrupt_signals {
let key = if p == "USB_DRD_FS" { "USB".to_string() } else { p };
chip_signals
.entry(key)
.or_default()
.push(stm32_data_serde::chip::core::peripheral::Interrupt {
signal: s,
interrupt: name.clone(),
});
for (p, mut ss) in peri_signals.into_iter() {
let known = valid_signals(&p);
// If we have no interrupt_signals for the peri, assume it's "global" so assign it all known ones
if ss.is_empty() {
if p.starts_with("COMP") {
ss = vec!["WKUP".to_string()];
} else {
ss = known.clone();
}
}
for s in ss {
if !known.contains(&s.clone()) {
panic!("Unknown signal {s} for peri {p}, known={known:?}, parts={parts:?}");
}
interrupt_signals.insert((p.clone(), s));
}
}
}
chip_interrupts.insert((parsed.name, parsed.version), chip_signals);
for (p, s) in interrupt_signals {
let key = if p == "USB_DRD_FS" { "USB".to_string() } else { p };
let irqs = chip_signals.entry(key).or_default();
let irq = stm32_data_serde::chip::core::peripheral::Interrupt {
signal: s,
interrupt: name.clone(),
};
if !irqs.contains(&irq) {
irqs.push(irq);
}
}
}
Ok(Self(chip_interrupts))
/*
for (p, s) in &chip_signals {
let mut m: HashMap<&str, Vec<&str>> = HashMap::new();
for i in s {
m.entry(&i.signal).or_default().push(&i.interrupt);
}
for (signal, irqs) in &mut m {
if irqs.len() != 1 {
*irqs = irqs.iter().copied().filter(|_| true).collect();
}
if irqs.len() != 1 {
panic!("dup irqs on file {:?} peri {} signal {}: {:?}", f, p, signal, irqs);
}
}
}
*/
for p in &mut core.peripherals {
if let Some(peri_irqs) = chip_signals.get(&p.name) {
use stm32_data_serde::chip::core::peripheral::Interrupt;
//filter by available, because some are conditioned on <Die>
static EQUIVALENT_IRQS: &[(&str, &[&str])] = &[
("HASH_RNG", &["RNG"]),
("USB_HP_CAN_TX", &["CAN_TX"]),
("USB_LP_CAN_RX0", &["CAN_RX0"]),
];
let mut irqs: Vec<_> = peri_irqs
.iter()
.filter_map(|i| {
if header_irqs.contains_key(&i.interrupt) {
return Some(i.clone());
}
if let Some((_, eq_irqs)) = EQUIVALENT_IRQS.iter().find(|(irq, _)| irq == &i.interrupt) {
for eq_irq in *eq_irqs {
if header_irqs.contains_key(*eq_irq) {
return Some(Interrupt {
signal: i.signal.clone(),
interrupt: eq_irq.to_string(),
});
}
}
}
None
})
.collect();
irqs.sort_by_key(|x| (x.signal.clone(), x.interrupt.clone()));
irqs.dedup_by_key(|x| (x.signal.clone(), x.interrupt.clone()));
/*
// check there are no duplicate signals.
for i in 0..(irqs.len() - 1) {
if irqs[i].signal == irqs[i + 1].signal {
panic!(
"duplicate interrupt on chip {} peripheral {} signal {}: irqs {} and {}",
chip_name,
p.name,
irqs[i].signal,
irqs[i].interrupt,
irqs[i + 1].interrupt
);
}
}
*/
p.interrupts = Some(irqs);
}
}
}
}
@ -370,3 +489,34 @@ fn valid_signals(peri: &str) -> Vec<String> {
}
vec!["GLOBAL".to_string()]
}
fn pick_nvic(chip_name: &str, core_name: &str) -> String {
// Most chips have a single NVIC, named "NVIC"
let mut res = "NVIC";
// Exception 1: Multicore: NVIC1 is the first core, NVIC2 is the second. We have to pick the right one.
if ["H745", "H747", "H755", "H757", "WL54", "WL55"].contains(&&chip_name[5..9]) {
if core_name == "cm7" {
res = "NVIC1";
} else {
res = "NVIC2"
}
}
if &chip_name[5..8] == "WL5" {
if core_name == "cm4" {
res = "NVIC1";
} else {
res = "NVIC2"
}
}
// Exception 2: TrustZone: NVIC1 is Secure mode, NVIC2 is NonSecure mode. For now, we pick the NonSecure one.
if ["L5", "U5"].contains(&&chip_name[5..7]) {
res = "NVIC2"
}
if ["H56", "H57", "WBA"].contains(&&chip_name[5..8]) {
res = "NVIC2"
}
res.to_string()
}