Skip to content

Commit 295f9df

Browse files
committed
Refactor DMA generation and add documentation
1 parent 2e38730 commit 295f9df

File tree

1 file changed

+143
-84
lines changed

1 file changed

+143
-84
lines changed

stm32-data-gen/src/generator.rs

Lines changed: 143 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,6 @@ use crate::chips::{Chip, ChipGroup};
1212
use crate::gpio_af::parse_signal_name;
1313
use crate::normalize_peris::normalize_peri_name;
1414

15-
fn corename(d: &str) -> String {
16-
let m = regex!(r".*Cortex-M(\d+)(\+?)\s*(.*)").captures(d).unwrap();
17-
let cm = m.get(1).unwrap().as_str();
18-
let p = if m.get(2).unwrap().as_str() == "+" { "p" } else { "" };
19-
let s = if m.get(3).unwrap().as_str() == "secure" {
20-
"s"
21-
} else {
22-
""
23-
};
24-
format!("cm{cm}{p}{s}")
25-
}
26-
2715
/// Merge AF information from GPIO file into peripheral pins.
2816
///
2917
/// `core_pins` is modified in-place and updated with AF information from `af_pins`.
@@ -110,9 +98,9 @@ fn process_group(
11098
.xml
11199
.cores
112100
.iter()
113-
.map(|core_xml| {
101+
.map(|long_core_name| {
114102
process_core(
115-
core_xml,
103+
long_core_name,
116104
h,
117105
&chip_name,
118106
&group,
@@ -135,7 +123,7 @@ fn process_group(
135123

136124
#[allow(clippy::too_many_arguments)]
137125
fn process_core(
138-
core_xml: &str,
126+
long_core_name: &str,
139127
h: &header::ParsedHeader,
140128
chip_name: &str,
141129
group: &ChipGroup,
@@ -145,7 +133,7 @@ fn process_core(
145133
chip_af: Option<&HashMap<String, Vec<stm32_data_serde::chip::core::peripheral::Pin>>>,
146134
dma_channels: &dma::DmaChannels,
147135
) -> anyhow::Result<stm32_data_serde::chip::Core> {
148-
let core_name = corename(core_xml);
136+
let core_name = create_short_core_name(long_core_name);
149137
let defines = h.get_defines(&core_name);
150138

151139
let peri_kinds = create_peripheral_map(chip_name, group, defines);
@@ -206,74 +194,10 @@ fn process_core(
206194

207195
let mut peripherals: Vec<_> = peripherals.into_values().collect();
208196
peripherals.sort_by_key(|x| x.name.clone());
209-
// Collect DMA versions in the chip
210-
let mut dmas: Vec<_> = group
211-
.ips
212-
.values()
213-
.filter_map(|ip| {
214-
let version = &ip.version;
215-
let instance = &ip.instance_name;
216-
dma_channels
217-
.0
218-
.get(version)
219-
.or_else(|| dma_channels.0.get(&format!("{version}:{instance}")))
220-
.map(|dma| (ip.name.clone(), instance.clone(), dma))
221-
})
222-
.collect();
223-
dmas.sort_by_key(|(name, instance, _)| {
224-
(
225-
match name.as_str() {
226-
"DMA" => 1,
227-
"BDMA" => 2,
228-
"BDMA1" => 3,
229-
"BDMA2" => 4,
230-
"GPDMA" => 5,
231-
"HPDMA" => 6,
232-
_ => 0,
233-
},
234-
instance.clone(),
235-
)
236-
});
237-
238-
// The dma_channels[xx] is generic for multiple chips. The current chip may have less DMAs,
239-
// so we have to filter it.
240-
static DMA_CHANNEL_COUNTS: RegexMap<usize> = RegexMap::new(&[
241-
("STM32F0[37]0.*:DMA1", 5),
242-
("STM32G4[34]1.*:DMA1", 6),
243-
("STM32G4[34]1.*:DMA2", 6),
244-
]);
245-
let have_peris: HashSet<_> = peripherals.iter().map(|p| p.name.clone()).collect();
246-
let dma_channels = dmas
247-
.iter()
248-
.flat_map(|(_, _, dma)| dma.channels.clone())
249-
.filter(|ch| have_peris.contains(&ch.dma))
250-
.filter(|ch| {
251-
DMA_CHANNEL_COUNTS
252-
.get(&format!("{}:{}", chip_name, ch.dma))
253-
.map_or_else(|| true, |&v| usize::from(ch.channel) < v)
254-
})
255-
.collect::<Vec<_>>();
256-
let have_chs: HashSet<_> = dma_channels.iter().map(|ch| ch.name.clone()).collect();
257197

258-
// Process peripheral - DMA channel associations
259-
for p in &mut peripherals {
260-
let mut chs = Vec::new();
261-
for (_, _, dma) in &dmas {
262-
if let Some(peri_chs) = dma.peripherals.get(&p.name) {
263-
chs.extend(
264-
peri_chs
265-
.iter()
266-
.filter(|ch| match &ch.channel {
267-
None => true,
268-
Some(channel) => have_chs.contains(channel),
269-
})
270-
.cloned(),
271-
);
272-
}
273-
}
274-
chs.sort_by_key(|ch| (ch.channel.clone(), ch.dmamux.clone(), ch.request));
275-
p.dma_channels = chs;
276-
}
198+
let dmas = collect_dma_instances(group, dma_channels);
199+
let dma_channels = extract_relevant_dma_channels(&peripherals, &dmas, chip_name);
200+
associate_peripherals_dma_channels(&mut peripherals, dmas, &dma_channels);
277201

278202
let mut pins: Vec<_> = group
279203
.pins
@@ -354,6 +278,26 @@ fn merge_i2s_into_spi_pins(
354278
Vec::new()
355279
}
356280

281+
/// Create a short core name from a long name.
282+
///
283+
/// Parse a string like "ARM Cortex-M4" or "ARM Cortex-M7 secure" and return a
284+
/// string like "cm4" or "cm7s". The input string is expected to contain the
285+
/// string "Cortex-M" followed by a number, optionally followed by the string
286+
/// "+" or " secure".
287+
///
288+
/// FIXME: "secure" does not appear the XML files in that attribute.
289+
fn create_short_core_name(d: &str) -> String {
290+
let m = regex!(r".*Cortex-M(\d+)(\+?)\s*(.*)").captures(d).unwrap();
291+
let cm = m.get(1).unwrap().as_str();
292+
let p = if m.get(2).unwrap().as_str() == "+" { "p" } else { "" };
293+
let s = if m.get(3).unwrap().as_str() == "secure" {
294+
"s"
295+
} else {
296+
""
297+
};
298+
format!("cm{cm}{p}{s}")
299+
}
300+
357301
/// Return a HashMap of peripheral names to their kind (name:version).
358302
///
359303
/// Some peripherals have different names in the xml files and the header files. We handle these cases here by
@@ -498,7 +442,7 @@ fn create_peripheral_map(chip_name: &str, group: &ChipGroup, defines: &header::D
498442
/// It's not the full info we would want (stuff like AFIO info which comes from
499443
/// the GPIO xml), but we actually need to use it because of the F1 line
500444
/// which doesn't include non-remappable peripherals in the GPIO xml and some
501-
/// weird edge cases like STM32F030C6 (see merge_periph_pins_info).
445+
/// weird edge cases like STM32F030C6 (see [merge_periph_pins_info]).
502446
///
503447
/// The function returns a HashMap of peripheral name to Vec of Pins.
504448
/// The Pins only contain the pin and signal name, and no AF information
@@ -612,6 +556,121 @@ fn apply_family_extras(group: &ChipGroup, peripherals: &mut HashMap<String, stm3
612556
}
613557
}
614558

559+
/// Collect and sort all DMA IP instances available on the current chip.
560+
///
561+
/// Iterates over the parsed MCU IP definitions (`group.ips`), matching each IP’s `version`
562+
/// and `instance_name` against the pre-built `dma_channels` map.
563+
/// Searches first by the DMA name (e.g. "BDMA"), then by the DMA name and instance
564+
/// (e.g. "STM32H7RS_dma3_Cube:GPDMA1"). Returns a `Vec<(ip_name, instance_name, ChipDma)>`.
565+
fn collect_dma_instances<'a>(
566+
group: &ChipGroup,
567+
dma_channels: &'a dma::DmaChannels,
568+
) -> Vec<(String, String, &'a dma::ChipDma)> {
569+
// Collect DMA versions in the chip
570+
let mut dmas: Vec<_> = group
571+
.ips
572+
.values()
573+
.filter_map(|ip| {
574+
let version = &ip.version;
575+
let instance = &ip.instance_name;
576+
dma_channels
577+
.0
578+
.get(version)
579+
.or_else(|| dma_channels.0.get(&format!("{version}:{instance}")))
580+
.map(|dma| (ip.name.clone(), instance.clone(), dma))
581+
})
582+
.collect();
583+
dmas.sort_by_key(|(name, instance, _)| {
584+
(
585+
match name.as_str() {
586+
"DMA" => 1,
587+
"BDMA" => 2,
588+
"BDMA1" => 3,
589+
"BDMA2" => 4,
590+
"GPDMA" => 5,
591+
"HPDMA" => 6,
592+
_ => 0,
593+
},
594+
instance.clone(),
595+
)
596+
});
597+
dmas
598+
}
599+
600+
/// Filters and returns a list of DMA channels for the current chip.
601+
///
602+
/// It takes the DMA instances from `dmas`, which also occur in `peripherals`.
603+
/// Of these, only the first channels corresponding to the number of channels
604+
/// in this chip family are then saved.
605+
///
606+
/// E.g. on an STM32F030CC (IP version “STM32F091_dma_v1_1”), DMA2 is removed because
607+
/// only DMA1 exists; on an STM32G431CB only the first 6 channels of each DMA block
608+
/// are kept per the hardware limit.
609+
///
610+
/// It returns `Vec<DmaChannels>` with the valid DMA channel definitions that
611+
/// can actually be used on this specific device.
612+
fn extract_relevant_dma_channels(
613+
peripherals: &Vec<stm32_data_serde::chip::core::Peripheral>,
614+
dmas: &Vec<(String, String, &dma::ChipDma)>,
615+
chip_name: &str,
616+
) -> Vec<stm32_data_serde::chip::core::DmaChannels> {
617+
// The dma_channels[xx] is generic for multiple chips. The current chip may have less DMAs,
618+
// so we have to filter it.
619+
static DMA_CHANNEL_COUNTS: RegexMap<usize> = RegexMap::new(&[
620+
("STM32F0[37]0.*:DMA1", 5),
621+
("STM32G4[34]1.*:DMA1", 6),
622+
("STM32G4[34]1.*:DMA2", 6),
623+
]);
624+
let have_peris: HashSet<_> = peripherals.iter().map(|p| p.name.clone()).collect();
625+
let dma_channels = dmas
626+
.iter()
627+
.flat_map(|(_, _, dma)| dma.channels.clone())
628+
.filter(|ch| have_peris.contains(&ch.dma))
629+
.filter(|ch| {
630+
DMA_CHANNEL_COUNTS
631+
.get(&format!("{}:{}", chip_name, ch.dma))
632+
.map_or_else(|| true, |&v| usize::from(ch.channel) < v)
633+
})
634+
.collect::<Vec<_>>();
635+
dma_channels
636+
}
637+
638+
/// Associates each peripheral with its available DMA channels.
639+
///
640+
/// This is done by retrieving its mappings from `dmas`, filtering out
641+
/// channels not present in `dma_channels`, sorting them, and assigning
642+
/// the sorted list to the peripheral’s `dma_channels` field.
643+
///
644+
/// This determines which DMA channels can be used for data transfers for each peripheral.
645+
/// Modifies each `Peripheral` in-place, setting `peripheral.dma_channels`.
646+
fn associate_peripherals_dma_channels(
647+
peripherals: &mut Vec<stm32_data_serde::chip::core::Peripheral>,
648+
dmas: Vec<(String, String, &dma::ChipDma)>,
649+
dma_channels: &Vec<stm32_data_serde::chip::core::DmaChannels>,
650+
) {
651+
let have_chs: HashSet<_> = dma_channels.iter().map(|ch| ch.name.clone()).collect();
652+
653+
// Process peripheral - DMA channel associations
654+
for p in peripherals {
655+
let mut chs = Vec::new();
656+
for (_, _, dma) in &dmas {
657+
if let Some(peri_chs) = dma.peripherals.get(&p.name) {
658+
chs.extend(
659+
peri_chs
660+
.iter()
661+
.filter(|ch| match &ch.channel {
662+
None => true,
663+
Some(channel) => have_chs.contains(channel),
664+
})
665+
.cloned(),
666+
);
667+
}
668+
}
669+
chs.sort_by_key(|ch| (ch.channel.clone(), ch.dmamux.clone(), ch.request));
670+
p.dma_channels = chs;
671+
}
672+
}
673+
615674
fn process_chip(
616675
chips: &HashMap<String, Chip>,
617676
chip_name: &str,

0 commit comments

Comments
 (0)