rust-embedded / svd2rust

Generate Rust register maps (`struct`s) from SVD files
Apache License 2.0
709 stars 151 forks source link

add code generation support of peripheral arrays #592

Closed duskmoon314 closed 2 years ago

duskmoon314 commented 2 years ago

Brief Intro

close issue: #492

Design

Notice: This is my first try of implement this feature. Please provide suggestions for this PR to better implement this feature.

This PR aims to achieve what issue #492 is asking for. This PR implements a basic try of generating codes depending on the const_generic flag.

Let's say we have an svd file with descriptions of peripherals like this:

<peripheral>
    <dim>2</dim>
    <dimIncrement>0x100</dimIncrement>
    <name>PTEST[%s]</name>
    <groupName>PGroup</groupName>
    <baseAddress>0x20000000</baseAddress>
    <addressBlock>
        <offset>0</offset>
        <size>0x100</size>
        <usage>registers</usage>
    </addressBlock>
    <registers>
        ...
    </registers>
</peripheral>

If the const_generic is false, there is no ArrayProxy. So I generate separate structs and only one mod:

pub struct PTEST0 {
    _marker: PhantomData<*const ()>,
}
...
impl PTEST0 {
    #[doc = r"Pointer to the register block"]
    pub const PTR: *const ptest::RegisterBlock = 0x2000_0000 as *const _;
    ...
}
impl Deref for PTEST0 {
    type Target = ptest::RegisterBlock;
    #[inline(always)]
    fn deref(&self) -> &Self::Target {
        unsafe { &*Self::PTR }
    }
}

pub struct PTEST1 {
    _marker: PhantomData<*const ()>,
}
...
impl PTEST1 {
    #[doc = r"Pointer to the register block"]
    pub const PTR: *const ptest::RegisterBlock = 0x2000_0100 as *const _;
    ...
}
impl Deref for PTEST1 {
    type Target = ptest::RegisterBlock;
    #[inline(always)]
    fn deref(&self) -> &Self::Target {
        unsafe { &*Self::PTR }
    }
}
#[doc = "PTEST"]
pub mod ptest;

NOTICE: The following design is actually incorrect and has been removed. See the following comment.

If the `const_generic` is `true`, we can use `ArrayProxy`. So I generate code like this: ```rust pub struct PTEST { _marker: PhantomData<*const ()>, } ... pub struct Peripherals { #[doc = "PTEST"] pub PTEST: crate::ArrayProxy, ... impl Peripherals { ... pub unsafe fn steal() -> Self { DEVICE_PERIPHERALS = true; Peripherals { PTEST: crate::ArrayProxy::new(), ``` Since `_array` is a private field of `ArrayProxy`, I add a `new` method to implement the `steal` method of `Peripherals`.

I also add the support of using name_of on MaybeArray<PeripheralInfo> to achieve this implementation.

Well, I haven't fully tested this implementation yet. So there may be problems that need to be solved.

rust-highfive commented 2 years ago

r? @reitermarkus

(rust-highfive has picked a reviewer for you, use r? to override)

burrbull commented 2 years ago

Looks good for me in general. Need advanced test in CI with SVD containing peripheral array. (possibly already present, need to check)

@therealprof any comments?

duskmoon314 commented 2 years ago

Need advanced test in CI with SVD containing peripheral array. (possibly already present, need to check)

It seems that CI use SVD in posborne/cmsis-svd to test. I try searching dim in this repo and cannot find a showcase of a peripheral array. May need to find a real example or build up one for testing.

ci failed: pattern bindings after an @ are unstable

I use p @ Peripheral::Array(_, dim) to extract dim_element and keep the p: MaybeArray<PeripheralInfo> at the same time. It is stable after 1.56. I will try to find out an alternative.

My mistake. I don't need to bind it. 🤣

Emilgardis commented 2 years ago

for future reference. If you need to break msrv, do it ;)

duskmoon314 commented 2 years ago

I think I have misunderstood how ArrayProxy works. The current design cannot use ArrayProxy to calculate the address since ArrayProxy uses these lines:

let base = self as *const Self as usize;
let address = base + S * index;
&*(address as *const T)

But the generated code when const_generic is false seems to work fine. And the usage in this way is the same as describing peripherals separately in the svd file. (I rearrange the svd file that I am maintaining locally and test a simple case.)

For now, I think maybe there is no need to provide something like ArrayProxy for peripheral arrays. Any suggestions?

burrbull commented 2 years ago

For now, I think maybe there is no need to provide something like ArrayProxy for peripheral arrays. Any suggestions?

Instead of ArrayProxy you could just create independent instances like if they are just derivedFrom first one.

duskmoon314 commented 2 years ago

Instead of ArrayProxy you could just create independent instances like if they are just derivedFrom the first one.

Yes. For now, the generated code is only organized in this way.

Though I think there is no problem, I still want to ask if there are any suggestions.

burrbull commented 2 years ago

I don't have suggestions now. Except missing CI test.

burrbull commented 2 years ago

bors r+

bors[bot] commented 2 years ago

Build succeeded: