Skip to content

Commit

Permalink
Use a custom type to represent clusters instead of an array
Browse files Browse the repository at this point in the history
Introduce a `Cluster` type that represents a cluster of register blocks,
with an `Index` impl that computes the address of the register we're
interested in, instead of computing all the register addresses up front
to store them in an array. This provides a significant improvement in
code size (see Infineon#46).

We have to do some trickery in order to support subscripting, because
the `Index` trait only supports indexes returing references. This commit
implements the solution proposed by @pellico: represent a register block
within a cluster as a reference to a zero-sized type. Any non-null,
sufficiently aligned reference to a ZST is valid, so we can forge
references to ZSTs and use the address of the reference to represent the
address of the register block. This is sound as long as we never expose
an *owned* ZST to safe code, and we mark the cluster ZST types as
`#[non_exhaustive]` so users can't construct their own.
  • Loading branch information
jnkr-ifx committed Dec 14, 2024
1 parent 82af538 commit b3ca270
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 14 deletions.
57 changes: 57 additions & 0 deletions templates/rust/common.tera
Original file line number Diff line number Diff line change
Expand Up @@ -900,3 +900,60 @@ where
START_OFFSET + (self.index * DIM_INCREMENT) as usize
}
}

/// A cluster of identical register blocks.
pub struct Cluster<T: Sized, const DIM: usize, const DIM_INCREMENT: usize> {
_t: ::core::marker::PhantomData<T>,
}

impl<T: Sized, const DIM: usize, const DIM_INCREMENT: usize> Cluster<T, DIM, DIM_INCREMENT> {
/// Returns the number of register blocks in the cluster.
pub fn len() -> usize {
DIM
}

/// Returns whether the cluster is empty (DIM == 0).
pub fn is_empty() -> bool {
DIM == 0
}

/// Returns an iterator over the elements of this cluster.
pub fn iter(&self) -> impl ::core::iter::ExactSizeIterator<Item = &T> {
(0..DIM).map(|i| &self[i])
}

/// Returns the cluster element with the specified index.
///
/// Panics if the index is out of bounds.
pub fn get(&self, index: usize) -> &T {
assert!(index < DIM);
unsafe { self.get_unchecked(index) }
}

/// Returns the cluster element with the specified index.
///
/// # Safety
///
/// `index` must be less than `DIM`.
pub unsafe fn get_unchecked(&self, index: usize) -> &T {
&*(self.as_ptr().add(index * DIM_INCREMENT) as *const _)
}

pub(crate) unsafe fn from_ptr(ptr: *mut u8) -> &'static Self {
&*(ptr as *const Self)
}

fn as_ptr(&self) -> *mut u8 {
self as *const _ as *mut _
}
}

