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
use crate::cmd::dap_server::{
DebuggerError,
debug_adapter::{dap::adapter::DebugAdapter, protocol::ProtocolAdapter},
};
use std::{fmt::Debug, fs::File, io::Read, path::Path};
use svd_parser::Config;
use super::svd_cache::{SvdVariable, SvdVariableCache};
/// The SVD file contents and related data
#[derive(Debug)]
pub struct SvdCache {
/// The SVD contents and structure will be stored as variables, down to the Field level.
/// Unlike other VariableCache instances, it will only be built once per DebugSession.
/// After that, only the SVD fields values change values, and the data for these will be re-read
/// every time they are queried by the debugger.
pub(crate) svd_variable_cache: SvdVariableCache,
}
impl SvdCache {
/// Create the SVD cache for a specific core. This function loads the file, parses it, and then builds the VariableCache.
pub(crate) fn new<P: ProtocolAdapter>(
svd_file: &Path,
debug_adapter: &mut DebugAdapter<P>,
dap_request_id: i64,
) -> Result<Self, DebuggerError> {
let svd_xml = &mut String::new();
let mut svd_opened_file = File::open(svd_file)?;
let progress_id = debug_adapter.start_progress(
format!("Loading SVD file: {}", svd_file.display()).as_str(),
Some(dap_request_id),
)?;
let _ = svd_opened_file.read_to_string(svd_xml)?;
let svd_cache = match svd_parser::parse_with_config(
svd_xml,
// `expand_properties` pushes each peripheral's / the device's
// `defaultRegisterProperties` (including `access`) down onto the individual
// registers, so a register whose `access` is still `None` afterwards is one
// that is genuinely unspecified anywhere in the SVD tree.
&Config::default()
.expand(true)
.expand_properties(true)
.ignore_enums(true),
) {
Ok(peripheral_device) => {
debug_adapter
.update_progress(
None,
Some(format!("Done loading SVD file: {}", svd_file.display())),
progress_id,
)
.ok();
Ok(SvdCache {
svd_variable_cache: variable_cache_from_svd(
peripheral_device,
debug_adapter,
progress_id,
)?,
})
}
Err(error) => Err(DebuggerError::Other(anyhow::anyhow!(
"Unable to parse CMSIS-SVD file: {svd_file:?}. {error:?}"
))),
};
debug_adapter.end_progress(progress_id)?;
svd_cache
}
}
/// Create a [`SvdVariableCache`] from a Device that was parsed from a CMSIS-SVD file.
#[tracing::instrument(skip_all)]
pub(crate) fn variable_cache_from_svd<P: ProtocolAdapter>(
peripheral_device: svd_parser::svd::Device,
debug_adapter: &mut DebugAdapter<P>,
progress_id: i64,
) -> Result<SvdVariableCache, DebuggerError> {
let mut svd_cache = SvdVariableCache::new_svd_cache();
let device_root_variable_key = svd_cache.root_variable_key();
let device_default_access = peripheral_device.default_register_properties.access;
for peripheral in &peripheral_device.peripherals {
let current_peripheral_group_name = peripheral.group_name.as_ref();
let peripheral_parent_key;
// Adding the Peripheral Group Name as an additional level in the structure helps to keep the 'variable tree' more compact,
// but more importantly, it helps to avoid having duplicate variable names that conflict with hal crates.
if let Some(peripheral_group_name) = &peripheral.group_name {
// Before we create a new group variable, check if we have one by that name already.
match svd_cache
.get_variable_by_name_and_parent(peripheral_group_name, device_root_variable_key)
{
Some(existing_peripheral_group_variable) => {
peripheral_parent_key = existing_peripheral_group_variable.variable_key();
}
None => {
peripheral_parent_key = svd_cache.add_variable(
device_root_variable_key,
peripheral_group_name.clone(),
SvdVariable::SvdPeripheralGroup {
description: peripheral.description.clone(),
},
)?;
}
};
debug_adapter
.update_progress(
None,
Some(format!(
"SVD loading peripheral group: {peripheral_group_name}",
)),
progress_id,
)
.ok();
} else {
peripheral_parent_key = device_root_variable_key;
}
let peripheral_name = if let Some(peripheral_group) = current_peripheral_group_name {
format!("{}.{}", peripheral_group, peripheral.name)
} else {
peripheral.name.clone()
};
let peripheral_key = svd_cache.add_variable(
peripheral_parent_key,
peripheral_name.clone(),
SvdVariable::SvdPeripheral {
base_address: peripheral.base_address,
description: peripheral.description.clone(),
},
)?;
for register in peripheral.all_registers() {
let register_address = peripheral.base_address + register.address_offset as u64;
// After property expansion above, a `None` access means no `<access>` was
// declared anywhere in the tree (register, peripheral or device). Treat such
// registers as readable: many recent SVDs (e.g. STM32H5) omit `<access>`
// entirely, and defaulting to restricted made every register unviewable in
// the debugger. Registers with a `read_action` are still restricted, so
// side-effecting reads remain protected.
let mut register_has_restricted_read = register.read_action.is_some()
|| register
.properties
.access
.map(|a| !a.can_read())
.or_else(|| device_default_access.map(|a| !a.can_read()))
.unwrap_or(false);
let register_name = format!("{peripheral_name}.{}", register.name);
let mut field_variables = Vec::new();
for field in register.fields() {
let field_has_restricted_read = register_has_restricted_read
|| field.read_action.is_some()
|| field
.access
.map(|a| !a.can_read())
.or_else(|| device_default_access.map(|a| !a.can_read()))
.unwrap_or(register_has_restricted_read);
let field_variable = (
format!("{}.{}", register_name, field.name),
SvdVariable::SvdField {
address: register_address,
restricted_read: field_has_restricted_read,
bit_range_lower_bound: field.bit_offset(),
bit_range_upper_bound: (field.bit_offset() + field.bit_width()),
description: field.description.clone(),
},
);
// If any of the fields in the register have restricted read, then the register has restricted read.
register_has_restricted_read |= field_has_restricted_read;
field_variables.push(field_variable);
}
let register_variable_key = svd_cache.add_variable(
peripheral_key,
format!("{peripheral_name}.{}", register.name),
SvdVariable::SvdRegister {
address: register_address,
restricted_read: register_has_restricted_read,
description: register.description.clone(),
size: register.properties.size.unwrap_or(32),
},
)?;
for (variable_name, variable) in field_variables {
// TODO: Extend the Variable definition, so that we can resolve the EnumeratedValues for fields.
svd_cache.add_variable(register_variable_key, variable_name, variable)?;
}
}
}
Ok(svd_cache)
}