diff --git a/Cargo.lock b/Cargo.lock index b01356d7..168eb15e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,11 +2,12 @@ name = "svd2rust" version = "0.1.2" dependencies = [ - "clap 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.19.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "inflections 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "svd-parser 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)", + "svd-parser 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -21,19 +22,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "clap" -version = "2.14.0" +version = "2.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "term_size 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-segmentation 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "either" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "inflections" version = "1.0.0" @@ -50,22 +56,22 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" -version = "0.3.3" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "strsim" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "svd-parser" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -73,10 +79,10 @@ dependencies = [ [[package]] name = "syn" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -86,13 +92,13 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unicode-segmentation" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -122,7 +128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "xml-rs" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -133,26 +139,27 @@ name = "xmltree" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" -"checksum clap 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5fa304b03c49ccbb005784fc26e985b5d2310b1d37f2c311ce90dbcd18ea5fde" +"checksum clap 2.19.2 (registry+https://github.com/rust-lang/crates.io-index)" = "305ad043f009db535a110200541d4567b63e172b1fe030313fbb92565da7ed24" +"checksum either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2b503c86dad62aaf414ecf2b8c527439abedb3f8d812537f0b12bfd6f32a91" "checksum inflections 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a825932b96cd7361036ec60126ded8524c618eb8dd0256575f482009a89cf8e5" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d" -"checksum quote 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b22ffb272d214bfc8b6c89ff856d0acb52f0cd6b85162243460c9291b93c56e" -"checksum strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "50c069df92e4b01425a8bf3576d5d417943a6a7272fbabaf5bd80b1aaa76442e" -"checksum svd-parser 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3df0243577c33a443b179ada44360e1ed5786a99e2c0c47ed1a97f1c2b296d63" -"checksum syn 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96fed4e825d615b0ffd74dabb1dc4c5a078ab44e2c8004798f01510edf6cf515" +"checksum libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "a51822fc847e7a8101514d1d44e354ba2ffa7d4c194dcab48870740e327cac70" +"checksum quote 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)" = "6732e32663c9c271bfc7c1823486b471f18c47a2dbf87c066897b7b51afc83be" +"checksum strsim 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "67f84c44fbb2f91db7fef94554e6b2ac05909c9c0b0bc23bb98d3a1aebfe7f7c" +"checksum svd-parser 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b40488b057aa79b7134dfe50fb81d98553c59de6996be2a1fd62b929697a26e4" +"checksum syn 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "76c2db66dc579998854d84ff0ff4a81cb73e69596764d144ce7cece4d04ce6b5" "checksum term_size 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f7f5f3f71b0040cecc71af239414c23fd3c73570f5ff54cf50e03cef637f2a0" -"checksum unicode-segmentation 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b905d0fc2a1f0befd86b0e72e31d1787944efef9d38b9358a9e92a69757f7e3b" +"checksum unicode-segmentation 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c3bc443ded17b11305ffffe6b37e2076f328a5a8cb6aa877b1b98f77699e98b5" "checksum unicode-width 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2d6722facc10989f63ee0e20a83cd4e1714a9ae11529403ac7e0afd069abc39e" "checksum unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb" "checksum vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cac5efe5cb0fa14ec2f84f83c701c562ee63f6dcc680861b21d65c682adfb05f" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65e74b96bd3179209dc70a980da6df843dff09e46eee103a0376c0949257e3ef" +"checksum xml-rs 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b15eed12692bd59d15e98ee7f8dc8408465b992d8ddb4d1672c24865132ec7" "checksum xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "472a9d37c7c53ab2391161df5b89b1f3bf76dab6ab150d7941ecbdd832282082" diff --git a/Cargo.toml b/Cargo.toml index 9996cd41..5233bd83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,8 @@ version = "0.1.2" [dependencies] clap = "2.14.0" +either = "1.0.2" inflections = "1.0.0" quote = "0.3.3" -svd-parser = "0.1.2" +svd-parser = "0.2.0" syn = "0.9" diff --git a/ci/run.sh b/ci/run.sh index 246970ef..c23ce909 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -2,7 +2,7 @@ set -ex test_gen() { echo 'extern crate volatile_register;' > $td/src/lib.rs - cargo run $flags --release -- -i $td/STM32F30x.svd $1 >> $td/src/lib.rs + cargo run $flags --release -- -i $td/$svd $1 >> $td/src/lib.rs cargo build $flags --manifest-path $td/Cargo.toml } @@ -15,11 +15,16 @@ test_mode() { https://raw.githubusercontent.com/posborne/cmsis-svd/python-0.4/data/STMicro/STM32F30x.svd \ > $td/STM32F30x.svd + curl -L \ + https://raw.githubusercontent.com/posborne/cmsis-svd/python-0.4/data/Nordic/nrf51.svd \ + > $td/nrf51.svd + # test the library cargo build $flags cargo build $flags --release # test the generated code + svd=STM32F30x.svd test_gen test_gen dbgmcu test_gen gpioa @@ -30,6 +35,10 @@ test_mode() { test_gen tim2 test_gen tim3 test_gen tim6 + + svd=nrf51.svd + test_gen + test_gen gpio } deploy_mode() { diff --git a/src/lib.rs b/src/lib.rs index bdc7f508..190608f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -240,21 +240,24 @@ #![recursion_limit = "128"] +extern crate either; extern crate inflections; extern crate svd_parser as svd; #[macro_use] extern crate quote; extern crate syn; -use std::io; +use std::borrow::Cow; use std::io::Write; +use std::io; +use std::rc::Rc; +use either::Either; +use inflections::Inflect; use quote::Tokens; +use svd::{Access, Defaults, Peripheral, Register, RegisterInfo}; use syn::*; -use inflections::Inflect; -use svd::{Access, Defaults, Peripheral, Register}; - /// Trait that sanitizes name avoiding rust keywords and the like. trait SanitizeName { /// Sanitize a name; avoiding Rust keywords and the like. @@ -265,6 +268,7 @@ impl SanitizeName for String { fn sanitize(&self) -> String { match self.as_str() { "fn" => "fn_".to_owned(), + "in" => "in_".to_owned(), "match" => "match_".to_owned(), "mod" => "mod_".to_owned(), _ => self.to_owned(), @@ -274,7 +278,8 @@ impl SanitizeName for String { #[doc(hidden)] pub fn gen_peripheral(p: &Peripheral, d: &Defaults) -> Vec<Tokens> { - assert!(p.derived_from.is_none(), "DerivedFrom not supported here (should be resolved earlier)"); + assert!(p.derived_from.is_none(), + "DerivedFrom not supported here (should be resolved earlier)"); let mut items = vec![]; let mut fields = vec![]; @@ -284,11 +289,8 @@ pub fn gen_peripheral(p: &Peripheral, d: &Defaults) -> Vec<Tokens> { .as_ref() .expect(&format!("{:#?} has no `registers` field", p)); - let mut registers: Vec<&Register> = registers.iter().collect(); - registers.sort_by_key(|x| x.address_offset); - - for register in registers.iter() { - let pad = if let Some(pad) = register.address_offset + for register in expand(registers).iter() { + let pad = if let Some(pad) = register.offset .checked_sub(offset) { pad } else { @@ -296,7 +298,7 @@ pub fn gen_peripheral(p: &Peripheral, d: &Defaults) -> Vec<Tokens> { "WARNING {} overlaps with another register at offset \ {}. Ignoring.", register.name, - register.address_offset) + register.offset) .ok(); continue; }; @@ -311,20 +313,25 @@ pub fn gen_peripheral(p: &Peripheral, d: &Defaults) -> Vec<Tokens> { } let comment = &format!("0x{:02x} - {}", - register.address_offset, - respace(®ister.description))[..]; + register.offset, + respace(®ister.info + .description))[..]; - let reg_ty = Ident::new(register.name.to_pascal_case()); - let reg_name = Ident::new(register.name.to_snake_case().sanitize()); + let reg_ty = match register.ty { + Either::Left(ref ty) => Ident::from(&**ty), + Either::Right(ref ty) => Ident::from(&***ty), + }; + let reg_name = Ident::new(&*register.name); fields.push(quote! { #[doc = #comment] pub #reg_name : #reg_ty }); - offset = register.address_offset + - register.size + offset = register.offset + + register.info + .size .or(d.size) - .expect(&format!("{:#?} has no `size` field", register)) / + .expect(&format!("{:#?} has no `size` field", register.info)) / 8; } @@ -347,24 +354,106 @@ pub fn gen_peripheral(p: &Peripheral, d: &Defaults) -> Vec<Tokens> { items.push(struct_); for register in registers { + let access = access(®ister); + items.extend(gen_register(register, d)); - items.extend(gen_register_r(register, d)); - items.extend(gen_register_w(register, d)); + if access != Access::WriteOnly { + items.extend(gen_register_r(register, d)); + } + if access != Access::ReadOnly { + items.extend(gen_register_w(register, d)); + } } items } -#[doc(hidden)] -pub fn gen_register(r: &Register, d: &Defaults) -> Vec<Tokens> { - let mut items = vec![]; +struct ExpandedRegister<'a> { + info: &'a RegisterInfo, + name: String, + offset: u32, + ty: Either<String, Rc<String>>, +} - let name = Ident::new(r.name.to_pascal_case()); - let bits_ty = r.size - .or(d.size) - .expect(&format!("{:#?} has no `size` field", r)) - .to_ty(); - let access = r.access.unwrap_or_else(|| { +/// Takes a list of "registers", some of which may actually be register arrays, +/// and turns it into a new *sorted* (by address offset) list of registers where +/// the register arrays have been expanded. +fn expand(registers: &[Register]) -> Vec<ExpandedRegister> { + let mut out = vec![]; + + for r in registers { + match *r { + Register::Single(ref info) => { + out.push(ExpandedRegister { + info: info, + name: info.name.to_snake_case().sanitize(), + offset: info.address_offset, + ty: Either::Left(info.name.to_pascal_case()), + }) + } + Register::Array(ref info, ref array_info) => { + let has_brackets = info.name.contains("[%s]"); + + let ty = if has_brackets { + info.name.replace("[%s]", "") + } else { + info.name.replace("%s", "") + }; + + let ty = Rc::new(ty.to_pascal_case()); + + let indices = array_info.dim_index + .as_ref() + .map(|v| Cow::from(&**v)) + .unwrap_or_else(|| { + Cow::from((0..array_info.dim) + .map(|i| i.to_string()) + .collect::<Vec<_>>()) + }); + + for (idx, i) in indices.iter().zip(0..) { + let name = if has_brackets { + info.name.replace("[%s]", idx) + } else { + info.name.replace("%s", idx) + }; + + let offset = info.address_offset + + i * array_info.dim_increment; + + out.push(ExpandedRegister { + info: info, + name: name.to_snake_case().sanitize(), + offset: offset, + ty: Either::Right(ty.clone()), + }); + } + } + } + } + + out.sort_by_key(|x| x.offset); + + out +} + +fn type_of(r: &Register) -> String { + let ty = match *r { + Register::Single(ref info) => Cow::from(&*info.name), + Register::Array(ref info, _) => { + if info.name.contains("[%s]") { + info.name.replace("[%s]", "").into() + } else { + info.name.replace("%s", "").into() + } + } + }; + + (&*ty).to_pascal_case() +} + +fn access(r: &Register) -> Access { + r.access.unwrap_or_else(|| { let fields = r.fields .as_ref() .expect(&format!("{:#?} has no `fields` field", r)); @@ -375,10 +464,23 @@ pub fn gen_register(r: &Register, d: &Defaults) -> Vec<Tokens> { } else { Access::ReadWrite } - }); + }) +} + +#[doc(hidden)] +pub fn gen_register(r: &Register, d: &Defaults) -> Vec<Tokens> { + let mut items = vec![]; + + let ty = type_of(r); + let name = Ident::new(&*ty); + let bits_ty = r.size + .or(d.size) + .expect(&format!("{:#?} has no `size` field", r)) + .to_ty(); + let access = access(r); - let name_r = Ident::new(format!("{}R", r.name.to_pascal_case())); - let name_w = Ident::new(format!("{}W", r.name.to_pascal_case())); + let name_r = Ident::new(format!("{}R", ty)); + let name_w = Ident::new(format!("{}W", ty)); match access { Access::ReadOnly => { items.push(quote! { @@ -463,7 +565,7 @@ pub fn gen_register(r: &Register, d: &Defaults) -> Vec<Tokens> { pub fn gen_register_r(r: &Register, d: &Defaults) -> Vec<Tokens> { let mut items = vec![]; - let name = Ident::new(format!("{}R", r.name.to_pascal_case())); + let name = Ident::new(format!("{}R", type_of(r))); let bits_ty = r.size .or(d.size) .expect(&format!("{:#?} has no `size` field", r)) @@ -544,7 +646,7 @@ pub fn gen_register_r(r: &Register, d: &Defaults) -> Vec<Tokens> { pub fn gen_register_w(r: &Register, d: &Defaults) -> Vec<Tokens> { let mut items = vec![]; - let name = Ident::new(format!("{}W", r.name.to_pascal_case())); + let name = Ident::new(format!("{}W", type_of(r))); let bits_ty = r.size .or(d.size) .expect(&format!("{:#?} has no `size` field", r)) @@ -559,7 +661,8 @@ pub fn gen_register_w(r: &Register, d: &Defaults) -> Vec<Tokens> { let mut impl_items = vec![]; - if let Some(reset_value) = r.reset_value + if let Some(reset_value) = + r.reset_value .or(d.reset_value) .map(|x| Lit::Int(x as u64, IntTy::Unsuffixed)) { impl_items.push(quote! { diff --git a/src/main.rs b/src/main.rs index 54089639..acfa59a2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,19 +43,29 @@ fn main() { Some(pattern) => { if let Some(peripheral) = find_peripheral(&d, |n| n == pattern) .or_else(|| find_peripheral(&d, |n| n.contains(pattern))) { - if let Some(base_peripheral) = peripheral.derived_from.as_ref() - .and_then(|bn| find_peripheral(&d, |n| n == bn.to_ascii_lowercase())) { + if let Some(base_peripheral) = + peripheral.derived_from + .as_ref() + .and_then(|bn| { + find_peripheral(&d, + |n| n == bn.to_ascii_lowercase()) + }) { let merged_peripheral = merge(peripheral, base_peripheral); - println!("{}", gen_peripheral_desc(&merged_peripheral, &d.defaults)); + println!("{}", + gen_peripheral_desc(&merged_peripheral, + &d.defaults)); } else { - println!("{}", gen_peripheral_desc(peripheral, &d.defaults)); + println!("{}", + gen_peripheral_desc(peripheral, &d.defaults)); } } } } } -fn find_peripheral<F: Fn(&str) -> bool>(device: &svd::Device, matcher: F) -> Option<&svd::Peripheral> { +fn find_peripheral<F: Fn(&str) -> bool>(device: &svd::Device, + matcher: F) + -> Option<&svd::Peripheral> { device.peripherals.iter().find(|x| matcher(&x.name.to_ascii_lowercase())) } @@ -68,7 +78,10 @@ fn gen_peripheral_desc(p: &svd::Peripheral, def: &svd::Defaults) -> String { } fn merge(p: &svd::Peripheral, bp: &svd::Peripheral) -> svd::Peripheral { - assert!(p.registers.is_none() || bp.registers.is_none(), "Either {} registers or {} registers must be absent in SVD", p.name, bp.name); + assert!(p.registers.is_none() || bp.registers.is_none(), + "Either {} registers or {} registers must be absent in SVD", + p.name, + bp.name); svd::Peripheral { name: p.name.clone(),