use super::{Chip, ChipFamily, ChipInfo, Core, Target, TargetDescriptionSource};
use crate::config::CoreType;
use once_cell::sync::Lazy;
use probe_rs_target::{CoreAccessOptions, RiscvCoreAccessOptions};
use std::io::Read;
use std::sync::{Arc, Mutex};
static REGISTRY: Lazy<Arc<Mutex<Registry>>> =
Lazy::new(|| Arc::new(Mutex::new(Registry::from_builtin_families())));
#[derive(Debug, thiserror::Error)]
pub enum RegistryError {
#[error("The requested chip '{0}' was not found in the list of known targets.")]
ChipNotFound(String),
#[error("Found multiple chips matching '{0}', unable to select a single chip.")]
ChipNotUnique(String),
#[error("The connected chip could not automatically be determined.")]
ChipAutodetectFailed,
#[error("The core type '{0}' is not supported in probe-rs.")]
UnknownCoreType(String),
#[error("An IO error was encountered")]
Io(#[from] std::io::Error),
#[error("Deserializing the yaml encountered an error")]
Yaml(#[from] serde_yaml::Error),
#[error("Invalid chip family definition ({})", .0.name)]
InvalidChipFamilyDefinition(Box<ChipFamily>, String),
}
fn add_generic_targets(vec: &mut Vec<ChipFamily>) {
vec.extend_from_slice(&[
ChipFamily {
name: "Generic ARMv6-M".to_owned(),
manufacturer: None,
generated_from_pack: false,
pack_file_release: None,
variants: vec![
Chip::generic_arm("Cortex-M0", CoreType::Armv6m),
Chip::generic_arm("Cortex-M0+", CoreType::Armv6m),
Chip::generic_arm("Cortex-M1", CoreType::Armv6m),
],
flash_algorithms: vec![],
source: TargetDescriptionSource::Generic,
},
ChipFamily {
name: "Generic ARMv7-M".to_owned(),
manufacturer: None,
generated_from_pack: false,
pack_file_release: None,
variants: vec![Chip::generic_arm("Cortex-M3", CoreType::Armv7m)],
flash_algorithms: vec![],
source: TargetDescriptionSource::Generic,
},
ChipFamily {
name: "Generic ARMv7E-M".to_owned(),
manufacturer: None,
generated_from_pack: false,
pack_file_release: None,
variants: vec![
Chip::generic_arm("Cortex-M4", CoreType::Armv7em),
Chip::generic_arm("Cortex-M7", CoreType::Armv7em),
],
flash_algorithms: vec![],
source: TargetDescriptionSource::Generic,
},
ChipFamily {
name: "Generic ARMv8-M".to_owned(),
manufacturer: None,
generated_from_pack: false,
pack_file_release: None,
variants: vec![
Chip::generic_arm("Cortex-M23", CoreType::Armv8m),
Chip::generic_arm("Cortex-M33", CoreType::Armv8m),
Chip::generic_arm("Cortex-M35P", CoreType::Armv8m),
Chip::generic_arm("Cortex-M55", CoreType::Armv8m),
],
flash_algorithms: vec![],
source: TargetDescriptionSource::Generic,
},
ChipFamily {
name: "Generic RISC-V".to_owned(),
manufacturer: None,
pack_file_release: None,
generated_from_pack: false,
variants: vec![Chip {
name: "riscv".to_owned(),
part: None,
cores: vec![Core {
name: "core".to_owned(),
core_type: CoreType::Riscv,
core_access_options: CoreAccessOptions::Riscv(RiscvCoreAccessOptions {}),
}],
memory_map: vec![],
flash_algorithms: vec![],
}],
flash_algorithms: vec![],
source: TargetDescriptionSource::Generic,
},
]);
}
struct Registry {
families: Vec<ChipFamily>,
}
impl Registry {
#[cfg(feature = "builtin-targets")]
fn from_builtin_families() -> Self {
const BUILTIN_TARGETS: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/targets.bincode"));
let mut families: Vec<ChipFamily> = match bincode::deserialize(BUILTIN_TARGETS) {
Ok(families) => families,
Err(err) => panic!("Failed to deserialize builtin targets. This is a bug : {err:?}"),
};
add_generic_targets(&mut families);
Self { families }
}
#[cfg(not(feature = "builtin-targets"))]
fn from_builtin_families() -> Self {
let mut families = vec![];
add_generic_targets(&mut families);
Self { families }
}
fn families(&self) -> &Vec<ChipFamily> {
&self.families
}
fn get_target_by_name(&self, name: impl AsRef<str>) -> Result<Target, RegistryError> {
let name = name.as_ref();
tracing::debug!("Searching registry for chip with name {}", name);
let (family, chip) = {
let mut selected_family_and_chip = None;
let mut exact_matches = 0;
let mut partial_matches = 0;
for family in &self.families {
for variant in family.variants.iter() {
if match_name_prefix(&variant.name, name) {
if variant.name.len() == name.len() {
tracing::debug!("Exact match for chip name: {}", variant.name);
exact_matches += 1;
} else {
tracing::debug!("Partial match for chip name: {}", variant.name);
partial_matches += 1;
if exact_matches > 0 {
continue;
}
}
selected_family_and_chip = Some((family, variant));
}
}
}
if exact_matches > 1 || (exact_matches == 0 && partial_matches > 1) {
tracing::warn!(
"Ignoring ambiguous matches for specified chip name {}",
name,
);
return Err(RegistryError::ChipNotUnique(name.to_owned()));
}
let (family, chip) = selected_family_and_chip
.ok_or_else(|| RegistryError::ChipNotFound(name.to_owned()))?;
if exact_matches == 0 && partial_matches == 1 {
tracing::warn!(
"Found chip {} which matches given partial name {}. Consider specifying its full name.",
chip.name,
name,
);
}
if chip.name.to_ascii_lowercase() != name.to_ascii_lowercase() {
tracing::warn!(
"Matching {} based on wildcard. Consider specifying the chip as {} instead.",
name,
chip.name,
);
}
(family, chip)
};
self.get_target(family, chip)
}
fn search_chips(&self, name: &str) -> Vec<String> {
tracing::debug!("Searching registry for chip with name {}", name);
let mut targets = Vec::new();
for family in &self.families {
for variant in family.variants.iter() {
if variant
.name
.to_ascii_lowercase()
.starts_with(&name.to_ascii_lowercase())
{
targets.push(variant.name.to_string())
}
}
}
targets
}
fn get_target_by_chip_info(&self, chip_info: ChipInfo) -> Result<Target, RegistryError> {
let (family, chip) = {
match chip_info {
ChipInfo::Arm(chip_info) => {
let families = self.families.iter().filter(|f| {
f.manufacturer
.map(|m| m == chip_info.manufacturer)
.unwrap_or(false)
});
let mut identified_chips = Vec::new();
for family in families {
tracing::debug!("Checking family {}", family.name);
let chips = family
.variants()
.iter()
.filter(|v| v.part.map(|p| p == chip_info.part).unwrap_or(false))
.map(|c| (family, c));
identified_chips.extend(chips)
}
if identified_chips.len() == 1 {
identified_chips.pop().unwrap()
} else {
tracing::debug!(
"Found {} matching chips for information {:?}, unable to determine chip",
identified_chips.len(),
chip_info
);
return Err(RegistryError::ChipAutodetectFailed);
}
}
}
};
self.get_target(family, chip)
}
fn get_target(&self, family: &ChipFamily, chip: &Chip) -> Result<Target, RegistryError> {
Target::new(family, &chip.name)
}
fn add_target_from_yaml<R>(&mut self, yaml_reader: R) -> Result<(), RegistryError>
where
R: Read,
{
let family: ChipFamily = serde_yaml::from_reader(yaml_reader)?;
family
.validate()
.map_err(|e| RegistryError::InvalidChipFamilyDefinition(Box::new(family.clone()), e))?;
let index = self
.families
.iter()
.position(|old_family| old_family.name == family.name);
if let Some(index) = index {
self.families.remove(index);
}
self.families.push(family);
Ok(())
}
}
pub fn get_target_by_name(name: impl AsRef<str>) -> Result<Target, RegistryError> {
REGISTRY.lock().unwrap().get_target_by_name(name)
}
pub fn search_chips(name: impl AsRef<str>) -> Result<Vec<String>, RegistryError> {
Ok(REGISTRY.lock().unwrap().search_chips(name.as_ref()))
}
pub(crate) fn get_target_by_chip_info(chip_info: ChipInfo) -> Result<Target, RegistryError> {
REGISTRY.lock().unwrap().get_target_by_chip_info(chip_info)
}
pub fn add_target_from_yaml<R>(yaml_reader: R) -> Result<(), RegistryError>
where
R: Read,
{
REGISTRY.lock().unwrap().add_target_from_yaml(yaml_reader)
}
pub fn families() -> Result<Vec<ChipFamily>, RegistryError> {
Ok(REGISTRY.lock().unwrap().families().clone())
}
fn match_name_prefix(pattern: &str, name: &str) -> bool {
for (n, p) in name.to_ascii_lowercase().chars().zip(pattern.chars()) {
if p.to_ascii_lowercase() != n && p != 'x' {
return false;
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn try_fetch_not_unique() {
let registry = Registry::from_builtin_families();
assert!(matches!(
registry.get_target_by_name("STM32G081KBU"),
Err(RegistryError::ChipNotUnique(_))
));
}
#[test]
fn try_fetch_not_found() {
let registry = Registry::from_builtin_families();
assert!(matches!(
registry.get_target_by_name("not_a_real_chip"),
Err(RegistryError::ChipNotFound(_))
));
}
#[test]
fn try_fetch2() {
let registry = Registry::from_builtin_families();
assert!(registry.get_target_by_name("stm32G081KBUx").is_ok());
}
#[test]
fn try_fetch3() {
let registry = Registry::from_builtin_families();
assert!(registry.get_target_by_name("STM32G081RBI").is_ok());
}
#[test]
fn try_fetch4() {
let registry = Registry::from_builtin_families();
assert!(registry.get_target_by_name("nrf51822_Xxaa").is_ok());
}
#[test]
fn validate_generic_targets() {
let mut families = vec![];
add_generic_targets(&mut families);
families
.iter()
.map(|family| family.validate())
.collect::<Result<Vec<_>, _>>()
.unwrap();
}
#[test]
fn validate_builtin() {
let registry = Registry::from_builtin_families();
registry
.families()
.iter()
.map(|family| family.validate())
.collect::<Result<Vec<_>, _>>()
.unwrap();
}
}