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};
#[derive(Debug)]
pub struct SvdCache {
pub(crate) svd_variable_cache: SvdVariableCache,
}
impl SvdCache {
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,
&Config::default().expand(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
}
}
#[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;
if let Some(peripheral_group_name) = &peripheral.group_name {
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;
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(true);
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(),
},
);
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 {
svd_cache.add_variable(register_variable_key, variable_name, variable)?;
}
}
}
Ok(svd_cache)
}