Skip to content

Commit cadbbef

Browse files
committed
Add helper to track overlapping fields
I want to use this information to emit unions. This commit deals solely with recognizing and tracking the overlaps.
1 parent d4c2c9a commit cadbbef

File tree

1 file changed

+121
-25
lines changed

1 file changed

+121
-25
lines changed

src/generate/peripheral.rs

Lines changed: 121 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -104,48 +104,146 @@ pub fn render(
104104
Ok(out)
105105
}
106106

107+
#[derive(Clone, Debug)]
107108
struct RegisterBlockField {
108109
field: syn::Field,
109110
description: String,
110111
offset: u32,
111112
size: u32,
112113
}
113114

115+
#[derive(Clone, Debug)]
116+
struct Region {
117+
fields: Vec<RegisterBlockField>,
118+
offset: u32,
119+
end: u32,
120+
}
121+
122+
/// FieldRegions keeps track of overlapping field regions,
123+
/// merging fields into appropriate regions as we process them.
124+
/// This allows us to reason about when to create a union
125+
/// rather than a struct.
126+
#[derive(Default, Debug)]
127+
struct FieldRegions {
128+
/// The set of regions we know about. This is maintained
129+
/// in sorted order, keyed by Region::offset.
130+
regions: Vec<Region>,
131+
}
132+
133+
impl FieldRegions {
134+
/// Track a field. If the field overlaps with 1 or more existing
135+
/// entries, they will be merged together.
136+
fn add(&mut self, field: &RegisterBlockField) {
137+
138+
// When merging, this holds the indices in self.regions
139+
// that the input `field` will be merging with.
140+
let mut indices = Vec::new();
141+
142+
let field_start = field.offset;
143+
let field_end = field_start + field.size / BITS_PER_BYTE;
144+
145+
// The region that we're going to insert
146+
let mut new_region = Region {
147+
fields: vec![field.clone()],
148+
offset: field.offset,
149+
end: field.offset + field.size / BITS_PER_BYTE
150+
};
151+
152+
// Locate existing region(s) that we intersect with and
153+
// fold them into the new region we're creating. There
154+
// may be multiple regions that we intersect with, so
155+
// we keep looping to find them all.
156+
for (idx, mut f) in self.regions.iter_mut().enumerate() {
157+
let f_start = f.offset;
158+
let f_end = f.end;
159+
160+
// Compute intersection range
161+
let begin = f_start.max(field_start);
162+
let end = f_end.min(field_end);
163+
164+
if end > begin {
165+
// We're going to remove this element and fold it
166+
// into our new region
167+
indices.push(idx);
168+
169+
// Expand the existing entry
170+
new_region.offset = new_region.offset.min(f_start);
171+
new_region.end = new_region.end.max(f_end);
172+
173+
// And merge in the fields
174+
new_region.fields.append(&mut f.fields);
175+
}
176+
}
177+
178+
// Now remove the entries that we collapsed together.
179+
// We do this in reverse order to ensure that the indices
180+
// are stable in the face of removal.
181+
for idx in indices.iter().rev() {
182+
self.regions.remove(*idx);
183+
}
184+
185+
new_region.fields.sort_by_key(|f| f.offset);
186+
187+
// maintain the regions ordered by starting offset
188+
let idx = self.regions.binary_search_by_key(&new_region.offset, |r| r.offset);
189+
match idx {
190+
Ok(idx) => {
191+
panic!("we shouldn't exist in the vec, but are at idx {} {:#?}\n{:#?}",
192+
idx, new_region, self.regions);
193+
}
194+
Err(idx) => self.regions.insert(idx, new_region)
195+
}
196+
}
197+
}
198+
114199
fn register_or_cluster_block(
115200
ercs: &[Either<Register, Cluster>],
116201
defs: &Defaults,
117202
name: Option<&str>,
118203
) -> Result<Tokens> {
119204
let mut fields = Tokens::new();
120-
// enumeration of reserved fields
121-
let mut i = 0;
122-
// offset from the base address, in bytes
123-
let mut offset = 0;
124205

125206
let ercs_expanded = expand(ercs, defs, name)?;
126207

127-
for reg_block_field in ercs_expanded {
128-
let pad = if let Some(pad) = reg_block_field.offset.checked_sub(offset) {
129-
pad
130-
} else {
131-
eprintln!(
132-
"WARNING {:?} overlaps with another register block at offset {}. \
133-
Ignoring.",
134-
reg_block_field.field.ident,
135-
reg_block_field.offset
136-
);
137-
continue;
138-
};
208+
// Locate conflicting regions; we'll need to use unions to represent them.
209+
let mut regions = FieldRegions::default();
139210

140-
if pad != 0 {
141-
let name = Ident::new(format!("_reserved{}", i));
142-
let pad = pad as usize;
143-
fields.append(quote! {
144-
#name : [u8; #pad],
145-
});
146-
i += 1;
211+
for reg_block_field in &ercs_expanded {
212+
regions.add(reg_block_field);
213+
}
214+
215+
// The end of the region from the prior iteration of the loop
216+
let mut last_end = None;
217+
218+
for (i, region) in regions.regions.iter().enumerate() {
219+
// Check if we need padding
220+
if let Some(end) = last_end {
221+
let pad = region.offset - end;
222+
if pad != 0 {
223+
let name = Ident::new(format!("_reserved{}", i));
224+
let pad = pad as usize;
225+
fields.append(quote! {
226+
#name : [u8; #pad],
227+
});
228+
}
147229
}
148230

231+
last_end = Some(region.end);
232+
233+
if region.fields.len() > 1 {
234+
// TODO: this is where we'd emit a union container
235+
eprintln!("WARNING: overlaps for region offset={}-{}. \
236+
Using the first one of these:",
237+
region.offset, region.end-1);
238+
239+
for f in &region.fields {
240+
eprintln!(" {:?} {}-{}", f.field.ident, f.offset,
241+
(f.offset + f.size / BITS_PER_BYTE)-1);
242+
}
243+
}
244+
245+
let reg_block_field = &region.fields[0];
246+
149247
let comment = &format!(
150248
"0x{:02x} - {}",
151249
reg_block_field.offset,
@@ -158,8 +256,6 @@ fn register_or_cluster_block(
158256

159257
reg_block_field.field.to_tokens(&mut fields);
160258
Ident::new(",").to_tokens(&mut fields);
161-
162-
offset = reg_block_field.offset + reg_block_field.size / BITS_PER_BYTE;
163259
}
164260

165261
let name = Ident::new(match name {

0 commit comments

Comments
 (0)