impl<T: Sized, const DIM: usize, const DIM_INCREMENT: usize> ::core::ops::Index<usize>
for Cluster<T, DIM, DIM_INCREMENT>
{
type Output = T;

fn index(&self, index: usize) -> &T {
self.get(index)
}
}
38 changes: 24 additions & 14 deletions templates/rust/macros.tera
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ Unsupported register size
#[inline(always)]
{% if reg.dim == 1 -%}
pub const fn {{reg.name | to_func_id }}(&self) -> crate::common::Reg<{{reg_struct_name}}_SPEC, crate::common::{{reg.access}}> {
unsafe { crate::common::Reg::from_ptr(self.ptr.add({{reg.offset}}usize)) }
unsafe { crate::common::Reg::from_ptr(self.as_ptr().add({{reg.offset}}usize)) }
}
{%- else -%}
pub const fn {{reg.name | to_func_id }}(&self) -> [crate::common::Reg<{{reg_struct_name}}_SPEC, crate::common::{{reg.access}}>;{{reg.dim}}] {
unsafe { [
{%- for index in range(end=reg.dim) -%}
crate::common::Reg::from_ptr(self.ptr.add({{reg.offset | to_hex }}usize + {{index * reg.dim_increment | to_hex }}usize )),
crate::common::Reg::from_ptr(self.as_ptr().add({{reg.offset | to_hex }}usize + {{index * reg.dim_increment | to_hex }}usize )),
{% endfor -%}
] }
}
Expand Down Expand Up @@ -120,20 +120,17 @@ pub mod {{reg_mod_name}} {
{%- set mod_struct_path = cluster.struct_module_path | join(sep="::") -%}
{%- set cluster_struct_id = cluster.struct_id | to_struct_id -%}
{%- set cluster_struct_path = "crate" ~ "::" ~ mod_struct_path ~ "::" ~ cluster_struct_id -%}
{%- set underscored_cluster_struct_path = "crate" ~ "::" ~ mod_struct_path ~ "::_" ~ cluster_struct_id -%}
{%- set cluster_func = cluster.name | to_func_id -%}
#[doc = "{{cluster.description | svd_description_to_doc}}"]
#[inline(always)]
{%- if cluster.dim == 1 %}
pub fn {{cluster_func}}(self) -> {{cluster_struct_path}}{
unsafe { {{cluster_struct_path}}{ptr:self.ptr.add({{cluster.offset}}usize)} }
unsafe { {{underscored_cluster_struct_path}}::from_ptr(self.as_ptr().add({{cluster.offset}}usize)) }
}
{%- else %}
pub fn {{cluster_func}}(self) -> [{{cluster_struct_path}};{{cluster.dim}}] {
unsafe { [
{%- for index in range(end=cluster.dim) -%}
{{cluster_struct_path}}{ptr:self.ptr.add({{cluster.offset | to_hex}}usize + {{index*cluster.dim_increment | to_hex }}usize)},
{% endfor -%}
] }
pub fn {{cluster_func}}(self) -> &'static crate::common::Cluster<{{underscored_cluster_struct_path}}, {{cluster.dim}}, {{cluster.dim_increment | to_hex}}> {
unsafe { crate::common::Cluster::from_ptr(self.as_ptr().add({{cluster.offset | to_hex}}usize)) }
}
{%- endif -%}
{%- endmacro -%}
Expand All @@ -145,11 +142,24 @@ pub fn {{cluster_func}}(self) -> [{{cluster_struct_path}};{{cluster.dim}}] {
{%- set cluster_struct = cluster.struct_id | to_struct_id -%}
{%- set cluster_mod = cluster.module_id -%}
#[doc = "{{cluster.description | svd_description_to_doc}}"]
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct {{ cluster_struct }}{pub(crate) ptr: *mut u8}
unsafe impl ::core::marker::Send for {{ cluster_struct}} {}
unsafe impl ::core::marker::Sync for {{ cluster_struct }} {}
impl {{cluster_struct}} {
#[non_exhaustive]
pub struct _{{ cluster_struct }};

#[doc = "{{cluster.description | svd_description_to_doc}}"]
pub type {{ cluster_struct }} = &'static _{{ cluster_struct }};

unsafe impl ::core::marker::Sync for _{{ cluster_struct }} {}
impl _{{cluster_struct}} {
#[allow(unused)]
pub(crate) unsafe fn from_ptr(ptr: *mut u8) -> &'static Self {
&*(ptr as *mut _)
}

#[allow(unused)]
pub(crate) const fn as_ptr(&self) -> *mut u8 {
self as *const Self as *mut u8
}

{% for register_name,reg in cluster.registers -%}
{{self::register_func(types_mod=cluster_mod,reg=reg)}}
{% endfor -%}
Expand Down
5 changes: 5 additions & 0 deletions templates/rust/peri_mod.tera
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ use crate::common::sealed;
unsafe impl ::core::marker::Send for super::{{ peri_struct }} {}
unsafe impl ::core::marker::Sync for super::{{ peri_struct }} {}
impl super::{{ peri_struct }} {
#[allow(unused)]
pub(crate) const fn as_ptr(&self) -> *mut u8 {
self.ptr
}

{%- for register_name,reg in peri.registers %}
{{macros::register_func(types_mod="self",reg=reg)}}
{% endfor -%}
Expand Down

0 comments on commit b3ca270

Please sign in to comment.