Auto merge of #50 - japaric:singletons, r=japaric

Peripherals as scoped singletons

See this RFC for details: japaric/svd2rust#157

- The first commit adapts this crate to the changes in japaric/cortex-m#65 and japaric/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 japaric/rtfm-syntax#2~~ postponed
This commit is contained in:
homunkulus 2017-12-09 12:09:35 +00:00
commit e78ca98c42
7 changed files with 176 additions and 233 deletions

View file

@ -10,13 +10,13 @@ keywords = ["arm", "cortex-m"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
name = "cortex-m-rtfm" name = "cortex-m-rtfm"
repository = "https://github.com/japaric/cortex-m-rtfm" repository = "https://github.com/japaric/cortex-m-rtfm"
version = "0.2.2" version = "0.3.0"
[dependencies] [dependencies]
cortex-m = "0.3.1" cortex-m = { git = "https://github.com/japaric/cortex-m" }
untagged-option = "0.1.1" untagged-option = "0.1.1"
rtfm-core = "0.1.0" rtfm-core = "0.1.0"
cortex-m-rtfm-macros = "0.2.1" cortex-m-rtfm-macros = { path = "macros" }
[target.'cfg(target_arch = "x86_64")'.dev-dependencies] [target.'cfg(target_arch = "x86_64")'.dev-dependencies]
compiletest_rs = "0.3.3" compiletest_rs = "0.3.3"
@ -27,7 +27,8 @@ version = "0.3.3"
[dev-dependencies.stm32f103xx] [dev-dependencies.stm32f103xx]
features = ["rt"] features = ["rt"]
version = "0.7.5" git = "https://github.com/japaric/stm32f103xx"
# version = "0.8.0"
[profile.release] [profile.release]
lto = true lto = true

View file

@ -12,6 +12,11 @@ use stm32f103xx::{SPI1, GPIOA};
app! { app! {
device: stm32f103xx, device: stm32f103xx,
resources: {
static GPIOA: GPIOA;
static SPI1: SPI1;
},
tasks: { tasks: {
EXTI0: { EXTI0: {
path: exti0, path: exti0,
@ -27,7 +32,12 @@ app! {
}, },
} }
fn init(_p: init::Peripherals) {} fn init(p: init::Peripherals) -> init::LateResourceValues {
init::LateResourceValues {
GPIOA: p.device.GPIOA,
SPI1: p.device.SPI1,
}
}
fn idle() -> ! { fn idle() -> ! {
loop { loop {

View file

@ -7,8 +7,9 @@ extern crate cortex_m;
extern crate cortex_m_rtfm as rtfm; extern crate cortex_m_rtfm as rtfm;
extern crate stm32f103xx; extern crate stm32f103xx;
use cortex_m::peripheral::SystClkSource; use cortex_m::peripheral::syst::SystClkSource;
use rtfm::{app, Threshold}; use rtfm::{app, Threshold};
use stm32f103xx::GPIOC;
app! { app! {
device: stm32f103xx, device: stm32f103xx,
@ -35,9 +36,8 @@ app! {
// These are the resources this task has access to. // These are the resources this task has access to.
// //
// A resource can be a peripheral like `GPIOC` or a static variable // The resources listed here must also appear in `app.resources`
// like `ON` resources: [ON],
resources: [GPIOC, ON],
}, },
} }
} }
@ -47,19 +47,20 @@ fn init(p: init::Peripherals, r: init::Resources) {
r.ON; r.ON;
// power on GPIOC // power on GPIOC
p.RCC.apb2enr.modify(|_, w| w.iopcen().enabled()); p.device.RCC.apb2enr.modify(|_, w| w.iopcen().enabled());
// configure PC13 as output // configure PC13 as output
p.GPIOC.bsrr.write(|w| w.bs13().set()); p.device.GPIOC.bsrr.write(|w| w.bs13().set());
p.GPIOC p.device
.GPIOC
.crh .crh
.modify(|_, w| w.mode13().output().cnf13().push()); .modify(|_, w| w.mode13().output().cnf13().push());
// configure the system timer to generate one interrupt every second // configure the system timer to generate one interrupt every second
p.SYST.set_clock_source(SystClkSource::Core); p.core.SYST.set_clock_source(SystClkSource::Core);
p.SYST.set_reload(8_000_000); // 1s p.core.SYST.set_reload(8_000_000); // 1s
p.SYST.enable_interrupt(); p.core.SYST.enable_interrupt();
p.SYST.enable_counter(); p.core.SYST.enable_counter();
} }
fn idle() -> ! { fn idle() -> ! {
@ -74,15 +75,22 @@ fn idle() -> ! {
// //
// `r` is the set of resources this task has access to. `SYS_TICK::Resources` // `r` is the set of resources this task has access to. `SYS_TICK::Resources`
// has one field per resource declared in `app!`. // has one field per resource declared in `app!`.
#[allow(unsafe_code)]
fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) { fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) {
// toggle state // toggle state
**r.ON = !**r.ON; **r.ON = !**r.ON;
if **r.ON { if **r.ON {
// set the pin PC13 high // set the pin PC13 high
r.GPIOC.bsrr.write(|w| w.bs13().set()); // NOTE(unsafe) atomic write to a stateless register
unsafe {
(*GPIOC::ptr()).bsrr.write(|w| w.bs13().set());
}
} else { } else {
// set the pin PC13 low // set the pin PC13 low
r.GPIOC.bsrr.write(|w| w.br13().reset()); // NOTE(unsafe) atomic write to a stateless register
unsafe {
(*GPIOC::ptr()).bsrr.write(|w| w.br13().reset());
}
} }
} }

View file

@ -25,8 +25,9 @@ app! {
// this function. // this function.
fn init(p: init::Peripherals) { fn init(p: init::Peripherals) {
// This function has access to all the peripherals of the device // This function has access to all the peripherals of the device
p.GPIOA; p.core.SYST;
p.RCC; p.device.GPIOA;
p.device.RCC;
// .. // ..
} }

View file

@ -63,16 +63,15 @@ pub fn app(app: check::App) -> Result<App> {
tasks: app.tasks tasks: app.tasks
.into_iter() .into_iter()
.map(|(k, v)| { .map(|(k, v)| {
let v = ::check::task(k.as_ref(), v) let v =
.chain_err(|| format!("checking task `{}`", k))?; ::check::task(k.as_ref(), v).chain_err(|| format!("checking task `{}`", k))?;
Ok((k, v)) Ok((k, v))
}) })
.collect::<Result<_>>()?, .collect::<Result<_>>()?,
}; };
::check::resources(&app) ::check::resources(&app).chain_err(|| "checking `resources`")?;
.chain_err(|| "checking `resources`")?;
Ok(app) Ok(app)
} }
@ -93,6 +92,17 @@ fn resources(app: &App) -> Result<()> {
bail!("resource `{}` is unused", resource); bail!("resource `{}` is unused", resource);
} }
for (name, task) in &app.tasks {
for resource in &task.resources {
ensure!(
app.resources.contains_key(&resource),
"task {} contains an undeclared resource with name {}",
name,
resource
);
}
}
Ok(()) Ok(())
} }

