Skip to content

Commit ead1f51

Browse files
bors[bot]rcls
andauthored
Merge #534
534: Emit more Cluster arrays as arrays instead of lists of elements. r=burrbull a=rcls (This obsoletes #531) Some Cluster arrays cannot be emitted as Rust (built-in) arrays due to padding between elements. Add a helper zero-sized type `ArrayProxy` that overloads `[]` to do pointer-arithmetic internally. I believe, although I'm not 100% confident, that I'm not breaking Rust safety, as follows: There is no way for (safe) code to create an `ArrayProxy`. Code that produces a (reference to) an `ArrayProxy` has to use `unsafe` somewhere. See rcls/svd2rust-example@b8645fe for any example of the difference this makes (or does not make): - STM32L051 and LPC408x get no change. - Cypress PSOC gets a significant difference. This uses const generics, so the new functionality requires the `--const_generic` command line optoin. Co-authored-by: Ralph Loader <[email protected]>
2 parents e0860b7 + af4cc76 commit ead1f51

File tree

4 files changed

+100
-22
lines changed

4 files changed

+100
-22
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99

1010
- Support for device.x generation for riscv targets and `__EXTERNAL_INTERRUPTS` vector table
1111
- Re-export base's module for derived peripherals
12+
- More Cluster arrays are now emitted as an array rather than a list of
13+
elements. An `ArrayProxy` wrapper is used when a Rust built-in array does not
14+
match the cluster layout. Requires the `--const_generic` command line option.
1215

1316
## [v0.19.0] - 2021-05-26
1417

src/generate/array_proxy.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
2+
/// Access an array of `COUNT` items of type `T` with the items `STRIDE` bytes
3+
/// apart. This is a zero-sized-type. No objects of this type are ever
4+
/// actually created, it is only a convenience for wrapping pointer arithmetic.
5+
///
6+
/// There is no safe way to produce items of this type. Unsafe code can produce
7+
/// references by pointer casting. It is up to the unsafe code doing that, to
8+
/// ensure that the memory really is backed by appropriate content.
9+
///
10+
/// Typically, this is used for accessing hardware registers.
11+
pub struct ArrayProxy<T, const COUNT: usize, const STRIDE: usize> {
12+
/// As well as providing a PhantomData, this field is non-public, and
13+
/// therefore ensures that code outside of this module can never create
14+
/// an ArrayProxy.
15+
_array: marker::PhantomData<T>
16+
}
17+
18+
impl<T, const C: usize, const S: usize> ArrayProxy<T, C, S> {
19+
/// Get a reference from an [ArrayProxy] with no bounds checking.
20+
pub unsafe fn get_ref(&self, index: usize) -> &T {
21+
let base = self as *const Self as usize;
22+
let address = base + S * index;
23+
&*(address as *const T)
24+
}
25+
/// Get a reference from an [ArrayProxy], or return `None` if the index
26+
/// is out of bounds.
27+
pub fn get(&self, index: usize) -> Option<&T> {
28+
if index < C {
29+
Some(unsafe { self.get_ref(index) })
30+
}
31+
else {
32+
None
33+
}
34+
}
35+
/// Return the number of items.
36+
pub fn len(&self) -> usize { C }
37+
}
38+
39+
impl<T, const C: usize, const S: usize> core::ops::Index<usize> for ArrayProxy<T, C, S> {
40+
type Output = T;
41+
fn index(&self, index: usize) -> &T {
42+
// Do a real array dereference for the bounds check.
43+
[(); C][index];
44+
unsafe { self.get_ref(index) }
45+
}
46+
}

src/generate/device.rs

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result<Toke
146146
std::str::from_utf8(include_bytes!("generic_msp430_atomic.rs"))?;
147147
writeln!(file, "\n{}", msp430_atomic_file)?;
148148
}
149+
if config.const_generic {
150+
let array_proxy = std::str::from_utf8(include_bytes!("array_proxy.rs"))?;
151+
writeln!(file, "{}", array_proxy)?;
152+
}
149153

