diff --git a/Rakefile b/Rakefile index 19098e53..e29ba3e0 100644 --- a/Rakefile +++ b/Rakefile @@ -34,10 +34,26 @@ compile_rust :core_crate, { recompile_on: :triple, } +# ioreg +compile_rust :ioreg_crate, { + source: 'ioreg/ioreg.rs'.in_root, + produce: 'ioreg/ioreg.rs'.in_root.as_rlib.in_build, + out_dir: true, + build_for: :host, +} + +compile_rust :macro_ioreg, { + source: 'macro/ioreg.rs'.in_root, + deps: [:ioreg_crate], + produce: 'macro/ioreg.rs'.in_root.as_dylib.in_build, + out_dir: true, + build_for: :host, +} + # zinc crate compile_rust :zinc_crate, { source: 'main.rs'.in_source, - deps: [:core_crate, :rlibc_crate], + deps: [:core_crate, :rlibc_crate, :macro_ioreg], produce: 'main.rs'.in_source.as_rlib.in_build, out_dir: true, recompile_on: [:triple, :platform], @@ -96,8 +112,10 @@ desc "Build API documentation" task build_docs: [:build_docs_html] task build_docs_html: [] do |t| - ['src/main.rs', 'platformtree/platformtree.rs'].each do |f| - sh ("rustdoc -w html -o build/doc " + f + ' ' + :config_flags.in_env.join(' ')) + ['src/main.rs', 'platformtree/platformtree.rs', 'ioreg/ioreg.rs'].each do |f| + build = Context.instance.build_dir + sh ("rustdoc -w html -o #{build}/doc -L #{build} " \ + + f + ' ' + :config_flags.in_env.join(' ')) end end diff --git a/ioreg/builder/accessors.rs b/ioreg/builder/accessors.rs new file mode 100644 index 00000000..6e0aa1f2 --- /dev/null +++ b/ioreg/builder/accessors.rs @@ -0,0 +1,203 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Ben Gamari +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syntax::ast; +use syntax::ast::P; +use syntax::codemap::DUMMY_SP; +use syntax::ext::base::ExtCtxt; +use syntax::ext::build::AstBuilder; +use syntax::ext::quote::rt::ToTokens; +use syntax::parse::token; + +use super::Builder; +use super::utils; +use super::super::node; + +/// A visitor to build accessor functions for each register struct +pub struct BuildAccessors<'a, 'b, 'c> { + builder: &'a mut Builder, + cx: &'b ExtCtxt<'c>, +} + +impl<'a, 'b, 'c> node::RegVisitor for BuildAccessors<'a, 'b, 'c> { + fn visit_prim_reg(&mut self, path: &Vec, reg: &node::Reg, + _width: node::RegWidth, fields: &Vec) { + let item = build_get_fn(self.cx, path, reg); + self.builder.push_item(item); + + for field in fields.iter() { + match build_field_accessors(self.cx, path, reg, field) { + Some(item) => self.builder.push_item(item), + None => {} + } + } + } +} + +impl<'a, 'b, 'c> BuildAccessors<'a, 'b, 'c> { + pub fn new(builder: &'a mut Builder, cx: &'b ExtCtxt<'c>) + -> BuildAccessors<'a, 'b, 'c> { + BuildAccessors {builder: builder, cx: cx} + } +} + +fn build_field_accessors<'a>(cx: &'a ExtCtxt, path: &Vec, + reg: &node::Reg, field: &node::Field) + -> Option> +{ + let reg_ty: P = + cx.ty_ident(DUMMY_SP, utils::path_ident(cx, path)); + + let items = match field.access { + node::ReadWrite => vec!(build_field_set_fn(cx, path, reg, field), + build_field_get_fn(cx, path, reg, field)), + node::ReadOnly => vec!(build_field_get_fn(cx, path, reg, field)), + node::WriteOnly => vec!(build_field_set_fn(cx, path, reg, field)), + node::SetToClear => vec!(build_field_clear_fn(cx, path, reg, field)), + }; + + let access_tag = match field.access { + node::ReadWrite => "read/write", + node::ReadOnly => "read-only", + node::WriteOnly => "write-only", + node::SetToClear => "set-to-clear", + }; + + let field_doc = match field.docstring { + Some(ref d) => { + let s = token::get_ident(d.node); + s.get().into_string() + }, + None => "no documentation".into_string() + }; + let docstring = format!("*[{}]* Field `{}`: {}", + access_tag, + field.name.node, + field_doc); + let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring)); + + quote_item!(cx, + $doc_attr + impl $reg_ty { + $items + } + ) +} + +fn build_get_fn<'a>(cx: &'a ExtCtxt, path: &Vec, reg: &node::Reg) + -> P +{ + let reg_ty: P = + cx.ty_ident(DUMMY_SP, utils::path_ident(cx, path)); + let getter_ty = utils::getter_name(cx, path); + + let docstring = format!("Fetch the value of the `{}` register", + reg.name.node); + let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring)); + + let item = quote_item!(cx, + impl $reg_ty { + $doc_attr + #[allow(dead_code)] + pub fn get(&'static self) -> $getter_ty { + $getter_ty::new(self) + } + } + ); + item.unwrap() +} + +fn build_field_set_fn<'a>(cx: &'a ExtCtxt, path: &Vec, + reg: &node::Reg, field: &node::Field) + -> P +{ + let fn_name = + cx.ident_of((String::from_str("set_")+field.name.node).as_slice()); + let field_ty: P = + cx.ty_path(utils::field_type_path(cx, path, reg, field), None); + let setter_ty = utils::setter_name(cx, path); + if field.count.node == 1 { + quote_method!(cx, + #[allow(dead_code, missing_doc)] + pub fn $fn_name(&'static self, new_value: $field_ty) -> $setter_ty { + let mut setter: $setter_ty = $setter_ty::new(self); + setter.$fn_name(new_value); + setter + } + ) + } else { + quote_method!(cx, + #[allow(dead_code, missing_doc)] + pub fn $fn_name(&'static self, idx: uint, new_value: $field_ty) -> $setter_ty { + let mut setter: $setter_ty = $setter_ty::new(self); + setter.$fn_name(idx, new_value); + setter + } + ) + } +} + +fn build_field_get_fn<'a>(cx: &'a ExtCtxt, path: &Vec, + reg: &node::Reg, field: &node::Field) + -> P +{ + let fn_name = cx.ident_of(field.name.node.as_slice()); + let field_ty: P = + cx.ty_path(utils::field_type_path(cx, path, reg, field), None); + let getter_ty = utils::getter_name(cx, path); + if field.count.node == 1 { + quote_method!(cx, + #[allow(dead_code, missing_doc)] + pub fn $fn_name(&'static self) -> $field_ty { + $getter_ty::new(self).$fn_name() + } + ) + } else { + quote_method!(cx, + #[allow(dead_code, missing_doc)] + pub fn $fn_name(&'static self, idx: uint) -> $field_ty { + $getter_ty::new(self).$fn_name(idx) + } + ) + } +} + +fn build_field_clear_fn<'a>(cx: &'a ExtCtxt, path: &Vec, + _reg: &node::Reg, field: &node::Field) + -> P +{ + let fn_name = + cx.ident_of((String::from_str("clear_")+field.name.node).as_slice()); + let setter_ty = utils::setter_name(cx, path); + if field.count.node == 1 { + quote_method!(cx, + #[allow(dead_code, missing_doc)] + pub fn $fn_name(&'static self) -> $setter_ty { + let mut setter: $setter_ty = $setter_ty::new(self); + setter.$fn_name(); + setter + } + ) + } else { + quote_method!(cx, + #[allow(dead_code, missing_doc)] + pub fn $fn_name(&'static self, idx: uint) -> $setter_ty { + let mut setter: $setter_ty = $setter_ty::new(self); + setter.$fn_name(idx); + setter + } + ) + } +} diff --git a/ioreg/builder/getter.rs b/ioreg/builder/getter.rs new file mode 100644 index 00000000..ec6744aa --- /dev/null +++ b/ioreg/builder/getter.rs @@ -0,0 +1,190 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Ben Gamari +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syntax::ast; +use syntax::ast::P; +use syntax::ext::base::ExtCtxt; +use syntax::codemap::DUMMY_SP; +use syntax::ext::build::AstBuilder; +use syntax::ext::quote::rt::ToTokens; +use syntax::parse::token; + +use super::Builder; +use super::super::node; +use super::utils; + +/// A visitor to build the field setters for primitive registers +pub struct BuildGetters<'a, 'b, 'c> { + builder: &'a mut Builder, + cx: &'b ExtCtxt<'c>, +} + +impl<'a, 'b, 'c> BuildGetters<'a, 'b, 'c> { + pub fn new(builder: &'a mut Builder, cx: &'b ExtCtxt<'c>) + -> BuildGetters<'a, 'b, 'c> { + BuildGetters { builder: builder, cx: cx } + } +} + +impl<'a, 'b, 'c> node::RegVisitor for BuildGetters<'a, 'b, 'c> { + fn visit_prim_reg<'a>(&'a mut self, path: &Vec, + reg: &'a node::Reg, _width: node::RegWidth, + fields: &Vec) { + if fields.iter().any(|f| f.access != node::WriteOnly) { + let it = build_type(self.cx, path, reg); + self.builder.push_item(it); + + let it = build_impl(self.cx, path, reg, fields); + self.builder.push_item(it); + } + } +} + +fn build_type<'a>(cx: &'a ExtCtxt, path: &Vec, + reg: &node::Reg) -> P +{ + let packed_ty = utils::reg_primitive_type(cx, reg) + .expect("Unexpected non-primitive register"); + let name = utils::getter_name(cx, path); + let reg_doc = match reg.docstring { + Some(d) => token::get_ident(d.node).get().into_string(), + None => "no documentation".into_string(), + }; + let docstring = format!("`{}`: {}", reg.name.node, reg_doc); + let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring)); + + let item = quote_item!(cx, + $doc_attr + #[allow(non_camel_case_types)] + pub struct $name { + value: $packed_ty, + } + ); + item.unwrap() +} + +fn build_new<'a>(cx: &'a ExtCtxt, path: &Vec) + -> P { + let reg_ty: P = + cx.ty_ident(DUMMY_SP, utils::path_ident(cx, path)); + let getter_ty: P = cx.ty_ident(DUMMY_SP, + utils::getter_name(cx, path)); + let item = quote_item!(cx, + #[doc = "Create a getter reflecting the current value of the given register."] + pub fn new(reg: &'static $reg_ty) -> $getter_ty { + $getter_ty { + value: reg.value.get(), + } + } + ); + item.unwrap() +} + +/// Given an `Expr` of the given register's primitive type, return +/// an `Expr` of the field type +fn from_primitive<'a>(cx: &'a ExtCtxt, reg: &node::Reg, + field: &node::Field, prim: P) + -> P { + match field.ty.node { + node::UIntField => prim, + node::BoolField => + cx.expr_binary(DUMMY_SP, ast::BiNe, + prim, utils::expr_int(cx, 0)), + node::EnumField {..} => { + let from = match reg.ty { + node::RegPrim(width,_) => + match width { + node::Reg32 => "from_u32", + node::Reg16 => "from_u16", + node::Reg8 => "from_u8", + }, + _ => fail!("Can't convert group register to primitive type"), + }; + cx.expr_method_call( + DUMMY_SP, + cx.expr_call_global( + DUMMY_SP, + vec!(cx.ident_of("core"), + cx.ident_of("num"), + cx.ident_of(from)), + vec!(prim) + ), + cx.ident_of("unwrap"), + Vec::new() + ) + }, + } +} + +fn build_impl(cx: &ExtCtxt, path: &Vec, reg: &node::Reg, + fields: &Vec) -> P { + let getter_ty = utils::getter_name(cx, path); + let new = build_new(cx, path); + let getters: Vec> = + FromIterator::from_iter( + fields.iter() + .map(|field| build_field_get_fn(cx, path, reg, field))); + + let it = quote_item!(cx, + #[allow(dead_code)] + impl $getter_ty { + $new + $getters + } + ); + it.unwrap() +} + +/// Build a getter for a field +fn build_field_get_fn<'a>(cx: &'a ExtCtxt, path: &Vec, reg: &node::Reg, + field: &node::Field) -> P +{ + let fn_name = cx.ident_of(field.name.node.as_slice()); + let field_ty: P = + cx.ty_path(utils::field_type_path(cx, path, reg, field), None); + let mask = utils::mask(cx, field); + let field_doc = match field.docstring { + Some(d) => d.node, + None => cx.ident_of("no documentation"), + }; + let docstring = format!("Get value of `{}` field: {}", + field.name.node, + token::get_ident(field_doc).get()); + let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring)); + + if field.count.node == 1 { + let shift = utils::shift(cx, None, field); + let value = from_primitive( + cx, reg, field, + quote_expr!(cx, (self.value >> $shift) & $mask)); + quote_method!(cx, + $doc_attr + pub fn $fn_name<'a>(&'a self) -> $field_ty { + $value + } + ) + } else { + let shift = utils::shift(cx, Some(quote_expr!(cx, idx)), field); + let value = from_primitive( + cx, reg, field, + quote_expr!(cx, (self.value >> $shift) & $mask)); + quote_method!(cx, + $doc_attr + pub fn $fn_name<'a>(&'a self, idx: uint) -> $field_ty { + $value + } + ) + } +} diff --git a/ioreg/builder/mod.rs b/ioreg/builder/mod.rs new file mode 100644 index 00000000..e2684c45 --- /dev/null +++ b/ioreg/builder/mod.rs @@ -0,0 +1,53 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Ben Gamari +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::gc::Gc; +use syntax::ast; +use syntax::ast::P; +use syntax::ext::base::ExtCtxt; + +use node; +mod utils; + +mod setter; +mod getter; +mod union; +mod register; +mod accessors; + +pub struct Builder { + items: Vec>, +} + +impl Builder { + pub fn new() -> Builder { + Builder {items: Vec::new()} + } + + pub fn emit_items<'a>(&mut self, cx: &'a ExtCtxt, reg: Gc) + -> Vec> { + node::visit_reg(&*reg, &mut setter::BuildSetters::new(self, cx)); + node::visit_reg(&*reg, &mut getter::BuildGetters::new(self, cx)); + node::visit_reg(&*reg, &mut register::BuildRegStructs::new(self, cx)); + node::visit_reg(&*reg, &mut union::BuildUnionTypes::new(self, cx)); + node::visit_reg(&*reg, &mut accessors::BuildAccessors::new(self, cx)); + self.items.clone() + } + + pub fn push_item(&mut self, item: Gc) { + self.items.push(item); + } +} + diff --git a/ioreg/builder/register.rs b/ioreg/builder/register.rs new file mode 100644 index 00000000..db79dbf2 --- /dev/null +++ b/ioreg/builder/register.rs @@ -0,0 +1,147 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Ben Gamari +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::gc::GC; +use std::iter::FromIterator; +use syntax::ast; +use syntax::ast::P; +use syntax::codemap::{respan, mk_sp}; +use syntax::ext::base::ExtCtxt; +use syntax::ext::build::AstBuilder; +use syntax::ext::quote::rt::ToTokens; +use syntax::parse::token; + +use super::Builder; +use super::utils; +use super::super::node; + +/// A visitor to build the struct for each register +pub struct BuildRegStructs<'a, 'b, 'c> { + builder: &'a mut Builder, + cx: &'b ExtCtxt<'c>, +} + +impl<'a, 'b, 'c> node::RegVisitor for BuildRegStructs<'a, 'b, 'c> { + fn visit_prim_reg(&mut self, path: &Vec, reg: &node::Reg, + width: node::RegWidth, fields: &Vec) { + for field in fields.iter() { + match build_field_type(self.cx, path, reg, field) { + Some(item) => self.builder.push_item(item), + None => {} + } + } + + let reg_struct = build_reg_struct(self.cx, path, reg, width); + self.builder.push_item(reg_struct); + } +} + +impl<'a, 'b, 'c> BuildRegStructs<'a, 'b, 'c> { + pub fn new(builder: &'a mut Builder, cx: &'b ExtCtxt<'c>) + -> BuildRegStructs<'a, 'b, 'c> { + BuildRegStructs {builder: builder, cx: cx} + } +} + +/// Build a field type if necessary (e.g. in the case of an `EnumField`) +fn build_field_type<'a>(cx: &'a ExtCtxt, path: &Vec, + reg: &node::Reg, field: &node::Field) + -> Option> { + match field.ty.node { + node::EnumField { variants: ref variants, .. } => { + // FIXME(bgamari): We construct a path, then only take the last + // segment, this could be more efficient + let name: ast::Ident = + utils::field_type_path(cx, path, reg, field) + .segments.last().unwrap().identifier; + let enum_def: ast::EnumDef = ast::EnumDef { + variants: FromIterator::from_iter( + variants.iter().map(|v| box(GC) build_enum_variant(cx, v))), + }; + let attrs: Vec = vec!( + utils::list_attribute(cx, "deriving", vec!("FromPrimitive")), + utils::list_attribute(cx, "allow", + vec!("uppercase_variables", + "dead_code", + "non_camel_case_types", + "missing_doc"))); + let item: P = box(GC) ast::Item { + ident: name, + id: ast::DUMMY_NODE_ID, + node: ast::ItemEnum(enum_def, utils::no_generics()), + vis: ast::Public, + attrs: attrs, + span: field.ty.span, + }; + Some(item) + }, + _ => None, + } +} + +/// Produce a register struct if necessary (for primitive typed registers). +/// In this case `None` indicates no struct is necessary, not failure. +/// For instance, +/// +/// pub struct REG {_value: u32} +fn build_reg_struct<'a>(cx: &'a ExtCtxt, path: &Vec, + reg: &node::Reg, _width: node::RegWidth) -> P { + let packed_ty = + utils::reg_primitive_type(cx, reg) + .expect("Unexpected non-primitive reg"); + + let reg_doc = match reg.docstring { + Some(d) => token::get_ident(d.node).get().into_string(), + None => "no documentation".into_string(), + }; + let docstring = format!("Register `{}`: {}", + reg.name.node, + reg_doc); + let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring)); + + let ty_name = utils::path_ident(cx, path); + let item = quote_item!(cx, + $doc_attr + #[allow(non_camel_case_types)] + pub struct $ty_name { + value: VolatileCell<$packed_ty>, + } + ); + item.unwrap() +} + +/// Build a variant of an `EnumField` +fn build_enum_variant<'a>(cx: &'a ExtCtxt, + variant: &node::Variant) -> ast::Variant { + let doc = match variant.docstring { + Some(d) => token::get_ident(d.node).get().into_string(), + None => "no documentation".into_string(), + }; + let docstring = format!("`0x{:x}`. {}", + variant.value.node, + doc); + let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring)); + respan( + mk_sp(variant.name.span.lo, variant.value.span.hi), + ast::Variant_ { + name: cx.ident_of(variant.name.node.as_slice()), + attrs: vec!(doc_attr), + kind: ast::TupleVariantKind(Vec::new()), + id: ast::DUMMY_NODE_ID, + disr_expr: Some(utils::expr_int(cx, variant.value.node as i64)), + vis: ast::Inherited, + } + ) +} diff --git a/ioreg/builder/setter.rs b/ioreg/builder/setter.rs new file mode 100644 index 00000000..9068b8a7 --- /dev/null +++ b/ioreg/builder/setter.rs @@ -0,0 +1,269 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Ben Gamari +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syntax::ast; +use syntax::ast::P; +use syntax::ext::base::ExtCtxt; +use syntax::codemap::DUMMY_SP; +use syntax::ext::build::AstBuilder; +use syntax::ext::quote::rt::ToTokens; +use syntax::parse::token; + +use super::Builder; +use super::super::node; +use super::utils; + +/// A visitor to build the field setters for primitive registers +pub struct BuildSetters<'a, 'b, 'c> { + builder: &'a mut Builder, + cx: &'b ExtCtxt<'c>, +} + +impl<'a, 'b, 'c> BuildSetters<'a, 'b, 'c> { + pub fn new(builder: &'a mut Builder, cx: &'b ExtCtxt<'c>) + -> BuildSetters<'a, 'b, 'c> { + BuildSetters { builder: builder, cx: cx } + } +} + +impl<'a, 'b, 'c> node::RegVisitor for BuildSetters<'a, 'b, 'c> { + fn visit_prim_reg<'a>(&'a mut self, path: &Vec, + reg: &'a node::Reg, _width: node::RegWidth, fields: &Vec) + { + if fields.iter().any(|f| f.access != node::ReadOnly) { + let it = build_type(self.cx, path, reg, fields); + self.builder.push_item(it); + + let it = build_drop(self.cx, path, reg, fields); + self.builder.push_item(it); + + let it = build_impl(self.cx, path, reg, fields); + self.builder.push_item(it); + } + } +} + +fn build_type<'a>(cx: &'a ExtCtxt, path: &Vec, + reg: &node::Reg, _fields: &Vec) -> P +{ + let packed_ty = utils::reg_primitive_type(cx, reg) + .expect("Unexpected non-primitive register"); + let name = utils::setter_name(cx, path); + let reg_ty = cx.ty_ident(DUMMY_SP, utils::path_ident(cx, path)); + + let reg_doc = match reg.docstring { + Some(d) => d.node, + None => cx.ident_of("no documentation"), + }; + let docstring = format!("Update value of `{}` register: {}", + reg.name.node, + reg_doc); + let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring)); + + let item = quote_item!(cx, + $doc_attr + #[allow(non_camel_case_types)] + pub struct $name { + value: $packed_ty, + mask: $packed_ty, + reg: &'static $reg_ty, + } + ); + item.unwrap() +} + +fn build_new<'a>(cx: &'a ExtCtxt, path: &Vec) + -> P { + let reg_ty: P = + cx.ty_ident(DUMMY_SP, utils::path_ident(cx, path)); + let setter_ty: P = cx.ty_ident(DUMMY_SP, + utils::setter_name(cx, path)); + let item = quote_item!(cx, + #[doc="Create a new updater"] + pub fn new(reg: &'static $reg_ty) -> $setter_ty { + $setter_ty { + value: 0, + mask: 0, + reg: reg, + } + }); + item.unwrap() +} + +fn build_drop<'a>(cx: &'a ExtCtxt, path: &Vec, + reg: &node::Reg, fields: &Vec) -> P +{ + let setter_ty: P = cx.ty_ident(DUMMY_SP, + utils::setter_name(cx, path)); + let unpacked_ty = utils::reg_primitive_type(cx, reg) + .expect("Unexpected non-primitive register"); + + // ensure we don't unintentionally clear a set-to-clear flag + let mut clear: u32 = 0; + for f in fields.iter() { + match f.access { + node::SetToClear => { + let mask = 1 << (f.count.node * f.width) - 1; + clear |= mask; + }, + _ => {}, + } + } + + let item = quote_item!(cx, + #[unsafe_destructor] + #[doc = "This performs the register update"] + impl Drop for $setter_ty { + fn drop(&mut self) { + let clear_mask: $unpacked_ty = $clear as $unpacked_ty; + if self.mask != 0 { + let v: $unpacked_ty = self.reg.value.get() & ! clear_mask & ! self.mask; + self.reg.value.set(self.value | v); + } + } + } + ); + item.unwrap() +} + +fn build_done<'a>(cx: &'a ExtCtxt) -> P +{ + quote_method!(cx, + #[doc="Commit changes to register. This is to allow chains of `set_*` \ + invocations to be used as a statement."] + pub fn done(self) {} + ) +} + +fn build_impl<'a>(cx: &'a ExtCtxt, path: &Vec, reg: &node::Reg, + fields: &Vec) -> P +{ + let new = build_new(cx, path); + let setter_ty: P = cx.ty_ident( + DUMMY_SP, + utils::setter_name(cx, path)); + let methods: Vec> = + FromIterator::from_iter( + fields.iter() + .filter_map(|field| build_field_fn(cx, path, reg, field))); + let done: P = build_done(cx); + let impl_ = quote_item!(cx, + #[allow(dead_code)] + impl $setter_ty { + $new + $methods + $done + } + ); + impl_.unwrap() +} + +fn build_field_fn<'a>(cx: &'a ExtCtxt, path: &Vec, reg: &node::Reg, + field: &node::Field) -> Option> +{ + match field.access { + node::ReadOnly => None, + node::SetToClear => Some(build_field_clear_fn(cx, path, reg, field)), + _ => Some(build_field_set_fn(cx, path, reg, field)), + } +} + +/// Build a setter for a field +fn build_field_set_fn<'a>(cx: &'a ExtCtxt, path: &Vec, reg: &node::Reg, + field: &node::Field) -> P +{ + let setter_ty = utils::setter_name(cx, path); + let unpacked_ty = utils::reg_primitive_type(cx, reg) + .expect("Unexpected non-primitive register"); + let fn_name = + cx.ident_of((String::from_str("set_")+field.name.node).as_slice()); + let field_ty: P = + cx.ty_path(utils::field_type_path(cx, path, reg, field), None); + let mask = utils::mask(cx, field); + + let field_doc = match field.docstring { + Some(d) => token::get_ident(d.node).get().into_string(), + None => "no documentation".into_string(), + }; + let docstring = format!("Set value of `{}` field: {}", + field.name.node, + field_doc); + let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring)); + + if field.count.node == 1 { + let shift = utils::shift(cx, None, field); + quote_method!(cx, + $doc_attr + pub fn $fn_name<'a>(&'a mut self, new_value: $field_ty) + -> &'a mut $setter_ty { + self.value |= (self.value & ! $mask) | ((new_value as $unpacked_ty) & $mask) << $shift; + self.mask |= $mask << $shift; + self + } + ) + } else { + let shift = utils::shift(cx, Some(quote_expr!(cx, idx)), field); + quote_method!(cx, + $doc_attr + pub fn $fn_name<'a>(&'a mut self, idx: uint, new_value: $field_ty) + -> &'a mut $setter_ty { + self.value |= (self.value & ! $mask) | ((new_value as $unpacked_ty) & $mask) << $shift; + self.mask |= $mask << $shift; + self + } + ) + } +} + +fn build_field_clear_fn<'a>(cx: &'a ExtCtxt, path: &Vec, + _: &node::Reg, field: &node::Field) -> P +{ + let setter_ty = utils::setter_name(cx, path); + let fn_name = + cx.ident_of((String::from_str("clear_")+field.name.node).as_slice()); + let mask = utils::mask(cx, field); + + let field_doc = match field.docstring { + Some(d) => token::get_ident(d.node).get().into_string(), + None => "no documentation".into_string(), + }; + let docstring = format!("Clear `{}` flag: {}", + field.name.node, + field_doc); + let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring)); + + if field.count.node == 1 { + let shift = utils::shift(cx, None, field); + quote_method!(cx, + $doc_attr + pub fn $fn_name<'a>(&'a mut self) -> &'a mut $setter_ty { + self.value |= $mask << $shift; + self.mask |= $mask << $shift; + self + } + ) + } else { + let shift = utils::shift(cx, Some(quote_expr!(cx, idx)), field); + quote_method!(cx, + $doc_attr + pub fn $fn_name<'a>(&'a mut self, idx: uint) + -> &'a mut $setter_ty { + self.value |= $mask << $shift; + self.mask |= $mask << $shift; + self + } + ) + } +} diff --git a/ioreg/builder/union.rs b/ioreg/builder/union.rs new file mode 100644 index 00000000..2f18cf6b --- /dev/null +++ b/ioreg/builder/union.rs @@ -0,0 +1,201 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Ben Gamari +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::gc::{Gc, GC}; +use std::iter::FromIterator; +use syntax::ast; +use syntax::ast::P; +use syntax::codemap::{DUMMY_SP, dummy_spanned}; +use syntax::ext::base::ExtCtxt; +use syntax::ext::build::AstBuilder; +use syntax::parse::token; + +use node; +use super::Builder; +use super::utils; + +enum RegOrPadding<'a> { + /// A register + Reg(&'a node::Reg), + /// A given number of bytes of padding + Pad(uint) +} + +/// An iterator which takes a potentially unsorted list of registers, +/// sorts them, and adds padding to make offsets correct +struct PaddedRegsIterator<'a> { + sorted_regs: &'a Vec, + index: uint, + last_offset: uint, +} + +impl<'a> PaddedRegsIterator<'a> { + fn new(regs: &'a mut Vec) -> PaddedRegsIterator<'a> { + regs.sort_by(|r1,r2| r1.offset.cmp(&r2.offset)); + PaddedRegsIterator { + sorted_regs: regs, + index: 0, + last_offset: 0, + } + } +} + +impl<'a> Iterator> for PaddedRegsIterator<'a> { + fn next(&mut self) -> Option> { + if self.index >= self.sorted_regs.len() { + None + } else { + let ref reg = self.sorted_regs[self.index]; + if reg.offset > self.last_offset { + let pad_length = reg.offset - self.last_offset; + self.last_offset = reg.offset + reg.size(); + Some(Pad(pad_length)) + } else { + self.index += 1; + self.last_offset += reg.size(); + Some(Reg(reg)) + } + } + } +} + +/// Build types for `RegUnions` +pub struct BuildUnionTypes<'a, 'b, 'c> { + builder: &'a mut Builder, + cx: &'b ExtCtxt<'c> +} + +impl<'a, 'b, 'c> BuildUnionTypes<'a, 'b, 'c> { + pub fn new(builder: &'a mut Builder, + cx: &'b ExtCtxt<'c>) + -> BuildUnionTypes<'a, 'b, 'c> { + BuildUnionTypes { builder: builder, cx: cx } + } +} + +/// Returns the type of the field representing the given register +/// within a `RegGroup` struct +fn reg_struct_type(cx: &ExtCtxt, path: &Vec, reg: &node::Reg) + -> P { + let base_ty_path = cx.path_ident(DUMMY_SP, utils::path_ident(cx, path)); + let base_ty: P = cx.ty_path(base_ty_path, None); + match reg.count.node { + 1 => base_ty, + n => + cx.ty(DUMMY_SP, + ast::TyFixedLengthVec(base_ty, + cx.expr_uint(DUMMY_SP, n))), + } +} + + +impl<'a, 'b, 'c> node::RegVisitor for BuildUnionTypes<'a, 'b, 'c> { + fn visit_union_reg<'a>(&'a mut self, path: &Vec, reg: &'a node::Reg, + subregs: Gc>) { + let union_type = self.build_union_type(path, reg, &*subregs); + self.builder.push_item(union_type); + } +} + +impl<'a, 'b, 'c> BuildUnionTypes<'a, 'b, 'c> { + /// Produce a field for the given register in a `RegUnion` struct + fn build_reg_union_field(&self, path: &Vec, reg: &node::Reg) + -> ast::StructField { + let attrs = match reg.docstring { + Some(doc) => vec!(utils::doc_attribute(self.cx, token::get_ident(doc.node))), + None => Vec::new(), + }; + let field_path = path.clone().append_one(reg.name.node.clone()); + dummy_spanned( + ast::StructField_ { + kind: ast::NamedField( + self.cx.ident_of(reg.name.node.as_slice()), + ast::Public), + id: ast::DUMMY_NODE_ID, + ty: reg_struct_type(self.cx, &field_path, reg), + attrs: attrs, + } + ) + } + + /// Build field for padding or a register + fn build_pad_or_reg<'a>(&self, path: &Vec, regOrPad: RegOrPadding<'a>, + index: uint) -> ast::StructField { + match regOrPad { + Reg(reg) => self.build_reg_union_field(path, reg), + Pad(length) => { + let u8_path = self.cx.path_ident( + DUMMY_SP, + self.cx.ident_of("u8")); + let u8_ty: P = self.cx.ty_path(u8_path, None); + let ty: P = + self.cx.ty( + DUMMY_SP, + ast::TyFixedLengthVec(u8_ty, + self.cx.expr_uint(DUMMY_SP, length))); + dummy_spanned( + ast::StructField_ { + kind: ast::NamedField( + self.cx.ident_of(format!("_pad{}", index).as_slice()), + ast::Inherited), + id: ast::DUMMY_NODE_ID, + ty: ty, + attrs: Vec::new(), + }, + ) + }, + } + } + + /// Build the type associated with a register group + fn build_union_type(&self, path: &Vec, reg: &node::Reg, + regs: &Vec) -> P { + let name = dummy_spanned( + String::from_str( + token::get_ident(utils::path_ident(self.cx, path)).get())); + // Registers are already sorted by parser + let mut regs = regs.clone(); + let padded_regs = PaddedRegsIterator::new(&mut regs); + let fields = + padded_regs.enumerate().map(|(n,r)| self.build_pad_or_reg(path, r, n)); + let struct_def = ast::StructDef { + fields: FromIterator::from_iter(fields), + ctor_id: None, + super_struct: None, + is_virtual: false, + }; + let mut attrs: Vec = vec!( + utils::list_attribute(self.cx, "allow", + vec!("non_camel_case_types", + "uppercase_variables", + "dead_code", + "missing_doc")), + ); + match reg.docstring { + Some(docstring) => + attrs.push( + utils::doc_attribute(self.cx, token::get_ident(docstring.node))), + None => (), + } + box(GC) ast::Item { + ident: self.cx.ident_of(name.node.as_slice()), + attrs: attrs, + id: ast::DUMMY_NODE_ID, + node: ast::ItemStruct(box(GC) struct_def, utils::no_generics()), + vis: ast::Public, + span: name.span, + } + } +} diff --git a/ioreg/builder/utils.rs b/ioreg/builder/utils.rs new file mode 100644 index 00000000..522308f9 --- /dev/null +++ b/ioreg/builder/utils.rs @@ -0,0 +1,150 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Ben Gamari +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syntax::ext::base::ExtCtxt; +use syntax::ast; +use syntax::ast::P; +use syntax::codemap::DUMMY_SP; +use syntax::ext::build::AstBuilder; +use syntax::owned_slice::OwnedSlice; +use syntax::parse::token; + +use super::super::node; + +pub fn no_generics() -> ast::Generics { + ast::Generics { + lifetimes: Vec::new(), + ty_params: OwnedSlice::empty() + } +} + +/// Generate an unsuffixed integer literal expression with a dummy span +pub fn expr_int<'a>(cx: &'a ExtCtxt, n: i64) -> P { + let sign = if n < 0 {ast::Minus} else {ast::Plus}; + cx.expr_lit(DUMMY_SP, ast::LitInt(n as u64, ast::UnsuffixedIntLit(sign))) +} + +/// The name of the structure representing a register +pub fn path_ident<'a>(cx: &'a ExtCtxt, path: &Vec) + -> ast::Ident { + cx.ident_of(path.clone().connect("_").as_slice()) +} + + +/// Generate a `#[name(...)]` attribute of the given type +pub fn list_attribute<'a>(cx: &'a ExtCtxt, name: &'static str, + list: Vec<&'static str>) -> ast::Attribute { + let words = + list.move_iter() + .map(|word| cx.meta_word(DUMMY_SP, token::InternedString::new(word))); + let allow = cx.meta_list(DUMMY_SP, token::InternedString::new(name), + FromIterator::from_iter(words)); + cx.attribute(DUMMY_SP, allow) +} + +/// Generate a `#[doc="..."]` attribute of the given type +pub fn doc_attribute<'a>(cx: &'a ExtCtxt, + docstring: token::InternedString) -> ast::Attribute { + let s: ast::Lit_ = ast::LitStr(docstring, ast::CookedStr); + let attr = + cx.meta_name_value(DUMMY_SP, token::InternedString::new("doc"), s); + cx.attribute(DUMMY_SP, attr) +} + +pub fn primitive_type_path(cx: &ExtCtxt, width: node::RegWidth) + -> ast::Path { + let name = match width { + node::Reg8 => "u8", + node::Reg16 => "u16", + node::Reg32 => "u32", + }; + cx.path_ident(DUMMY_SP, cx.ident_of(name)) +} + +/// The `Path` to the type corresponding to the primitive type of +/// the given register +pub fn reg_primitive_type_path(cx: &ExtCtxt, reg: &node::Reg) + -> Option { + match reg.ty { + node::RegPrim(width, _) => Some(primitive_type_path(cx, width)), + _ => None, + } +} + +pub fn reg_primitive_type(cx: &ExtCtxt, reg: &node::Reg) + -> Option> { + let path = reg_primitive_type_path(cx, reg); + path.map(|p| cx.ty_path(p, None)) +} + +pub fn field_type_path<'a>(cx: &'a ExtCtxt, path: &Vec, + reg: &node::Reg, field: &node::Field) -> ast::Path { + let span = field.ty.span; + match field.ty.node { + node::UIntField => { + match reg.ty { + node::RegPrim(width, _) => primitive_type_path(cx, width), + _ => fail!("The impossible happened: a union register with fields"), + } + }, + node::BoolField => cx.path_ident(span, cx.ident_of("bool")), + node::EnumField { opt_name: ref opt_name, ..} => { + match opt_name { + &Some(ref name) => + cx.path_ident(span, cx.ident_of(name.as_slice())), + &None => { + let name = + path.clone().append_one(field.name.node.clone()).connect("_"); + cx.path_ident(span, cx.ident_of(name.as_slice())) + } + } + }, + } +} + +/// Build an expression for the mask of a field +pub fn mask<'a>(cx: &'a ExtCtxt, field: &node::Field) -> P { + expr_int(cx, ((1i << field.width) - 1) as i64) +} + +/// Build an expression for the shift of a field (including the array +/// index if necessary) +pub fn shift<'a>(cx: &'a ExtCtxt, idx: Option>, + field: &node::Field) -> P { + let low = expr_int(cx, field.low_bit as i64); + match idx { + Some(idx) => { + let width = expr_int(cx, field.width as i64); + quote_expr!(cx, $low + $idx * $width) + }, + None => low, + } +} + +/// The name of the setter type for a register +pub fn setter_name<'a>(cx: &'a ExtCtxt, path: &Vec) -> ast::Ident { + let s = path.clone().append_one("Update".to_string()); + path_ident(cx, &s) +} + +/// The name of the getter type for a register +pub fn getter_name<'a>(cx: &'a ExtCtxt, path: &Vec) -> ast::Ident { + let s = path.clone().append_one("Get".to_string()); + path_ident(cx, &s) +} + +pub fn intern_string(cx: &ExtCtxt, s: String) -> token::InternedString { + token::get_ident(cx.ident_of(s.as_slice())) +} diff --git a/ioreg/ioreg.rs b/ioreg/ioreg.rs new file mode 100644 index 00000000..8d03886c --- /dev/null +++ b/ioreg/ioreg.rs @@ -0,0 +1,332 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Ben Gamari +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! +## I/O register interface + +On most embedded platforms interaction with hardware peripherals +occurs through memory-mapped registers. This crate provides a syntax +extension for `rustc` to generate convenient, type-safe register +interfaces from a minimal definition. + +### Concepts + +A *register* is group of bits (typically a word, although on some +platforms smaller). By manipulating the bits of a register one can +affect the state of the associated peripheral. + +### Example register definition + +Let's consider a register block for a simple UART-like peripheral. The +documentation for the peripheral will likely have a table defining the +interface resembling the following, + +```notrust +offset name description +─────── ──────── ────────────────────────── +0x0 CR Configuration register + bit 0 RXE Receive enable + bit 1 TXE Transmit enable + bit 2 RXIE Recieve interrupt enable + bit 3 TXIE Transmit interrupt enable + bit 12:4 BR Baudrate + bit 16:14 PARITY Parity + 0x0 No parity + 0x1 Reserved + 0x2 Even parity + 0x3 Odd parity + +0x4 SR Status register + bit 0 RXNE Receive data register not empty flag (read-only) + bit 1 TXE Transmit data register not empty flag (read-only) + bit 2 FE Framing error flag (set to clear) + +0x8 DR Data register + bits 7:0 D Read returns received data + Write transmits data +``` + +The syntax extension is invoked through through the `ioregs!` macro. A +register definition for the above peripheral might look like this, + +``` +ioregs!(UART = { + 0x0 => reg32 cr { + 0 => rxe, + 1 => txe, + 2 => rxie, + 3 => txie, + 4..12 => br, + 14..16 => parity { + 0x0 => NoParity, + 0x2 => EvenParity, + 0x3 => OddParity, + } + } + + 0x4 => reg32 sr { + 0 => rxne: ro, + 1 => txe: ro, + 2 => fe: set_to_clear, + } + + 0x8 => reg32 dr { + 0..7 => d + } +}) +``` + +Here we've defined a register block called `UART` consisting of a +three registers: `cr`, `sr`, and `dr`. Each register definition +consists of an offset from the beginning of the register block width, +a register type giving the width of the register (`reg32` in this +case), a name, and a list of fields. + +The `cr` register has four boolean flags, an integer +field `br`, and a field `parity` with four possible values +(`NoParity`, `EvenParity`, and `OddParity`). Each field is defined by +a bit or bit range, a name, some optional modifiers (e.g. `ro` in the +case of `rxne`), and an optional list of values. + +This register definition will produce a variety of types, along with +associated accessor methods for convenient, safe manipulation of the +described registers. In the process of generating these, `ioregs!` +will perform a variety of sanity checks (e.g. ensuring that registers +and bitfields are free of overlap). + +#### Documenting register definitions + +It is highly recommended that register definitions include +docstrings. Registers, fields, and `enum` values can all be annotated +with docstrings with the typical Rust `/// comment` syntax +although, in contrast to Rust, a comment will be assumed to describe +the item preceding it. This makes it convenient to place a definition +and its documentation on the same line. + +For instance, we might document the above example as follows, + +``` +ioregs!(UART = { + 0x0 => reg32 cr { ///! Control register + 0 => rxe, /// Receive enable + 1 => txe, /// Transmit enable + 2 => rxie, /// Receive interrupt enable + 3 => txie, /// Transmit interrupt enable + 4..12 => br, /// Baud rate + 14..16 => parity { /// Parity selection + 0x0 => NoParity, /// No parity + 0x2 => EvenParity, /// Even parity + 0x3 => OddParity, /// Odd parity + } + } + + ... +}) +``` + +#### Nesting register blocks + +In addition to primitive register types (e.g. `reg32`), one can also +nest groups of logically related registers. For instance, in the case +of a DMA peripheral it is common that the same block of registers will +be replicated, one for each DMA channel. This can be accomplished with +`ioregs!` as follows, + +``` +ioregs!(DMA = { + 0x0 => reg32 cr { ... } + 0x10 => group channel[4] { + 0x0 => reg32 cr { ... } + 0x4 => reg32 sr { ... } + } + 0x30 => reg32 sr { ... } +}) +``` + +This will produce the following layout in memory, + +```notrust +address register +──────── ────────────── +0x0 cr +0x10 channel[0].cr +0x14 channel[0].sr +0x18 channel[1].cr +0x1c channel[1].sr +0x20 channel[2].cr +0x24 channel[2].sr +0x28 channel[3].cr +0x2c channel[3].sr +0x30 sr +``` + +### What is produced + +The `ioregs!` extension produces a variety of types and methods for +each register and field. Let's start by examining the top-level types +representing the structure of the interface. + +``` +pub enum UART_cr_parity { + NoParity = 0, EvenParity = 2, OddParity = 3, +} + +pub struct UART_cr { ... } +pub struct UART_sr { ... } +pub struct UART_dr { ... } + +pub struct UART { + pub cr: UART_cr, + pub sr: UART_sr, + pub dr: UART_dr, +} +``` + +The `UART` struct is the the "entry-point" into the interface and is +ultimately what will be instantiated to represent the peripheral's +register window, typically as a `static extern` item, + +``` +extern { pub static UART: UART; } +``` + +The register structs (`UART_cr`, `UART_sr`, and `UART_dr`) +have no user visible members but expose a variety of methods. Let's +look at `cr` in particular, + +``` +impl UART_cr { + pub fn get(&'static self) -> UART_cr_Get { ... } + + pub fn set_rxe(&'static self, new_value: bool) -> UART_cr_Update { ... } + pub fn rxe(&'static self) -> bool { ... } + + // similar methods for `txe`, `rxie`, `txie` + + pub fn set_br(&'static self, new_value: u32) -> UART_cr_Update { ... } + pub fn br(&'static self) -> u32 { ... } + + pub fn set_parity(&'static self, new_value: UART_cr_parity) -> UART_cr_Update { ... } + pub fn parity(&'static self) -> UART_cr_parity { ... } +} +``` + +Here we see each field has a corresponding "get" function (e.g. `rxe`, +`br`, and `parity`) as well as a "set" function. Note that the set +function returns a `UART_cr_Update` object. This object mirrors the +setter methods of `UART_cr`, collecting multiple field updates within +a register, performing them on destruction with the `Drop` trait, + +``` +pub struct UART_cr_Update { ... } +impl Drop for UART_cr_Update { ... } + +impl UART_cr_Update { + pub fn set_rxe<'a>(&'a mut self, new_value: bool) -> &'a mut UART_cr_Update { ... } + pub fn set_txe<'a>(&'a mut self, new_value: bool) -> &'a mut UART_cr_Update { ... } + ... +} +``` + +As the set methods return references to `self` they can be easily +chained together. For instance, we can update the `rxe` and `txe` +fields of the `cr` register atomically, + +``` +UART.cr.set_rxe(true).set_txe(false); +``` + +In addition to get and set methods, `UART_cr` also implements a `get` +method which returns a `UART_cr_Get` object mirroring the get methods +of `UART_cr`. This object captures the state of the register allowing +field values to be later atomically queried, + +``` +let cr: UART_cr_Get = UART.cr.get(); +format!("txe={}, rxe={}, br={}", cr.txe(), cr.rxe(), cr.br()) +``` + +In the case of read-only (resp. write-only) fields the set (resp. get) +method is omitted. In the case of `set_to_clear` fields a `clear` +method is instead produced. For instance, in the case of the `sr` +register's `fe` flag, + +``` +pub fn clear_fe(&'static self) -> UART_sr_Update { ... } +``` + +### Informal grammar + +In the below discussion `THING, ...` will denote a list of one or more +`THING`s. The `THING`s must be comma separated except when ending +with a brace-enclosed block. Optional elements are enclosed in `⟦...⟧` +brackets. + +The `ioregs!` macro expects a definition of the form, + +``` +ioregs!(IDENT = { REG, ... }) +``` + +Where a `REG` is either a register group, + +```notrust +OFFSET => group IDENT⟦[COUNT]⟧ { REG, ... } +``` + +or a primitive register, + +```notrust +OFFSET => TYPE IDENT⟦[COUNT]⟧ { FIELD, ... } +``` + +`COUNT` is an integer count and a register `TYPE` is one of `reg8` (a +one byte wide register), `reg16` (two bytes wide), or `reg32` (four +bytes wide). + +A field is given by + +```notrust +BITS => IDENT⟦[COUNT]⟧ ⟦: MODIFIER⟧ ⟦{ VALUE, ... }⟧ +``` + +where `BITS` is either an inclusive range of integers (`N..M`) or a +single integer (shorthand for `N..N`). If a list of values is given +the field is of an enumerated type. Otherwise single bit fields are +of type `bool` and wider fields unsigned integers (in particular, of +the same width as the containing register). + +A `MODIFIER` is one of `rw` (read/write), `ro` (read-only), `wo` +(write-only), or `set_to_clear` (a flag which can be cleared by +setting to one). + +A `VALUE` is given by, + +```notrust +N => NAME +``` + +*/ + +#![feature(quote, struct_variant)] +#![crate_name="ioreg"] +#![crate_type="rlib"] + +extern crate syntax; +extern crate serialize; + +pub mod node; +pub mod parser; +pub mod builder; diff --git a/ioreg/node.rs b/ioreg/node.rs new file mode 100644 index 00000000..065c8e79 --- /dev/null +++ b/ioreg/node.rs @@ -0,0 +1,165 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Ben Gamari +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syntax::codemap::{Spanned, Span}; +use syntax::ast; +use std::gc::Gc; +use serialize::Encodable; + +/// A variant of an enum field type +#[deriving(Clone, Decodable, Encodable)] +pub struct Variant { + pub name: Spanned, + pub value: Spanned, + pub docstring: Option>, +} + +/// A bit field type +#[deriving(Clone, Decodable, Encodable)] +pub enum FieldType { + /// A unsigned integer + UIntField, + /// A boolean flag + BoolField, + /// A enum + EnumField { + pub opt_name: Option, + pub variants: Vec, + }, +} + +#[deriving(PartialEq, Eq, Clone, Decodable, Encodable)] +pub enum Access { + ReadWrite, + ReadOnly, + WriteOnly, + /// A flag which can be set to clear + SetToClear, +} + +#[deriving(Clone, Decodable, Encodable)] +pub struct Field { + pub name: Spanned, + /// The index of the first (lowest order) bit of the field + pub low_bit: uint, + /// The width in bits of a single array element + pub width: uint, + /// The number of array elements + pub count: Spanned, + pub bit_range_span: Span, + pub access: Access, + pub ty: Spanned, + pub docstring: Option>, +} + +impl Field { + /// The index of the highest order bit owned by this field + pub fn high_bit(&self) -> uint { + self.low_bit + self.width * self.count.node - 1 + } +} + +#[deriving(Clone, Decodable, Encodable)] +pub enum RegWidth { + /// A 32-bit wide register + Reg32, + /// A 16-bit wide register + Reg16, + /// An 8-bit wide register + Reg8, +} + +impl RegWidth { + /// Size of register type in bytes + pub fn size(&self) -> uint { + match *self { + Reg32 => 4, + Reg16 => 2, + Reg8 => 1, + } + } +} + +#[deriving(Clone, Decodable, Encodable)] +pub enum RegType { + /// A primitive bitfield + RegPrim(RegWidth, Vec), + /// A group + RegUnion(Gc>), +} + +impl RegType { + /// Size of register type in bytes + pub fn size(&self) -> uint { + match *self { + RegPrim(width, _) => width.size(), + RegUnion(regs) => regs_size(regs), + } + } +} + +/// A single register, either a union or primitive +#[deriving(Clone, Decodable, Encodable)] +pub struct Reg { + pub offset: uint, + pub name: Spanned, + pub ty: RegType, + pub count: Spanned, + pub docstring: Option>, +} + +impl Reg { + /// Size of a register in bytes + pub fn size(&self) -> uint { + self.count.node * self.ty.size() + } + /// The offset of the last byte owned by this register + pub fn last_byte(&self) -> uint { + self.offset + self.size() - 1 + } +} + +/// Size of registers of register group in bytes +pub fn regs_size(regs: Gc>) -> uint { + match regs.iter().max_by(|r| r.offset) { + Some(last) => last.offset + last.ty.size(), + None => 0, + } +} + +pub trait RegVisitor { + /// Path includes name of `Reg` being visited + fn visit_prim_reg<'a>(&'a mut self, _path: &Vec, _reg: &'a Reg, + _width: RegWidth, _fields: &Vec) {} + fn visit_union_reg<'a>(&'a mut self, _path: &Vec, _reg: &'a Reg, + _subregs: Gc>) {} +} + +pub fn visit_reg(reg: &Reg, visitor: &mut T) { + visit_reg_(reg, visitor, vec!(reg.name.node.clone())) +} + +fn visit_reg_(reg: &Reg, visitor: &mut T, path: Vec) { + match reg.ty { + RegUnion(ref regs) => { + visitor.visit_union_reg(&path, reg, *regs); + for r in regs.iter() { + visit_reg_(r, visitor, path.clone().append_one(r.name.node.clone())); // FIXME clone + } + }, + RegPrim(width, ref fields) => + visitor.visit_prim_reg(&path, reg, width, fields) + } +} diff --git a/ioreg/parser.rs b/ioreg/parser.rs new file mode 100644 index 00000000..e60dd22a --- /dev/null +++ b/ioreg/parser.rs @@ -0,0 +1,588 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Ben Gamari +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::gc::{Gc, GC}; +use syntax::ast; +use syntax::ast::{Ident, TokenTree}; +use syntax::codemap::{Span, Spanned, respan, dummy_spanned, mk_sp}; +use syntax::ext::base::ExtCtxt; +use syntax::parse; +use syntax::parse::{token, ParseSess, lexer}; + +use node; + +pub struct Parser<'a,'b> { + cx: &'a ExtCtxt<'b>, + sess: &'a ParseSess, + reader: Box, + token: token::Token, + span: Span, + + last_token: Option>, + last_span: Span, +} + +impl<'a, 'b> Parser<'a, 'b> { + pub fn new<'a, 'b>(cx: &'a ExtCtxt<'b>, tts: &[TokenTree]) -> Parser<'a, 'b> { + let sess = cx.parse_sess(); + let ttsvec = tts.iter().map(|x| (*x).clone()).collect(); + let mut reader = box lexer::new_tt_reader( + &sess.span_diagnostic, None, ttsvec) as Box; + + let tok0 = reader.next_token(); + let token = tok0.tok; + let span = tok0.sp; + + Parser { + cx: cx, + sess: sess, + reader: reader, + + token: token, + span: span, + + last_token: None, + last_span: span, + } + } + + /// Parse the ioregs from passed in tokens. + pub fn parse_ioregs(&mut self) -> Option> { + let name = match self.expect_ident() { + Some(name) => respan(self.last_span, name), + None => return None, + }; + + if !self.expect(&token::EQ) { + return None; + } + + if !self.expect(&token::LBRACE) { + return None; + } + + let docstring = self.parse_docstring(); + + let regs = match self.parse_regs() { + Some(regs) => regs, + None => return None, + }; + + let group = node::Reg { + offset: 0, + name: name, + ty: node::RegUnion(box(GC) regs), + count: dummy_spanned(1), + docstring: docstring, + }; + + Some(box(GC) group) + } + + /// Parse a block of regs + fn parse_regs(&mut self) -> Option> { + // sitting at start of first register, after LBRACE so that the + // owner of this block can catch its docstrings + + let mut regs: Vec = Vec::new(); + loop { + match self.token.clone() { + // End of block + token::RBRACE => { + self.bump(); + + // Eat optional comma after closing brace + if self.token == token::COMMA { + self.bump(); + } + + break; + }, + + // Presumably a register + _ => { + match self.parse_reg() { + Some(reg) => regs.push(reg), + None => return None, + } + }, + } + } + + regs.sort_by(|r1,r2| r1.offset.cmp(&r2.offset)); + + // Verify that registers don't overlap + let mut failed = false; + for (r1,r2) in regs.iter().zip(regs.iter().skip(1)) { + if r2.offset <= r1.last_byte() { + self.sess.span_diagnostic.span_err( + r1.name.span, + format!("The byte range of register ({} to {})", + r1.offset, r1.last_byte()).as_slice()); + self.sess.span_diagnostic.span_err( + r2.name.span, + format!("overlaps with the range of this register ({} to {})", + r2.offset, r2.last_byte()).as_slice()); + failed = true; + } + } + + if failed { + None + } else { + Some(regs) + } + } + + /// Parse the introduction of a register + fn parse_reg(&mut self) -> Option { + // we are still sitting at the offset + let offset = match self.expect_uint() { + Some(offset) => offset, + None => return None, + }; + if !self.expect(&token::FAT_ARROW) { + return None; + } + + let ty = match self.expect_ident() { + Some(ref i) if i.equiv(&"reg32") => node::RegPrim(node::Reg32, Vec::new()), + Some(ref i) if i.equiv(&"reg16") => node::RegPrim(node::Reg16, Vec::new()), + Some(ref i) if i.equiv(&"reg8") => node::RegPrim(node::Reg8, Vec::new()), + Some(ref i) if i.equiv(&"group") => { + // registers will get filled in later + node::RegUnion(box(GC) Vec::new()) + }, + _ => { + self.error(format!("expected register type but found `{}`", + token::to_string(&self.token))); + return None; + }, + }; + + let name = match self.expect_ident() { + Some(name) => respan(self.last_span, name), + None => return None, + }; + let count = match self.parse_count() { + None => return None, + Some(count) => count, + }; + + let docstring = self.parse_docstring(); + + let ty = match ty { + node::RegPrim(width, _) => { + if !self.expect(&token::LBRACE) { + return None; + } + match self.parse_fields() { + None => return None, + Some(mut fields) => { + // Check for overlapping fields + fields.sort_by(|f1,f2| f1.low_bit.cmp(&f2.low_bit)); + for (f1,f2) in fields.iter().zip(fields.iter().skip(1)) { + if f2.low_bit <= f1.high_bit() { + self.sess.span_diagnostic.span_err( + f1.bit_range_span, "The bit range of this field,".as_slice()); + self.sess.span_diagnostic.span_err( + f2.bit_range_span, + "overlaps with the bit range of this field".as_slice()); + return None; + } + } + + // Verify fields fit in register + match fields.last().map(|f| f.high_bit()) { + Some(last_bit) if last_bit >= 8*width.size() => { + self.sess.span_diagnostic.span_err( + name.span, + format!("Width of fields ({} bits) exceeds access size of register ({} bits)", + last_bit+1, 8*width.size()).as_slice()); + return None; + }, + _ => {} + } + + node::RegPrim(width, fields) + }, + } + }, + node::RegUnion(_) => { + if !self.expect(&token::LBRACE) { + return None; + } + match self.parse_regs() { + Some(regs) => node::RegUnion(box(GC) regs), + None => return None, + } + }, + }; + + Some(node::Reg { + offset: offset, + name: name, + ty: ty, + count: count, + docstring: docstring, + }) + } + + fn parse_fields(&mut self) -> Option> { + // sitting at starting bit number + let mut fields: Vec = Vec::new(); + loop { + if self.token == token::RBRACE { + self.bump(); + + // Eat optional comma after closing brace + if self.token == token::COMMA { + self.bump(); + } + + break; + } + + match self.parse_field() { + None => return None, + Some(field) => fields.push(field), + } + } + Some(fields) + } + + /// Parse a field. + /// + /// `None` indicates parse failure otherwise we return whether a + /// comma is required before the next field (as we might have + /// already seen the comma before the docstring) in addition to the + /// parsed field. + /// + fn parse_field(&mut self) -> Option { + let mut require_comma: bool = true; + + // sitting at starting bit number + let low_bit = match self.expect_uint() { + Some(bit) => bit, + None => return None, + }; + let bits_span = self.span; + let high_bit = match self.token { + token::DOTDOT => { + self.bump(); + match self.expect_uint() { + Some(bit) => bit as uint, + None => return None, + } + }, + _ => low_bit as uint, + }; + + // TODO(bgamari): Do we want to enforce an order here? + let (low_bit, high_bit) = + if high_bit < low_bit { + (high_bit, low_bit) + } else { + (low_bit, high_bit) + }; + + if !self.expect(&token::FAT_ARROW) { + return None; + } + + let name = match self.expect_ident() { + Some(name) => respan(self.last_span, name), + None => return None, + }; + + let (count, width): (Spanned, uint) = + match self.parse_count() { + Some(count) => { + let w = high_bit - low_bit + 1; + if w % count.node == 0 { + (count, w / count.node) + } else { + self.sess.span_diagnostic.span_err( + mk_sp(bits_span.lo, self.last_span.hi), + format!("Bit width ({}) not divisible by count ({})", + w, count.node).as_slice()); + return None; + } + }, + None => return None, + }; + + let access = match self.token.clone() { + token::COLON => { + self.bump(); + match self.token.clone() { + ref t@token::IDENT(_,_) => { + match token::to_string(t) { + ref s if s.equiv(&"rw") => { self.bump(); node::ReadWrite }, + ref s if s.equiv(&"ro") => { self.bump(); node::ReadOnly }, + ref s if s.equiv(&"wo") => { self.bump(); node::WriteOnly }, + ref s if s.equiv(&"set_to_clear") => { self.bump(); node::SetToClear }, + s => { + self.error(format!("Expected access type, saw `{}`", s)); + return None; + }, + } + }, + ref t => { + self.error(format!("Expected access type, saw `{}`", + token::to_string(t))); + return None; + }, + } + }, + _ => node::ReadWrite, + }; + + if self.token == token::COMMA { + self.bump(); + require_comma = false; + } + + let docstring = self.parse_docstring(); + + let ty = match self.token { + // A list of enumeration variants + token::LBRACE if !require_comma => { + self.error(String::from_str("Unexpected enumeration list after comma")); + return None; + }, + token::LBRACE => { + // we don't require a delimiting comma after a block + require_comma = false; + match self.parse_enum_variants() { + Some(variants) => node::EnumField {opt_name: None, variants: variants}, + None => return None, + } + }, + _ => { + if width == 1 { + node::BoolField + } else { + node::UIntField + } + }, + }; + + // Require a comma unless we are the last element in the block + if self.token != token::RBRACE { + if require_comma { + if !self.expect(&token::COMMA) { + return None; + } + } else { + match self.token { + token::COMMA => {self.bump();}, + _ => {} + } + } + } + + let field = node::Field { + name: name, + low_bit: low_bit, + width: width, + count: count, + bit_range_span: bits_span, + access: access, + ty: dummy_spanned(ty), + docstring: docstring, + }; + Some(field) + } + + fn parse_enum_variants(&mut self) -> Option> { + // sitting on LBRACE + let mut variants: Vec = Vec::new(); + + if !self.expect(&token::LBRACE) { + return None; + } + + let mut require_comma: bool = false; + loop { + if self.token == token::RBRACE { + self.bump(); + break; + } + + if require_comma && !self.expect(&token::COMMA) { + return None; + } + require_comma = true; + + if self.token == token::RBRACE { + self.bump(); + break; + } + + let value = match self.expect_uint() { + Some(v) => respan(self.last_span, v), + _ => return None, + }; + + if !self.expect(&token::FAT_ARROW) { + return None; + } + + let name = match self.expect_ident() { + Some(name) => respan(self.span, name), + None => return None, + }; + + // Catch commas before the docstring + match self.token { + token::COMMA => { + require_comma = false; + self.bump(); + } + _ => {} + } + + let docstring = self.parse_docstring(); + + let value: node::Variant = node::Variant { name: name, value: value, docstring: docstring }; + variants.push(value); + } + Some(variants) + } + + fn parse_docstring(&mut self) -> Option> { + let mut docs: Vec = Vec::new(); + loop { + match self.token { + token::DOC_COMMENT(docstring) => { + self.bump(); + // for some reason ident begins with '/// ' + let s = token::get_ident(docstring.ident()); + let stripped = s.get().trim_left_chars(&['/',' ']); + docs.push(String::from_str(stripped)); + }, + _ => break, + } + } + let string = docs.connect("\n"); + let string = string.as_slice().trim(); + if !string.is_empty() { + Some(respan(self.last_span, self.cx.ident_of(string))) + } else { + None + } + } + + fn parse_uint(&mut self) -> Option { + match self.token { + token::LIT_INTEGER(n) => { + self.bump(); + let lit = parse::integer_lit(n.as_str(), + &self.sess.span_diagnostic, + self.span); + match lit { + ast::LitInt(n, _) => Some(n as uint), + _ => None, + } + }, + _ => None, + } + } + + fn expect_uint(&mut self) -> Option { + match self.parse_uint() { + Some(n) => Some(n), + None => { + let this_token_str = token::to_string(&self.token); + self.error(format!("expected integer but found `{}`", this_token_str)); + None + }, + } + } + + /// `None` indicates parse failure. + /// If no count is given, a default of 1 is used + fn parse_count(&mut self) -> Option> { + match self.token { + token::LBRACKET => { + self.bump(); + let ret = match self.expect_uint() { + Some(count) => respan(self.last_span, count), + None => return None, + }; + if !self.expect(&token::RBRACKET) { + self.error(format!("expected `]` but found `{}`", + token::to_string(&self.token))); + return None; + } + Some(ret) + }, + _ => Some(dummy_spanned(1)), + } + } + + fn error(&self, m: String) { + self.sess.span_diagnostic.span_err(self.span, m.as_slice()); + } + + /// Bumps a token. + /// + /// This moves current token to last token, pops a new token from backlog or + /// reader and returns the last token (i.e. the 'current' token at the time of + /// method call). + fn bump(&mut self) -> token::Token { + let tok = self.token.clone(); + self.last_span = self.span; + self.last_token = Some(box tok.clone()); + + let next = self.reader.next_token(); + + self.span = next.sp; + self.token = next.tok; + + tok + } + + /// Expects that the current token is t. Bumps on success. + fn expect(&mut self, t: &token::Token) -> bool { + if self.token == *t { + self.bump(); + true + } else { + let token_str = token::to_string(t); + let this_token_str = token::to_string(&self.token); + self.error(format!("expected `{}` but found `{}`", token_str, + this_token_str)); + false + } + } + + /// Expects that the current token is IDENT, returns its string value. Bumps + /// on success. + fn expect_ident(&mut self) -> Option { + let tok_str = token::to_string(&self.token); + match self.token { + token::IDENT(_, _) => { + self.bump(); + Some(tok_str) + }, + _ => { + self.error(format!("expected identifier but found `{}`", tok_str)); + None + }, + } + } +} diff --git a/macro/ioreg.rs b/macro/ioreg.rs new file mode 100644 index 00000000..a6dae86a --- /dev/null +++ b/macro/ioreg.rs @@ -0,0 +1,68 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Ben Gamari +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![crate_name="macro_ioreg"] +#![crate_type="dylib"] + +#![feature(plugin_registrar, quote, managed_boxes)] + +extern crate rustc; +extern crate syntax; +extern crate ioreg; + +use rustc::plugin::Registry; +use std::gc::Gc; +use syntax::ast; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, MacResult}; +use syntax::util::small_vector::SmallVector; + +use ioreg::parser::Parser; +use ioreg::builder::Builder; + +#[plugin_registrar] +pub fn plugin_registrar(reg: &mut Registry) { + reg.register_macro("ioregs", macro_ioregs); +} + +pub fn macro_ioregs(cx: &mut ExtCtxt, _: Span, tts: &[ast::TokenTree]) + -> Box { + match Parser::new(cx, tts).parse_ioregs() { + Some(group) => { + let mut builder = Builder::new(); + let items = builder.emit_items(cx, group); + MacItems::new(items) + }, + None => { + fail!(); + } + } +} + +pub struct MacItems { + items: Vec> +} + +impl MacItems { + pub fn new(items: Vec>) -> Box { + box MacItems { items: items } as Box + } +} + +impl MacResult for MacItems { + fn make_items(&self) -> Option>> { + Some(SmallVector::many(self.items.clone())) + } +} diff --git a/src/main.rs b/src/main.rs index 06203c7f..0f6de9e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,6 +49,7 @@ extern crate rlibc; #[cfg(test)] #[phase(plugin,link)] extern crate std; #[cfg(test)] extern crate native; +#[phase(plugin)] extern crate macro_ioreg; pub mod drivers; pub mod hal; @@ -59,4 +60,6 @@ pub mod os; #[cfg(not(test))] mod std { pub use core::cmp; // used for #[deriving(Eq)] until fixed in rust. + pub use core::option; + pub use core::num; }