Select Git revision
trans.rs 20.27 KiB
use quote::{Ident, Tokens};
use syn::{Lit, StrStyle};
use analyze::{Ownership, Ownerships};
use check::{App, Kind};
fn krate() -> Ident {
Ident::from("rtfm")
}
pub fn app(app: &App, ownerships: &Ownerships) -> Tokens {
let mut root = vec![];
let mut main = vec![];
::trans::check(app, &mut main);
::trans::init(app, &mut main, &mut root);
::trans::idle(app, ownerships, &mut main, &mut root);
::trans::resources(app, ownerships, &mut root);
::trans::tasks(app, ownerships, &mut root);
root.push(quote! {
#[allow(unsafe_code)]
fn main() {
#(#main)*
}
});
quote!(#(#root)*)
}
// Checks that the resource types are valid
// Sadly we can't do this test at expansion time. Instead we'll generate some
// code that won't compile if the types don't meet the requirements
fn check(app: &App, main: &mut Vec<Tokens>) {
if !app.resources.is_empty() {
main.push(quote! {
fn is_send<T>() where T: Send {}
});
}
for resource in app.resources.values() {
let ty = &resource.ty;
main.push(quote! {
is_send::<#ty>();
});
}
}
fn idle(
app: &App,
ownerships: &Ownerships,
main: &mut Vec<Tokens>,
root: &mut Vec<Tokens>,
) {
let krate = krate();
let mut mod_items = vec![];
let mut tys = vec![];
let mut exprs = vec![];
if !app.idle.resources.is_empty() {
tys.push(quote!(&mut #krate::Threshold));
exprs.push(quote!(unsafe { &mut #krate::Threshold::new(0) }));
}
if !app.idle.resources.is_empty() {
let device = &app.device;
let mut needs_reexport = false;
for name in &app.idle.resources {
if ownerships[name].is_owned() {
if app.resources.get(name).is_some() {
needs_reexport = true;
break;
}
}
}
let super_ = if needs_reexport {
None
} else {
Some(Ident::new("super"))
};
let mut rexprs = vec![];
let mut rfields = vec![];
for name in &app.idle.resources {
if ownerships[name].is_owned() {
if let Some(resource) = app.resources.get(name) {
let ty = &resource.ty;
rfields.push(quote! {
pub #name: &'static mut #ty,
});
let _name = Ident::new(format!("_{}", name.as_ref()));
rexprs.push(quote! {
#name: &mut #super_::#_name,
});
} else {
rfields.push(quote! {
pub #name: &'static mut ::#device::#name,
});
rexprs.push(quote! {
#name: &mut *::#device::#name.get(),
});
}
} else {
rfields.push(quote! {
pub #name: #super_::_resource::#name,
});
rexprs.push(quote! {
#name: #super_::_resource::#name::new(),
});
}
}
if needs_reexport {
root.push(quote! {
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
pub struct _idleResources {
#(#rfields)*
}
});
mod_items.push(quote! {
pub use ::_idleResources as Resources;
});
} else {
mod_items.push(quote! {
#[allow(non_snake_case)]
pub struct Resources {
#(#rfields)*
}
});
}
mod_items.push(quote! {
#[allow(unsafe_code)]
impl Resources {
pub unsafe fn new() -> Self {
Resources {
#(#rexprs)*
}
}
}
});
tys.push(quote!(idle::Resources));
exprs.push(quote!(unsafe { idle::Resources::new() }));
}
if !mod_items.is_empty() {
root.push(quote! {
#[allow(unsafe_code)]
mod idle {
#(#mod_items)*
}
});
}
let idle = &app.idle.path;
main.push(quote! {
// type check
let idle: fn(#(#tys),*) -> ! = #idle;
idle(#(#exprs),*);
});
}
fn init(app: &App, main: &mut Vec<Tokens>, root: &mut Vec<Tokens>) {
let device = &app.device;
let krate = krate();
let mut tys = vec![quote!(#device::Peripherals)];
let mut exprs = vec![quote!(#device::Peripherals::all())];
let mut mod_items = vec![];
if !app.resources.is_empty() {
let mut fields = vec![];
let mut lifetime = None;
let mut rexprs = vec![];
for (name, resource) in &app.resources {
let _name = Ident::new(format!("_{}", name.as_ref()));
lifetime = Some(quote!('a));
let ty = &resource.ty;
fields.push(quote! {
pub #name: &'a mut #krate::Static<#ty>,
});
rexprs.push(quote! {
#name: ::#krate::Static::ref_mut(&mut ::#_name),
});
}
root.push(quote! {
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
pub struct _initResources<#lifetime> {
#(#fields)*
}
});
mod_items.push(quote! {
pub use ::_initResources as Resources;
#[allow(unsafe_code)]
impl<#lifetime> Resources<#lifetime> {
pub unsafe fn new() -> Self {
Resources {
#(#rexprs)*
}
}
}
});
tys.push(quote!(init::Resources));
exprs.push(quote!(init::Resources::new()));
}
root.push(quote! {
#[allow(unsafe_code)]
mod init {
pub use ::#device::Peripherals;
#(#mod_items)*
}
});
let mut exceptions = vec![];
let mut interrupts = vec![];
for (name, task) in &app.tasks {
match task.kind {
Kind::Exception(ref e) => {
if exceptions.is_empty() {
exceptions.push(quote! {
let scb = &*#device::SCB.get();
});
}
let nr = e.nr();
let priority = task.priority;
exceptions.push(quote! {
let prio_bits = #device::NVIC_PRIO_BITS;
let hw = ((1 << prio_bits) - #priority) << (8 - prio_bits);
scb.shpr[#nr - 4].write(hw);
});
}
Kind::Interrupt { enabled } => {
// Interrupt. These can be enabled / disabled through the NVIC
if interrupts.is_empty() {
interrupts.push(quote! {
let nvic = &*#device::NVIC.get();
});
}
let priority = task.priority;
interrupts.push(quote! {
let prio_bits = #device::NVIC_PRIO_BITS;
let hw = ((1 << prio_bits) - #priority) << (8 - prio_bits);
nvic.set_priority(#device::Interrupt::#name, hw);
});
if enabled {
interrupts.push(quote! {
nvic.enable(#device::Interrupt::#name);
});
} else {
interrupts.push(quote! {
nvic.disable(#device::Interrupt::#name);
});
}
}
}
}
let init = &app.init.path;
main.push(quote! {
// type check
let init: fn(#(#tys,)*) = #init;
#krate::atomic(unsafe { &mut #krate::Threshold::new(0) }, |_t| unsafe {
init(#(#exprs,)*);
#(#exceptions)*
#(#interrupts)*
});
});
}
fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
let krate = krate();
let device = &app.device;
let mut items = vec![];
let mut impls = vec![];
for (name, ownership) in ownerships {
let mut impl_items = vec![];
let _name = Ident::new(format!("_{}", name.as_ref()));
match *ownership {
Ownership::Owned { .. } => {
if let Some(resource) = app.resources.get(name) {
// For owned resources we don't need claim() or borrow()
let expr = &resource.expr;
let ty = &resource.ty;
root.push(quote! {
static mut #_name: #ty = #expr;
});
} else {
// Peripheral
continue;
}
}
Ownership::Shared { ceiling } => {
if let Some(resource) = app.resources.get(name) {
let expr = &resource.expr;
let ty = &resource.ty;
root.push(quote! {
static mut #_name: #ty = #expr;
});
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_(&#_name) }
}
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 #_name)
}
}
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_(&#_name),
#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 #_name),
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
)
}
}
});
} else {
impl_items.push(quote! {
type Data = #device::#name;
fn borrow<'cs>(
&'cs self,
t: &'cs #krate::Threshold,
) -> &'cs #krate::Static<#device::#name> {
assert!(t.value() >= #ceiling);
unsafe {
#krate::Static::ref_(&*#device::#name.get())
}
}
fn borrow_mut<'cs>(
&'cs mut self,
t: &'cs #krate::Threshold,
) -> &'cs mut #krate::Static<#device::#name> {
assert!(t.value() >= #ceiling);
unsafe {
#krate::Static::ref_mut(
&mut *#device::#name.get(),
)
}
}
fn claim<R, F>(
&self,
t: &mut #krate::Threshold,
f: F,
) -> R
where
F: FnOnce(
&#krate::Static<#device::#name>,
&mut #krate::Threshold) -> R
{
unsafe {
#krate::claim(
#krate::Static::ref_(
&*#device::#name.get(),
),
#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<#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! {
#[allow(unsafe_code)]
unsafe impl #krate::Resource for _resource::#name {
#(#impl_items)*
}
});
items.push(quote! {
#[allow(non_camel_case_types)]
pub struct #name { _0: () }
#[allow(unsafe_code)]
impl #name {
pub unsafe fn new() -> Self {
#name { _0: () }
}
}
});
}
}
}
if !items.is_empty() {
root.push(quote! {
#[allow(unsafe_code)]
mod _resource {
#(#items)*
}
})
}
root.push(quote! {
#(#impls)*
});
}
fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
let device = &app.device;
let krate = krate();
for (name, task) in &app.tasks {
let mut exprs = vec![];
let mut fields = vec![];
let mut items = vec![];
let mut lifetime = None;
let mut needs_reexport = false;
let mut needs_threshold = false;
let has_resources = !task.resources.is_empty();
if has_resources {
needs_threshold = !task.resources.is_empty();
for name in &task.resources {
let _name = Ident::new(format!("_{}", name.as_ref()));
match ownerships[name] {
Ownership::Shared { ceiling }
if ceiling > task.priority =>
{
needs_threshold = true;
fields.push(quote! {
pub #name: ::_resource::#name,
});
exprs.push(quote! {
#name: {
::_resource::#name::new()
},
});
}
_ => {
lifetime = Some(quote!('a));
if let Some(resource) = app.resources.get(name) {
needs_reexport = true;
let ty = &resource.ty;
fields.push(quote! {
pub #name: &'a mut ::#krate::Static<#ty>,
});
exprs.push(quote! {
#name: ::#krate::Static::ref_mut(&mut ::#_name),
});
} else {
fields.push(quote! {
pub #name:
&'a mut ::#krate::Static<::#device::#name>,
});
exprs.push(quote! {
#name: ::#krate::Static::ref_mut(
&mut *::#device::#name.get(),
),
});
}
}
}
}
if needs_reexport {
let rname = Ident::new(format!("_{}Resources", name));
root.push(quote! {
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
pub struct #rname<#lifetime> {
#(#fields)*
}
});
items.push(quote! {
pub use ::#rname as Resources;
});
} else {
items.push(quote! {
#[allow(non_snake_case)]
pub struct Resources<#lifetime> {
#(#fields)*
}
});
}
items.push(quote! {
#[allow(unsafe_code)]
impl<#lifetime> Resources<#lifetime> {
pub unsafe fn new() -> Self {
Resources {
#(#exprs)*
}
}
}
});
}
let mut tys = vec![];
let mut exprs = vec![];
let priority = task.priority;
if needs_threshold {
tys.push(quote!(&mut #krate::Threshold));
exprs.push(quote! {
&mut if #priority == 1 << #device::NVIC_PRIO_BITS {
#krate::Threshold::new(::core::u8::MAX)
} else {
#krate::Threshold::new(#priority)
}
});
}
if has_resources {
tys.push(quote!(#name::Resources));
exprs.push(quote!(#name::Resources::new()));
}
let path = &task.path;
let _name = Ident::new(format!("_{}", name));
let export_name =
Lit::Str(name.as_ref().to_owned(), StrStyle::Cooked);
root.push(quote! {
#[allow(non_snake_case)]
#[allow(unsafe_code)]
#[export_name = #export_name]
pub unsafe extern "C" fn #_name() {
let f: fn(#(#tys,)*) = #path;
f(#(#exprs,)*)
}
});
root.push(quote!{
#[allow(non_snake_case)]
#[allow(unsafe_code)]
mod #name {
#[allow(dead_code)]
#[deny(const_err)]
const CHECK_PRIORITY: (u8, u8) = (
#priority - 1,
(1 << ::#device::NVIC_PRIO_BITS) - #priority,
);
#(#items)*
}
});
}
}