View file

@ -27,12 +27,7 @@ pub fn app(app: &App, ownerships: &Ownerships) -> Tokens {
quote!(#(#root)*) quote!(#(#root)*)
} }
fn idle( fn idle(app: &App, ownerships: &Ownerships, main: &mut Vec<Tokens>, root: &mut Vec<Tokens>) {
app: &App,
ownerships: &Ownerships,
main: &mut Vec<Tokens>,
root: &mut Vec<Tokens>,
) {
let krate = krate(); let krate = krate();
let mut mod_items = vec![]; let mut mod_items = vec![];
@ -45,8 +40,6 @@ fn idle(
} }
if !app.idle.resources.is_empty() { if !app.idle.resources.is_empty() {
let device = &app.device;
let mut needs_reexport = false; let mut needs_reexport = false;
for name in &app.idle.resources { for name in &app.idle.resources {
if ownerships[name].is_owned() { if ownerships[name].is_owned() {
@ -66,32 +59,26 @@ fn idle(
let mut rfields = vec![]; let mut rfields = vec![];
for name in &app.idle.resources { for name in &app.idle.resources {
if ownerships[name].is_owned() { if ownerships[name].is_owned() {
if let Some(resource) = app.resources.get(name) { let resource = app.resources.get(name).expect(&format!(
let ty = &resource.ty; "BUG: resource {} assigned to `idle` has no definition",
name
));
let ty = &resource.ty;
rfields.push(quote! { rfields.push(quote! {
pub #name: &'static mut #ty, pub #name: &'static mut #ty,
}); });
let _name = Ident::new(format!("_{}", name.as_ref())); let _name = Ident::new(format!("_{}", name.as_ref()));
rexprs.push(if resource.expr.is_some() { rexprs.push(if resource.expr.is_some() {
quote! { quote! {
#name: &mut #super_::#_name, #name: &mut #super_::#_name,
} }
} else {
quote! {
#name: #super_::#_name.as_mut(),
}
});
} else { } else {
rfields.push(quote! { quote! {
pub #name: &'static mut ::#device::#name, #name: #super_::#_name.as_mut(),
}); }
});
rexprs.push(quote! {
#name: &mut *::#device::#name.get(),
});
}
} else { } else {
rfields.push(quote! { rfields.push(quote! {
pub #name: #super_::_resource::#name, pub #name: #super_::_resource::#name,
@ -161,12 +148,20 @@ fn init(app: &App, main: &mut Vec<Tokens>, root: &mut Vec<Tokens>) {
let device = &app.device; let device = &app.device;
let krate = krate(); let krate = krate();
let mut tys = vec![quote!(#device::Peripherals)]; let mut tys = vec![quote!(init::Peripherals)];
let mut exprs = vec![quote!(#device::Peripherals::all())]; let mut exprs = vec![
quote!{
init::Peripherals {
core: ::#device::CorePeripherals::steal(),
device: ::#device::Peripherals::steal(),
}
},
];
let mut ret = None; let mut ret = None;
let mut mod_items = vec![]; let mut mod_items = vec![];
let (init_resources, late_resources): (Vec<_>, Vec<_>) = app.resources.iter() let (init_resources, late_resources): (Vec<_>, Vec<_>) = app.resources
.iter()
.partition(|&(_, res)| res.expr.is_some()); .partition(|&(_, res)| res.expr.is_some());
if !init_resources.is_empty() { if !init_resources.is_empty() {
@ -255,7 +250,10 @@ fn init(app: &App, main: &mut Vec<Tokens>, root: &mut Vec<Tokens>) {
root.push(quote! { root.push(quote! {
#[allow(unsafe_code)] #[allow(unsafe_code)]
mod init { mod init {
pub use ::#device::Peripherals; pub struct Peripherals {
pub core: ::#device::CorePeripherals,
pub device: ::#device::Peripherals,
}
#(#mod_items)* #(#mod_items)*
} }
@ -268,7 +266,7 @@ fn init(app: &App, main: &mut Vec<Tokens>, root: &mut Vec<Tokens>) {
Kind::Exception(ref e) => { Kind::Exception(ref e) => {
if exceptions.is_empty() { if exceptions.is_empty() {
exceptions.push(quote! { exceptions.push(quote! {
let scb = &*#device::SCB.get(); let scb = &*#device::SCB::ptr();
}); });
} }
@ -284,7 +282,7 @@ fn init(app: &App, main: &mut Vec<Tokens>, root: &mut Vec<Tokens>) {
// Interrupt. These can be enabled / disabled through the NVIC // Interrupt. These can be enabled / disabled through the NVIC
if interrupts.is_empty() { if interrupts.is_empty() {
interrupts.push(quote! { interrupts.push(quote! {
let nvic = &*#device::NVIC.get(); let nvic = &*#device::NVIC::ptr();
}); });
} }
@ -355,154 +353,81 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
// For owned resources we don't need claim() or borrow() // For owned resources we don't need claim() or borrow()
} }
Ownership::Shared { ceiling } => { Ownership::Shared { ceiling } => {
if let Some(resource) = app.resources.get(name) { let resource = app.resources
let ty = &resource.ty; .get(name)
let res_rvalue = if resource.expr.is_some() { .expect(&format!("BUG: resource {} has no definition", name));
quote!(#_name) let ty = &resource.ty;
} else { let res_rvalue = if resource.expr.is_some() {
quote!(#_name.some) quote!(#_name)
};
impl_items.push(quote! {
type Data = #ty;
fn borrow<'cs>(
&'cs self,
t: &'cs #krate::Threshold,
) -> &'cs #krate::Static<#ty> {
assert!(t.value() >= #ceiling);
unsafe { #krate::Static::ref_(&#res_rvalue) }
}
fn borrow_mut<'cs>(
&'cs mut self,
t: &'cs #krate::Threshold,
) -> &'cs mut #krate::Static<#ty> {
assert!(t.value() >= #ceiling);
unsafe {
#krate::Static::ref_mut(&mut #res_rvalue)
}
}
fn claim<R, F>(
&self,
t: &mut #krate::Threshold,
f: F,
) -> R
where
F: FnOnce(
&#krate::Static<#ty>,
&mut #krate::Threshold) -> R
{
unsafe {
#krate::claim(
#krate::Static::ref_(&#res_rvalue),
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
)
}
}
fn claim_mut<R, F>(
&mut self,
t: &mut #krate::Threshold,
f: F,
) -> R
where
F: FnOnce(
&mut #krate::Static<#ty>,
&mut #krate::Threshold) -> R
{
unsafe {
#krate::claim(
#krate::Static::ref_mut(&mut #res_rvalue),
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
)
}
}
});
} else { } else {
impl_items.push(quote! { quote!(#_name.some)
type Data = #device::#name; };
fn borrow<'cs>( impl_items.push(quote! {
&'cs self, type Data = #ty;
t: &'cs #krate::Threshold,
) -> &'cs #krate::Static<#device::#name> {
assert!(t.value() >= #ceiling);
unsafe { fn borrow<'cs>(
#krate::Static::ref_(&*#device::#name.get()) &'cs self,
} t: &'cs #krate::Threshold,
) -> &'cs #krate::Static<#ty> {
assert!(t.value() >= #ceiling);
unsafe { #krate::Static::ref_(&#res_rvalue) }
}
fn borrow_mut<'cs>(
&'cs mut self,
t: &'cs #krate::Threshold,
) -> &'cs mut #krate::Static<#ty> {
assert!(t.value() >= #ceiling);
unsafe {
#krate::Static::ref_mut(&mut #res_rvalue)
} }
}
fn borrow_mut<'cs>( fn claim<R, F>(
&'cs mut self, &self,
t: &'cs #krate::Threshold, t: &mut #krate::Threshold,
) -> &'cs mut #krate::Static<#device::#name> { f: F,
assert!(t.value() >= #ceiling); ) -> R
where
unsafe { F: FnOnce(
#krate::Static::ref_mut( &#krate::Static<#ty>,
&mut *#device::#name.get(), &mut #krate::Threshold) -> R
) {
} unsafe {
#krate::claim(
#krate::Static::ref_(&#res_rvalue),
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
)
} }
}
fn claim<R, F>( fn claim_mut<R, F>(
&self, &mut self,
t: &mut #krate::Threshold, t: &mut #krate::Threshold,
f: F, f: F,
) -> R ) -> R
where where
F: FnOnce( F: FnOnce(
&#krate::Static<#device::#name>, &mut #krate::Static<#ty>,
&mut #krate::Threshold) -> R &mut #krate::Threshold) -> R
{ {
unsafe { unsafe {
#krate::claim( #krate::claim(
#krate::Static::ref_( #krate::Static::ref_mut(&mut #res_rvalue),
&*#device::#name.get(), #ceiling,
), #device::NVIC_PRIO_BITS,
#ceiling, t,
#device::NVIC_PRIO_BITS, f,
t, )
f,
)
}
} }
}
fn claim_mut<R, F>( });
&mut self,
t: &mut #krate::Threshold,
f: F,
) -> R
where
F: FnOnce(
&mut #krate::Static<#device::#name>,
&mut #krate::Threshold) -> R
{
unsafe {
#krate::claim(
#krate::Static::ref_mut(
&mut *#device::#name.get(),
),
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
)
}
}
});
}
impls.push(quote! { impls.push(quote! {
#[allow(unsafe_code)] #[allow(unsafe_code)]
@ -560,9 +485,7 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
let _name = Ident::new(format!("_{}", name.as_ref())); let _name = Ident::new(format!("_{}", name.as_ref()));
match ownerships[name] { match ownerships[name] {
Ownership::Shared { ceiling } Ownership::Shared { ceiling } if ceiling > task.priority => {
if ceiling > task.priority =>
{
needs_threshold = true; needs_threshold = true;
fields.push(quote! { fields.push(quote! {
@ -577,35 +500,26 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
} }
_ => { _ => {
lifetime = Some(quote!('a)); lifetime = Some(quote!('a));
if let Some(resource) = app.resources.get(name) { let resource = app.resources
needs_reexport = true; .get(name)
let ty = &resource.ty; .expect(&format!("BUG: resource {} has no definition", name));
fields.push(quote! { needs_reexport = true;
pub #name: &'a mut ::#krate::Static<#ty>, let ty = &resource.ty;
});
exprs.push(if resource.expr.is_some() { fields.push(quote! {
quote! { pub #name: &'a mut ::#krate::Static<#ty>,
#name: ::#krate::Static::ref_mut(&mut ::#_name), });
}
} else { exprs.push(if resource.expr.is_some() {
quote! { quote! {
#name: ::#krate::Static::ref_mut(::#_name.as_mut()), #name: ::#krate::Static::ref_mut(&mut ::#_name),
} }
});
} else { } else {
fields.push(quote! { quote! {
pub #name: #name: ::#krate::Static::ref_mut(::#_name.as_mut()),
&'a mut ::#krate::Static<::#device::#name>, }
}); });
exprs.push(quote! {
#name: ::#krate::Static::ref_mut(
&mut *::#device::#name.get(),
),
});
}
} }
} }
} }
@ -666,8 +580,7 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
let path = &task.path; let path = &task.path;
let _name = Ident::new(format!("_{}", name)); let _name = Ident::new(format!("_{}", name));
let export_name = let export_name = Lit::Str(name.as_ref().to_owned(), StrStyle::Cooked);
Lit::Str(name.as_ref().to_owned(), StrStyle::Cooked);
root.push(quote! { root.push(quote! {
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[allow(unsafe_code)] #[allow(unsafe_code)]

View file

@ -160,6 +160,6 @@ where
I: Nr, I: Nr,
{ {
// NOTE(safe) atomic write // NOTE(safe) atomic write
let nvic = unsafe { &*cortex_m::peripheral::NVIC.get() }; let nvic = unsafe { &*cortex_m::peripheral::NVIC::ptr() };
nvic.set_pending(interrupt); nvic.set_pending(interrupt);
} }