Skip to content

Commit 1f3542f

Browse files
committed
Auto merge of #121 - kjetilkjeka:field_array, r=japaric
Register array in RegisterBlock I guess this pull request is dropping a bit out of the sky, but I realized I needed the feature sooner rather than later so decided to just go for it. When it's merged it should fix #114 Some refactoring was done to allow for integration register arrays, the most significant are - expand function only takes 1 register instead of slice. This means that the function has to be called for every register that should be expanded. This was done so a register can be conditionaly expanded depending on if it's possible to convert to a Rust array or not. - register_to_rust method was factored out from register_block. Mainly so register block wouldn't grow very large with lots of nested control flow. - ExpandedRegister was removed, the functions only use Register struct now. This was because if not it would require conversion functions when registers was not expanded. The struct would also need changing as it would only have to support arrays. - Since the ExpandedRegisters was dropped the expand function now writes ```%s(i)``` (where i is the index) to the names of the expanded registers. This is used later on to resolve name and type. - regex is a dependecy, this is used to replace ```%s(i)``` with the index. Adding this dependecy might not be desired? and if not I can reimplement it with some logic that iterates over the string. I've tested with [S32K144.svd](https://github.com/kjetilkjeka/s32k144.rs/blob/master/src/S32K144.svd) and it seems to work perfectly with that file. As mentioned in #114 i do hope that the method of using svd array information instead of unrolling arrays after is acceptable.
2 parents ad2c8d8 + 52cf267 commit 1f3542f

File tree

2 files changed

+198
-105
lines changed

2 files changed

+198
-105
lines changed

src/generate.rs

Lines changed: 99 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@ use std::collections::HashMap;
22
use std::io::{self, Write};
33

44
use cast::u64;
5-
use either::Either;
6-
use quote::Tokens;
5+
use quote::{Tokens, ToTokens};
76
use svd::{Access, BitRange, Defaults, Device, EnumeratedValues, Field,
87
Peripheral, Register, Usage, WriteConstraint};
9-
use syn::{Ident, Lit};
8+
use syn::{self, Ident, Lit};
109

1110
use errors::*;
12-
use util::{self, ToSanitizedSnakeCase, ToSanitizedUpperCase, U32Ext};
11+
use util::{self, ToSanitizedSnakeCase, ToSanitizedUpperCase, U32Ext, BITS_PER_BYTE};
1312
use Target;
1413

1514
/// Whole device generation
@@ -503,69 +502,135 @@ pub fn peripheral(
503502
Ok(())
504503
}
505504

505+
struct RegisterBlockField {
506+
field: syn::Field,
507+
description: String,
508+
offset: u32,
509+
size: u32,
510+
}
511+
506512
fn register_block(registers: &[Register], defs: &Defaults) -> Result<Tokens> {
507-
let mut fields = vec![];
513+
let mut fields = Tokens::new();
508514
// enumeration of reserved fields
509515
let mut i = 0;
510516
// offset from the base address, in bytes
511517
let mut offset = 0;
512-
for register in util::expand(registers) {
518+
let mut registers_expanded = vec![];
519+
520+
// 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
521+
for register in registers {
522+
let register_size = register.size.or(defs.size)
523+
.ok_or_else(
524+
|| {
525+
format!("Register {} has no `size` field", register.name)
526+
},)?;
527+
528+
match *register {
529+
Register::Single(ref info) => registers_expanded.push(
530+
RegisterBlockField{
531+
field: util::convert_svd_register(register),
532+
description: info.description.clone(),
533+
offset: info.address_offset,
534+
size: register_size,
535+
}
536+
),
537+
Register::Array(ref info, ref array_info) => {
538+
let sequential_adresses = register_size == array_info.dim_increment*BITS_PER_BYTE;
539+
540+
let numeral_indexes = array_info.dim_index.clone()
541+
.ok_or_else( || format!("Register {} has no `dim_index` field", register.name))?
542+
.iter()
543+
.all(|element| element.parse::<usize>().is_ok());
544+
545+
let sequential_indexes = if numeral_indexes && sequential_adresses {
546+
array_info.dim_index.clone()
547+
.unwrap()
548+
.iter()
549+
.map(|element| element.parse::<u32>().unwrap())
550+
.collect::<Vec<u32>>()
551+
.eq(&(0..array_info.dim).collect::<Vec<u32>>())
552+
} else {
553+
false
554+
};
555+
556+
let array_convertible = sequential_indexes && numeral_indexes && sequential_adresses;
557+
558+
if array_convertible {
559+
registers_expanded.push(
560+
RegisterBlockField{
561+
field: util::convert_svd_register(&register),
562+
description: info.description.clone(),
563+
offset: info.address_offset,
564+
size: register_size * array_info.dim,
565+
});
566+
} else {
567+
let mut field_num = 0;
568+
for field in util::expand_svd_register(register).iter() {
569+
registers_expanded.push(
570+
RegisterBlockField{
571+
field: field.clone(),
572+
description: info.description.clone(),
573+
offset: info.address_offset + field_num * array_info.dim_increment,
574+
size: register_size,
575+
});
576+
field_num += 1;
577+
}
578+
}
579+
},
580+
}
581+
}
582+
583+
registers_expanded.sort_by_key(|x| x.offset);
584+
585+
for register in registers_expanded {
513586
let pad = if let Some(pad) = register.offset.checked_sub(offset) {
514587
pad
515588
} else {
516589
writeln!(
517590
io::stderr(),
518591
"WARNING {} overlaps with another register at offset {}. \
519592
Ignoring.",
520-
register.name,
593+
register.field.ident.unwrap(),
521594
register.offset
522-
).ok();
595+
)
596+
.ok();
523597
continue;
524598
};
525599

526600
if pad != 0 {
527601
let name = Ident::new(format!("_reserved{}", i));
528602
let pad = pad as usize;
529-
fields.push(quote! {
530-
#name : [u8; #pad],
531-
});
603+
fields.append(
604+
quote! {
605+
#name : [u8; #pad],
606+
});
532607
i += 1;
533608
}
534609

535610
let comment = &format!(
536611
"0x{:02x} - {}",
537612
register.offset,
538-
util::respace(&register.info.description)
613+
util::respace(&register.description),
539614
)
540615
[..];
541-
542-
let rty = match register.ty {
543-
Either::Left(ref ty) => Ident::from(&**ty),
544-
Either::Right(ref ty) => Ident::from(&***ty),
545-
};
546-
let reg_name = Ident::new(&*register.name.to_sanitized_snake_case());
547-
fields.push(quote! {
548-
#[doc = #comment]
549-
pub #reg_name : #rty,
550-
});
551-
552-
offset = register.offset +
553-
register
554-
.info
555-
.size
556-
.or(defs.size)
557-
.ok_or_else(
558-
|| {
559-
format!("Register {} has no `size` field", register.name)
560-
},
561-
)? / 8;
616+
617+
fields.append(
618+
quote! {
619+
#[doc = #comment]
620+
}
621+
);
622+
623+
register.field.to_tokens(&mut fields);
624+
Ident::new(",").to_tokens(&mut fields);
625+
626+
offset = register.offset + register.size/BITS_PER_BYTE;
562627
}
563628

564629
Ok(quote! {
565630
/// Register block
566631
#[repr(C)]
567632
pub struct RegisterBlock {
568-
#(#fields)*
633+
#fields
569634
}
570635
})
571636
}

src/util.rs

Lines changed: 99 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use std::borrow::Cow;
2-
use std::rc::Rc;
32

4-
use either::Either;
53
use inflections::Inflect;
6-
use svd::{Access, EnumeratedValues, Field, Peripheral, Register, RegisterInfo,
4+
use svd::{self, Access, EnumeratedValues, Field, Peripheral, Register,
75
Usage};
8-
use syn::{Ident, IntTy, Lit};
6+
use syn::{self, Ident, IntTy, Lit};
97

108
use errors::*;
119

10+
pub const BITS_PER_BYTE: u32 = 8;
11+
1212
/// List of chars that some vendors use in their peripheral/field names but
1313
/// that are not valid in Rust ident
1414
const BLACKLIST_CHARS: &'static [char] = &['(', ')', '[', ']'];
@@ -136,86 +136,114 @@ pub fn respace(s: &str) -> String {
136136
s.split_whitespace().collect::<Vec<_>>().join(" ")
137137
}
138138

139-
pub struct ExpandedRegister<'a> {
140-
pub info: &'a RegisterInfo,
141-
pub name: String,
142-
pub offset: u32,
143-
pub ty: Either<String, Rc<String>>,
144-
}
139+
/// Takes a svd::Register which may be a register array, and turn in into
140+
/// a list of syn::Field where the register arrays have been expanded.
141+
pub fn expand_svd_register(register: &Register) -> Vec<syn::Field> {
142+
let name_to_ty = |name: &String| -> syn::Ty {
143+
syn::Ty::Path(None, syn::Path{
144+
global: false,
145+
segments: vec![syn::PathSegment{
146+
ident: Ident::new(name.to_sanitized_upper_case()),
147+
parameters: syn::PathParameters::none(),
148+
}],
149+
})
150+
};
145151

146-
/// Takes a list of "registers", some of which may actually be register arrays,
147-
/// and turns it into a new *sorted* (by address offset) list of registers where
148-
/// the register arrays have been expanded.
149-
pub fn expand(registers: &[Register]) -> Vec<ExpandedRegister> {
150152
let mut out = vec![];
151153

152-
for r in registers {
153-
match *r {
154-
Register::Single(ref info) => {
155-
out.push(
156-
ExpandedRegister {
157-
info: info,
158-
name: info.name.to_sanitized_snake_case().into_owned(),
159-
offset: info.address_offset,
160-
ty: Either::Left(
161-
info.name
162-
.to_sanitized_upper_case()
163-
.into_owned(),
164-
),
154+
match *register {
155+
Register::Single(ref _info) => out.push( convert_svd_register(register) ),
156+
Register::Array(ref info, ref array_info) => {
157+
let has_brackets = info.name.contains("[%s]");
158+
159+
let indices = array_info
160+
.dim_index
161+
.as_ref()
162+
.map(|v| Cow::from(&**v))
163+
.unwrap_or_else(
164+
|| {
165+
Cow::from(
166+
(0..array_info.dim)
167+
.map(|i| i.to_string())
168+
.collect::<Vec<_>>(),
169+
)
165170
},
166-
)
167-
}
168-
Register::Array(ref info, ref array_info) => {
169-
let has_brackets = info.name.contains("[%s]");
170-
171-
let ty = if has_brackets {
171+
);
172+
173+
for (idx, _i) in indices.iter().zip(0..) {
174+
let name = if has_brackets {
175+
info.name.replace("[%s]", format!("{}", idx).as_str())
176+
} else {
177+
info.name.replace("%s", format!("{}", idx).as_str())
178+
};
179+
180+
let ty_name = if has_brackets {
172181
info.name.replace("[%s]", "")
173182
} else {
174183
info.name.replace("%s", "")
175184
};
185+
186+
let ident = Ident::new(name.to_sanitized_snake_case());
187+
let ty = name_to_ty(&ty_name);
176188

177-
let ty = Rc::new(ty.to_sanitized_upper_case().into_owned());
178-
179-
let indices = array_info
180-
.dim_index
181-
.as_ref()
182-
.map(|v| Cow::from(&**v))
183-
.unwrap_or_else(
184-
|| {
185-
Cow::from(
186-
(0..array_info.dim)
187-
.map(|i| i.to_string())
188-
.collect::<Vec<_>>(),
189-
)
190-
},
191-
);
192-
193-
for (idx, i) in indices.iter().zip(0..) {
194-
let name = if has_brackets {
195-
info.name.replace("[%s]", idx)
196-
} else {
197-
info.name.replace("%s", idx)
198-
};
199-
200-
let offset = info.address_offset +
201-
i * array_info.dim_increment;
202-
203-
out.push(
204-
ExpandedRegister {
205-
info: info,
206-
name: name.to_sanitized_snake_case().into_owned(),
207-
offset: offset,
208-
ty: Either::Right(ty.clone()),
209-
},
210-
);
211-
}
189+
out.push(
190+
syn::Field{
191+
ident: Some(ident),
192+
vis: syn::Visibility::Public,
193+
attrs: vec![],
194+
ty: ty,
195+
}
196+
);
212197
}
213-
}
198+
},
214199
}
200+
out
201+
}
215202

216-
out.sort_by_key(|x| x.offset);
203+
pub fn convert_svd_register(register: &svd::Register) -> syn::Field {
204+
let name_to_ty = |name: &String| -> syn::Ty {
205+
syn::Ty::Path(None, syn::Path{
206+
global: false,
207+
segments: vec![syn::PathSegment{
208+
ident: Ident::new(name.to_sanitized_upper_case()),
209+
parameters: syn::PathParameters::none(),
210+
}],
211+
})
212+
};
213+
214+
match *register {
215+
Register::Single(ref info) => {
216+
syn::Field{
217+
ident: Some(Ident::new(info.name.to_sanitized_snake_case())),
218+
vis: syn::Visibility::Public,
219+
attrs: vec![],
220+
ty: name_to_ty(&info.name),
221+
}
222+
},
223+
Register::Array(ref info, ref array_info) => {
224+
let has_brackets = info.name.contains("[%s]");
217225

218-
out
226+
let name = if has_brackets {
227+
info.name.replace("[%s]", "")
228+
} else {
229+
info.name.replace("%s", "")
230+
};
231+
232+
let ident = Ident::new(name.to_sanitized_snake_case());
233+
234+
let ty = syn::Ty::Array(
235+
Box::new(name_to_ty(&name)),
236+
syn::ConstExpr::Lit(syn::Lit::Int(array_info.dim as u64, syn::IntTy::Unsuffixed)),
237+
);
238+
239+
syn::Field{
240+
ident: Some(ident),
241+
vis: syn::Visibility::Public,
242+
attrs: vec![],
243+
ty: ty,
244+
}
245+
},
246+
}
219247
}
220248

221249
pub fn name_of(register: &Register) -> Cow<str> {

0 commit comments

Comments
 (0)