Skip to content

Commit 8c5d22f

Browse files
committed
SVD Loader: Support subregion register address blocks
Previously we assumed a well-formed SVD file to only address its register block with a single definition, this is unfortunately not always the case and to handle it we must attempt to merge blocks, more work on this needs to be done later to support multi-register address blocks.
1 parent dc8015f commit 8c5d22f

File tree

1 file changed

+71
-47
lines changed

1 file changed

+71
-47
lines changed

plugins/svd/src/mapper.rs

Lines changed: 71 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ pub fn byte_width(bit_width: u32) -> u32 {
3232

3333
#[derive(Clone, Debug)]
3434
pub struct AddressBlockMemoryInfo {
35-
pub name: String,
3635
pub segment: SegmentBuilder,
3736
pub segment_flags: SegmentFlags,
3837
pub section: SectionBuilder,
@@ -96,9 +95,43 @@ impl DeviceMapper {
9695

9796
// TODO: Add address blocks from derived peripherals?
9897
pub fn map_peripheral_to_view(&self, view: &BinaryView, peripheral: &PeripheralInfo) {
98+
// Get the size to extend the current block by.
99+
let extend_block_size = |current_block: &AddressBlock, new_block: &AddressBlock| {
100+
(new_block.offset + new_block.size - current_block.offset).max(current_block.size)
101+
};
102+
103+
let merge_blocks = |mut coalesced_blocks: Vec<AddressBlock>,
104+
new_block: AddressBlock|
105+
-> Vec<AddressBlock> {
106+
if let Some(current_block) = coalesced_blocks.last_mut() {
107+
// Check if the new block can be merged with the last block.
108+
// TODO: We don't account for the offset between the blocks
109+
// TODO: Because we dont that means a register block 0x1000 away from another register block
110+
// TODO: will still be merged, which is undesirable considering that SVD address blocks
111+
// TODO: are suppose to be distinct memory regions!
112+
if current_block.usage == new_block.usage {
113+
current_block.size = extend_block_size(current_block, &new_block);
114+
return coalesced_blocks;
115+
}
116+
}
117+
// Push as a new block if not mergeable.
118+
coalesced_blocks.push(new_block);
119+
coalesced_blocks
120+
};
121+
99122
if let Some(address_blocks) = &peripheral.address_block {
100-
for address_block in address_blocks {
101-
self.map_peripheral_block_to_view(view, peripheral, address_block);
123+
// Because some SVD authors decided to create address blocks for sub-regions
124+
// we must first coalesce all contiguous register address blocks.
125+
let mut sorted_blocks = address_blocks.clone();
126+
sorted_blocks.sort_by_key(|block| block.offset);
127+
let merged_blocks: Vec<AddressBlock> =
128+
sorted_blocks.into_iter().fold(Vec::new(), merge_blocks);
129+
// Update the peripheral so downstream usage sees only merged blocks.
130+
let mut updated_peripheral = peripheral.clone();
131+
updated_peripheral.address_block = Some(merged_blocks.clone());
132+
133+
for address_block in merged_blocks {
134+
self.map_peripheral_block_to_view(view, &updated_peripheral, &address_block);
102135
}
103136
}
104137
}
@@ -115,12 +148,25 @@ impl DeviceMapper {
115148
block_addr,
116149
peripheral.name
117150
);
118-
let memory_info = self.peripheral_block_memory_info(peripheral, address_block);
151+
152+
// We don't postfix the block offset in case we only have a single peripheral address block
153+
// as it is unnecessary to talk about a unique block in that case.
154+
let periph_block_len = peripheral.address_block.as_ref().unwrap().len();
155+
let block_name = if address_block.offset == 0 || periph_block_len == 1 {
156+
// Block name: "PERIPH"
157+
peripheral.name.to_owned()
158+
} else {
159+
// Block name: "PERIPH_0x40"
160+
format!("{}_0x{:x}", peripheral.name, address_block.offset)
161+
};
162+
163+
let memory_info =
164+
self.peripheral_block_memory_info(peripheral, address_block, block_name.clone());
119165

120166
// Add the block segment, section and backing memory.
121167
let data_memory = DataBuffer::new(&vec![0; address_block.size as usize]).unwrap();
122168
let added_memory = view.memory_map().add_data_memory_region(
123-
&memory_info.name,
169+
&block_name,
124170
block_addr,
125171
&data_memory,
126172
Some(memory_info.segment_flags),
@@ -131,7 +177,7 @@ impl DeviceMapper {
131177
if !added_memory {
132178
log::error!(
133179
"Failed to add memory for peripheral block! {} @ 0x{:x}",
134-
memory_info.name,
180+
block_name,
135181
block_addr
136182
);
137183
}
@@ -146,11 +192,11 @@ impl DeviceMapper {
146192
view.set_comment_at(block_addr, periph_desc);
147193
}
148194
// Add register descriptions
149-
self.add_comments_for_registers(view, peripheral, address_block);
195+
self.add_comments_for_registers(view, peripheral);
150196
}
151197

152198
// Registers will get the peripheral type.
153-
let peripheral_ty = self.peripheral_type(peripheral);
199+
let peripheral_ty = self.peripheral_type(peripheral, address_block);
154200
let peripheral_ty_id = format!("SVD:{}", peripheral.name);
155201
let id = view.define_auto_type_with_id(
156202
&peripheral.name,
@@ -186,19 +232,12 @@ impl DeviceMapper {
186232
}
187233
}
188234

189-
pub fn add_comments_for_registers(
190-
&self,
191-
view: &BinaryView,
192-
peripheral: &PeripheralInfo,
193-
address_block: &AddressBlock,
194-
) {
195-
let block_addr = peripheral.base_address + address_block.offset as u64;
235+
pub fn add_comments_for_registers(&self, view: &BinaryView, peripheral: &PeripheralInfo) {
196236
// Adding comments will add a bunch of undo actions.
197237
let undo_id = view.file().begin_undo_actions(true);
198238
for register in peripheral.all_registers() {
199-
// TODO: The register offset is the enclosing element.
200-
// TODO: We need to add a recursive function that keeps track of the offset.
201-
let register_addr = block_addr + register.address_offset as u64;
239+
// Turns out the "enclosing element" seems to always be the peripheral base address?
240+
let register_addr = peripheral.base_address + register.address_offset as u64;
202241
if let Some(description) = &register.description {
203242
view.set_comment_at(register_addr, description);
204243
}
@@ -253,16 +292,10 @@ impl DeviceMapper {
253292
&self,
254293
peripheral: &PeripheralInfo,
255294
address_block: &AddressBlock,
295+
block_name: String,
256296
) -> AddressBlockMemoryInfo {
257297
let block_addr = peripheral.base_address + address_block.offset as u64;
258298
let block_range = block_addr..(block_addr + address_block.size as u64);
259-
let block_name = if address_block.offset == 0 {
260-
// Block name: "PERIPH"
261-
peripheral.name.to_owned()
262-
} else {
263-
// Block name: "PERIPH_0x40"
264-
format!("{}_0x{:x}", peripheral.name, address_block.offset)
265-
};
266299

267300
let block_access = peripheral.default_register_properties.access;
268301
let semantics = match block_access {
@@ -292,7 +325,7 @@ impl DeviceMapper {
292325
}
293326
};
294327

295-
let section = SectionBuilder::new(block_name.clone(), block_range.clone())
328+
let section = SectionBuilder::new(block_name, block_range.clone())
296329
.section_type(section_type_str)
297330
.semantics(semantics);
298331
let segment_flags = SegmentFlags::new()
@@ -304,7 +337,6 @@ impl DeviceMapper {
304337
let segment = SegmentBuilder::new(block_range).flags(segment_flags);
305338

306339
AddressBlockMemoryInfo {
307-
name: block_name,
308340
segment,
309341
segment_flags,
310342
section,
@@ -314,7 +346,11 @@ impl DeviceMapper {
314346
// TODO: In the future we might need to have partial types for each [`AddressBlock`]
315347
// TODO: Support using header name, this requires we define the peripheral type id as the real peripheral name.
316348
// TODO: cont. the reason is so that we can resolve the derived peripheral.
317-
pub fn peripheral_type(&self, peripheral: &PeripheralInfo) -> Ref<Type> {
349+
pub fn peripheral_type(
350+
&self,
351+
peripheral: &PeripheralInfo,
352+
address_block: &AddressBlock,
353+
) -> Ref<Type> {
318354
let mut peripheral_struct = StructureBuilder::new();
319355

320356
if let Some(derived_periph_name) = &peripheral.derived_from {
@@ -327,30 +363,19 @@ impl DeviceMapper {
327363
peripheral_struct.base_structures(&[base_struct]);
328364
}
329365

366+
// Take the address block size and use it as the structure width.
330367
// TODO: Support non-contiguous register address blocks (i.e. partial types).
331-
if let Some(address_blocks) = &peripheral.address_block {
332-
// If we have more than one address block with registers we likely have an incorrect type.
333-
let register_address_blocks: Vec<_> = address_blocks
334-
.iter()
335-
.filter(|a| a.usage == AddressBlockUsage::Registers)
336-
.collect();
337-
if register_address_blocks.len() > 1 {
338-
log::warn!(
339-
"Peripheral {} has more than one register address block. The type likely is incorrect.",
340-
peripheral.name
341-
);
342-
} else if register_address_blocks.len() == 1 {
343-
// Take the address block size and use it as the structure width.
344-
let register_address_block = register_address_blocks[0];
345-
peripheral_struct.width(register_address_block.size as u64);
346-
}
347-
}
368+
peripheral_struct.width(address_block.size as u64);
348369

349370
if let Some(register_clusters) = &peripheral.registers {
350371
for register_cluster in register_clusters {
351372
match register_cluster {
352373
RegisterCluster::Register(register) => {
353-
let register_member = self.register_member(register);
374+
let mut register_member = self.register_member(register);
375+
// TODO: If we want registers to be relative to the peripheral than we must
376+
// TODO: assert that we create the peripheral type at offset 0 from the base address.
377+
// Make the register member relative to the address block, not the peripheral.
378+
register_member.offset -= address_block.offset as u64;
354379
let overwrite = false; // TODO: Handle overwrites?
355380
peripheral_struct.insert_member(register_member, overwrite);
356381
}
@@ -367,7 +392,6 @@ impl DeviceMapper {
367392
pub fn register_member(&self, register: &Register) -> StructureMember {
368393
let register_ty = self.register_type(register);
369394
let conf_register_ty = Conf::new(register_ty, MAX_CONFIDENCE);
370-
// TODO: Offset in peripheral
371395
StructureMember::new(
372396
conf_register_ty,
373397
register.name.to_owned(),

0 commit comments

Comments
 (0)