Skip to content

Commit 9c75302

Browse files
bors[bot]wez
andcommitted
Merge #318
318: No unions3 r=therealprof a=burrbull Implemented by @wez this PR replace untagged unions with functions for access to alternate registers. Closes #230, #218, stm32-rs/stm32-rs#149 . cc @therealprof Co-authored-by: Wez Furlong <wez@wezfurlong.org>
2 parents 0bc1480 + 36a661e commit 9c75302

File tree

2 files changed

+67
-158
lines changed

2 files changed

+67
-158
lines changed

src/generate/device.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,6 @@ pub fn render(
4848
#![no_std]
4949
});
5050

51-
if nightly {
52-
out.push(quote! {
53-
#![feature(untagged_unions)]
54-
});
55-
}
56-
5751
match target {
5852
Target::CortexM => {
5953
out.push(quote! {

src/generate/peripheral.rs

Lines changed: 67 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -213,24 +213,9 @@ impl Region {
213213
self.shortest_ident()
214214
}
215215
}
216-
/// Return a description of this region
217-
fn description(&self) -> String {
218-
let mut result = String::new();
219-
for f in &self.fields {
220-
// In the Atmel SVDs the union variants all tend to
221-
// have the same description. Rather than emitting
222-
// the same text three times over, only join in the
223-
// text from the other variants if it is different.
224-
// This isn't a foolproof way of emitting the most
225-
// reasonable short description, but it's good enough.
226-
if f.description != result {
227-
if !result.is_empty() {
228-
result.push(' ');
229-
}
230-
result.push_str(&f.description);
231-
}
232-
}
233-
result
216+
217+
fn is_union(&self) -> bool {
218+
self.fields.len() > 1
234219
}
235220
}
236221

@@ -318,10 +303,6 @@ impl FieldRegions {
318303
Ok(())
319304
}
320305

321-
pub fn is_union(&self) -> bool {
322-
self.regions.len() == 1 && self.regions[0].fields.len() > 1
323-
}
324-
325306
/// Resolves type name conflicts
326307
pub fn resolve_idents(&mut self) -> Result<()> {
327308
let idents: Vec<_> = {
@@ -358,86 +339,11 @@ fn register_or_cluster_block(
358339
ercs: &[RegisterCluster],
359340
defs: &Defaults,
360341
name: Option<&str>,
361-
nightly: bool,
362-
) -> Result<Tokens> {
363-
if nightly {
364-
register_or_cluster_block_nightly(ercs, defs, name)
365-
} else {
366-
register_or_cluster_block_stable(ercs, defs, name)
367-
}
368-
}
369-
370-
fn register_or_cluster_block_stable(
371-
ercs: &[RegisterCluster],
372-
defs: &Defaults,
373-
name: Option<&str>,
342+
_nightly: bool,
374343
) -> Result<Tokens> {
375344
let mut fields = Tokens::new();
376-
// enumeration of reserved fields
377-
let mut i = 0;
378-
// offset from the base address, in bytes
379-
let mut offset = 0;
380-
381-
let ercs_expanded = expand(ercs, defs, name)?;
382-
383-
for reg_block_field in ercs_expanded {
384-
let pad = if let Some(pad) = reg_block_field.offset.checked_sub(offset) {
385-
pad
386-
} else {
387-
warn!(
388-
"{:?} overlaps with another register block at offset {}. \
389-
Ignoring.",
390-
reg_block_field.field.ident, reg_block_field.offset
391-
);
392-
continue;
393-
};
394-
395-
if pad != 0 {
396-
let name = Ident::new(format!("_reserved{}", i));
397-
let pad = pad as usize;
398-
fields.append(quote! {
399-
#name : [u8; #pad],
400-
});
401-
i += 1;
402-
}
403-
404-
let comment = &format!(
405-
"0x{:02x} - {}",
406-
reg_block_field.offset,
407-
util::escape_brackets(util::respace(&reg_block_field.description).as_ref()),
408-
)[..];
409-
410-
fields.append(quote! {
411-
#[doc = #comment]
412-
});
413-
414-
reg_block_field.field.to_tokens(&mut fields);
415-
Ident::new(",").to_tokens(&mut fields);
416-
417-
offset = reg_block_field.offset + reg_block_field.size / BITS_PER_BYTE;
418-
}
419-
420-
let name = Ident::new(match name {
421-
Some(name) => name.to_sanitized_upper_case(),
422-
None => "RegisterBlock".into(),
423-
});
424-
425-
Ok(quote! {
426-
/// Register block
427-
#[repr(C)]
428-
pub struct #name {
429-
#fields
430-
}
431-
})
432-
}
433-
434-
fn register_or_cluster_block_nightly(
435-
ercs: &[RegisterCluster],
436-
defs: &Defaults,
437-
name: Option<&str>,
438-
) -> Result<Tokens> {
439-
let mut fields = Tokens::new();
440-
let mut helper_types = Tokens::new();
345+
let mut accessors = Tokens::new();
346+
let mut have_accessors = false;
441347

442348
let ercs_expanded = expand(ercs, defs, name)?;
443349

@@ -448,10 +354,9 @@ fn register_or_cluster_block_nightly(
448354
regions.add(reg_block_field)?;
449355
}
450356

451-
let block_is_union = regions.is_union();
452357
// We need to compute the idents of each register/union block first to make sure no conflicts exists.
453358
regions.resolve_idents()?;
454-
// The end of the region from the prior iteration of the loop
359+
// The end of the region for which we previously emitted a field into `fields`
455360
let mut last_end = 0;
456361

457362
for (i, region) in regions.regions.iter().enumerate() {
@@ -465,86 +370,96 @@ fn register_or_cluster_block_nightly(
465370
});
466371
}
467372

468-
last_end = region.end;
469-
470373
let mut region_fields = Tokens::new();
374+
let is_region_a_union = region.is_union();
471375

472376
for reg_block_field in &region.fields {
473-
if reg_block_field.offset != region.offset {
474-
// TODO: need to emit padding for this case.
475-
// Happens for freescale_mkl43z4
476-
warn!(
477-
"field {:?} has different offset {} than its union container {}",
478-
reg_block_field.field.ident, reg_block_field.offset, region.offset
479-
);
480-
}
481377
let comment = &format!(
482378
"0x{:02x} - {}",
483379
reg_block_field.offset,
484380
util::escape_brackets(util::respace(&reg_block_field.description).as_ref()),
485381
)[..];
486382

487-
region_fields.append(quote! {
488-
#[doc = #comment]
489-
});
490-
491-
reg_block_field.field.to_tokens(&mut region_fields);
492-
Ident::new(",").to_tokens(&mut region_fields);
493-
}
494-
495-
if region.fields.len() > 1 && !block_is_union {
496-
let (type_name, name) = match region.ident.clone() {
497-
Some(prefix) => (
498-
Ident::new(format!("{}_UNION", prefix.to_sanitized_upper_case())),
499-
Ident::new(prefix),
500-
),
501-
// If we can't find a name, fall back to the region index as a
502-
// unique-within-this-block identifier counter.
503-
None => {
504-
let ident = Ident::new(format!("U{}", i));
505-
(ident.clone(), ident)
506-
}
507-
};
383+
if is_region_a_union {
384+
let name = &reg_block_field.field.ident;
385+
let mut_name = Ident::new(format!("{}_mut", name.as_ref().unwrap()));
386+
let ty = &reg_block_field.field.ty;
387+
let offset = reg_block_field.offset as usize;
388+
have_accessors = true;
389+
accessors.append(quote! {
390+
#[doc = #comment]
391+
#[inline(always)]
392+
pub fn #name(&self) -> &#ty {
393+
unsafe {
394+
&*(((self as *const Self) as *const u8).add(#offset) as *const #ty)
395+
}
396+
}
508397

509-
let description = region.description();
398+
#[doc = #comment]
399+
#[inline(always)]
400+
pub fn #mut_name(&self) -> &mut #ty {
401+
unsafe {
402+
&mut *(((self as *const Self) as *mut u8).add(#offset) as *mut #ty)
403+
}
404+
}
405+
});
406+
} else {
407+
region_fields.append(quote! {
408+
#[doc = #comment]
409+
});
510410

511-
helper_types.append(quote! {
512-
#[doc = #description]
513-
#[repr(C)]
514-
pub union #type_name {
515-
#region_fields
516-
}
517-
});
411+
reg_block_field.field.to_tokens(&mut region_fields);
412+
Ident::new(",").to_tokens(&mut region_fields);
413+
}
414+
}
518415

519-
fields.append(quote! {
520-
#[doc = #description]
521-
pub #name: #type_name
522-
});
523-
Ident::new(",").to_tokens(&mut fields);
524-
} else {
416+
if !is_region_a_union {
525417
fields.append(&region_fields);
418+
} else {
419+
// Emit padding for the items that we're not emitting
420+
// as fields so that subsequent fields have the correct
421+
// alignment in the struct. We could omit this and just
422+
// not updated `last_end`, so that the padding check in
423+
// the outer loop kicks in, but it is nice to be able to
424+
// see that the padding is attributed to a union when
425+
// visually inspecting the alignment in the struct.
426+
//
427+
// Include the computed ident for the union in the padding
428+
// name, along with the region number, falling back to
429+
// the offset and end in case we couldn't figure out a
430+
// nice identifier.
431+
let name = Ident::new(format!("_reserved_{}_{}", i, region.compute_ident().unwrap_or_else(|| format!("{}_{}", region.offset, region.end))));
432+
let pad = (region.end - region.offset) as usize;
433+
fields.append(quote! {
434+
#name: [u8; #pad],
435+
})
526436
}
437+
last_end = region.end;
527438
}
528439

529440
let name = Ident::new(match name {
530441
Some(name) => name.to_sanitized_upper_case(),
531442
None => "RegisterBlock".into(),
532443
});
533444

534-
let block_type = if block_is_union {
535-
Ident::new("union")
445+
let accessors = if have_accessors {
446+
quote! {
447+
impl #name {
448+
#accessors
449+
}
450+
}
536451
} else {
537-
Ident::new("struct")
452+
quote! {}
538453
};
539454

540455
Ok(quote! {
541456
/// Register block
542457
#[repr(C)]
543-
pub #block_type #name {
458+
pub struct #name {
544459
#fields
545460
}
546461

547-
#helper_types
462+
#accessors
548463
})
549464
}
550465

0 commit comments

Comments
 (0)