11use std:: borrow:: Cow ;
22use std:: cmp:: Ordering ;
3+ use std:: collections:: HashMap ;
34
45use quote:: { ToTokens , Tokens } ;
5- use crate :: svd:: { Cluster , ClusterInfo , Defaults , Peripheral , Register , RegisterCluster } ;
6+ use crate :: svd:: { Cluster , ClusterInfo , Defaults , Peripheral , Register , RegisterCluster , RegisterInfo } ;
67use syn:: { self , Ident } ;
78use log:: warn;
89
@@ -12,26 +13,36 @@ use crate::util::{self, ToSanitizedSnakeCase, ToSanitizedUpperCase, BITS_PER_BYT
1213use crate :: generate:: register;
1314
1415pub fn render (
15- p : & Peripheral ,
16+ p_original : & Peripheral ,
1617 all_peripherals : & [ Peripheral ] ,
1718 defaults : & Defaults ,
1819 nightly : bool ,
1920) -> Result < Vec < Tokens > > {
2021 let mut out = vec ! [ ] ;
2122
23+ let p_derivedfrom = p_original. derived_from . as_ref ( ) . and_then ( |s| {
24+ all_peripherals. iter ( ) . find ( |x| x. name == * s)
25+ } ) ;
26+
27+ let p_merged = p_derivedfrom. map ( |ancestor| p_original. derive_from ( ancestor) ) ;
28+ let p = p_merged. as_ref ( ) . unwrap_or ( p_original) ;
29+
30+ if p_original. derived_from . is_some ( ) && p_derivedfrom. is_none ( ) {
31+ eprintln ! ( "Couldn't find derivedFrom original: {} for {}, skipping" ,
32+ p_original. derived_from. as_ref( ) . unwrap( ) , p_original. name) ;
33+ return Ok ( out) ;
34+ }
35+
2236 let name_pc = Ident :: new ( & * p. name . to_sanitized_upper_case ( ) ) ;
2337 let address = util:: hex ( p. base_address ) ;
24- let description =
25- util :: escape_brackets ( util :: respace ( p . description . as_ref ( ) . unwrap_or ( & p . name ) ) . as_ref ( ) ) ;
38+ let description = util :: respace ( p . description . as_ref ( ) . unwrap_or ( & p . name ) ) ;
39+ let derive_regs = p_derivedfrom . is_some ( ) && p_original . registers . is_none ( ) ;
2640
2741 let name_sc = Ident :: new ( & * p. name . to_sanitized_snake_case ( ) ) ;
28- let ( base, derived) = if let Some ( base) = p. derived_from . as_ref ( ) {
29- // TODO Verify that base exists
30- // TODO We don't handle inheritance style `derivedFrom`, we should raise
31- // an error in that case
32- ( Ident :: new ( & * base. to_sanitized_snake_case ( ) ) , true )
42+ let base = if derive_regs {
43+ Ident :: new ( & * p_derivedfrom. unwrap ( ) . name . to_sanitized_snake_case ( ) )
3344 } else {
34- ( name_sc. clone ( ) , false )
45+ name_sc. clone ( )
3546 } ;
3647
3748 // Insert the peripheral structure
@@ -57,17 +68,92 @@ pub fn render(
5768 }
5869 } ) ;
5970
60- // Derived peripherals do not require re-implementation, and will instead
61- // use a single definition of the non-derived version
62- if derived {
71+ // Derived peripherals may not require re-implementation, and will instead
72+ // use a single definition of the non-derived version.
73+ if derive_regs {
6374 return Ok ( out) ;
6475 }
6576
6677 // erc: *E*ither *R*egister or *C*luster
6778 let ercs = p. registers . as_ref ( ) . map ( |x| x. as_ref ( ) ) . unwrap_or ( & [ ] [ ..] ) ;
68-
6979 let registers: & [ & Register ] = & util:: only_registers ( & ercs) [ ..] ;
80+
81+ // make a pass to expand derived registers. Ideally, for the most minimal
82+ // code size, we'd do some analysis to figure out if we can 100% reuse the
83+ // code that we're deriving from. For the sake of proving the concept, we're
84+ // just going to emit a second copy of the accessor code. It'll probably
85+ // get inlined by the compiler anyway, right? :-)
86+
87+ // Build a map so that we can look up registers within this peripheral
88+ let mut reg_map = HashMap :: new ( ) ;
89+ for r in registers {
90+ reg_map. insert ( & r. name , r. clone ( ) ) ;
91+ }
92+
93+ // Compute the effective, derived version of a register given the definition
94+ // with the derived_from property on it (`info`) and its `ancestor`
95+ fn derive_reg_info ( info : & RegisterInfo , ancestor : & RegisterInfo ) -> RegisterInfo {
96+ let mut derived = info. clone ( ) ;
97+
98+ if derived. size . is_none ( ) {
99+ derived. size = ancestor. size . clone ( ) ;
100+ }
101+ if derived. access . is_none ( ) {
102+ derived. access = ancestor. access . clone ( ) ;
103+ }
104+ if derived. reset_value . is_none ( ) {
105+ derived. reset_value = ancestor. reset_value . clone ( ) ;
106+ }
107+ if derived. reset_mask . is_none ( ) {
108+ derived. reset_mask = ancestor. reset_mask . clone ( ) ;
109+ }
110+ if derived. fields . is_none ( ) {
111+ derived. fields = ancestor. fields . clone ( ) ;
112+ }
113+ if derived. write_constraint . is_none ( ) {
114+ derived. write_constraint = ancestor. write_constraint . clone ( ) ;
115+ }
116+
117+ derived
118+ }
119+
120+ // Build up an alternate erc list by expanding any derived registers
121+ let mut alt_erc : Vec < RegisterCluster > = registers. iter ( ) . filter_map ( |r| {
122+ match r. derived_from {
123+ Some ( ref derived) => {
124+ let ancestor = match reg_map. get ( derived) {
125+ Some ( r) => r,
126+ None => {
127+ eprintln ! ( "register {} derivedFrom missing register {}" , r. name, derived) ;
128+ return None
129+ }
130+ } ;
131+
132+ let d = match * * ancestor {
133+ Register :: Array ( ref info, ref array_info) => {
134+ Some ( RegisterCluster :: Register ( Register :: Array ( derive_reg_info ( * r, info) , array_info. clone ( ) ) ) )
135+ }
136+ Register :: Single ( ref info) => {
137+ Some ( RegisterCluster :: Register ( Register :: Single ( derive_reg_info ( * r, info) ) ) )
138+ }
139+ } ;
140+
141+ d
142+ }
143+ None => Some ( RegisterCluster :: Register ( ( * r) . clone ( ) ) ) ,
144+ }
145+ } ) . collect ( ) ;
146+
147+ // Now add the clusters to our alternate erc list
148+ let clusters = util:: only_clusters ( ercs) ;
149+ for cluster in & clusters {
150+ alt_erc. push ( RegisterCluster :: Cluster ( ( * cluster) . clone ( ) ) ) ;
151+ }
152+
153+ // And revise registers, clusters and ercs to refer to our expanded versions
154+ let registers: & [ & Register ] = & util:: only_registers ( & alt_erc) [ ..] ;
70155 let clusters = util:: only_clusters ( ercs) ;
156+ let ercs = & alt_erc;
71157
72158 // No `struct RegisterBlock` can be generated
73159 if registers. is_empty ( ) && clusters. is_empty ( ) {
0 commit comments