1
1
use crate :: svd:: {
2
- self , Access , BitRange , DimElement , EnumeratedValues , Field , MaybeArray , ModifiedWriteValues ,
3
- ReadAction , Register , RegisterProperties , Usage , WriteConstraint ,
2
+ self , Access , BitRange , DimElement , EnumeratedValue , EnumeratedValues , Field , MaybeArray ,
3
+ ModifiedWriteValues , ReadAction , Register , RegisterProperties , Usage , WriteConstraint ,
4
4
} ;
5
5
use core:: u64;
6
6
use log:: warn;
7
7
use proc_macro2:: { Ident , Punct , Spacing , Span , TokenStream } ;
8
8
use quote:: { quote, ToTokens } ;
9
- use std:: borrow:: Cow ;
10
9
use std:: collections:: HashSet ;
11
10
use std:: fmt:: Write ;
11
+ use std:: { borrow:: Cow , collections:: BTreeMap } ;
12
12
use svd_parser:: expand:: {
13
13
derive_enumerated_values, derive_field, BlockPath , EnumPath , FieldPath , Index , RegisterPath ,
14
14
} ;
@@ -472,7 +472,7 @@ fn render_register_mod_debug(
472
472
log:: debug!( "register={} field={}" , name, f. name) ;
473
473
if field_access. can_read ( ) && f. read_action . is_none ( ) {
474
474
if let Field :: Array ( _, de) = & f {
475
- for ( _ , suffix) in de. indexes ( ) . enumerate ( ) {
475
+ for suffix in de. indexes ( ) {
476
476
let f_name_n = util:: replace_suffix ( & f. name , & suffix)
477
477
. to_snake_case_ident ( Span :: call_site ( ) ) ;
478
478
let f_name_n_s = format ! ( "{f_name_n}" ) ;
@@ -730,11 +730,24 @@ pub fn fields(
730
730
// later on is the same as the read enumeration, we reuse and do not generate again.
731
731
evs_r = Some ( evs) ;
732
732
733
- // do we have finite definition of this enumeration in svd? If not, the later code would
734
- // return an Option when the value read from field does not match any defined values.
735
- let has_reserved_variant = evs. values . len ( ) != ( 1 << width) ;
736
733
// parse enum variants from enumeratedValues svd record
737
- let variants = Variant :: from_enumerated_values ( evs, config. pascal_enum_values ) ?;
734
+ let mut variants = Variant :: from_enumerated_values ( evs, config. pascal_enum_values ) ?;
735
+
736
+ let map = enums_to_map ( evs) ;
737
+ let mut def = evs
738
+ . default_value ( )
739
+ . and_then ( |def| {
740
+ minimal_hole ( & map, width)
741
+ . map ( |v| Variant :: from_value ( v, def, config. pascal_enum_values ) )
742
+ } )
743
+ . transpose ( ) ?;
744
+ if variants. len ( ) == 1 << width {
745
+ def = None ;
746
+ } else if variants. len ( ) == ( 1 << width) - 1 {
747
+ if let Some ( def) = def. take ( ) {
748
+ variants. push ( def) ;
749
+ }
750
+ }
738
751
739
752
// if there's no variant defined in enumeratedValues, generate enumeratedValues with new-type
740
753
// wrapper struct, and generate From conversation only.
@@ -743,8 +756,32 @@ pub fn fields(
743
756
// generate struct VALUE_READ_TY_A(fty) and From<fty> for VALUE_READ_TY_A.
744
757
add_with_no_variants ( mod_items, & value_read_ty, & fty, & description, rv) ;
745
758
} else {
759
+ // do we have finite definition of this enumeration in svd? If not, the later code would
760
+ // return an Option when the value read from field does not match any defined values.
761
+ let has_reserved_variant;
762
+
746
763
// generate enum VALUE_READ_TY_A { ... each variants ... } and and From<fty> for VALUE_READ_TY_A.
747
- add_from_variants ( mod_items, & variants, & value_read_ty, & fty, & description, rv) ;
764
+ if let Some ( def) = def. as_ref ( ) {
765
+ add_from_variants (
766
+ mod_items,
767
+ variants. iter ( ) . chain ( std:: iter:: once ( def) ) ,
768
+ & value_read_ty,
769
+ & fty,
770
+ & description,
771
+ rv,
772
+ ) ;
773
+ has_reserved_variant = false ;
774
+ } else {
775
+ add_from_variants (
776
+ mod_items,
777
+ variants. iter ( ) ,
778
+ & value_read_ty,
779
+ & fty,
780
+ & description,
781
+ rv,
782
+ ) ;
783
+ has_reserved_variant = evs. values . len ( ) != ( 1 << width) ;
784
+ }
748
785
749
786
// prepare code for each match arm. If we have reserved variant, the match operation would
750
787
// return an Option, thus we wrap the return value with Some.
@@ -771,6 +808,11 @@ pub fn fields(
771
808
arms. extend ( quote ! {
772
809
_ => None ,
773
810
} ) ;
811
+ } else if let Some ( v) = def. as_ref ( ) {
812
+ let pc = & v. pc ;
813
+ arms. extend ( quote ! {
814
+ _ => #value_read_ty:: #pc,
815
+ } ) ;
774
816
} else if 1 << width. to_ty_width ( ) ? != variants. len ( ) {
775
817
arms. extend ( quote ! {
776
818
_ => unreachable!( ) ,
@@ -779,26 +821,20 @@ pub fn fields(
779
821
780
822
// prepare the `variant` function. This function would return field value in
781
823
// Rust structure; if we have reserved variant we return by Option.
782
- if has_reserved_variant {
783
- enum_items. extend ( quote ! {
784
- #[ doc = "Get enumerated values variant" ]
785
- #inline
786
- pub const fn variant( & self ) -> Option <#value_read_ty> {
787
- match self . bits {
788
- #arms
789
- }
790
- }
791
- } ) ;
824
+ let ret_ty = if has_reserved_variant {
825
+ quote ! ( Option <#value_read_ty>)
792
826
} else {
793
- enum_items. extend ( quote ! {
827
+ quote ! ( #value_read_ty)
828
+ } ;
829
+ enum_items. extend ( quote ! {
794
830
#[ doc = "Get enumerated values variant" ]
795
831
#inline
796
- pub const fn variant( & self ) -> #value_read_ty {
832
+ pub const fn variant( & self ) -> #ret_ty {
797
833
match self . bits {
798
834
#arms
799
835
}
800
- } } ) ;
801
- }
836
+ }
837
+ } ) ;
802
838
803
839
// for each variant defined, we generate an `is_variant` function.
804
840
for v in & variants {
@@ -823,6 +859,28 @@ pub fn fields(
823
859
}
824
860
} ) ;
825
861
}
862
+ if let Some ( v) = def. as_ref ( ) {
863
+ let pc = & v. pc ;
864
+ let sc = & v. nksc ;
865
+
866
+ let is_variant = Ident :: new (
867
+ & if sc. to_string ( ) . starts_with ( '_' ) {
868
+ format ! ( "is{sc}" )
869
+ } else {
870
+ format ! ( "is_{sc}" )
871
+ } ,
872
+ span,
873
+ ) ;
874
+
875
+ let doc = util:: escape_special_chars ( & util:: respace ( & v. doc ) ) ;
876
+ enum_items. extend ( quote ! {
877
+ #[ doc = #doc]
878
+ #inline
879
+ pub fn #is_variant( & self ) -> bool {
880
+ matches!( self . variant( ) , #value_read_ty:: #pc)
881
+ }
882
+ } ) ;
883
+ }
826
884
}
827
885
}
828
886
@@ -876,7 +934,7 @@ pub fn fields(
876
934
}
877
935
} ) ;
878
936
879
- for fi in svd:: field:: expand ( & f, de) {
937
+ for fi in svd:: field:: expand ( f, de) {
880
938
let sub_offset = fi. bit_offset ( ) as u64 ;
881
939
let value = if sub_offset != 0 {
882
940
let sub_offset = & unsuffixed ( sub_offset) ;
@@ -961,7 +1019,20 @@ pub fn fields(
961
1019
// if we writes to enumeratedValues, generate its structure if it differs from read structure.
962
1020
if let Some ( ( evs, None ) ) = lookup_filter ( & lookup_results, Usage :: Write ) {
963
1021
// parse variants from enumeratedValues svd record
964
- let variants = Variant :: from_enumerated_values ( evs, config. pascal_enum_values ) ?;
1022
+ let mut variants = Variant :: from_enumerated_values ( evs, config. pascal_enum_values ) ?;
1023
+ let map = enums_to_map ( evs) ;
1024
+ let mut def = evs
1025
+ . default_value ( )
1026
+ . and_then ( |def| {
1027
+ minimal_hole ( & map, width)
1028
+ . map ( |v| Variant :: from_value ( v, def, config. pascal_enum_values ) )
1029
+ } )
1030
+ . transpose ( ) ?;
1031
+ if variants. len ( ) == 1 << width {
1032
+ } else if let Some ( def) = def. take ( ) {
1033
+ variants. push ( def) ;
1034
+ unsafety = false ;
1035
+ }
965
1036
966
1037
// if the write structure is finite, it can be safely written.
967
1038
if variants. len ( ) == 1 << width {
@@ -979,7 +1050,7 @@ pub fn fields(
979
1050
} else {
980
1051
add_from_variants (
981
1052
mod_items,
982
- & variants,
1053
+ variants. iter ( ) ,
983
1054
& value_write_ty,
984
1055
& fty,
985
1056
& description,
@@ -1130,7 +1201,7 @@ pub fn fields(
1130
1201
}
1131
1202
} ) ;
1132
1203
1133
- for fi in svd:: field:: expand ( & f, de) {
1204
+ for fi in svd:: field:: expand ( f, de) {
1134
1205
let sub_offset = fi. bit_offset ( ) as u64 ;
1135
1206
let name_snake_case_n = & fi. name . to_snake_case_ident ( Span :: call_site ( ) ) ;
1136
1207
let doc = description_with_bits (
@@ -1212,36 +1283,38 @@ struct Variant {
1212
1283
1213
1284
impl Variant {
1214
1285
fn from_enumerated_values ( evs : & EnumeratedValues , pc : bool ) -> Result < Vec < Self > > {
1215
- let span = Span :: call_site ( ) ;
1216
1286
evs. values
1217
1287
. iter ( )
1218
1288
// filter out all reserved variants, as we should not
1219
1289
// generate code for them
1220
- . filter ( |field| field . name . to_lowercase ( ) != "reserved" && field . is_default . is_none ( ) )
1290
+ . filter ( |ev| ev . name . to_lowercase ( ) != "reserved" && !ev . is_default ( ) )
1221
1291
. map ( |ev| {
1222
1292
let value = ev
1223
1293
. value
1224
- . ok_or_else ( || anyhow ! ( "EnumeratedValue {} has no `<value>` field" , ev. name) ) ?;
1225
-
1226
- let nksc = ev. name . to_sanitized_not_keyword_snake_case ( ) ;
1227
- let sc = util:: sanitize_keyword ( nksc. clone ( ) ) ;
1228
- Ok ( Variant {
1229
- doc : ev
1230
- . description
1231
- . clone ( )
1232
- . unwrap_or_else ( || format ! ( "`{value:b}`" ) ) ,
1233
- pc : if pc {
1234
- ev. name . to_pascal_case_ident ( span)
1235
- } else {
1236
- ev. name . to_constant_case_ident ( span)
1237
- } ,
1238
- nksc : Ident :: new ( & nksc, span) ,
1239
- sc : Ident :: new ( & sc, span) ,
1240
- value,
1241
- } )
1294
+ . ok_or_else ( || anyhow ! ( "EnumeratedValue {} has no `<value>` entry" , ev. name) ) ?;
1295
+ Self :: from_value ( value, ev, pc)
1242
1296
} )
1243
1297
. collect :: < Result < Vec < _ > > > ( )
1244
1298
}
1299
+ fn from_value ( value : u64 , ev : & EnumeratedValue , pc : bool ) -> Result < Self > {
1300
+ let span = Span :: call_site ( ) ;
1301
+ let nksc = ev. name . to_sanitized_not_keyword_snake_case ( ) ;
1302
+ let sc = util:: sanitize_keyword ( nksc. clone ( ) ) ;
1303
+ Ok ( Variant {
1304
+ doc : ev
1305
+ . description
1306
+ . clone ( )
1307
+ . unwrap_or_else ( || format ! ( "`{value:b}`" ) ) ,
1308
+ pc : if pc {
1309
+ ev. name . to_pascal_case_ident ( span)
1310
+ } else {
1311
+ ev. name . to_constant_case_ident ( span)
1312
+ } ,
1313
+ nksc : Ident :: new ( & nksc, span) ,
1314
+ sc : Ident :: new ( & sc, span) ,
1315
+ value,
1316
+ } )
1317
+ }
1245
1318
}
1246
1319
1247
1320
fn add_with_no_variants (
@@ -1283,9 +1356,9 @@ fn add_with_no_variants(
1283
1356
}
1284
1357
}
1285
1358
1286
- fn add_from_variants (
1359
+ fn add_from_variants < ' a > (
1287
1360
mod_items : & mut TokenStream ,
1288
- variants : & [ Variant ] ,
1361
+ variants : impl Iterator < Item = & ' a Variant > ,
1289
1362
pc : & Ident ,
1290
1363
fty : & Ident ,
1291
1364
desc : & str ,
@@ -1298,7 +1371,7 @@ fn add_from_variants(
1298
1371
} ;
1299
1372
1300
1373
let mut vars = TokenStream :: new ( ) ;
1301
- for v in variants. iter ( ) . map ( |v| {
1374
+ for v in variants. map ( |v| {
1302
1375
let desc = util:: escape_special_chars ( & util:: respace ( & format ! ( "{}: {}" , v. value, v. doc) ) ) ;
1303
1376
let pcv = & v. pc ;
1304
1377
let pcval = & unsuffixed ( v. value ) ;
@@ -1400,3 +1473,17 @@ fn lookup_filter(
1400
1473
. find ( |evsbase| evsbase. 0 . usage == Some ( usage) )
1401
1474
. or_else ( || evs. first ( ) )
1402
1475
}
1476
+
1477
+ fn enums_to_map ( evs : & EnumeratedValues ) -> BTreeMap < u64 , & EnumeratedValue > {
1478
+ let mut map = BTreeMap :: new ( ) ;
1479
+ for ev in & evs. values {
1480
+ if let Some ( v) = ev. value {
1481
+ map. insert ( v, ev) ;
1482
+ }
1483
+ }
1484
+ map
1485
+ }
1486
+
1487
+ fn minimal_hole ( map : & BTreeMap < u64 , & EnumeratedValue > , width : u32 ) -> Option < u64 > {
1488
+ ( 0 ..( 1u64 << width) ) . find ( |& v| !map. contains_key ( & v) )
1489
+ }
0 commit comments