use crate::debug_access::{self, Statement};
use log::{error, trace, warn};
use roxmltree::Node;
use serde::{Deserialize, Serialize};
use serde_roxmltree::RawNode;
use std::{collections::HashMap, fmt::Debug};
fn de_uint<'de, D>(d: D) -> Result<u32, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(d)?;
let trimmed = s.trim();
trimmed
.strip_prefix("0x")
.or_else(|| trimmed.strip_prefix("0X"))
.map_or_else(
|| trimmed.parse::<u32>().map_err(serde::de::Error::custom),
|hex| u32::from_str_radix(hex, 16).map_err(serde::de::Error::custom),
)
}
fn de_opt_uint<'de, D>(d: D) -> Result<Option<u32>, D::Error>
where
D: serde::Deserializer<'de>,
{
de_uint(d).map(Some)
}
fn de_opt_bool<'de, D>(d: D) -> Result<Option<bool>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(d)?;
match s.as_str() {
"true" | "1" => Ok(Some(true)),
"false" | "0" => Ok(Some(false)),
other => Err(serde::de::Error::custom(format!(
"expected xs:boolean, got: {other}"
))),
}
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
pub enum FamilyParseError {
MissingAttribute(String),
UnknownElementType(String),
MalformedDebugvar(String),
#[default]
UnimplementedContent,
}
impl std::fmt::Display for FamilyParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MissingAttribute(attr) => write!(f, "missing attribute: {attr}"),
Self::UnknownElementType(tag) => write!(f, "unknown element type: {tag}"),
Self::MalformedDebugvar(msg) => write!(f, "malformed debugvar: {msg}"),
Self::UnimplementedContent => write!(f, "unimplemented content-parse branch"),
}
}
}
impl std::error::Error for FamilyParseError {}
impl From<FamilyParseError> for crate::Error {
fn from(value: FamilyParseError) -> Self {
Self::Family(value)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
pub struct Family<'a> {
#[serde(rename = "Dfamily")]
pub device_family: String,
#[serde(rename = "Dvendor")]
pub vendor: String,
pub deprecated: Option<String>,
#[serde(rename = "description", default)]
pub description: Vec<String>,
#[serde(rename = "processor", default)]
pub processor: Vec<Processor>,
#[serde(rename = "compile", default)]
pub compile: Vec<Compile>,
#[serde(rename = "debug", default)]
pub debug: Vec<FamilyDebug>,
pub debugconfig: Option<DebugConfig>,
#[serde(rename = "debugport", default)]
pub debugport: Vec<DebugPort>,
#[serde(rename = "accessportV1", default)]
pub access_port_v1: Vec<AccessPortV1>,
#[serde(rename = "accessportV2", default)]
pub access_port_v2: Vec<AccessPortV2>,
#[serde(rename = "algorithm", default)]
pub algorithm: Vec<Algorithm>,
#[serde(rename = "flashinfo", default, borrow, skip_serializing)]
pub flashinfo_raw: Vec<RawNode<'a>>,
#[serde(skip_deserializing)]
pub flashinfo: Vec<FlashInfo>,
#[serde(rename = "memory", default)]
pub memory: Vec<Memory>,
#[serde(rename = "trace", default)]
pub trace: Vec<Trace>,
#[serde(rename = "book", default)]
pub book: Vec<Book>,
#[serde(rename = "feature", default)]
pub feature: Vec<Feature>,
#[serde(rename = "environment", default)]
pub environment: Vec<FamilyEnvironment>,
#[serde(rename = "subFamily", default)]
pub sub_families: Vec<SubFamily<'a>>,
#[serde(rename = "device", default)]
pub devices: Vec<Device<'a>>,
#[serde(rename = "debugvars", default, borrow, skip_serializing)]
pub debugvars_raw: Vec<RawNode<'a>>,
#[serde(skip_deserializing)]
pub debugvars: Debugvars,
#[serde(rename = "sequences", default, borrow, skip_serializing)]
pub sequences_raw: Vec<RawNode<'a>>,
#[serde(skip_deserializing)]
pub sequences: Sequences<'a>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
pub enum TraceSetup {
#[serde(rename = "full")]
Full,
#[serde(rename = "legacy")]
#[default]
Legacy,
}
fn de_opt_trace_setup<'de, D>(d: D) -> Result<Option<TraceSetup>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(d)?;
match s.as_str() {
"full" => Ok(Some(TraceSetup::Full)),
"legacy" => Ok(Some(TraceSetup::Legacy)),
other => Err(serde::de::Error::custom(format!(
"expected TraceSetupEnum, got: {other}"
))),
}
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
pub struct Debugvars {
pub configfile: Option<String>,
pub version: Option<String>,
#[serde(rename = "#content")]
pub content: String,
pub parsed_debugvars: Option<HashMap<String, u64>>,
}
impl Debugvars {
fn parse_value_string(value: &str) -> Option<u64> {
if let Some(hex_value_str) = value.strip_prefix("0x")
&& let Ok(k) = u64::from_str_radix(hex_value_str, 16)
{
return Some(k);
}
match value.parse() {
Ok(k) => Some(k),
Err(e) => {
error!("Failed to parse debugvar value ({value}) to u64 with error: {e}");
None
}
}
}
pub fn parse_single_debugvar(line: &str) -> Result<(String, u64), FamilyParseError> {
trace!("Parsing debugvar line: {line}");
let declaration = if line.trim_start().starts_with("//") {
let parts: Vec<&str> = line.split('\n').collect();
match parts.get(1) {
Some(v) => v.trim(),
None => {
return Err(FamilyParseError::MalformedDebugvar(
"Unable to get comment body".to_string(),
));
}
}
} else {
line.trim()
};
if declaration.is_empty() {
return Err(FamilyParseError::MalformedDebugvar(
"empty declaration".to_string(),
));
}
let Some(stripped_declaration) = declaration.strip_prefix("__var") else {
warn!("Variable in debugvars does not start with \"__var \": {declaration:?}");
return Err(FamilyParseError::MalformedDebugvar(format!(
"missing '__var' prefix: {declaration:?}"
)));
};
let parts: Vec<&str> = stripped_declaration.split('=').collect();
if parts.len() != 2 {
warn!("Got something other than 2 fields when parsing debugvar: {parts:?}");
return Err(FamilyParseError::MalformedDebugvar(format!(
"expected exactly one '=' separator, got {} fields",
parts.len()
)));
}
let name: String = if let Some(v) = parts.first() {
v.trim().to_string()
} else {
return Err(FamilyParseError::MalformedDebugvar(
"Unable to variable name in declaration".to_string(),
));
};
let value_str = if let Some(v) = parts.get(1) {
v.trim()
} else {
return Err(FamilyParseError::MalformedDebugvar(
"Unable to variable value in declaration".to_string(),
));
};
Self::parse_value_string(value_str).map_or_else(
|| {
Err(FamilyParseError::MalformedDebugvar(format!(
"could not parse value as u64: {value_str:?}"
)))
},
|value| Ok((name, value)),
)
}
#[must_use]
pub fn parse_debugvars_content(&self) -> HashMap<String, u64> {
let content: String = self
.content
.split('\n')
.filter_map(|line| {
if line.trim().starts_with("//") {
trace!("Ignoring line: {line}");
None
} else {
Some(line.to_owned() + "\n") }
})
.collect();
let variables: Vec<&str> = content.split(';').collect();
variables
.iter()
.filter_map(|var| {
Self::parse_single_debugvar(var).ok()
})
.collect()
}
pub fn parse_debugvars(&mut self) {
let vars = self.parse_debugvars_content();
self.parsed_debugvars = Some(vars);
}
}
pub fn merge_debugvars(raw_nodes: &[RawNode<'_>]) -> Result<Debugvars, crate::Error> {
let mut merged = Debugvars::default();
let mut contents: Vec<String> = Vec::new();
for node in raw_nodes {
let parsed: Debugvars = serde_roxmltree::from_node(node.0)?;
if merged.configfile.is_none() {
merged.configfile = parsed.configfile;
}
if merged.version.is_none() {
merged.version = parsed.version;
}
if !parsed.content.is_empty() {
contents.push(parsed.content);
}
}
merged.content = contents.join("\n");
Ok(merged)
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct Processor {
#[serde(rename = "Pname")]
pub pname: Option<String>,
#[serde(rename = "Punits")]
pub punits: Option<u32>,
#[serde(rename = "Dcore")]
pub dcore: Option<String>,
#[serde(rename = "DcoreVersion")]
pub dcore_version: Option<String>,
#[serde(rename = "Dfpu")]
pub dfpu: Option<String>,
#[serde(rename = "Dmpu")]
pub dmpu: Option<String>,
#[serde(rename = "Ddsp")]
pub ddsp: Option<String>,
#[serde(rename = "Dmve")]
pub dmve: Option<String>,
#[serde(rename = "Dpacbti")]
pub dpacbti: Option<String>,
#[serde(rename = "Dtz")]
pub dtz: Option<String>,
#[serde(rename = "Dendian")]
pub dendian: Option<String>,
#[serde(rename = "Dclock")]
pub dclock: Option<u64>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct Compile {
#[serde(rename = "Pname")]
pub pname: Option<String>,
pub header: Option<String>,
pub define: Option<String>,
#[serde(rename = "Pdefine")]
pub pdefine: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct FamilyDebug {
#[serde(rename = "Pname")]
pub pname: Option<String>,
#[serde(rename = "Punit")]
pub punit: Option<u32>,
pub svd: Option<String>,
#[serde(rename = "__dp")]
pub dp: Option<u32>,
#[serde(rename = "__ap")]
pub ap: Option<u32>,
#[serde(rename = "__apid")]
pub apid: Option<u32>,
pub address: Option<String>,
#[serde(rename = "defaultResetSequence")]
pub default_reset_sequence: Option<String>,
#[serde(rename = "datapatch", default)]
pub datapatch: Vec<DataPatch>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct DataPatch {
#[serde(rename = "type")]
pub patch_type: Option<String>,
#[serde(deserialize_with = "de_uint")]
pub address: u32,
#[serde(rename = "__dp")]
pub dp: Option<u32>,
#[serde(rename = "__ap")]
pub ap: Option<u32>,
#[serde(deserialize_with = "de_uint")]
pub value: u32,
pub mask: Option<String>,
pub info: Option<String>,
#[serde(rename = "__apid")]
pub apid: Option<u32>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct DebugConfig {
pub default: Option<String>,
pub clock: Option<u64>,
#[serde(default, deserialize_with = "de_opt_bool")]
pub swj: Option<bool>,
#[serde(default, deserialize_with = "de_opt_bool")]
pub dormant: Option<bool>,
pub sdf: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct DebugPort {
#[serde(rename = "__dp")]
pub dp: Option<u32>,
pub jtag: Option<DebugPortJtag>,
pub swd: Option<DebugPortSwd>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct DebugPortJtag {
#[serde(rename = "tapindex", default, deserialize_with = "de_opt_uint")]
pub tapindex: Option<u32>,
#[serde(default, deserialize_with = "de_opt_uint")]
pub idcode: Option<u32>,
#[serde(default, deserialize_with = "de_opt_uint")]
pub targetsel: Option<u32>,
pub irlen: Option<u32>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct DebugPortSwd {
#[serde(default, deserialize_with = "de_opt_uint")]
pub idcode: Option<u32>,
#[serde(default, deserialize_with = "de_opt_uint")]
pub targetsel: Option<u32>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct AccessPortV1 {
#[serde(rename = "__apid")]
pub apid: u32,
#[serde(rename = "__dp")]
pub dp: Option<u32>,
pub index: u32,
#[serde(rename = "HPROT")]
pub hprot: Option<u32>,
#[serde(rename = "SPROT")]
pub sprot: Option<u32>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct AccessPortV2 {
#[serde(rename = "__apid")]
pub apid: u32,
#[serde(rename = "__dp")]
pub dp: Option<u32>,
pub address: String,
#[serde(rename = "HPROT")]
pub hprot: Option<u32>,
#[serde(rename = "SPROT")]
pub sprot: Option<u32>,
pub parent: Option<u32>,
}
fn default_algorithm_endian() -> String {
"Little-endian".to_string()
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct Algorithm {
pub name: String,
pub start: Option<String>,
pub size: Option<String>,
#[serde(rename = "RAMstart")]
pub ram_start: Option<String>,
#[serde(rename = "RAMsize")]
pub ram_size: Option<String>,
#[serde(default, deserialize_with = "de_opt_bool")]
pub default: Option<bool>,
pub style: Option<String>,
#[serde(rename = "Pname")]
pub pname: Option<String>,
#[serde(rename = "deviceIndex")]
pub device_index: Option<String>,
pub parameter: Option<String>,
#[serde(default = "default_algorithm_endian")]
pub endian: String,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct FlashInfo {
pub name: String,
pub start: String,
pub pagesize: String,
pub blankval: Option<String>,
pub filler: Option<String>,
pub ptime: Option<u32>,
pub etime: Option<u32>,
#[serde(rename = "Pname")]
pub pname: Option<String>,
#[serde(skip_deserializing)]
pub elements: Vec<FlashInfoElement>,
}
impl<'a, 'input: 'a> TryFrom<Node<'a, 'input>> for FlashInfo {
type Error = crate::Error;
fn try_from(value: Node<'a, 'input>) -> Result<Self, Self::Error> {
let mut info: Self = serde_roxmltree::from_node(value)?;
for child in value.children().filter(roxmltree::Node::is_element) {
info.elements.push(child.try_into()?);
}
Ok(info)
}
}
pub fn parse_flashinfo(raw_nodes: &[RawNode<'_>]) -> Result<Vec<FlashInfo>, crate::Error> {
raw_nodes
.iter()
.map(|node| {
node.0.try_into().inspect_err(|e| {
error!("Failed to parse flashinfo node `{node:#?}` with err `{e:#?}`");
})
})
.collect()
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct FlashBlock {
#[serde(deserialize_with = "de_uint")]
pub count: u32,
pub size: String,
pub arg: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct FlashGap {
pub size: String,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
pub enum FlashInfoElement {
Block(FlashBlock),
Gap(FlashGap),
}
impl Default for FlashInfoElement {
fn default() -> Self {
Self::Block(FlashBlock::default())
}
}
impl<'a, 'input: 'a> TryFrom<Node<'a, 'input>> for FlashInfoElement {
type Error = crate::Error;
fn try_from(value: Node<'a, 'input>) -> Result<Self, Self::Error> {
if !value.is_element() {
return Err(FamilyParseError::UnimplementedContent.into());
}
match value.tag_name().name().to_lowercase().as_str() {
"block" => Ok(Self::Block(serde_roxmltree::from_node(value)?)),
"gap" => Ok(Self::Gap(serde_roxmltree::from_node(value)?)),
other => Err(FamilyParseError::UnknownElementType(other.to_string()).into()),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct Memory {
pub name: Option<String>,
pub access: Option<String>,
pub start: String,
pub size: String,
#[serde(default, deserialize_with = "de_opt_bool")]
pub default: Option<bool>,
#[serde(default, deserialize_with = "de_opt_bool")]
pub startup: Option<bool>,
#[serde(default, deserialize_with = "de_opt_bool")]
pub uninit: Option<bool>,
pub alias: Option<String>,
#[serde(rename = "Pname")]
pub pname: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct Trace {
#[serde(rename = "Pname")]
pub pname: Option<String>,
#[serde(rename = "serialwire", default)]
pub serialwire: Vec<SerialWire>,
#[serde(rename = "traceport", default)]
pub traceport: Vec<TracePort>,
#[serde(rename = "tracebuffer", default)]
pub tracebuffer: Vec<TraceBuffer>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct SerialWire {
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct TracePort {
#[serde(default, deserialize_with = "de_opt_uint")]
pub width: Option<u32>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct TraceBuffer {
pub start: Option<String>,
pub size: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct Book {
pub name: String,
pub title: String,
#[serde(rename = "Pname")]
pub pname: Option<String>,
#[serde(default, deserialize_with = "de_opt_bool")]
pub public: Option<bool>,
}
#[allow(clippy::too_long_first_doc_paragraph)]
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct Feature {
#[serde(rename = "type")]
pub feature_type: String,
pub n: Option<String>,
pub m: Option<String>,
pub name: Option<String>,
#[serde(rename = "Pname")]
pub pname: Option<String>,
pub count: Option<i32>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct FamilyEnvironment {
pub name: String,
#[serde(rename = "Pname")]
pub pname: Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
pub struct Variant<'a> {
#[serde(rename = "Dvariant")]
pub variant_name: String,
pub deprecated: Option<String>,
#[serde(rename = "description", default)]
pub description: Vec<String>,
#[serde(rename = "processor", default)]
pub processor: Vec<Processor>,
#[serde(rename = "compile", default)]
pub compile: Vec<Compile>,
#[serde(rename = "debug", default)]
pub debug: Vec<FamilyDebug>,
pub debugconfig: Option<DebugConfig>,
#[serde(rename = "debugport", default)]
pub debugport: Vec<DebugPort>,
#[serde(rename = "accessportV1", default)]
pub access_port_v1: Vec<AccessPortV1>,
#[serde(rename = "accessportV2", default)]
pub access_port_v2: Vec<AccessPortV2>,
#[serde(rename = "algorithm", default)]
pub algorithm: Vec<Algorithm>,
#[serde(rename = "flashinfo", default, borrow, skip_serializing)]
pub flashinfo_raw: Vec<RawNode<'a>>,
#[serde(skip_deserializing)]
pub flashinfo: Vec<FlashInfo>,
#[serde(rename = "memory", default)]
pub memory: Vec<Memory>,
#[serde(rename = "trace", default)]
pub trace: Vec<Trace>,
#[serde(rename = "book", default)]
pub book: Vec<Book>,
#[serde(rename = "feature", default)]
pub feature: Vec<Feature>,
#[serde(rename = "environment", default)]
pub environment: Vec<FamilyEnvironment>,
#[serde(rename = "debugvars", default, borrow, skip_serializing)]
pub debugvars_raw: Vec<RawNode<'a>>,
#[serde(skip_deserializing)]
pub debugvars: Debugvars,
#[serde(rename = "sequences", default, borrow, skip_serializing)]
pub sequences_raw: Vec<RawNode<'a>>,
#[serde(skip_deserializing)]
pub sequences: Sequences<'a>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
pub struct Device<'a> {
#[serde(rename = "Dname")]
pub device_name: String,
pub deprecated: Option<String>,
#[serde(rename = "description", default)]
pub description: Vec<String>,
#[serde(rename = "processor", default)]
pub processor: Vec<Processor>,
#[serde(rename = "compile", default)]
pub compile: Vec<Compile>,
#[serde(rename = "debug", default)]
pub debug: Vec<FamilyDebug>,
pub debugconfig: Option<DebugConfig>,
#[serde(rename = "debugport", default)]
pub debugport: Vec<DebugPort>,
#[serde(rename = "accessportV1", default)]
pub access_port_v1: Vec<AccessPortV1>,
#[serde(rename = "accessportV2", default)]
pub access_port_v2: Vec<AccessPortV2>,
#[serde(rename = "algorithm", default)]
pub algorithm: Vec<Algorithm>,
#[serde(rename = "flashinfo", default, borrow, skip_serializing)]
pub flashinfo_raw: Vec<RawNode<'a>>,
#[serde(skip_deserializing)]
pub flashinfo: Vec<FlashInfo>,
#[serde(rename = "memory", default)]
pub memory: Vec<Memory>,
#[serde(rename = "trace", default)]
pub trace: Vec<Trace>,
#[serde(rename = "book", default)]
pub book: Vec<Book>,
#[serde(rename = "feature", default)]
pub feature: Vec<Feature>,
#[serde(rename = "environment", default)]
pub environment: Vec<FamilyEnvironment>,
#[serde(rename = "debugvars", default, borrow, skip_serializing)]
pub debugvars_raw: Vec<RawNode<'a>>,
#[serde(skip_deserializing)]
pub debugvars: Debugvars,
#[serde(rename = "sequences", default, borrow, skip_serializing)]
pub sequences_raw: Vec<RawNode<'a>>,
#[serde(skip_deserializing)]
pub sequences: Sequences<'a>,
#[serde(rename = "variant", default)]
pub variants: Vec<Variant<'a>>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
pub struct SubFamily<'a> {
#[serde(rename = "DsubFamily")]
pub sub_family_name: String,
pub deprecated: Option<String>,
#[serde(rename = "description", default)]
pub description: Vec<String>,
#[serde(rename = "processor", default)]
pub processor: Vec<Processor>,
#[serde(rename = "compile", default)]
pub compile: Vec<Compile>,
#[serde(rename = "debug", default)]
pub debug: Vec<FamilyDebug>,
pub debugconfig: Option<DebugConfig>,
#[serde(rename = "debugport", default)]
pub debugport: Vec<DebugPort>,
#[serde(rename = "accessportV1", default)]
pub access_port_v1: Vec<AccessPortV1>,
#[serde(rename = "accessportV2", default)]
pub access_port_v2: Vec<AccessPortV2>,
#[serde(rename = "algorithm", default)]
pub algorithm: Vec<Algorithm>,
#[serde(rename = "flashinfo", default, borrow, skip_serializing)]
pub flashinfo_raw: Vec<RawNode<'a>>,
#[serde(skip_deserializing)]
pub flashinfo: Vec<FlashInfo>,
#[serde(rename = "memory", default)]
pub memory: Vec<Memory>,
#[serde(rename = "trace", default)]
pub trace: Vec<Trace>,
#[serde(rename = "book", default)]
pub book: Vec<Book>,
#[serde(rename = "feature", default)]
pub feature: Vec<Feature>,
#[serde(rename = "environment", default)]
pub environment: Vec<FamilyEnvironment>,
#[serde(rename = "debugvars", default, borrow, skip_serializing)]
pub debugvars_raw: Vec<RawNode<'a>>,
#[serde(skip_deserializing)]
pub debugvars: Debugvars,
#[serde(rename = "sequences", default, borrow, skip_serializing)]
pub sequences_raw: Vec<RawNode<'a>>,
#[serde(skip_deserializing)]
pub sequences: Sequences<'a>,
#[serde(rename = "device", default)]
pub devices: Vec<Device<'a>>,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
pub struct Sequences<'a> {
#[serde(
rename = "traceSetup",
default,
deserialize_with = "de_opt_trace_setup"
)]
pub trace_setup: Option<TraceSetup>,
#[serde(rename = "sequence", default)]
#[serde(borrow)]
#[serde(skip_serializing)]
pub raw_nodes: Vec<RawNode<'a>>,
#[serde(skip_deserializing)]
pub sequences: Vec<Sequence>,
}
impl Sequences<'_> {
pub fn parse_raw_nodes_content(&self) -> Result<Vec<Sequence>, crate::Error> {
self.raw_nodes
.iter()
.map(|node| {
node.0.try_into().inspect_err(|e| {
error!("Failed to parse sequence node `{node:#?}` with err `{e:#?}`");
})
})
.collect()
}
pub fn parse_sequences(&mut self) -> Result<(), crate::Error> {
let sequences = Self::parse_raw_nodes_content(self)?;
self.sequences = sequences;
Ok(())
}
}
pub fn merge_sequences<'a>(raw_nodes: &[RawNode<'a>]) -> Result<Sequences<'a>, crate::Error> {
let mut merged = Sequences::default();
for node in raw_nodes {
let parsed: Sequences<'a> = serde_roxmltree::from_node(node.0)?;
if merged.trace_setup.is_none() {
merged.trace_setup = parsed.trace_setup;
}
merged.raw_nodes.extend(parsed.raw_nodes);
}
Ok(merged)
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
pub struct Sequence {
pub name: String,
pub processor_name: Option<String>,
pub disable: Option<bool>,
pub info: Option<String>,
#[serde(skip_serializing)]
pub elements: Vec<SequenceElement>,
}
impl<'a, 'input: 'a> TryFrom<Node<'a, 'input>> for Sequence {
type Error = crate::Error;
fn try_from(value: Node<'a, 'input>) -> Result<Self, Self::Error> {
let node_name = value.tag_name().name();
if node_name != "sequence" {
return Err(FamilyParseError::UnknownElementType(node_name.to_string()).into());
}
let sequence_name = value
.attribute("name")
.ok_or_else(|| FamilyParseError::MissingAttribute("name".to_string()))?;
let sequence_processor_name = value
.attribute("Pname")
.map_or_else(|| None, |v| Some(v.to_string()));
let sequence_disable = {
value.attribute("disable").and_then(|v| {
let disable_value: bool = match v.parse() {
Ok(k) => k,
Err(e) => {
error!("Failed to parse non-boolean value `{v}` with error `{e:#?}`");
return None;
}
};
Some(disable_value)
})
};
let sequence_info = value
.attribute("info")
.map_or_else(|| None, |v| Some(v.to_string()));
let mut elements: Vec<SequenceElement> = Vec::new();
for child in value.children().filter(roxmltree::Node::is_element) {
let element: SequenceElement = child.try_into()?;
elements.push(element);
}
Ok(Self {
name: sequence_name.to_string(),
processor_name: sequence_processor_name,
disable: sequence_disable,
info: sequence_info,
elements,
})
}
}
impl<'a, 'input: 'a> TryFrom<Node<'a, 'input>> for SequenceElement {
type Error = crate::Error;
fn try_from(value: Node<'a, 'input>) -> Result<Self, Self::Error> {
if !value.is_element() {
return Err(FamilyParseError::UnimplementedContent.into());
}
match value.tag_name().name().to_lowercase().as_str() {
"block" => Ok(<Node<'_, '_> as TryInto<SequenceBlock>>::try_into(value)?.into()),
"control" => Ok(<Node<'_, '_> as TryInto<SequenceControl>>::try_into(value)?.into()),
other => Err(FamilyParseError::UnknownElementType(other.to_string()).into()),
}
}
}
impl<'a, 'input: 'a> TryFrom<Node<'a, 'input>> for SequenceBlock {
type Error = crate::Error;
fn try_from(value: Node<'a, 'input>) -> Result<Self, Self::Error> {
let mut block: Self = serde_roxmltree::from_node(value)?;
block.parse_statements()?;
Ok(block)
}
}
impl<'a, 'input: 'a> TryFrom<Node<'a, 'input>> for SequenceControl {
type Error = crate::Error;
fn try_from(value: Node<'a, 'input>) -> Result<Self, Self::Error> {
let mut block: Self = serde_roxmltree::from_node(value)?;
for child in value.children().filter(roxmltree::Node::is_element) {
let element: SequenceElement = child.try_into()?;
block.elements.push(element);
}
let conditional_string: &str;
if let Some(ref val) = block.conditional_if {
conditional_string = val.as_str();
} else if let Some(ref val) = block.conditional_while {
conditional_string = val.as_str();
} else {
return Err(FamilyParseError::MissingAttribute(
"conditional 'if' or 'while' attribute".to_string(),
)
.into());
}
let conditional: debug_access::Expression = conditional_string.try_into()?;
block.conditional = Some(conditional);
Ok(block)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
pub enum SequenceElement {
Control(SequenceControl),
Block(SequenceBlock),
}
impl Default for SequenceElement {
fn default() -> Self {
Self::Block(SequenceBlock::default())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
pub struct SequenceControl {
#[serde(rename = "if")]
pub conditional_if: Option<String>,
#[serde(rename = "while")]
pub conditional_while: Option<String>,
pub timeout: Option<u64>,
pub info: Option<String>,
#[serde(skip_deserializing)]
pub elements: Vec<SequenceElement>,
#[serde(skip_deserializing)]
pub conditional: Option<debug_access::Expression>,
}
impl From<SequenceControl> for SequenceElement {
fn from(value: SequenceControl) -> Self {
Self::Control(value)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
pub struct SequenceBlock {
pub atomic: Option<bool>,
pub info: Option<String>,
#[serde(rename = "#content")]
pub content: String,
#[serde(skip_deserializing)]
pub statements: Vec<Statement>,
}
impl SequenceBlock {
pub fn parse_statements_content(&self) -> Result<Vec<Statement>, crate::Error> {
self.content
.lines()
.flat_map(|line| line.split(';'))
.map(str::trim)
.filter(|s| !s.is_empty())
.map(|s| Statement::try_from(s.to_string()))
.collect()
}
pub fn parse_statements(&mut self) -> Result<(), crate::Error> {
self.statements = self.parse_statements_content()?;
Ok(())
}
}
impl From<SequenceBlock> for SequenceElement {
fn from(value: SequenceBlock) -> Self {
Self::Block(value)
}
}
#[cfg(test)]
mod tests {
use roxmltree::Document;
use serde_roxmltree::RawNode;
use crate::{
debug_access::{
Assignment, DebugFunction, Expression,
Statement::{self},
},
family::{
FamilyParseError, FlashBlock, FlashGap, FlashInfoElement, Sequence, SequenceBlock,
SequenceControl, SequenceElement, TraceSetup, parse_flashinfo,
},
};
#[test]
fn basic_sequence() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<sequence name="ResetSystem">
<block>
Sequence("ResetAndHalt");
</block>
</sequence>"#;
let document = Document::parse(xml_str).unwrap();
let sequence_node = document.root_element();
let raw_node: RawNode = RawNode(sequence_node);
let sequence: Sequence = raw_node.0.try_into().unwrap();
assert_eq!(sequence.name, "ResetSystem".to_string());
assert_eq!(
sequence.elements,
vec![
SequenceBlock {
atomic: None,
info: None,
content: "\n Sequence(\"ResetAndHalt\");\n ".to_string(),
statements: vec![Statement::Expression(Expression::FunctionCall(Box::new(
DebugFunction::Sequence {
name: Expression::Normal("\"ResetAndHalt\"".to_string())
}
)))]
}
.into()
]
);
}
#[test]
fn full_sequence() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<sequence name="UserSequence">
<block info="Define variables and do debug accesses">
__var tpWidth = (__traceout & 0x003F0000) >> 16;
</block>
<control if="__traceout & 0x2" info="Parallel Trace Port enabled">
<block>
// Do something generic for parallel trace port trace
</block>
<control if="tpWidth == 1" info="Configure device for 1-bit TPIU trace.">
<block>
// Do debug accesses
</block>
</control>
<control if="tpWidth == 2" info="Configure device for 2-bit TPIU trace.">
<block>
// Do debug accesses
</block>
</control>
<control if="tpWidth == 4" info="Configure device for 4-bit TPIU trace.">
<block>
// Do debug accesses
</block>
</control>
</control>
</sequence>"#;
let document = Document::parse(xml_str).unwrap();
let sequence_node = document.root_element();
let raw_node: RawNode = RawNode(sequence_node);
let sequence: Sequence = raw_node.0.try_into().unwrap();
assert_eq!(sequence.name, "UserSequence".to_string());
assert_eq!(sequence.disable, None);
assert_eq!(sequence.info, None);
assert_eq!(sequence.processor_name, None);
let debug_access_block: SequenceElement = SequenceBlock {
atomic: None,
info: None,
content: r#"
// Do debug accesses
"#
.to_string(),
statements: vec![Statement::Comment("Do debug accesses".to_string())],
}
.into();
let expected_elements: Vec<SequenceElement> = vec![
SequenceBlock {
atomic: None,
info: Some("Define variables and do debug accesses".to_string()),
content: r#"
__var tpWidth = (__traceout & 0x003F0000) >> 16;
"#
.to_string(),
statements: vec![Statement::Definition(Assignment {
variable: "tpWidth".to_string(),
expression: Expression::Normal("(__traceout & 0x003F0000) >> 16".to_string()),
})],
}
.into(),
SequenceControl {
conditional_if: Some("__traceout & 0x2".to_string()),
conditional_while: None,
timeout: None,
info: Some("Parallel Trace Port enabled".to_string()),
elements: vec![
SequenceBlock {
atomic: None,
info: None,
content: r#"
// Do something generic for parallel trace port trace
"#
.to_string(),
statements: vec![Statement::Comment(
"Do something generic for parallel trace port trace".to_string(),
)],
}
.into(),
SequenceControl {
conditional_if: Some("tpWidth == 1".to_string()),
conditional_while: None,
timeout: None,
info: Some("Configure device for 1-bit TPIU trace.".to_string()),
elements: vec![debug_access_block.clone().into()],
conditional: Some(Expression::Normal("tpWidth == 1".to_string())),
}
.into(),
SequenceControl {
conditional_if: Some("tpWidth == 2".to_string()),
conditional_while: None,
timeout: None,
info: Some("Configure device for 2-bit TPIU trace.".to_string()),
elements: vec![debug_access_block.clone().into()],
conditional: Some(Expression::Normal("tpWidth == 2".to_string())),
}
.into(),
SequenceControl {
conditional_if: Some("tpWidth == 4".to_string()),
conditional_while: None,
timeout: None,
info: Some("Configure device for 4-bit TPIU trace.".to_string()),
elements: vec![debug_access_block.clone().into()],
conditional: Some(Expression::Normal("tpWidth == 4".to_string())),
}
.into(),
],
conditional: Some(Expression::Normal("__traceout & 0x2".to_string())),
}
.into(),
];
println!("Expected: {:#?}", expected_elements);
println!("Actual: {:#?}", sequence.elements);
assert_eq!(sequence.elements, expected_elements);
}
#[test]
fn basic_sequence_block() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<block info="Define condition variales for later use in block elements.">
// Variable definition by __var keyword
__var doIfBlock = 1;
__var whileCondition = 1;
</block>"#;
let document = Document::parse(xml_str).unwrap();
let sequence_node = document.root_element();
let raw_node: RawNode = RawNode(sequence_node);
let block: SequenceBlock = raw_node.0.try_into().unwrap();
assert_eq!(
block.info,
Some("Define condition variales for later use in block elements.".to_string())
);
assert_eq!(block.atomic, None);
assert_eq!(
block.content,
r#"
// Variable definition by __var keyword
__var doIfBlock = 1;
__var whileCondition = 1;
"#
);
assert_eq!(
block.statements,
vec![
Statement::Comment("Variable definition by __var keyword".to_string()),
Statement::Definition(Assignment {
variable: "doIfBlock".to_string(),
expression: Expression::Normal("1".to_string())
}),
Statement::Definition(Assignment {
variable: "whileCondition".to_string(),
expression: Expression::Normal("1".to_string())
})
]
);
}
#[test]
fn parse_control_element_if() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<control if="doIfBlock">
<block>
// Do debug accesses
</block>
</control>"#;
let document = Document::parse(xml_str).unwrap();
let sequence_node = document.root_element();
let raw_node: RawNode = RawNode(sequence_node);
let block: SequenceControl = raw_node.0.try_into().unwrap();
println!("{:#?}", block);
assert_eq!(block.info, None);
assert_eq!(block.conditional_if, Some("doIfBlock".to_string()));
assert_eq!(block.conditional_while, None);
assert_eq!(block.timeout, None);
assert_eq!(
block.elements,
vec![
SequenceBlock {
atomic: None,
info: None,
content: "\n // Do debug accesses\n ".to_string(),
statements: vec![Statement::Comment("Do debug accesses".to_string())]
}
.into()
]
);
}
#[test]
fn sequence_missing_name_attribute() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<sequence>
<block>Write32(0x40000000, 0x1);</block>
</sequence>"#;
let document = Document::parse(xml_str).unwrap();
let node = document.root_element();
let result: Result<Sequence, _> = node.try_into();
let err = result.unwrap_err();
let crate::Error::Family(inner) = err else {
panic!("wrong error type")
};
assert_eq!(
inner,
FamilyParseError::MissingAttribute("name".to_string())
);
}
#[test]
fn sequence_element_unknown_tag() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<unknown/>"#;
let document = Document::parse(xml_str).unwrap();
let node = document.root_element();
let result: Result<SequenceElement, _> = node.try_into();
let err = result.unwrap_err();
let crate::Error::Family(inner) = err else {
panic!("wrong error type")
};
assert_eq!(
inner,
FamilyParseError::UnknownElementType("unknown".to_string())
);
}
#[test]
fn debugvar_missing_var_prefix() {
use crate::family::Debugvars;
let result = Debugvars::parse_single_debugvar(" myVar = 0x1;");
assert!(matches!(
result,
Err(FamilyParseError::MalformedDebugvar(_))
));
}
#[test]
fn debugvar_empty_segment() {
use crate::family::Debugvars;
let result = Debugvars::parse_single_debugvar(" ");
assert!(matches!(
result,
Err(FamilyParseError::MalformedDebugvar(_))
));
}
#[test]
fn parse_control_element_while() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<control while="whileCondition" timeout="5000">
<block>
// Execute while "whileCondition" different from '0' with a timeout of 5ms
whileCondition = 0;
</block>
</control>"#;
let document = Document::parse(xml_str).unwrap();
let sequence_node = document.root_element();
let raw_node: RawNode = RawNode(sequence_node);
let block: SequenceControl = raw_node.0.try_into().unwrap();
println!("{:#?}", block);
assert_eq!(block.info, None);
assert_eq!(block.conditional_if, None);
assert_eq!(block.conditional_while, Some("whileCondition".to_string()));
assert_eq!(block.timeout, Some(5000));
assert_eq!(block.elements, vec![
SequenceBlock{
atomic: None,
info: None,
content: r#"
// Execute while "whileCondition" different from '0' with a timeout of 5ms
whileCondition = 0;
"#.to_string(),
statements: vec![
Statement::Comment("Execute while \"whileCondition\" different from '0' with a timeout of 5ms".to_string()),
Statement::Assignment(Assignment {
variable: "whileCondition".to_string(),
expression: Expression::Normal("0".to_string())
})
]
}.into()
]);
}
#[test]
fn parse_family_with_description() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<description>STM32F1 Arm Cortex-M3 microcontroller series</description>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(
family.description,
vec!["STM32F1 Arm Cortex-M3 microcontroller series".to_string()]
);
assert_eq!(family.processor.len(), 0);
}
#[test]
fn parse_family_processor() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="LPC" Dvendor="NXP:11">
<processor Pname="Cortex-M3" Dcore="Cortex-M3" Dfpu="NO_FPU" Dmpu="NO_MPU" Dclock="120000000"/>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.processor.len(), 1);
let p = &family.processor[0];
assert_eq!(p.pname, Some("Cortex-M3".to_string()));
assert_eq!(p.dcore, Some("Cortex-M3".to_string()));
assert_eq!(p.dfpu, Some("NO_FPU".to_string()));
assert_eq!(p.dclock, Some(120_000_000));
}
#[test]
fn parse_family_algorithm() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<algorithm name="Flash/STM32F1xx_128.FLM" start="0x08000000" size="0x00020000"
RAMstart="0x20000000" RAMsize="0x00001000" default="true"/>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.algorithm.len(), 1);
let a = &family.algorithm[0];
assert_eq!(a.name, "Flash/STM32F1xx_128.FLM");
assert_eq!(a.start, Some("0x08000000".to_string()));
assert_eq!(a.size, Some("0x00020000".to_string()));
assert_eq!(a.ram_start, Some("0x20000000".to_string()));
assert_eq!(a.ram_size, Some("0x00001000".to_string()));
assert_eq!(a.default, Some(true));
assert_eq!(a.device_index, None);
assert_eq!(a.parameter, None);
assert_eq!(a.endian, "Little-endian");
}
#[test]
fn parse_family_memory() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<memory name="IROM1" access="rx" start="0x08000000" size="0x00020000" startup="true" default="true"/>
<memory name="IRAM1" access="rw" start="0x20000000" size="0x00005000" uninit="true" default="true"/>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.memory.len(), 2);
let rom = &family.memory[0];
assert_eq!(rom.name, Some("IROM1".to_string()));
assert_eq!(rom.access, Some("rx".to_string()));
assert_eq!(rom.start, "0x08000000");
assert_eq!(rom.startup, Some(true));
assert_eq!(rom.default, Some(true));
let ram = &family.memory[1];
assert_eq!(ram.uninit, Some(true));
}
#[test]
fn parse_family_book() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<book name="Docs/DM00031936.pdf" title="STM32F1 Reference Manual"/>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.book.len(), 1);
assert_eq!(family.book[0].name, "Docs/DM00031936.pdf");
assert_eq!(family.book[0].title, "STM32F1 Reference Manual");
assert_eq!(family.book[0].public, None);
}
#[test]
fn parse_family_feature() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<feature type="UART" n="3" name="Universal Asynchronous Receiver/Transmitter"/>
<feature type="ADC" n="1" m="12"/>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.feature.len(), 2);
assert_eq!(family.feature[0].feature_type, "UART");
assert_eq!(family.feature[0].n, Some("3".to_string()));
assert_eq!(
family.feature[0].name,
Some("Universal Asynchronous Receiver/Transmitter".to_string())
);
assert_eq!(family.feature[1].m, Some("12".to_string()));
}
#[test]
fn parse_family_feature_count() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<feature type="UART" n="3" count="2"/>
<feature type="ADC" n="1" m="12"/>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.feature[0].count, Some(2));
assert_eq!(family.feature[1].count, None);
}
#[test]
fn parse_family_debug_and_debugconfig() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<debug svd="SVD/STM32F103xx.svd" __dp="0" __ap="0"/>
<debugconfig default="swd" clock="5000000" swj="true"/>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.debug.len(), 1);
assert_eq!(family.debug[0].svd, Some("SVD/STM32F103xx.svd".to_string()));
assert_eq!(family.debug[0].dp, Some(0));
assert_eq!(family.debug[0].ap, Some(0));
let dc = family.debugconfig.as_ref().unwrap();
assert_eq!(dc.default, Some("swd".to_string()));
assert_eq!(dc.clock, Some(5_000_000));
assert_eq!(dc.swj, Some(true));
}
#[test]
fn parse_family_debugport() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<debugport __dp="0">
<swd/>
<jtag tapindex="0"/>
</debugport>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.debugport.len(), 1);
let dp = &family.debugport[0];
assert_eq!(dp.dp, Some(0));
assert!(dp.swd.is_some());
assert!(dp.jtag.is_some());
assert_eq!(dp.jtag.as_ref().unwrap().tapindex, Some(0));
}
#[test]
fn parse_family_access_ports() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<accessportV1 __apid="0" __dp="0" index="0"/>
<accessportV2 __apid="1" __dp="0" address="0xE0041000"/>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.access_port_v1.len(), 1);
assert_eq!(family.access_port_v1[0].apid, 0);
assert_eq!(family.access_port_v1[0].dp, Some(0));
assert_eq!(family.access_port_v1[0].index, 0);
assert_eq!(family.access_port_v2.len(), 1);
assert_eq!(family.access_port_v2[0].apid, 1);
assert_eq!(family.access_port_v2[0].address, "0xE0041000");
}
#[test]
fn parse_family_compile() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<compile header="Include/stm32f1xx.h" define="STM32F1"/>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.compile.len(), 1);
assert_eq!(
family.compile[0].header,
Some("Include/stm32f1xx.h".to_string())
);
assert_eq!(family.compile[0].define, Some("STM32F1".to_string()));
}
#[test]
fn parse_family_flashinfo() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<flashinfo name="Flash" start="0x08000000" pagesize="0x400" blankval="0xFF">
<block count="128" size="0x400"/>
</flashinfo>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.flashinfo_raw.len(), 1);
let flashinfo = parse_flashinfo(&family.flashinfo_raw).unwrap();
assert_eq!(flashinfo.len(), 1);
let fi = &flashinfo[0];
assert_eq!(fi.name, "Flash");
assert_eq!(fi.start, "0x08000000");
assert_eq!(fi.pagesize, "0x400");
assert_eq!(fi.blankval, Some("0xFF".to_string()));
assert_eq!(
fi.elements,
vec![FlashInfoElement::Block(FlashBlock {
count: 128,
size: "0x400".to_string(),
arg: None,
})]
);
}
#[test]
fn parse_family_flashinfo_interleaved_order() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<flashinfo name="Flash" start="0x08000000" pagesize="0x400" blankval="0xFF">
<gap size="0x10"/>
<block count="128" size="0x400"/>
<gap size="0x20"/>
</flashinfo>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
let flashinfo = parse_flashinfo(&family.flashinfo_raw).unwrap();
assert_eq!(flashinfo.len(), 1);
assert_eq!(
flashinfo[0].elements,
vec![
FlashInfoElement::Gap(FlashGap {
size: "0x10".to_string(),
}),
FlashInfoElement::Block(FlashBlock {
count: 128,
size: "0x400".to_string(),
arg: None,
}),
FlashInfoElement::Gap(FlashGap {
size: "0x20".to_string(),
}),
]
);
}
#[test]
fn parse_family_trace() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<trace Pname="Core0">
<serialwire/>
<traceport width="4"/>
<tracebuffer start="0xE0041000" size="0x1000"/>
</trace>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.trace.len(), 1);
let trace = &family.trace[0];
assert_eq!(trace.pname, Some("Core0".to_string()));
assert_eq!(trace.serialwire.len(), 1);
assert_eq!(trace.traceport.len(), 1);
assert_eq!(trace.traceport[0].width, Some(4));
assert_eq!(trace.tracebuffer.len(), 1);
assert_eq!(trace.tracebuffer[0].start, Some("0xE0041000".to_string()));
assert_eq!(trace.tracebuffer[0].size, Some("0x1000".to_string()));
}
#[test]
fn parse_family_environment() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<environment name="uv" Pname="Core0"/>
<debugvars></debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.environment.len(), 1);
assert_eq!(family.environment[0].name, "uv");
assert_eq!(family.environment[0].pname, Some("Core0".to_string()));
}
#[test]
fn parse_device_basic() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<device Dname="PIC32CM1216PL10028">
<processor Dcore="Cortex-M0+" Dendian="Little-endian" Dmpu="NO_MPU" Dfpu="NO_FPU"
Ddsp="NO_DSP" Dtz="NO_TZ" Dmve="NO_MVE" Dclock="24000000" DcoreVersion="r0p0"/>
<memory name="FLASH" start="0x0C000000" size="0x20000" access="rx" default="1" startup="1"/>
<memory name="HSRAM" start="0x20000000" size="0x4000" default="1" access="rwx"/>
</device>"#;
let document = Document::parse(xml_str).unwrap();
let device: crate::family::Device = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(device.device_name, "PIC32CM1216PL10028");
assert_eq!(device.processor.len(), 1);
assert_eq!(device.processor[0].dcore, Some("Cortex-M0+".to_string()));
assert_eq!(device.processor[0].dclock, Some(24000000));
assert_eq!(device.memory.len(), 2);
assert_eq!(device.memory[0].name, Some("FLASH".to_string()));
assert_eq!(device.memory[0].start, "0x0C000000");
assert_eq!(device.memory[1].name, Some("HSRAM".to_string()));
assert_eq!(device.variants.len(), 0);
}
#[test]
fn parse_device_with_variant() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<device Dname="LPC1768">
<processor Dcore="Cortex-M3" Dclock="100000000"/>
<variant Dvariant="LPC1768FBD100">
<memory name="FLASH" start="0x00000000" size="0x80000" access="rx"/>
</variant>
<variant Dvariant="LPC1768FET100">
<processor Dcore="Cortex-M3" Dclock="120000000"/>
</variant>
</device>"#;
let document = Document::parse(xml_str).unwrap();
let device: crate::family::Device = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(device.device_name, "LPC1768");
assert_eq!(device.variants.len(), 2);
assert_eq!(device.variants[0].variant_name, "LPC1768FBD100");
assert_eq!(device.variants[0].memory.len(), 1);
assert_eq!(device.variants[1].variant_name, "LPC1768FET100");
assert_eq!(device.variants[1].processor[0].dclock, Some(120000000));
}
#[test]
fn parse_subfamily_with_devices() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<subFamily DsubFamily="LPC176x">
<processor Dcore="Cortex-M3"/>
<device Dname="LPC1768">
<processor Dclock="100000000"/>
<memory name="FLASH" start="0x00000000" size="0x80000" access="rx"/>
</device>
<device Dname="LPC1766">
<processor Dclock="100000000"/>
<memory name="FLASH" start="0x00000000" size="0x40000" access="rx"/>
</device>
</subFamily>"#;
let document = Document::parse(xml_str).unwrap();
let sf: crate::family::SubFamily = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(sf.sub_family_name, "LPC176x");
assert_eq!(sf.processor.len(), 1);
assert_eq!(sf.processor[0].dcore, Some("Cortex-M3".to_string()));
assert_eq!(sf.devices.len(), 2);
assert_eq!(sf.devices[0].device_name, "LPC1768");
assert_eq!(sf.devices[0].memory[0].size, "0x80000");
assert_eq!(sf.devices[1].device_name, "LPC1766");
}
#[test]
fn parse_family_with_direct_devices() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="PIC32CM-PL" Dvendor="Microchip:3">
<debugvars configfile="debug.dbgconf" version="1.0.0">__var myVar = 0x1;</debugvars>
<sequences/>
<device Dname="PIC32CM1216PL10028">
<processor Dcore="Cortex-M0+" Dclock="24000000"/>
<debugconfig default="swd" clock="2000000"/>
<compile header="pic32cm1216pl/include/pic32c.h" define="__PIC32CM1216PL10028__"/>
<memory name="FLASH" start="0x0C000000" size="0x20000" access="rx"/>
<algorithm name="keil/Flash/PIC32CM-PL_FLASH_128.FLM" start="0x0C000000" size="0x20000"
RAMstart="0x20000000" RAMsize="0x2000" default="1" style="Keil"/>
</device>
<device Dname="PIC32CM2532PL10028">
<processor Dcore="Cortex-M0+" Dclock="24000000"/>
<memory name="FLASH" start="0x0C000000" size="0x40000" access="rx"/>
</device>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.device_family, "PIC32CM-PL");
assert_eq!(family.devices.len(), 2);
assert_eq!(family.sub_families.len(), 0);
let d0 = &family.devices[0];
assert_eq!(d0.device_name, "PIC32CM1216PL10028");
assert_eq!(d0.processor[0].dcore, Some("Cortex-M0+".to_string()));
assert_eq!(
d0.debugconfig.as_ref().unwrap().default,
Some("swd".to_string())
);
assert_eq!(
d0.compile[0].header,
Some("pic32cm1216pl/include/pic32c.h".to_string())
);
assert_eq!(d0.memory[0].name, Some("FLASH".to_string()));
assert_eq!(d0.algorithm[0].name, "keil/Flash/PIC32CM-PL_FLASH_128.FLM");
assert_eq!(d0.algorithm[0].default, Some(true));
let d1 = &family.devices[1];
assert_eq!(d1.device_name, "PIC32CM2532PL10028");
assert_eq!(d1.memory[0].size, "0x40000");
}
#[test]
fn parse_deprecated_element() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<deprecated>2020-01-01</deprecated>
<debugvars></debugvars>
<sequences/>
<subFamily DsubFamily="STM32F103">
<deprecated>2020-02-02</deprecated>
</subFamily>
<device Dname="STM32F103C8">
<deprecated>2020-03-03</deprecated>
<variant Dvariant="STM32F103C8Tx">
<deprecated>2020-04-04</deprecated>
</variant>
</device>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.deprecated, Some("2020-01-01".to_string()));
assert_eq!(
family.sub_families[0].deprecated,
Some("2020-02-02".to_string())
);
assert_eq!(family.devices[0].deprecated, Some("2020-03-03".to_string()));
assert_eq!(
family.devices[0].variants[0].deprecated,
Some("2020-04-04".to_string())
);
}
#[test]
fn parse_variant_debugvars_and_sequences() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<device Dname="LPC1768">
<variant Dvariant="LPC1768FBD100">
<debugvars configfile="debug.dbgconf" version="1.0.0">__var myVar = 0x1;</debugvars>
<sequences/>
</variant>
</device>"#;
let document = Document::parse(xml_str).unwrap();
let mut device: crate::family::Device = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(device.variants.len(), 1);
device.variants[0].debugvars =
crate::family::merge_debugvars(&device.variants[0].debugvars_raw).unwrap();
assert_eq!(
device.variants[0].debugvars.configfile,
Some("debug.dbgconf".to_string())
);
assert_eq!(
device.variants[0].debugvars.version,
Some("1.0.0".to_string())
);
}
#[test]
fn parse_family_debugvars_multiple_siblings() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<debugvars configfile="debug.dbgconf" version="1.0.0">__var myVar = 0x1;</debugvars>
<debugvars>__var otherVar = 0x2;</debugvars>
<sequences/>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let mut family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.debugvars_raw.len(), 2);
family.debugvars = crate::family::merge_debugvars(&family.debugvars_raw).unwrap();
assert_eq!(
family.debugvars.configfile,
Some("debug.dbgconf".to_string())
);
assert_eq!(family.debugvars.version, Some("1.0.0".to_string()));
family.debugvars.parse_debugvars();
let parsed = family.debugvars.parsed_debugvars.unwrap();
assert_eq!(parsed.get("myVar"), Some(&1));
assert_eq!(parsed.get("otherVar"), Some(&2));
}
#[test]
fn parse_family_sequences_multiple_siblings() {
let xml_str = r#"<?xml version="1.0" encoding="UTF-8"?>
<family Dfamily="STM32F1" Dvendor="STMicroelectronics:13">
<debugvars></debugvars>
<sequences traceSetup="full">
<sequence name="SeqA">
<block>Sequence("A");</block>
</sequence>
</sequences>
<sequences>
<sequence name="SeqB">
<block>Sequence("B");</block>
</sequence>
</sequences>
</family>"#;
let document = Document::parse(xml_str).unwrap();
let mut family: crate::family::Family = serde_roxmltree::from_doc(&document).unwrap();
assert_eq!(family.sequences_raw.len(), 2);
family.sequences = crate::family::merge_sequences(&family.sequences_raw).unwrap();
assert_eq!(family.sequences.trace_setup, Some(TraceSetup::Full));
assert_eq!(family.sequences.raw_nodes.len(), 2);
family.sequences.parse_sequences().unwrap();
assert_eq!(family.sequences.sequences.len(), 2);
assert_eq!(family.sequences.sequences[0].name, "SeqA");
assert_eq!(family.sequences.sequences[1].name, "SeqB");
}
}