Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] safe creation of &'static mut references #49

Closed
wants to merge 1 commit into from
Closed

Conversation

japaric
Copy link
Collaborator

@japaric japaric commented Nov 9, 2017

This implements the "rooting" mechanism proposed in #47. However, it implements a root constructor
function instead of list of roots values as originally proposed.

In a nutshell:

  • There's a new field, root, which takes a path to the "root" constructor function.
  • This constructor has signature fn() -> T
  • When the root field is used the signature of init changes to accommodate a &'static mut T
    argument at the end. The T in that argument type matches the type returned by the "root"
    constructor.
  • The "root"-ed value is stack allocated.

This enables the safe creation of &'static mut references. Example below:

#![feature(proc_macro)]
#![no_std]

extern crate blue_pill;
extern crate cortex_m_rt;
extern crate cortex_m_rtfm as rtfm;
extern crate heapless;

use blue_pill::stm32f103xx;
use heapless::RingBuffer;
use heapless::ring_buffer::{Consumer, Producer};
use rtfm::{app, Threshold};
use stm32f103xx::Interrupt;

app! {
    device: stm32f103xx,

    resources: {
        static CONSUMER: Consumer<'static, u32, [u32; 8]>;
        static PRODUCER: Producer<'static, u32, [u32; 8]>;
    },

    root: root,

    idle: {
        resources: [CONSUMER],
    },

    tasks: {
        EXTI0: {
            path: exti0,
            resources: [PRODUCER],
        },
    }
}

struct Root {
    rb: RingBuffer<u32, [u32; 8]>,
}

fn root() -> Root {
    Root {
        rb: RingBuffer::new(),
    }
}

fn init(_p: init::Peripherals, root: &'static mut Root) -> init::LateResourceValues {
    let (p, c) = root.rb.split();

    init::LateResourceValues {
        CONSUMER: c,
        PRODUCER: p,
    }
}

fn idle(_t: &mut Threshold, r: idle::Resources) -> ! {
    rtfm::set_pending(Interrupt::EXTI0);

    loop {
        if r.CONSUMER.dequeue().is_some() {
            rtfm::bkpt();
        } else {
            rtfm::wfi();
        }
    }
}

fn exti0(_t: &mut Threshold, r: EXTI0::Resources) {
    r.PRODUCER.enqueue(42).ok();

    rtfm::bkpt();
}

This produces the following machine code:

0800019c <EXTI0>:
 800019c:       f240 0000       movw    r0, #0
 80001a0:       f2c2 0000       movt    r0, #8192       ; 0x2000
 80001a4:       6800            ldr     r0, [r0, #0]
 80001a6:       6803            ldr     r3, [r0, #0]
 80001a8:       6842            ldr     r2, [r0, #4]
 80001aa:       1c51            adds    r1, r2, #1
 80001ac:       f001 0107       and.w   r1, r1, #7
 80001b0:       4299            cmp     r1, r3
 80001b2:       d006            beq.n   80001c2 <EXTI0+0x26>
 80001b4:       eb00 0282       add.w   r2, r0, r2, lsl #2
 80001b8:       232a            movs    r3, #42 ; 0x2a
 80001ba:       6093            str     r3, [r2, #8]
 80001bc:       f3bf 8f5f       dmb     sy
 80001c0:       6041            str     r1, [r0, #4]
 80001c2:       be00            bkpt    0x0000
 80001c4:       4770            bx      lr

080001c6 <main>:
 80001c6:       b08a            sub     sp, #40 ; 0x28  ; Root allocation
 80001c8:       f240 1030       movw    r0, #304        ; 0x130
 80001cc:       4669            mov     r1, sp
 80001ce:       22f0            movs    r2, #240        ; 0xf0
 80001d0:       f6c0 0000       movt    r0, #2048       ; 0x800
 80001d4:       7800            ldrb    r0, [r0, #0]
 80001d6:       2000            movs    r0, #0
 80001d8:       e9cd 0000       strd    r0, r0, [sp]
 80001dc:       f240 0000       movw    r0, #0
 80001e0:       f2c2 0000       movt    r0, #8192       ; 0x2000
 80001e4:       b672            cpsid   i
 80001e6:       6001            str     r1, [r0, #0]    ; PRODUCER = ..
 80001e8:       f240 0004       movw    r0, #4
 80001ec:       f2c2 0000       movt    r0, #8192       ; 0x2000
 80001f0:       6001            str     r1, [r0, #0]    ; CONSUMER = ..
 80001f2:       f24e 4106       movw    r1, #58374      ; 0xe406
 80001f6:       f2ce 0100       movt    r1, #57344      ; 0xe000
 80001fa:       700a            strb    r2, [r1, #0]
 80001fc:       f24e 1100       movw    r1, #57600      ; 0xe100
 8000200:       2240            movs    r2, #64 ; 0x40
 8000202:       f2ce 0100       movt    r1, #57344      ; 0xe000
 8000206:       600a            str     r2, [r1, #0]
 8000208:       b662            cpsie   i
 800020a:       f8c1 2100       str.w   r2, [r1, #256]  ; 0x100
 800020e:       e006            b.n     800021e <main+0x58>
 8000210:       f3bf 8f5f       dmb     sy
 8000214:       3201            adds    r2, #1
 8000216:       f002 0207       and.w   r2, r2, #7
 800021a:       600a            str     r2, [r1, #0]
 800021c:       be00            bkpt    0x0000
 800021e:       6801            ldr     r1, [r0, #0]
 8000220:       684b            ldr     r3, [r1, #4]
 8000222:       680a            ldr     r2, [r1, #0]
 8000224:       429a            cmp     r2, r3
 8000226:       d1f3            bne.n   8000210 <main+0x4a>
 8000228:       bf30            wfi
 800022a:       e7f8            b.n     800021e <main+0x58>

Unresolved questions:

cc @pftbest

This implements the "rooting" mechanism proposed in #47. However, it implements a `root` constructor
function instead of list of `roots` values as originally proposed.

In a nutshell:

- There's a new field, `root`, which takes a path to the "root" constructor function.
- This constructor has signature `fn() -> T`
- When the `root` field is used the signature of `init` changes to accommodate a `&'static mut T`
  argument at the end. The `T` in that argument type matches the type returned by the "root"
  constructor.
- The "root"-ed value is stack allocated.

This enables the safe creation of `&'static mut` references. Example below:

``` rust
//#![feature(proc_macro)]
//#![no_std]

extern crate blue_pill;
extern crate cortex_m_rt;
extern crate cortex_m_rtfm as rtfm;
extern crate heapless;

use blue_pill::stm32f103xx;
use heapless::RingBuffer;
use heapless::ring_buffer::{Consumer, Producer};
use rtfm::{app, Threshold};
use stm32f103xx::Interrupt;

app! {
    device: stm32f103xx,

    resources: {
        static CONSUMER: Consumer<'static, u32, [u32; 8]>;
        static PRODUCER: Producer<'static, u32, [u32; 8]>;
    },

    root: root,

    idle: {
        resources: [CONSUMER],
    },

    tasks: {
        EXTI0: {
            path: exti0,
            resources: [PRODUCER],
        },
    }
}

struct Root {
    rb: RingBuffer<u32, [u32; 8]>,
}

fn root() -> Root {
    Root {
        rb: RingBuffer::new(),
    }
}

fn init(_p: init::Peripherals, root: &'static mut Root) -> init::LateResourceValues {
    let (p, c) = root.rb.split();

    init::LateResourceValues {
        CONSUMER: c,
        PRODUCER: p,
    }
}

fn idle(_t: &mut Threshold, r: idle::Resources) -> ! {
    rtfm::set_pending(Interrupt::EXTI0);

    loop {
        if r.CONSUMER.dequeue().is_some() {
            rtfm::bkpt();
        } else {
            rtfm::wfi();
        }
    }
}

fn exti0(_t: &mut Threshold, r: EXTI0::Resources) {
    r.PRODUCER.enqueue(42).ok();

    rtfm::bkpt();
}
```

This produces the following machine code:

``` armasm
0800019c <EXTI0>:
 800019c:       f240 0000       movw    r0, #0
 80001a0:       f2c2 0000       movt    r0, #8192       ; 0x2000
 80001a4:       6800            ldr     r0, [r0, #0]
 80001a6:       6803            ldr     r3, [r0, #0]
 80001a8:       6842            ldr     r2, [r0, #4]
 80001aa:       1c51            adds    r1, r2, #1
 80001ac:       f001 0107       and.w   r1, r1, #7
 80001b0:       4299            cmp     r1, r3
 80001b2:       d006            beq.n   80001c2 <EXTI0+0x26>
 80001b4:       eb00 0282       add.w   r2, r0, r2, lsl #2
 80001b8:       232a            movs    r3, #42 ; 0x2a
 80001ba:       6093            str     r3, [r2, #8]
 80001bc:       f3bf 8f5f       dmb     sy
 80001c0:       6041            str     r1, [r0, #4]
 80001c2:       be00            bkpt    0x0000
 80001c4:       4770            bx      lr

080001c6 <main>:
 80001c6:       b08a            sub     sp, #40 ; 0x28  ; Root allocation
 80001c8:       f240 1030       movw    r0, #304        ; 0x130
 80001cc:       4669            mov     r1, sp
 80001ce:       22f0            movs    r2, #240        ; 0xf0
 80001d0:       f6c0 0000       movt    r0, #2048       ; 0x800
 80001d4:       7800            ldrb    r0, [r0, #0]
 80001d6:       2000            movs    r0, #0
 80001d8:       e9cd 0000       strd    r0, r0, [sp]
 80001dc:       f240 0000       movw    r0, #0
 80001e0:       f2c2 0000       movt    r0, #8192       ; 0x2000
 80001e4:       b672            cpsid   i
 80001e6:       6001            str     r1, [r0, #0]    ; PRODUCER = ..
 80001e8:       f240 0004       movw    r0, #4
 80001ec:       f2c2 0000       movt    r0, #8192       ; 0x2000
 80001f0:       6001            str     r1, [r0, #0]    ; CONSUMER = ..
 80001f2:       f24e 4106       movw    r1, #58374      ; 0xe406
 80001f6:       f2ce 0100       movt    r1, #57344      ; 0xe000
 80001fa:       700a            strb    r2, [r1, #0]
 80001fc:       f24e 1100       movw    r1, #57600      ; 0xe100
 8000200:       2240            movs    r2, #64 ; 0x40
 8000202:       f2ce 0100       movt    r1, #57344      ; 0xe000
 8000206:       600a            str     r2, [r1, #0]
 8000208:       b662            cpsie   i
 800020a:       f8c1 2100       str.w   r2, [r1, #256]  ; 0x100
 800020e:       e006            b.n     800021e <main+0x58>
 8000210:       f3bf 8f5f       dmb     sy
 8000214:       3201            adds    r2, #1
 8000216:       f002 0207       and.w   r2, r2, #7
 800021a:       600a            str     r2, [r1, #0]
 800021c:       be00            bkpt    0x0000
 800021e:       6801            ldr     r1, [r0, #0]
 8000220:       684b            ldr     r3, [r1, #4]
 8000222:       680a            ldr     r2, [r1, #0]
 8000224:       429a            cmp     r2, r3
 8000226:       d1f3            bne.n   8000210 <main+0x4a>
 8000228:       bf30            wfi
 800022a:       e7f8            b.n     800021e <main+0x58>
```

Unresolved questions:

- Is this mechanism memory safe in presence of `panic!` unwinding?
  - If not, can we generate a compile error if `panic = abort` is *not* used?
- How does this affect the DMA API proposed in rust-embedded/embedded-hal#14

cc @pftbest
@japaric japaric mentioned this pull request Nov 21, 2017
1 task
@japaric
Copy link
Collaborator Author

japaric commented Nov 22, 2017

I have an alternative implementation of the second commit of PR #50. I think that implementation is less invasive syntax wise. It adds a resources field to app.init and basically the resources listed there will appear as &'static mut in init. Example below:

app! {
    resources: {
        static BUFFER: [u8; 8] = [0; 8];
    },

    init: {
        resources: [BUFFER],
    },
}

fn init(_p: init::Peripherals, r: init::Resources) {
    let buffer: &'static mut [u8; 8] = r.BUFFER;
}

There are a few semantic differences with the implementation in this PR:

  • The initial value of an init resource is subject to const eval limitations whereas the rooted resource in this PR is not.
  • The memory allocation of an init resource is in .data instead of in the stack. This means that the value should remain valid even in the event of stack unwinding.
  • Due to how cortex-m-rt works init resources will always be allocated in the RAM region whereas rooted resources could be allocated in RAM or CCRAM (a second RAM region) depending on where the stack is allocated.

japaric pushed a commit that referenced this pull request Dec 9, 2017
Peripherals as scoped singletons

See this RFC for details: rust-embedded/svd2rust#157

- The first commit adapts this crate to the changes in rust-embedded/cortex-m#65 and rust-embedded/svd2rust#158
- ~~The second commit is an alternative implementation of RFC #47 (there's another implementation in #49. This second commit is not required for RFC157 but let us experiment with safe DMA abstractions.~~ postponed

### TODO

- [x] un-bless peripherals as resources. Peripherals as resources were special cased: if resource listed in e.g. `app.tasks.FOO.resources` didn't appear in `app.resources` then it was assumed to be a peripheral and special code was generated for it. This is no longer required under RFC157.

~~This depends on PR rtic-rs/rtic-syntax#2~~ postponed
@japaric
Copy link
Collaborator Author

japaric commented Dec 9, 2017

Closing in favor of #59

@japaric japaric closed this Dec 9, 2017
@AfoHT AfoHT deleted the root branch January 26, 2023 22:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant