From 8ed24a1d4d91a9225b2ba6540d54cf7514d445f2 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Wed, 5 Jul 2017 11:24:48 +0200 Subject: [PATCH 01/21] Extracted sort from expand into its own function --- src/generate.rs | 2 +- src/util.rs | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/generate.rs b/src/generate.rs index a5e4fa18..a38ec52d 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -467,7 +467,7 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { let mut i = 0; // offset from the base address, in bytes let mut offset = 0; - for register in util::expand(registers) { + for register in util::sort_by_offset(util::expand(registers)) { let pad = if let Some(pad) = register.offset.checked_sub(offset) { pad } else { diff --git a/src/util.rs b/src/util.rs index ca419943..8ef3137f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -144,8 +144,7 @@ pub struct ExpandedRegister<'a> { } /// 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. +/// and turn in into alist of registers where the register arrays have been expanded. pub fn expand(registers: &[Register]) -> Vec { let mut out = vec![]; @@ -213,11 +212,15 @@ pub fn expand(registers: &[Register]) -> Vec { } } - out.sort_by_key(|x| x.offset); - out } + +pub fn sort_by_offset<'a>(mut registers: Vec>) -> Vec> { + registers.sort_by_key(|x| x.offset); + return registers; +} + pub fn name_of(register: &Register) -> Cow { match *register { Register::Single(ref info) => Cow::from(&*info.name), From c47764d3793e4b5198747ad46e52e12ccd5e68ba Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Wed, 5 Jul 2017 12:57:24 +0200 Subject: [PATCH 02/21] Made expand work directly on Register structure instead of using ExpandedRegister extension --- src/generate.rs | 39 +++++++++++++++++------------------ src/util.rs | 55 +++++++++++-------------------------------------- 2 files changed, 31 insertions(+), 63 deletions(-) diff --git a/src/generate.rs b/src/generate.rs index a38ec52d..f4fa5939 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::io::{self, Write}; use cast::u64; -use either::Either; use quote::Tokens; use svd::{Access, BitRange, Defaults, Device, EnumeratedValues, Field, Peripheral, Register, Usage, WriteConstraint}; @@ -468,7 +467,7 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { // offset from the base address, in bytes let mut offset = 0; for register in util::sort_by_offset(util::expand(registers)) { - let pad = if let Some(pad) = register.offset.checked_sub(offset) { + let pad = if let Some(pad) = register.address_offset.checked_sub(offset) { pad } else { writeln!( @@ -476,7 +475,7 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { "WARNING {} overlaps with another register at offset {}. \ Ignoring.", register.name, - register.offset + register.address_offset ).ok(); continue; }; @@ -492,27 +491,27 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { let comment = &format!( "0x{:02x} - {}", - register.offset, - util::respace(®ister.info.description) + register.address_offset, + util::respace(®ister.description) ) [..]; - let rty = match register.ty { - Either::Left(ref ty) => Ident::from(&**ty), - Either::Right(ref ty) => Ident::from(&***ty), - }; - let reg_name = Ident::new(&*register.name.to_sanitized_snake_case()); - fields.push(quote! { - #[doc = #comment] - pub #reg_name : #rty, - }); + let rty = Ident::new(register.name.split('[').next().unwrap().to_sanitized_upper_case()); + let reg_name = Ident::new(register.name.to_sanitized_snake_case()); + + fields.push( + quote! { + #[doc = #comment] + pub #reg_name : #rty, + }); - offset = register.offset + - register - .info - .size - .or(defs.size) - .ok_or_else( + offset = + register.address_offset + + + register + .size + .or(defs.size) + .ok_or_else( || { format!("Register {} has no `size` field", register.name) }, diff --git a/src/util.rs b/src/util.rs index 8ef3137f..8b503dc0 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,9 +1,7 @@ use std::borrow::Cow; -use std::rc::Rc; -use either::Either; use inflections::Inflect; -use svd::{Access, EnumeratedValues, Field, Peripheral, Register, RegisterInfo, +use svd::{Access, EnumeratedValues, Field, Peripheral, Register, Usage}; use syn::{Ident, IntTy, Lit}; @@ -136,45 +134,17 @@ pub fn respace(s: &str) -> String { s.split_whitespace().collect::>().join(" ") } -pub struct ExpandedRegister<'a> { - pub info: &'a RegisterInfo, - pub name: String, - pub offset: u32, - pub ty: Either>, -} - /// Takes a list of "registers", some of which may actually be register arrays, /// and turn in into alist of registers where the register arrays have been expanded. -pub fn expand(registers: &[Register]) -> Vec { +pub fn expand(registers: &[Register]) -> Vec { let mut out = vec![]; for r in registers { match *r { - Register::Single(ref info) => { - out.push( - ExpandedRegister { - info: info, - name: info.name.to_sanitized_snake_case().into_owned(), - offset: info.address_offset, - ty: Either::Left( - info.name - .to_sanitized_upper_case() - .into_owned(), - ), - }, - ) - } + Register::Single(ref _info) => out.push( r.clone() ), 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_sanitized_upper_case().into_owned()); - let indices = array_info .dim_index .as_ref() @@ -191,21 +161,20 @@ pub fn expand(registers: &[Register]) -> Vec { for (idx, i) in indices.iter().zip(0..) { let name = if has_brackets { - info.name.replace("[%s]", idx) + info.name.replace("[%s]", format!("[{}]", idx).as_str()) } else { - info.name.replace("%s", idx) + info.name.replace("%s", format!("[{}]", idx).as_str()) }; let offset = info.address_offset + i * array_info.dim_increment; + let mut expanded_info = info.clone(); + expanded_info.name = name; + expanded_info.address_offset = offset; + out.push( - ExpandedRegister { - info: info, - name: name.to_sanitized_snake_case().into_owned(), - offset: offset, - ty: Either::Right(ty.clone()), - }, + Register::Single(expanded_info) ); } } @@ -216,8 +185,8 @@ pub fn expand(registers: &[Register]) -> Vec { } -pub fn sort_by_offset<'a>(mut registers: Vec>) -> Vec> { - registers.sort_by_key(|x| x.offset); +pub fn sort_by_offset(mut registers: Vec) -> Vec { + registers.sort_by_key(|x| x.address_offset); return registers; } From dd8c1b1a439fcd9e23858eb2d2712d93c423b726 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Wed, 5 Jul 2017 13:17:55 +0200 Subject: [PATCH 03/21] Refactored out the piece of code that formats the rust field --- src/generate.rs | 16 +--------------- src/util.rs | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/generate.rs b/src/generate.rs index f4fa5939..84fb1a0a 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -488,22 +488,8 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { }); i += 1; } - - let comment = &format!( - "0x{:02x} - {}", - register.address_offset, - util::respace(®ister.description) - ) - [..]; - - let rty = Ident::new(register.name.split('[').next().unwrap().to_sanitized_upper_case()); - let reg_name = Ident::new(register.name.to_sanitized_snake_case()); - fields.push( - quote! { - #[doc = #comment] - pub #reg_name : #rty, - }); + fields.push( util::register_to_rust(®ister) ); offset = register.address_offset diff --git a/src/util.rs b/src/util.rs index 8b503dc0..88e85d0c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -4,6 +4,7 @@ use inflections::Inflect; use svd::{Access, EnumeratedValues, Field, Peripheral, Register, Usage}; use syn::{Ident, IntTy, Lit}; +use quote::Tokens; use errors::*; @@ -134,6 +135,28 @@ pub fn respace(s: &str) -> String { s.split_whitespace().collect::>().join(" ") } +pub fn register_to_rust(register: &Register) -> Tokens { + match *register { + Register::Single(ref _info) => { + let comment = &format!( + "0x{:02x} - {}", + register.address_offset, + respace(®ister.description) + ) + [..]; + + let rty = Ident::new(register.name.split('[').next().unwrap().to_sanitized_upper_case()); + let reg_name = Ident::new(register.name.to_sanitized_snake_case()); + + quote! { + #[doc = #comment] + pub #reg_name : #rty, + } + }, + Register::Array(ref info, ref array_info) => unimplemented!(), + } +} + /// Takes a list of "registers", some of which may actually be register arrays, /// and turn in into alist of registers where the register arrays have been expanded. pub fn expand(registers: &[Register]) -> Vec { From 806f7223693b7b865895df01b58859c0c8afbc5a Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Wed, 5 Jul 2017 14:18:32 +0200 Subject: [PATCH 04/21] Made it possible to only expand certain registers by making expand callable on single registers instead of the whole slice --- src/generate.rs | 20 ++++++++++++- src/util.rs | 76 ++++++++++++++++++++++++------------------------- 2 files changed, 56 insertions(+), 40 deletions(-) diff --git a/src/generate.rs b/src/generate.rs index 84fb1a0a..0c65d735 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -466,7 +466,25 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { let mut i = 0; // offset from the base address, in bytes let mut offset = 0; - for register in util::sort_by_offset(util::expand(registers)) { + let mut registers_expanded = vec![]; + + for register in registers { + match *register { + Register::Single(ref _into) => registers_expanded.push(register.clone()), + Register::Array(ref _into, ref array_info) => { + let array_convertable = false; // TODO: write this condition + + if array_convertable { + registers_expanded.push(register.clone()); + } else { + registers_expanded.append(&mut util::expand(register)); + } + }, + } + } + + + for register in util::sort_by_offset(registers_expanded) { let pad = if let Some(pad) = register.address_offset.checked_sub(offset) { pad } else { diff --git a/src/util.rs b/src/util.rs index 88e85d0c..6c4f68e2 100644 --- a/src/util.rs +++ b/src/util.rs @@ -159,47 +159,45 @@ pub fn register_to_rust(register: &Register) -> Tokens { /// Takes a list of "registers", some of which may actually be register arrays, /// and turn in into alist of registers where the register arrays have been expanded. -pub fn expand(registers: &[Register]) -> Vec { +pub fn expand(register: &Register) -> Vec { let mut out = vec![]; - for r in registers { - match *r { - Register::Single(ref _info) => out.push( r.clone() ), - Register::Array(ref info, ref array_info) => { - let has_brackets = info.name.contains("[%s]"); - - 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::>(), - ) - }, - ); - - for (idx, i) in indices.iter().zip(0..) { - let name = if has_brackets { - info.name.replace("[%s]", format!("[{}]", idx).as_str()) - } else { - info.name.replace("%s", format!("[{}]", idx).as_str()) - }; - - let offset = info.address_offset + - i * array_info.dim_increment; - - let mut expanded_info = info.clone(); - expanded_info.name = name; - expanded_info.address_offset = offset; - - out.push( - Register::Single(expanded_info) - ); - } + match *register { + Register::Single(ref _info) => out.push( register.clone() ), + Register::Array(ref info, ref array_info) => { + let has_brackets = info.name.contains("[%s]"); + + 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::>(), + ) + }, + ); + + for (idx, i) in indices.iter().zip(0..) { + let name = if has_brackets { + info.name.replace("[%s]", format!("[{}]", idx).as_str()) + } else { + info.name.replace("%s", format!("[{}]", idx).as_str()) + }; + + let offset = info.address_offset + + i * array_info.dim_increment; + + let mut expanded_info = info.clone(); + expanded_info.name = name; + expanded_info.address_offset = offset; + + out.push( + Register::Single(expanded_info) + ); } } } From 63d1af881f53b13947b62d7e7243a8c967363d5a Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Wed, 5 Jul 2017 15:04:57 +0200 Subject: [PATCH 05/21] Implemented conversion from Array type register to rust array --- src/util.rs | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/util.rs b/src/util.rs index 6c4f68e2..8392380f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -153,10 +153,46 @@ pub fn register_to_rust(register: &Register) -> Tokens { pub #reg_name : #rty, } }, - Register::Array(ref info, ref array_info) => unimplemented!(), + Register::Array(ref info, ref array_info) => { + let has_brackets = info.name.contains("[%s]"); + + let comment = &format!( + "0x{:02x} - {} [{}]", + register.address_offset, + respace(®ister.description), + array_info.dim, + ) + [..]; + + let rty = Ident::from({ + let name = if has_brackets { + info.name.replace("[%s]", "") + } else { + info.name.replace("%s", "") + }; + name.to_sanitized_upper_case().into_owned() + }); + + let reg_name = Ident::from({ + let name = if has_brackets { + info.name.replace("[%s]", "") + } else { + info.name.replace("%s", "") + }; + name.to_sanitized_snake_case().into_owned() + }); + + let length = Ident::from(array_info.dim.to_string()); + + quote! { + #[doc = #comment] + pub #reg_name : [#rty; #length], + } + }, } } + /// Takes a list of "registers", some of which may actually be register arrays, /// and turn in into alist of registers where the register arrays have been expanded. pub fn expand(register: &Register) -> Vec { From 6110797155e9fadb16a01e9b69f3694591c79a2c Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Wed, 5 Jul 2017 16:56:40 +0200 Subject: [PATCH 06/21] Wrote the condition that checks if the array is convertable. If it only contains numeric indexes and it starts on 0 and contains every element in the range. Then it is convertable to a rust array --- src/generate.rs | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/generate.rs b/src/generate.rs index 0c65d735..80a1b5b7 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -472,7 +472,41 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { match *register { Register::Single(ref _into) => registers_expanded.push(register.clone()), Register::Array(ref _into, ref array_info) => { - let array_convertable = false; // TODO: write this condition + let array_convertable = if let Some(ref indexes) = array_info.dim_index { + let mut index_iter = indexes.iter(); + let mut previous = 0; + + let get_number_option = |element_option: Option<&String>| -> Option {element_option + .and_then(|element| match element.parse::() { + Ok(i) => Some(i), + _ => None, + }) + }; + + let mut array_convertable_temp = match get_number_option(index_iter.next()) { + Some(0) => true, + _ => false, + }; + + for element in index_iter { + if !array_convertable_temp { break; } + + array_convertable_temp = match get_number_option(Some(element)) { + Some(i) => i == previous+1, + _ => false, + }; + + previous = match get_number_option(Some(element)) { + Some(i) => i, + _ => {break;}, + }; + } + + array_convertable_temp + } else { + false + }; + if array_convertable { registers_expanded.push(register.clone()); From e8b5035938e7d80f53378c7bbbe8f31daab26203 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Wed, 5 Jul 2017 17:03:21 +0200 Subject: [PATCH 07/21] Fixed bug where offset was calculated incorrectly with register arrays --- src/generate.rs | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/generate.rs b/src/generate.rs index 80a1b5b7..076f2278 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -542,18 +542,29 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { } fields.push( util::register_to_rust(®ister) ); - - offset = - register.address_offset - + - register - .size - .or(defs.size) - .ok_or_else( - || { - format!("Register {} has no `size` field", register.name) - }, - )? / 8; + + offset = match register { + Register::Single(ref _into) => register.address_offset + + + register + .size + .or(defs.size) + .ok_or_else( + || { + format!("Register {} has no `size` field", register.name) + }, + )? / 8, + Register::Array(ref _into, ref array_info) => register.address_offset + + + register + .size + .or(defs.size) + .ok_or_else( + || { + format!("Register {} has no `size` field", register.name) + }, + )? * array_info.dim / 8, + }; } Ok(quote! { From 244b06cab587162d096c7152d06b5af7326a13e1 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Wed, 5 Jul 2017 17:38:58 +0200 Subject: [PATCH 08/21] Require that the svd array represented a packed memory area to be converted to a rust array --- src/generate.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/generate.rs b/src/generate.rs index 076f2278..574b91c1 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -472,7 +472,7 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { match *register { Register::Single(ref _into) => registers_expanded.push(register.clone()), Register::Array(ref _into, ref array_info) => { - let array_convertable = if let Some(ref indexes) = array_info.dim_index { + let index_convertible = if let Some(ref indexes) = array_info.dim_index { let mut index_iter = indexes.iter(); let mut previous = 0; @@ -483,15 +483,15 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { }) }; - let mut array_convertable_temp = match get_number_option(index_iter.next()) { + let mut index_convertible_temp = match get_number_option(index_iter.next()) { Some(0) => true, _ => false, }; for element in index_iter { - if !array_convertable_temp { break; } + if !index_convertible_temp { break; } - array_convertable_temp = match get_number_option(Some(element)) { + index_convertible_temp = match get_number_option(Some(element)) { Some(i) => i == previous+1, _ => false, }; @@ -502,13 +502,16 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { }; } - array_convertable_temp + index_convertible_temp } else { false }; + + let packed_registers = array_info.dim_increment == 4; + let array_convertible = index_convertible && packed_registers; - if array_convertable { + if array_convertible { registers_expanded.push(register.clone()); } else { registers_expanded.append(&mut util::expand(register)); From f0e85fd6e394b7610c9a37bddb7c0582094f3781 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Wed, 5 Jul 2017 18:54:00 +0200 Subject: [PATCH 09/21] Used regex to replace %s on expanded fields, the previous used hacked of spliting on [ had a bug when the index was in the middle of the name. --- Cargo.toml | 1 + src/main.rs | 1 + src/util.rs | 11 +++++++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a2b83283..920e0c5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ inflections = "1.1.0" quote = "0.3.15" svd-parser = "0.5.2" syn = "0.11.9" +regex = "0.2.2" [[bin]] doc = false diff --git a/src/main.rs b/src/main.rs index e10bb0f0..840d3e15 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ extern crate inflections; extern crate quote; extern crate svd_parser as svd; extern crate syn; +extern crate regex; mod errors; mod generate; diff --git a/src/util.rs b/src/util.rs index 8392380f..ad1d135a 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; +use regex::Regex; use inflections::Inflect; use svd::{Access, EnumeratedValues, Field, Peripheral, Register, Usage}; @@ -145,8 +146,10 @@ pub fn register_to_rust(register: &Register) -> Tokens { ) [..]; - let rty = Ident::new(register.name.split('[').next().unwrap().to_sanitized_upper_case()); - let reg_name = Ident::new(register.name.to_sanitized_snake_case()); + let array_index_match = Regex::new(r"%s\((?P[ a-zA-Z0-9]*)\)").unwrap(); + //println!("{}", register.name.as_str()); + let rty = Ident::new(array_index_match.replace_all(register.name.as_str(), "").to_sanitized_upper_case()); + let reg_name = Ident::new(array_index_match.replace_all(register.name.as_str(), "$i").to_sanitized_snake_case()); quote! { #[doc = #comment] @@ -219,9 +222,9 @@ pub fn expand(register: &Register) -> Vec { for (idx, i) in indices.iter().zip(0..) { let name = if has_brackets { - info.name.replace("[%s]", format!("[{}]", idx).as_str()) + info.name.replace("[%s]", format!("%s({})", idx).as_str()) } else { - info.name.replace("%s", format!("[{}]", idx).as_str()) + info.name.replace("%s", format!("%s({})", idx).as_str()) }; let offset = info.address_offset + From a24a2456b4d15accd7d8edc0fec6ad9fd572853b Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Thu, 6 Jul 2017 14:27:17 +0200 Subject: [PATCH 10/21] Added function for converting svd registers into rust fields --- src/util.rs | 89 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 35 deletions(-) diff --git a/src/util.rs b/src/util.rs index ad1d135a..a052bd4f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,10 +2,12 @@ use std::borrow::Cow; use regex::Regex; use inflections::Inflect; +use svd; use svd::{Access, EnumeratedValues, Field, Peripheral, Register, Usage}; +use syn; use syn::{Ident, IntTy, Lit}; -use quote::Tokens; +use quote::{Tokens, ToTokens}; use errors::*; @@ -157,40 +159,11 @@ pub fn register_to_rust(register: &Register) -> Tokens { } }, Register::Array(ref info, ref array_info) => { - let has_brackets = info.name.contains("[%s]"); - - let comment = &format!( - "0x{:02x} - {} [{}]", - register.address_offset, - respace(®ister.description), - array_info.dim, - ) - [..]; - - let rty = Ident::from({ - let name = if has_brackets { - info.name.replace("[%s]", "") - } else { - info.name.replace("%s", "") - }; - name.to_sanitized_upper_case().into_owned() - }); - - let reg_name = Ident::from({ - let name = if has_brackets { - info.name.replace("[%s]", "") - } else { - info.name.replace("%s", "") - }; - name.to_sanitized_snake_case().into_owned() - }); - - let length = Ident::from(array_info.dim.to_string()); - - quote! { - #[doc = #comment] - pub #reg_name : [#rty; #length], - } + let field = convert_svd_register(register); + let mut tokens = Tokens::new(); + field.to_tokens(&mut tokens); + Ident::new(",").to_tokens(&mut tokens); + tokens }, } } @@ -244,6 +217,52 @@ pub fn expand(register: &Register) -> Vec { out } +pub fn convert_svd_register(register: &svd::Register) -> syn::Field { + let name_to_ty = |name: &String| -> syn::Ty { + syn::Ty::Path(None, syn::Path{ + global: false, + segments: vec![syn::PathSegment{ + ident: Ident::new(name.to_sanitized_upper_case()), + parameters: syn::PathParameters::none(), + }], + }) + }; + + match *register { + Register::Single(ref info) => { + syn::Field{ + ident: Some(Ident::new(info.name.to_sanitized_snake_case())), + vis: syn::Visibility::Public, + attrs: vec![], + ty: name_to_ty(&info.name), + } + }, + Register::Array(ref info, ref array_info) => { + let has_brackets = info.name.contains("[%s]"); + + let name = if has_brackets { + info.name.replace("[%s]", "") + } else { + info.name.replace("%s", "") + }; + + let ident = Ident::new(name.to_sanitized_snake_case()); + + let ty = syn::Ty::Array( + Box::new(name_to_ty(&name)), + syn::ConstExpr::Lit(syn::Lit::Int(array_info.dim as u64, syn::IntTy::Unsuffixed)), + ); + + syn::Field{ + ident: Some(ident), + vis: syn::Visibility::Public, + attrs: vec![], + ty: ty, + } + }, + } +} + pub fn sort_by_offset(mut registers: Vec) -> Vec { registers.sort_by_key(|x| x.address_offset); From 8a526498fae71d40d275423065c7ccb990efd31d Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Thu, 6 Jul 2017 16:58:08 +0200 Subject: [PATCH 11/21] Instead of working on Register structs inside register_block we now work on the new struct RegisterBlockField. Instead of creating a brand new type representing a rust field it's an extension (by composition) of the syn::Field to also give information about offset, size and description. This means that the util extend function now extend to a vector of syn::Field. --- src/generate.rs | 118 ++++++++++++++++++++++++++++++++---------------- src/util.rs | 88 ++++++++++++++---------------------- 2 files changed, 112 insertions(+), 94 deletions(-) diff --git a/src/generate.rs b/src/generate.rs index 574b91c1..16e154aa 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -2,9 +2,10 @@ use std::collections::HashMap; use std::io::{self, Write}; use cast::u64; -use quote::Tokens; +use quote::{Tokens, ToTokens}; use svd::{Access, BitRange, Defaults, Device, EnumeratedValues, Field, Peripheral, Register, Usage, WriteConstraint}; +use syn; use syn::{Ident, Lit}; use errors::*; @@ -460,8 +461,15 @@ pub fn peripheral( Ok(()) } +struct RegisterBlockField { + field: syn::Field, + description: Ident, + offset: u32, + size: u32, +} + fn register_block(registers: &[Register], defs: &Defaults) -> Result { - let mut fields = vec![]; + let mut fields = Tokens::new(); // enumeration of reserved fields let mut i = 0; // offset from the base address, in bytes @@ -470,8 +478,19 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { for register in registers { match *register { - Register::Single(ref _into) => registers_expanded.push(register.clone()), - Register::Array(ref _into, ref array_info) => { + Register::Single(ref info) => registers_expanded.push( + RegisterBlockField{ + field: util::convert_svd_register(register), + description: Ident::from(info.description.clone()), + offset: info.address_offset, + size: info.size.or(defs.size) + .ok_or_else( + || { + format!("Register {} has no `size` field", register.name) + },)?, + } + ), + Register::Array(ref info, ref array_info) => { let index_convertible = if let Some(ref indexes) = array_info.dim_index { let mut index_iter = indexes.iter(); let mut previous = 0; @@ -512,69 +531,90 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { let array_convertible = index_convertible && packed_registers; if array_convertible { - registers_expanded.push(register.clone()); + registers_expanded.push( + RegisterBlockField{ + field: util::convert_svd_register(®ister), + description: Ident::from(info.description.clone()), + offset: info.address_offset, + size: info.size.or(defs.size) + .ok_or_else( + || { + format!("Register {} has no `size` field", register.name) + },)? + * array_info.dim, + }); } else { - registers_expanded.append(&mut util::expand(register)); + let mut field_num = 0; + for field in util::expand_svd_register(register).iter() { + registers_expanded.push( + RegisterBlockField{ + field: field.clone(), + description: Ident::from(info.description.clone()), + offset: info.address_offset + field_num * array_info.dim_increment, + size: info.size.or(defs.size) + .ok_or_else( + || { + format!("Register {} has no `size` field", register.name) + },)? + }); + field_num += 1; + } } }, } } - - for register in util::sort_by_offset(registers_expanded) { - let pad = if let Some(pad) = register.address_offset.checked_sub(offset) { + registers_expanded.sort_by_key(|x| x.offset); + + for register in registers_expanded { + let pad = if let Some(pad) = register.offset.checked_sub(offset) { pad } else { writeln!( io::stderr(), "WARNING {} overlaps with another register at offset {}. \ Ignoring.", - register.name, - register.address_offset - ).ok(); + register.field.ident.unwrap(), + register.offset + ) + .ok(); continue; }; if pad != 0 { let name = Ident::new(format!("_reserved{}", i)); let pad = pad as usize; - fields.push(quote! { - #name : [u8; #pad], - }); + fields.append( + quote! { + #name : [u8; #pad], + }); i += 1; } + + let comment = &format!( + "0x{:02x} - {}", + register.offset, + register.description, + ) + [..]; - fields.push( util::register_to_rust(®ister) ); + fields.append( + quote! { + #[doc = #comment] + } + ); - offset = match register { - Register::Single(ref _into) => register.address_offset - + - register - .size - .or(defs.size) - .ok_or_else( - || { - format!("Register {} has no `size` field", register.name) - }, - )? / 8, - Register::Array(ref _into, ref array_info) => register.address_offset - + - register - .size - .or(defs.size) - .ok_or_else( - || { - format!("Register {} has no `size` field", register.name) - }, - )? * array_info.dim / 8, - }; + register.field.to_tokens(&mut fields); + Ident::new(",").to_tokens(&mut fields); + + offset = register.offset + register.size/8; } Ok(quote! { /// Register block #[repr(C)] pub struct RegisterBlock { - #(#fields)* + #fields } }) } diff --git a/src/util.rs b/src/util.rs index a052bd4f..9147af8b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -7,7 +7,6 @@ use svd::{Access, EnumeratedValues, Field, Peripheral, Register, Usage}; use syn; use syn::{Ident, IntTy, Lit}; -use quote::{Tokens, ToTokens}; use errors::*; @@ -138,44 +137,23 @@ pub fn respace(s: &str) -> String { s.split_whitespace().collect::>().join(" ") } -pub fn register_to_rust(register: &Register) -> Tokens { - match *register { - Register::Single(ref _info) => { - let comment = &format!( - "0x{:02x} - {}", - register.address_offset, - respace(®ister.description) - ) - [..]; - - let array_index_match = Regex::new(r"%s\((?P[ a-zA-Z0-9]*)\)").unwrap(); - //println!("{}", register.name.as_str()); - let rty = Ident::new(array_index_match.replace_all(register.name.as_str(), "").to_sanitized_upper_case()); - let reg_name = Ident::new(array_index_match.replace_all(register.name.as_str(), "$i").to_sanitized_snake_case()); - - quote! { - #[doc = #comment] - pub #reg_name : #rty, - } - }, - Register::Array(ref info, ref array_info) => { - let field = convert_svd_register(register); - let mut tokens = Tokens::new(); - field.to_tokens(&mut tokens); - Ident::new(",").to_tokens(&mut tokens); - tokens - }, - } -} - +/// Takes a svd::Register which may be a register array, and turn in into +/// a list of syn::Field where the register arrays have been expanded. +pub fn expand_svd_register(register: &Register) -> Vec { + let name_to_ty = |name: &String| -> syn::Ty { + syn::Ty::Path(None, syn::Path{ + global: false, + segments: vec![syn::PathSegment{ + ident: Ident::new(name.to_sanitized_upper_case()), + parameters: syn::PathParameters::none(), + }], + }) + }; -/// Takes a list of "registers", some of which may actually be register arrays, -/// and turn in into alist of registers where the register arrays have been expanded. -pub fn expand(register: &Register) -> Vec { let mut out = vec![]; match *register { - Register::Single(ref _info) => out.push( register.clone() ), + Register::Single(ref _info) => out.push( convert_svd_register(register) ), Register::Array(ref info, ref array_info) => { let has_brackets = info.name.contains("[%s]"); @@ -192,28 +170,34 @@ pub fn expand(register: &Register) -> Vec { ) }, ); - - for (idx, i) in indices.iter().zip(0..) { + + for (idx, _i) in indices.iter().zip(0..) { let name = if has_brackets { - info.name.replace("[%s]", format!("%s({})", idx).as_str()) + info.name.replace("[%s]", format!("{}", idx).as_str()) } else { - info.name.replace("%s", format!("%s({})", idx).as_str()) + info.name.replace("%s", format!("{}", idx).as_str()) }; - - let offset = info.address_offset + - i * array_info.dim_increment; - - let mut expanded_info = info.clone(); - expanded_info.name = name; - expanded_info.address_offset = offset; + let ty_name = if has_brackets { + info.name.replace("[%s]", "") + } else { + info.name.replace("%s", "") + }; + + let ident = Ident::new(name.to_sanitized_snake_case()); + let ty = name_to_ty(&ty_name); + out.push( - Register::Single(expanded_info) + syn::Field{ + ident: Some(ident), + vis: syn::Visibility::Public, + attrs: vec![], + ty: ty, + } ); } - } + }, } - out } @@ -263,12 +247,6 @@ pub fn convert_svd_register(register: &svd::Register) -> syn::Field { } } - -pub fn sort_by_offset(mut registers: Vec) -> Vec { - registers.sort_by_key(|x| x.address_offset); - return registers; -} - pub fn name_of(register: &Register) -> Cow { match *register { Register::Single(ref info) => Cow::from(&*info.name), From fc238af8389eb81079e24cd644461857574e151c Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Thu, 6 Jul 2017 17:00:03 +0200 Subject: [PATCH 12/21] Removed everything relating to regex as it's not needed (and never should have been in the first place) --- Cargo.toml | 1 - src/main.rs | 1 - src/util.rs | 1 - 3 files changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 920e0c5e..a2b83283 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ inflections = "1.1.0" quote = "0.3.15" svd-parser = "0.5.2" syn = "0.11.9" -regex = "0.2.2" [[bin]] doc = false diff --git a/src/main.rs b/src/main.rs index 840d3e15..e10bb0f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,6 @@ extern crate inflections; extern crate quote; extern crate svd_parser as svd; extern crate syn; -extern crate regex; mod errors; mod generate; diff --git a/src/util.rs b/src/util.rs index 9147af8b..b7a3f151 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; -use regex::Regex; use inflections::Inflect; use svd; use svd::{Access, EnumeratedValues, Field, Peripheral, Register, From 09f191ea28532cd87b00db5f36d0089ee0b3b861 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Thu, 6 Jul 2017 21:12:04 +0200 Subject: [PATCH 13/21] Refactored out register size to avoid duplication of error detection logic --- src/generate.rs | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/generate.rs b/src/generate.rs index 16e154aa..83fce678 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -475,19 +475,21 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { // offset from the base address, in bytes let mut offset = 0; let mut registers_expanded = vec![]; - + for register in registers { + let register_size = register.size.or(defs.size) + .ok_or_else( + || { + format!("Register {} has no `size` field", register.name) + },)?; + match *register { Register::Single(ref info) => registers_expanded.push( RegisterBlockField{ field: util::convert_svd_register(register), description: Ident::from(info.description.clone()), offset: info.address_offset, - size: info.size.or(defs.size) - .ok_or_else( - || { - format!("Register {} has no `size` field", register.name) - },)?, + size: register_size, } ), Register::Array(ref info, ref array_info) => { @@ -536,12 +538,7 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { field: util::convert_svd_register(®ister), description: Ident::from(info.description.clone()), offset: info.address_offset, - size: info.size.or(defs.size) - .ok_or_else( - || { - format!("Register {} has no `size` field", register.name) - },)? - * array_info.dim, + size: register_size * array_info.dim, }); } else { let mut field_num = 0; @@ -551,11 +548,7 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { field: field.clone(), description: Ident::from(info.description.clone()), offset: info.address_offset + field_num * array_info.dim_increment, - size: info.size.or(defs.size) - .ok_or_else( - || { - format!("Register {} has no `size` field", register.name) - },)? + size: register_size, }); field_num += 1; } From db90f78e2a20bbb9e937a6ee9e725cf2201921b6 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Thu, 6 Jul 2017 21:23:22 +0200 Subject: [PATCH 14/21] Added comment explaining when registers will be expanded --- src/generate.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/generate.rs b/src/generate.rs index 83fce678..ec65f3e3 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -475,7 +475,8 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { // offset from the base address, in bytes let mut offset = 0; let mut registers_expanded = vec![]; - + + // If svd register arrays can't be converted to rust arrays (non sequential adresses, non numeral indexes, or not containing all elements from 0 to size) they will be expanded for register in registers { let register_size = register.size.or(defs.size) .ok_or_else( From aff44106f579e154692450db7ad5ca19a215770c Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Thu, 6 Jul 2017 21:37:53 +0200 Subject: [PATCH 15/21] Added BITS_PER_BYTE constant --- src/generate.rs | 2 +- src/util.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/generate.rs b/src/generate.rs index ec65f3e3..1388b06e 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -9,7 +9,7 @@ use syn; use syn::{Ident, Lit}; use errors::*; -use util::{self, ToSanitizedSnakeCase, ToSanitizedUpperCase, U32Ext}; +use util::{self, ToSanitizedSnakeCase, ToSanitizedUpperCase, U32Ext, BITS_PER_BYTE}; use Target; /// Whole device generation diff --git a/src/util.rs b/src/util.rs index b7a3f151..71a1fdce 100644 --- a/src/util.rs +++ b/src/util.rs @@ -9,6 +9,8 @@ use syn::{Ident, IntTy, Lit}; use errors::*; +pub const BITS_PER_BYTE: u32 = 8; + /// List of chars that some vendors use in their peripheral/field names but /// that are not valid in Rust ident const BLACKLIST_CHARS: &'static [char] = &['(', ')', '[', ']']; From d3c063248082592c9282b9659e87cba33bac002c Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Thu, 6 Jul 2017 21:39:32 +0200 Subject: [PATCH 16/21] Fixed coding error. A condition for converting from svd array to rust array is that it's packed in memory, not that fields are 32 bits --- src/generate.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/generate.rs b/src/generate.rs index 1388b06e..3e8540b2 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -494,6 +494,8 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { } ), Register::Array(ref info, ref array_info) => { + let sequential_adresses = register_size == array_info.dim_increment*BITS_PER_BYTE; + let index_convertible = if let Some(ref indexes) = array_info.dim_index { let mut index_iter = indexes.iter(); let mut previous = 0; @@ -529,9 +531,7 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { false }; - let packed_registers = array_info.dim_increment == 4; - - let array_convertible = index_convertible && packed_registers; + let array_convertible = index_convertible && sequential_adresses; if array_convertible { registers_expanded.push( From 3d3cd94be07ea55f0f8441cce1aa38f69c91a1f5 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Thu, 6 Jul 2017 22:34:27 +0200 Subject: [PATCH 17/21] Cleaned up conditions that find out if register should be expanded or not --- src/generate.rs | 45 +++++++++++++-------------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/src/generate.rs b/src/generate.rs index 3e8540b2..6964b2d7 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -495,43 +495,24 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { ), Register::Array(ref info, ref array_info) => { let sequential_adresses = register_size == array_info.dim_increment*BITS_PER_BYTE; - - let index_convertible = if let Some(ref indexes) = array_info.dim_index { - let mut index_iter = indexes.iter(); - let mut previous = 0; - - let get_number_option = |element_option: Option<&String>| -> Option {element_option - .and_then(|element| match element.parse::() { - Ok(i) => Some(i), - _ => None, - }) - }; - - let mut index_convertible_temp = match get_number_option(index_iter.next()) { - Some(0) => true, - _ => false, - }; - - for element in index_iter { - if !index_convertible_temp { break; } - - index_convertible_temp = match get_number_option(Some(element)) { - Some(i) => i == previous+1, - _ => false, - }; - previous = match get_number_option(Some(element)) { - Some(i) => i, - _ => {break;}, - }; - } - - index_convertible_temp + let numeral_indexes = array_info.dim_index.clone() + .ok_or_else( || format!("Register {} has no `dim_index` field", register.name))? + .iter() + .all(|element| element.parse::().is_ok()); + + let sequential_indexes = if numeral_indexes && sequential_adresses { + array_info.dim_index.clone() + .unwrap() + .iter() + .map(|element| element.parse::().unwrap()) + .collect::>() + .eq(&(0..array_info.dim).collect::>()) } else { false }; - let array_convertible = index_convertible && sequential_adresses; + let array_convertible = sequential_indexes && numeral_indexes && sequential_adresses; if array_convertible { registers_expanded.push( From 5d1a0f7f98785f66548de8e876bf4431360b29c8 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Thu, 6 Jul 2017 22:37:19 +0200 Subject: [PATCH 18/21] Swaped out magic number 8 by BITS_PER_BYTE --- src/generate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generate.rs b/src/generate.rs index 6964b2d7..a3e0f244 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -582,7 +582,7 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { register.field.to_tokens(&mut fields); Ident::new(",").to_tokens(&mut fields); - offset = register.offset + register.size/8; + offset = register.offset + register.size/BITS_PER_BYTE; } Ok(quote! { From 26849d6b19b67d3fc4edd3f9bb06c5958e12615e Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Fri, 7 Jul 2017 15:26:30 +0200 Subject: [PATCH 19/21] Made include self style consistent with the rest of the project --- src/generate.rs | 3 +-- src/util.rs | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/generate.rs b/src/generate.rs index a3e0f244..7d96eb1c 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -5,8 +5,7 @@ use cast::u64; use quote::{Tokens, ToTokens}; use svd::{Access, BitRange, Defaults, Device, EnumeratedValues, Field, Peripheral, Register, Usage, WriteConstraint}; -use syn; -use syn::{Ident, Lit}; +use syn::{self, Ident, Lit}; use errors::*; use util::{self, ToSanitizedSnakeCase, ToSanitizedUpperCase, U32Ext, BITS_PER_BYTE}; diff --git a/src/util.rs b/src/util.rs index 71a1fdce..c90583a0 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,11 +1,9 @@ use std::borrow::Cow; use inflections::Inflect; -use svd; -use svd::{Access, EnumeratedValues, Field, Peripheral, Register, +use svd::{self, Access, EnumeratedValues, Field, Peripheral, Register, Usage}; -use syn; -use syn::{Ident, IntTy, Lit}; +use syn::{self, Ident, IntTy, Lit}; use errors::*; From 1ee087249601a1eba517d66cdc8eea7bd6730530 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Fri, 15 Sep 2017 18:22:31 +0200 Subject: [PATCH 20/21] Changed description type to String from Ident --- src/generate.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/generate.rs b/src/generate.rs index 7d96eb1c..3f0c4e0d 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -462,7 +462,7 @@ pub fn peripheral( struct RegisterBlockField { field: syn::Field, - description: Ident, + description: String, offset: u32, size: u32, } @@ -487,7 +487,7 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { Register::Single(ref info) => registers_expanded.push( RegisterBlockField{ field: util::convert_svd_register(register), - description: Ident::from(info.description.clone()), + description: info.description.clone(), offset: info.address_offset, size: register_size, } @@ -517,7 +517,7 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { registers_expanded.push( RegisterBlockField{ field: util::convert_svd_register(®ister), - description: Ident::from(info.description.clone()), + description: info.description.clone(), offset: info.address_offset, size: register_size * array_info.dim, }); @@ -527,7 +527,7 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { registers_expanded.push( RegisterBlockField{ field: field.clone(), - description: Ident::from(info.description.clone()), + description: info.description.clone(), offset: info.address_offset + field_num * array_info.dim_increment, size: register_size, }); From 52cf26750f435ddfb18719f70e711d9d903d570b Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Fri, 15 Sep 2017 18:28:27 +0200 Subject: [PATCH 21/21] Added respace to avoid breaking docs --- src/generate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generate.rs b/src/generate.rs index 3f0c4e0d..3ffc2455 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -568,7 +568,7 @@ fn register_block(registers: &[Register], defs: &Defaults) -> Result { let comment = &format!( "0x{:02x} - {}", register.offset, - register.description, + util::respace(®ister.description), ) [..];