150154
if !config.make_mod {
151155
out.extend(quote! {
@@ -156,32 +160,27 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result<Toke
156160
});
157161
}
158162
} else {
159-
let tokens = syn::parse_file(generic_file)?.into_token_stream();
160-
163+
let mut tokens = syn::parse_file(generic_file)?.into_token_stream();
161164
if config.target == Target::Msp430 && config.nightly {
162165
let msp430_atomic_file =
163166
std::str::from_utf8(include_bytes!("generic_msp430_atomic.rs"))?;
164167
let generic_msp430_atomic = syn::parse_file(msp430_atomic_file)?.into_token_stream();
165-
out.extend(quote! {
166-
#[allow(unused_imports)]
167-
use generic::*;
168-
///Common register and bit access and modify traits
169-
pub mod generic {
170-
#tokens
171-
172-
#generic_msp430_atomic
173-
}
174-
});
175-
} else {
176-
out.extend(quote! {
177-
#[allow(unused_imports)]
178-
use generic::*;
179-
///Common register and bit access and modify traits
180-
pub mod generic {
181-
#tokens
182-
}
183-
});
168+
tokens.extend(generic_msp430_atomic);
169+
}
170+
if config.const_generic {
171+
let array_proxy = std::str::from_utf8(include_bytes!("array_proxy.rs"))?;
172+
let generic_array_proxy = syn::parse_file(array_proxy)?.into_token_stream();
173+
tokens.extend(generic_array_proxy);
184174
}
175+
176+
out.extend(quote! {
177+
#[allow(unused_imports)]
178+
use generic::*;
179+
///Common register and bit access and modify traits
180+
pub mod generic {
181+
#tokens
182+
}
183+
});
185184
}
186185

187186
out.extend(interrupt::render(config.target, &d.peripherals, device_x)?);

src/generate/peripheral.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use std::borrow::Cow;
22
use std::cmp::Ordering;
33
use std::collections::HashMap;
44

5-
use crate::svd::{Cluster, ClusterInfo, Peripheral, Register, RegisterCluster, RegisterProperties};
5+
use crate::svd::{
6+
Cluster, ClusterInfo, DimElement, Peripheral, Register, RegisterCluster, RegisterProperties,
7+
};
68
use log::warn;
79
use proc_macro2::TokenStream;
810
use proc_macro2::{Ident, Punct, Spacing, Span};
@@ -701,6 +703,10 @@ fn expand_cluster(
701703
offset: info.address_offset,
702704
size: cluster_size * array_info.dim,
703705
});
706+
} else if sequential_indexes && config.const_generic {
707+
// Include a ZST ArrayProxy giving indexed access to the
708+
// elements.
709+
cluster_expanded.push(array_proxy(info, array_info, name)?);
704710
} else {
705711
for (field_num, field) in expand_svd_cluster(cluster, name)?.iter().enumerate() {
706712
cluster_expanded.push(RegisterBlockField {
@@ -901,6 +907,30 @@ fn convert_svd_register(
901907
})
902908
}
903909

910+
/// Return an syn::Type for an ArrayProxy.
911+
fn array_proxy(
912+
info: &ClusterInfo,
913+
array_info: &DimElement,
914+
name: Option<&str>,
915+
) -> Result<RegisterBlockField, syn::Error> {
916+
let ty_name = util::replace_suffix(&info.name, "");
917+
let tys = name_to_ty_str(&ty_name, name);
918+
919+
let ap_path = parse_str::<syn::TypePath>(&format!(
920+
"crate::ArrayProxy<{}, {}, {}>",
921+
tys,
922+
array_info.dim,
923+
util::hex(array_info.dim_increment as u64)
924+
))?;
925+
926+
Ok(RegisterBlockField {
927+
field: new_syn_field(&ty_name.to_sanitized_snake_case(), ap_path.into()),
928+
description: info.description.as_ref().unwrap_or(&info.name).into(),
929+
offset: info.address_offset,
930+
size: 0,
931+
})
932+
}
933+
904934
/// Takes a svd::Cluster which may contain a register array, and turn in into
905935
/// a list of syn::Field where the register arrays have been expanded.
906936
fn expand_svd_cluster(

0 commit comments

Comments
 (0)