Skip to content

Commit

Permalink
Merge #319
Browse files Browse the repository at this point in the history
319: register Derived from r=therealprof a=burrbull

First three commits from #256 .

cc @therealprof 

Co-authored-by: Nebojsa Sabovic <nsabovic@gmail.com>
Co-authored-by: Wez Furlong <wez@wezfurlong.org>
Co-authored-by: Andrey Zgarbul <zgarbul.andrey@gmail.com>
  • Loading branch information
4 people committed Jul 19, 2019
2 parents a7e89d1 + dbffe2a commit fbaaa28
Showing 1 changed file with 100 additions and 14 deletions.
114 changes: 100 additions & 14 deletions src/generate/peripheral.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::HashMap;

use quote::{ToTokens, Tokens};
use crate::svd::{Cluster, ClusterInfo, Defaults, Peripheral, Register, RegisterCluster};
use crate::svd::{Cluster, ClusterInfo, Defaults, Peripheral, Register, RegisterCluster, RegisterInfo};
use syn::{self, Ident};
use log::warn;

Expand All @@ -12,26 +13,36 @@ use crate::util::{self, ToSanitizedSnakeCase, ToSanitizedUpperCase, BITS_PER_BYT
use crate::generate::register;

pub fn render(
p: &Peripheral,
p_original: &Peripheral,
all_peripherals: &[Peripheral],
defaults: &Defaults,
nightly: bool,
) -> Result<Vec<Tokens>> {
let mut out = vec![];

let p_derivedfrom = p_original.derived_from.as_ref().and_then(|s| {
all_peripherals.iter().find(|x| x.name == *s)
});

let p_merged = p_derivedfrom.map(|ancestor| p_original.derive_from(ancestor));
let p = p_merged.as_ref().unwrap_or(p_original);

if p_original.derived_from.is_some() && p_derivedfrom.is_none() {
eprintln!("Couldn't find derivedFrom original: {} for {}, skipping",
p_original.derived_from.as_ref().unwrap(), p_original.name);
return Ok(out);
}

let name_pc = Ident::new(&*p.name.to_sanitized_upper_case());
let address = util::hex(p.base_address);
let description =
util::escape_brackets(util::respace(p.description.as_ref().unwrap_or(&p.name)).as_ref());
let description = util::respace(p.description.as_ref().unwrap_or(&p.name));
let derive_regs = p_derivedfrom.is_some() && p_original.registers.is_none();

let name_sc = Ident::new(&*p.name.to_sanitized_snake_case());
let (base, derived) = if let Some(base) = p.derived_from.as_ref() {
// TODO Verify that base exists
// TODO We don't handle inheritance style `derivedFrom`, we should raise
// an error in that case
(Ident::new(&*base.to_sanitized_snake_case()), true)
let base = if derive_regs {
Ident::new(&*p_derivedfrom.unwrap().name.to_sanitized_snake_case())
} else {
(name_sc.clone(), false)
name_sc.clone()
};

// Insert the peripheral structure
Expand All @@ -57,17 +68,92 @@ pub fn render(
}
});

// Derived peripherals do not require re-implementation, and will instead
// use a single definition of the non-derived version
if derived {
// Derived peripherals may not require re-implementation, and will instead
// use a single definition of the non-derived version.
if derive_regs {
return Ok(out);
}

// erc: *E*ither *R*egister or *C*luster
let ercs = p.registers.as_ref().map(|x| x.as_ref()).unwrap_or(&[][..]);

let registers: &[&Register] = &util::only_registers(&ercs)[..];

// make a pass to expand derived registers. Ideally, for the most minimal
// code size, we'd do some analysis to figure out if we can 100% reuse the
// code that we're deriving from. For the sake of proving the concept, we're
// just going to emit a second copy of the accessor code. It'll probably
// get inlined by the compiler anyway, right? :-)

// Build a map so that we can look up registers within this peripheral
let mut reg_map = HashMap::new();
for r in registers {
reg_map.insert(&r.name, r.clone());
}

// Compute the effective, derived version of a register given the definition
// with the derived_from property on it (`info`) and its `ancestor`
fn derive_reg_info(info: &RegisterInfo, ancestor: &RegisterInfo) -> RegisterInfo {
let mut derived = info.clone();

if derived.size.is_none() {
derived.size = ancestor.size.clone();
}
if derived.access.is_none() {
derived.access = ancestor.access.clone();
}
if derived.reset_value.is_none() {
derived.reset_value = ancestor.reset_value.clone();
}
if derived.reset_mask.is_none() {
derived.reset_mask = ancestor.reset_mask.clone();
}
if derived.fields.is_none() {
derived.fields = ancestor.fields.clone();
}
if derived.write_constraint.is_none() {
derived.write_constraint = ancestor.write_constraint.clone();
}

derived
}

// Build up an alternate erc list by expanding any derived registers
let mut alt_erc :Vec<RegisterCluster> = registers.iter().filter_map(|r| {
match r.derived_from {
Some(ref derived) => {
let ancestor = match reg_map.get(derived) {
Some(r) => r,
None => {
eprintln!("register {} derivedFrom missing register {}", r.name, derived);
return None
}
};

let d = match **ancestor {
Register::Array(ref info, ref array_info) => {
Some(RegisterCluster::Register(Register::Array(derive_reg_info(*r, info), array_info.clone())))
}
Register::Single(ref info) => {
Some(RegisterCluster::Register(Register::Single(derive_reg_info(*r, info))))
}
};

d
}
None => Some(RegisterCluster::Register((*r).clone())),
}
}).collect();

// Now add the clusters to our alternate erc list
let clusters = util::only_clusters(ercs);
for cluster in &clusters {
alt_erc.push(RegisterCluster::Cluster((*cluster).clone()));
}

// And revise registers, clusters and ercs to refer to our expanded versions
let registers: &[&Register] = &util::only_registers(&alt_erc)[..];
let clusters = util::only_clusters(ercs);
let ercs = &alt_erc;

// No `struct RegisterBlock` can be generated
if registers.is_empty() && clusters.is_empty() {
Expand Down

0 comments on commit fbaaa28

Please sign in to comment.