1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
use std::collections::BTreeMap;
use probe_rs::MemoryInterface;
use probe_rs_debug::{DebugError, ObjectRef, get_object_reference};
/// VariableCache stores available `Variable`s, and provides methods to create and navigate the parent-child relationships of the Variables.
#[derive(Debug, Clone, PartialEq)]
pub struct SvdVariableCache {
root_variable_key: ObjectRef,
variable_hash_map: BTreeMap<ObjectRef, Variable>,
}
impl SvdVariableCache {
/// Create a new cache for SVD variables
pub fn new_svd_cache() -> Self {
let root_variable_key = get_object_reference();
let root_variable = Variable {
variable_key: root_variable_key,
parent_key: ObjectRef::Invalid,
name: "Peripheral variable".to_string(),
variable_kind: SvdVariable::Root,
};
SvdVariableCache {
root_variable_key,
variable_hash_map: BTreeMap::from([(root_variable_key, root_variable)]),
}
}
/// Retrieve all the children of a `Variable`.
/// If `parent_key == None`, it will return all the top level variables (no parents) in this cache.
pub fn get_children(&self, parent_key: ObjectRef) -> Vec<&Variable> {
self.variable_hash_map
.values()
.filter(|child_variable| child_variable.parent_key == parent_key)
.collect::<Vec<_>>()
}
/// Retrieve a specific `Variable`, using the `variable_key`.
pub fn get_variable_by_key(&self, variable_key: ObjectRef) -> Option<&Variable> {
self.variable_hash_map.get(&variable_key)
}
/// Get the root variable of the cache
pub fn root_variable_key(&self) -> ObjectRef {
self.root_variable_key
}
/// Retrieve a clone of a specific `Variable`, using the `name`.
/// If there is more than one, it will be logged (tracing::warn!), and only the first will be returned.
/// It is possible for a hierarchy of variables in a cache to have duplicate names under different parents.
pub fn get_variable_by_name(&self, variable_name: &str) -> Option<&Variable> {
let child_variables = self
.variable_hash_map
.values()
.filter(|child_variable| child_variable.name.eq(variable_name))
.collect::<Vec<&Variable>>();
match &child_variables[..] {
[] => None,
[variable] => Some(*variable),
[first, ..] => {
tracing::warn!(
"Found {} variables with name={}. Please report this as a bug.",
child_variables.len(),
variable_name
);
Some(*first)
}
}
}
/// Retrieve a clone of a specific `Variable`, using the `name` and `parent_key`.
/// If there is more than one, it will be logged (tracing::error!), and only the last will be returned.
pub fn get_variable_by_name_and_parent(
&self,
variable_name: &str,
parent_key: ObjectRef,
) -> Option<&Variable> {
let child_variables = self
.variable_hash_map
.values()
.filter(|child_variable| {
child_variable.name == variable_name && child_variable.parent_key == parent_key
})
.collect::<Vec<&Variable>>();
match &child_variables[..] {
[] => None,
[variable] => Some(variable),
[.., last] => {
tracing::error!(
"Found {} variables with parent_key={:?} and name={}. Please report this as a bug.",
child_variables.len(),
parent_key,
variable_name
);
Some(last)
}
}
}
pub fn add_variable(
&mut self,
parent_key: ObjectRef,
name: String,
variable: SvdVariable,
) -> Result<ObjectRef, DebugError> {
let cache_variable = {
let variable_key = get_object_reference();
Variable {
variable_key,
parent_key,
name,
variable_kind: variable,
}
};
// Validate that the parent_key exists ...
if !self.variable_hash_map.contains_key(&parent_key) {
return Err(DebugError::Other(format!(
"SvdVariableCache: Attempted to add a new variable: {} with non existent `parent_key`: {:?}. Please report this as a bug",
cache_variable.name, parent_key
)));
}
tracing::trace!(
"SvdVariableCache: Add Variable: key={:?}, parent={:?}, name={:?}",
cache_variable.variable_key,
cache_variable.parent_key,
&cache_variable.name
);
if let Some(old_variable) = self
.variable_hash_map
.insert(cache_variable.variable_key, cache_variable.clone())
{
return Err(DebugError::Other(format!(
"Attempt to insert a new `SvdVariable`:{:?} with a duplicate cache key: {:?}. Please report this as a bug.",
cache_variable.name, old_variable.variable_key
)));
}
Ok(cache_variable.variable_key)
}
}
/// The `Variable` struct is used in conjunction with `VariableCache` to cache data about variables.
///
/// Any modifications to the `Variable` value will be transient (lost when it goes out of scope),
/// unless it is updated through one of the available methods on `VariableCache`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Variable {
/// Every variable must have a unique key value assigned to it. The value will be zero until it is stored in VariableCache, at which time its value will be set to the same as the VariableCache::variable_cache_key
variable_key: ObjectRef,
/// Every variable must have a unique parent assigned to it when stored in the VariableCache.
parent_key: ObjectRef,
/// The name of the SVD variable, the name of a register, field, peripheral, etc.
name: String,
pub variable_kind: SvdVariable,
}
impl Variable {
pub fn name(&self) -> &str {
&self.name
}
/// Get a unique key for this variable.
pub fn variable_key(&self) -> ObjectRef {
self.variable_key
}
/// Memory reference, compatible with DAP
pub fn memory_reference(&self) -> Option<String> {
match self.variable_kind {
SvdVariable::SvdRegister {
address,
restricted_read: false,
..
} => Some(format!("{:#010X}", address)),
SvdVariable::SvdField {
address,
restricted_read: false,
..
} => Some(format!("{:#010X}", address)),
_ => None,
}
}
pub fn type_name(&self) -> Option<String> {
self.variable_kind.type_name()
}
/// Value of the variable, compatible with DAP
///
/// The value might be retrieved using the `MemoryInterface` to read the value from the target.
pub fn get_value(&self, memory: &mut dyn MemoryInterface) -> String {
self.variable_kind.get_value(memory)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SvdVariable {
Root,
/// Register with address
SvdRegister {
address: u64,
/// true if the register is write-only or if a ride has side effects
restricted_read: bool,
/// Description of the register, used as type in DAP
description: Option<String>,
/// Size in bits of the register
size: u32,
},
/// Field with address
SvdField {
address: u64,
/// true if the register is write-only or if a ride has side effects
restricted_read: bool,
bit_range_lower_bound: u32,
bit_range_upper_bound: u32,
description: Option<String>,
},
/// Peripheral with peripheral base address
SvdPeripheral {
base_address: u64,
description: Option<String>,
},
SvdPeripheralGroup {
description: Option<String>,
},
}
impl SvdVariable {
fn get_value(&self, memory: &mut dyn MemoryInterface) -> String {
match &self {
SvdVariable::Root => "".to_string(),
// For peripheral and peripheral group, we use the description as the value if there is one, otherwise there is no value
SvdVariable::SvdPeripheral { description, .. }
| SvdVariable::SvdPeripheralGroup { description } => {
description.as_ref().cloned().unwrap_or_default()
}
SvdVariable::SvdRegister {
address,
restricted_read,
size,
..
} => {
if *restricted_read {
format!(
"Register cannot be read without side effects @ {:#010X}",
address
)
} else {
let size_bytes = (*size / 8).max(1);
let bits_alignment_offset = (*address % size_bytes as u64) as u32 * 8;
let value = match *size {
0..=8 => memory
.read_word_8(*address)
.map(|val| format!("{val:#04X}")),
9..=16 => {
let aligned_address = *address & !0b1;
memory
.read_word_16(aligned_address)
.map(|val| format!("{:#06X}", val >> bits_alignment_offset))
}
_ => {
let aligned_address = *address & !0b11;
memory
.read_word_32(aligned_address)
.map(|val| format!("{:#010X}", val >> bits_alignment_offset))
}
};
match value {
Ok(value) => value,
Err(error) => format!(
"Unable to read peripheral register value @ {:#010X} : {:?}",
address, error
),
}
}
}
SvdVariable::SvdField {
address,
restricted_read,
bit_range_lower_bound,
bit_range_upper_bound,
..
} => {
if *restricted_read {
format!(
"Field cannot be read without side effects @ {:#010X}",
address
)
} else {
let value = match *bit_range_upper_bound {
0..8 => memory.read_word_8(*address).map(u32::from),
8..16 => memory.read_word_16(*address & !0b1).map(u32::from),
_ => memory.read_word_32(*address & !0b11),
};
// In this special case, we extract just the bits we need from the stored value of the register.
match value {
Ok(register_u32_value) => {
let mut bit_value: u32 = register_u32_value;
bit_value <<= 32 - bit_range_upper_bound;
bit_value >>= 32 - (bit_range_upper_bound - bit_range_lower_bound);
format!(
"{:0width$b} @ {:#010X}:{}..{}",
bit_value,
address,
bit_range_lower_bound,
bit_range_upper_bound,
width = (*bit_range_upper_bound - *bit_range_lower_bound) as usize
)
}
Err(error) => format!(
"Unable to read peripheral register field value @ {:#010X} : {:?}",
address, error
),
}
}
}
}
}
fn type_name(&self) -> Option<String> {
match &self {
SvdVariable::SvdRegister { description, .. }
| SvdVariable::SvdField { description, .. } => description.clone(),
SvdVariable::SvdPeripheral { .. } => Some("Peripheral".to_string()),
SvdVariable::SvdPeripheralGroup { .. } => Some("Peripheral Group".to_string()),
SvdVariable::Root => None,
}
}
}