use crate::architecture::arm::{
ap::{AccessPortError, AccessPortType},
communication_interface::ArmProbeInterface,
memory::ArmMemoryInterface,
ArmError, FullyQualifiedApAddress,
};
#[derive(thiserror::Error, Debug, docsplay::Display)]
pub enum RomTableError {
NotARomtable,
AccessPort(#[from] AccessPortError),
CSComponentIdentification,
Memory(#[source] Box<ArmError>),
ComponentNotFound(PeripheralType),
NoComponents,
}
impl RomTableError {
fn memory(error: ArmError) -> Self {
RomTableError::Memory(Box::new(error))
}
}
struct RomTableReader<'probe: 'memory, 'memory> {
base_address: u64,
memory: &'memory mut (dyn ArmMemoryInterface + 'probe),
}
impl<'probe: 'memory, 'memory> RomTableReader<'probe, 'memory> {
fn new(memory: &'memory mut (dyn ArmMemoryInterface + 'probe), base_address: u64) -> Self {
RomTableReader {
base_address,
memory,
}
}
fn entries(&mut self) -> RomTableIterator<'probe, 'memory, '_> {
RomTableIterator::new(self)
}
}
struct RomTableIterator<'probe: 'memory, 'memory: 'reader, 'reader> {
rom_table_reader: &'reader mut RomTableReader<'probe, 'memory>,
offset: u64,
}
impl<'probe: 'memory, 'memory: 'reader, 'reader> RomTableIterator<'probe, 'memory, 'reader> {
fn new(reader: &'reader mut RomTableReader<'probe, 'memory>) -> Self {
RomTableIterator {
rom_table_reader: reader,
offset: 0,
}
}
}
impl Iterator for RomTableIterator<'_, '_, '_> {
type Item = Result<RomTableEntryRaw, RomTableError>;
fn next(&mut self) -> Option<Self::Item> {
let component_address = self.rom_table_reader.base_address + self.offset;
tracing::debug!("Reading rom table entry at {:#010x}", component_address);
self.offset += 4;
let mut entry_data = [0u32; 1];
if let Err(e) = self
.rom_table_reader
.memory
.read_32(component_address, &mut entry_data)
{
return Some(Err(RomTableError::memory(e)));
}
if entry_data[0] == 0 {
tracing::debug!("Entry consists of all zeroes, stopping.");
return None;
}
let entry_data =
RomTableEntryRaw::new(self.rom_table_reader.base_address as u32, entry_data[0]);
tracing::debug!("ROM Table Entry: {:#x?}", entry_data);
Some(Ok(entry_data))
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct RomTable {
entries: Vec<RomTableEntry>,
}
impl RomTable {
fn try_parse(
memory: &mut dyn ArmMemoryInterface,
base_address: u64,
) -> Result<RomTable, RomTableError> {
let mut entries = vec![];
tracing::debug!("Parsing romtable at base_address {:#010x}", base_address);
let reader = RomTableReader::new(memory, base_address)
.entries()
.filter_map(Result::ok)
.collect::<Vec<RomTableEntryRaw>>();
for raw_entry in reader.into_iter() {
let entry_base_addr = raw_entry.component_address();
tracing::debug!("Parsing entry at {:#010x}", entry_base_addr);
if raw_entry.entry_present {
let component = Component::try_parse(memory, u64::from(entry_base_addr))?;
entries.push(RomTableEntry {
format: raw_entry.format,
power_domain_id: raw_entry.power_domain_id,
power_domain_valid: raw_entry.power_domain_valid,
component: CoresightComponent::new(component, memory.ap().ap_address().clone()),
});
}
}
Ok(RomTable { entries })
}
pub fn entries(&self) -> impl Iterator<Item = &RomTableEntry> {
self.entries.iter()
}
}
#[derive(Debug, PartialEq)]
struct RomTableEntryRaw {
address_offset: i32,
power_domain_id: u8,
power_domain_valid: bool,
format: bool,
pub entry_present: bool,
base_address: u32,
}
impl RomTableEntryRaw {
fn new(base_address: u32, raw: u32) -> Self {
tracing::debug!("Parsing raw rom table entry: {:#07x}", raw);
let address_offset = ((raw >> 12) & 0xf_ff_ff) as i32;
let power_domain_id = ((raw >> 4) & 0xf) as u8;
let power_domain_valid = (raw & 4) == 4;
let format = (raw & 2) == 2;
let entry_present = (raw & 1) == 1;
RomTableEntryRaw {
address_offset,
power_domain_id,
power_domain_valid,
format,
entry_present,
base_address,
}
}
pub fn component_address(&self) -> u32 {
(i64::from(self.base_address) + (i64::from(self.address_offset << 12))) as u32
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct RomTableEntry {
power_domain_id: u8,
power_domain_valid: bool,
format: bool,
pub(crate) component: CoresightComponent,
}
impl RomTableEntry {
pub fn component(&self) -> &Component {
&self.component.component
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ComponentId {
component_address: u64,
class: RawComponent,
peripheral_id: PeripheralID,
}
impl ComponentId {
pub fn component_address(&self) -> u64 {
self.component_address
}
pub fn peripheral_id(&self) -> &PeripheralID {
&self.peripheral_id
}
}
pub struct ComponentInformationReader<'probe: 'memory, 'memory> {
base_address: u64,
memory: &'memory mut (dyn ArmMemoryInterface + 'probe),
}
impl<'probe: 'memory, 'memory> ComponentInformationReader<'probe, 'memory> {
pub fn new(base_address: u64, memory: &'memory mut (dyn ArmMemoryInterface + 'probe)) -> Self {
ComponentInformationReader {
base_address,
memory,
}
}
fn component_class(&mut self) -> Result<RawComponent, RomTableError> {
#![allow(clippy::verbose_bit_mask)]
let mut cidr = [0u32; 4];
self.memory
.read_32(self.base_address + 0xFF0, &mut cidr)
.map_err(RomTableError::memory)?;
tracing::debug!("CIDR: {:x?}", cidr);
let preambles = [
cidr[0] & 0xff,
cidr[1] & 0x0f,
cidr[2] & 0xff,
cidr[3] & 0xff,
];
let expected = [0x0D, 0x0, 0x05, 0xB1];
for i in 0..4 {
if preambles[i] != expected[i] {
tracing::warn!(
"Component at 0x{:x}: CIDR{} has invalid preamble (expected 0x{:x}, got 0x{:x})",
self.base_address, i, expected[i], preambles[i],
);
}
}
RawComponent::from_u8((cidr[1] >> 4) & 0x0F).ok_or(RomTableError::CSComponentIdentification)
}
fn peripheral_id(&mut self) -> Result<PeripheralID, RomTableError> {
let mut data = [0u32; 8];
let peripheral_id_address = self.base_address + 0xFD0;
tracing::debug!(
"Reading debug id from address: {:#010x}",
peripheral_id_address
);
self.memory
.read_32(self.base_address + 0xFD0, &mut data[4..])
.map_err(RomTableError::memory)?;
self.memory
.read_32(self.base_address + 0xFE0, &mut data[..4])
.map_err(RomTableError::memory)?;
tracing::debug!("Raw peripheral id: {:x?}", data);
const DEV_TYPE_OFFSET: u64 = 0xFCC;
const DEV_TYPE_MASK: u32 = 0xFF;
let dev_type = self
.memory
.read_word_32(self.base_address + DEV_TYPE_OFFSET)
.map_err(RomTableError::memory)
.map(|v| (v & DEV_TYPE_MASK) as u8)?;
const ARCH_ID_OFFSET: u64 = 0xFBC;
const ARCH_ID_MASK: u32 = 0xFFFF;
const ARCH_ID_PRESENT_BIT: u32 = 1 << 20;
let arch_id = self
.memory
.read_word_32(self.base_address + ARCH_ID_OFFSET)
.map_err(RomTableError::memory)
.map(|v| {
if v & ARCH_ID_PRESENT_BIT > 0 {
(v & ARCH_ID_MASK) as u16
} else {
0
}
})?;
tracing::debug!("Dev type: {:x}, arch id: {:x}", dev_type, arch_id);
Ok(PeripheralID::from_raw(&data, dev_type, arch_id))
}
fn read_all(&mut self) -> Result<ComponentId, RomTableError> {
Ok(ComponentId {
component_address: self.base_address,
class: self.component_class()?,
peripheral_id: self.peripheral_id()?,
})
}
}
#[derive(Clone, Debug, PartialEq)]
enum RawComponent {
GenericVerificationComponent = 0,
RomTable = 1,
CoreSightComponent = 9,
PeripheralTestBlock = 0xB,
GenericIPComponent = 0xE,
CoreLinkOrPrimeCellOrSystemComponent = 0xF,
}
impl RawComponent {
fn from_u8(value: u32) -> Option<Self> {
match value {
0 => Some(RawComponent::GenericVerificationComponent),
1 => Some(RawComponent::RomTable),
9 => Some(RawComponent::CoreSightComponent),
0xB => Some(RawComponent::PeripheralTestBlock),
0xE => Some(RawComponent::GenericIPComponent),
0xF => Some(RawComponent::CoreLinkOrPrimeCellOrSystemComponent),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Component {
GenericVerificationComponent(ComponentId),
Class1RomTable(ComponentId, RomTable),
CoresightComponent(ComponentId),
PeripheralTestBlock(ComponentId),
GenericIPComponent(ComponentId),
CoreLinkOrPrimeCellOrSystemComponent(ComponentId),
}
impl Component {
pub fn try_parse<'probe: 'memory, 'memory>(
memory: &'memory mut (dyn ArmMemoryInterface + 'probe),
baseaddr: u64,
) -> Result<Component, RomTableError> {
tracing::debug!("\tReading component data at: {:#010x}", baseaddr);
let component_id = ComponentInformationReader::new(baseaddr, memory).read_all()?;
tracing::debug!("\tComponent class: {:x?}", component_id.class);
tracing::debug!(
"\tComponent peripheral id: {:x?}",
component_id.peripheral_id
);
if let Some(info) = component_id.peripheral_id.determine_part() {
tracing::debug!("\tComponent is known: {}", info);
}
let class = match component_id.class {
RawComponent::GenericVerificationComponent => {
Component::GenericVerificationComponent(component_id)
}
RawComponent::RomTable => {
let rom_table = RomTable::try_parse(memory, component_id.component_address)?;
Component::Class1RomTable(component_id, rom_table)
}
RawComponent::CoreSightComponent => Component::CoresightComponent(component_id),
RawComponent::PeripheralTestBlock => Component::PeripheralTestBlock(component_id),
RawComponent::GenericIPComponent => Component::GenericIPComponent(component_id),
RawComponent::CoreLinkOrPrimeCellOrSystemComponent => {
Component::CoreLinkOrPrimeCellOrSystemComponent(component_id)
}
};
Ok(class)
}
pub fn id(&self) -> &ComponentId {
match self {
Component::GenericVerificationComponent(component_id) => component_id,
Component::Class1RomTable(component_id, ..) => component_id,
Component::CoresightComponent(component_id, ..) => component_id,
Component::PeripheralTestBlock(component_id) => component_id,
Component::GenericIPComponent(component_id) => component_id,
Component::CoreLinkOrPrimeCellOrSystemComponent(component_id) => component_id,
}
}
}
#[derive(Clone, Debug)]
pub struct CoresightComponent {
pub component: Component,
pub ap_address: FullyQualifiedApAddress,
}
impl CoresightComponent {
pub fn new(component: Component, ap: FullyQualifiedApAddress) -> Self {
Self {
component,
ap_address: ap,
}
}
pub fn read_reg(
&self,
interface: &mut dyn ArmProbeInterface,
offset: u32,
) -> Result<u32, ArmError> {
let mut memory = interface.memory_interface(&self.ap_address)?;
let value = memory.read_word_32(self.component.id().component_address + offset as u64)?;
Ok(value)
}
pub fn write_reg(
&self,
interface: &mut dyn ArmProbeInterface,
offset: u32,
value: u32,
) -> Result<(), ArmError> {
let mut memory = interface.memory_interface(&self.ap_address)?;
memory.write_word_32(self.component.id().component_address + offset as u64, value)?;
Ok(())
}
pub fn find_component(&self, peripheral_type: PeripheralType) -> Option<&CoresightComponent> {
self.iter().find(|&component| {
component
.component
.id()
.peripheral_id
.is_of_type(peripheral_type)
})
}
pub fn iter(&self) -> CoresightComponentIter {
CoresightComponentIter::new(vec![self])
}
}
impl PartialEq for CoresightComponent {
fn eq(&self, other: &Self) -> bool {
self.component.eq(&other.component)
}
}
pub struct CoresightComponentIter<'a> {
components: Vec<&'a CoresightComponent>,
current: usize,
children: Option<Box<CoresightComponentIter<'a>>>,
}
impl<'a> CoresightComponentIter<'a> {
pub(crate) fn new(components: Vec<&'a CoresightComponent>) -> Self {
Self {
components,
current: 0,
children: None,
}
}
}
impl<'a> Iterator for CoresightComponentIter<'a> {
type Item = &'a CoresightComponent;
fn next(&mut self) -> Option<Self::Item> {
if let Some(children) = &mut self.children {
if let Some(child) = children.next() {
return Some(child);
} else {
self.children = None;
}
}
if let Some(component) = self.components.get(self.current) {
self.children = match &component.component {
Component::Class1RomTable(_, v) => Some(Box::new(CoresightComponentIter::new(
v.entries.iter().map(|v| &v.component).collect(),
))),
_ => None,
};
self.current += 1;
return Some(component);
}
None
}
}
#[derive(Clone, Debug, PartialEq)]
enum ComponentModification {
No,
Yes(u8),
}
#[allow(non_snake_case)]
#[derive(Clone, Debug, PartialEq)]
pub struct PeripheralID {
REVAND: u8,
CMOD: ComponentModification,
REVISION: u8,
JEP106: Option<jep106::JEP106Code>,
PART: u16,
SIZE: u8,
dev_type: u8,
arch_id: u16,
}
impl PeripheralID {
fn from_raw(data: &[u32; 8], dev_type: u8, arch_id: u16) -> Self {
let jep106id = (((data[2] & 0x07) << 4) | ((data[1] >> 4) & 0x0F)) as u8;
let jep106 = jep106::JEP106Code::new((data[4] & 0x0F) as u8, jep106id);
let legacy = (data[2] & 0x8) > 1;
PeripheralID {
REVAND: ((data[3] >> 4) & 0x0F) as u8,
CMOD: match (data[3] & 0x0F) as u8 {
0x0 => ComponentModification::No,
v => ComponentModification::Yes(v),
},
REVISION: ((data[2] >> 4) & 0x0F) as u8,
JEP106: if legacy { Some(jep106) } else { None },
PART: (((data[1] & 0x0F) << 8) | (data[0] & 0xFF)) as u16,
SIZE: 2u32.pow((data[4] >> 4) & 0x0F) as u8,
dev_type,
arch_id,
}
}
pub fn is_of_type(&self, peripheral_type: PeripheralType) -> bool {
self.determine_part()
.map(|info| info.peripheral_type() == peripheral_type)
.unwrap_or(false)
}
pub fn jep106(&self) -> Option<jep106::JEP106Code> {
self.JEP106
}
pub fn designer(&self) -> Option<&'static str> {
self.JEP106.and_then(|jep106| jep106.get())
}
pub fn part(&self) -> u16 {
self.PART
}
pub fn arch_id(&self) -> u16 {
self.arch_id
}
pub fn dev_type(&self) -> u8 {
self.dev_type
}
pub fn revision(&self) -> u8 {
self.REVISION
}
#[rustfmt::skip]
pub fn determine_part(&self) -> Option<PartInfo> {
match (
self.designer().unwrap_or(""),
self.PART,
self.dev_type,
self.arch_id,
) {
("ARM Ltd", 0x000, 0x00, 0x0000) => Some(PartInfo::new("Cortex-M3 SCS", PeripheralType::Scs)),
("ARM Ltd", 0x001, 0x00, 0x0000) => Some(PartInfo::new("Cortex-M3 ITM", PeripheralType::Itm)),
("ARM Ltd", 0x002, 0x00, 0x0000) => Some(PartInfo::new("Cortex-M3 DWT", PeripheralType::Dwt)),
("ARM Ltd", 0x003, 0x00, 0x0000) => Some(PartInfo::new("Cortex-M3 FBP", PeripheralType::Fbp)),
("ARM Ltd", 0x008, 0x00, 0x0000) => Some(PartInfo::new("Cortex-M0 SCS", PeripheralType::Scs)),
("ARM Ltd", 0x00A, 0x00, 0x0000) => Some(PartInfo::new("Cortex-M0 DWT", PeripheralType::Dwt)),
("ARM Ltd", 0x00B, 0x00, 0x0000) => Some(PartInfo::new("Cortex-M0 BPU", PeripheralType::Bpu)),
("ARM Ltd", 0x00C, 0x00, 0x0000) => Some(PartInfo::new("Cortex-M4 SCS", PeripheralType::Scs)),
("ARM Ltd", 0x00D, 0x00, 0x0000) => Some(PartInfo::new("CoreSight ETM11", PeripheralType::Etm)),
("ARM Ltd", 0x00E, 0x00, 0x0000) => Some(PartInfo::new("Cortex-M7 FBP", PeripheralType::Fbp)),
("ARM Ltd", 0x101, 0x00, 0x0000) => Some(PartInfo::new("System TSGEN", PeripheralType::Tsgen)),
("ARM Ltd", 0x471, 0x00, 0x0000) => Some(PartInfo::new("Cortex-M0 ROM", PeripheralType::Rom)),
("ARM Ltd", 0x4C0, 0x00, 0x0000) => Some(PartInfo::new("Cortex-M0+ ROM", PeripheralType::Rom)),
("ARM Ltd", 0x4C4, 0x00, 0x0000) => Some(PartInfo::new("Cortex-M4 ROM", PeripheralType::Rom)),
("ARM Ltd", 0x907, 0x21, 0x0000) => Some(PartInfo::new("CoreSight ETB", PeripheralType::Etb)),
("ARM Ltd", 0x908, 0x12, 0x0000) => Some(PartInfo::new("CoreSight TraceFunnel", PeripheralType::TraceFunnel)),
("ARM Ltd", 0x910, 0x00, 0x0000) => Some(PartInfo::new("CoreSight ETM9", PeripheralType::Etm)),
("ARM Ltd", 0x912, 0x11, 0x0000) => Some(PartInfo::new("CoreSight TPIU", PeripheralType::Tpiu)),
("ARM Ltd", 0x913, 0x00, 0x0000) => Some(PartInfo::new("CoreSight ITM", PeripheralType::Itm)),
("ARM Ltd", 0x914, 0x11, 0x0000) => Some(PartInfo::new("CoreSight SWO", PeripheralType::Swo)),
("ARM Ltd", 0x920, 0x00, 0x0000) => Some(PartInfo::new("CoreSight ETM11", PeripheralType::Etm)),
("ARM Ltd", 0x923, 0x11, 0x0000) => Some(PartInfo::new("Cortex-M3 TPIU", PeripheralType::Tpiu)),
("ARM Ltd", 0x924, 0x13, 0x0000) => Some(PartInfo::new("Cortex-M3 ETM", PeripheralType::Etm)),
("ARM Ltd", 0x925, 0x13, 0x0000) => Some(PartInfo::new("Cortex-M4 ETM", PeripheralType::Etm)),
("ARM Ltd", 0x961, _, 0x0000) => Some(PartInfo::new("CoreSight TMC", PeripheralType::Tmc)),
("ARM Ltd", 0x962, 0x00, 0x0000) => Some(PartInfo::new("CoreSight STM", PeripheralType::Stm)),
("ARM Ltd", 0x963, 0x63, 0x0a63) => Some(PartInfo::new("CoreSight STM", PeripheralType::Stm)),
("ARM Ltd", 0x975, 0x13, 0x4a13) => Some(PartInfo::new("Cortex-M7 ETM", PeripheralType::Etm)),
("ARM Ltd", 0x9A1, 0x11, 0x0000) => Some(PartInfo::new("Cortex-M4 TPIU", PeripheralType::Tpiu)),
("ARM Ltd", 0x9A9, 0x11, 0x0000) => Some(PartInfo::new("Cortex-M7 TPIU", PeripheralType::Tpiu)),
("ARM Ltd", 0xD20, 0x00, 0x2A04) => Some(PartInfo::new("Cortex-M23 SCS", PeripheralType::Scs)),
("ARM Ltd", 0xD20, 0x11, 0x0000) => Some(PartInfo::new("Cortex-M23 TPIU", PeripheralType::Tpiu)),
("ARM Ltd", 0xD20, 0x13, 0x0000) => Some(PartInfo::new("Cortex-M23 ETM", PeripheralType::Etm)),
("ARM Ltd", 0xD20, 0x00, 0x1A02) => Some(PartInfo::new("Cortex-M23 DWT", PeripheralType::Dwt)),
("ARM Ltd", 0xD20, 0x00, 0x1A03) => Some(PartInfo::new("Cortex-M23 FBP", PeripheralType::Fbp)),
("ARM Ltd", 0xD20, 0x14, 0x1A14) => Some(PartInfo::new("Cortex-M23 CTI", PeripheralType::Cti)),
("ARM Ltd", 0xD21, 0x00, 0x2A04) => Some(PartInfo::new("Cortex-M33 SCS", PeripheralType::Scs)),
("ARM Ltd", 0xD21, 0x43, 0x1A01) => Some(PartInfo::new("Cortex-M33 ITM", PeripheralType::Itm)),
("ARM Ltd", 0xD21, 0x00, 0x1A02) => Some(PartInfo::new("Cortex-M33 DWT", PeripheralType::Dwt)),
("ARM Ltd", 0xD21, 0x00, 0x1A03) => Some(PartInfo::new("Cortex-M33 BPU", PeripheralType::Bpu)),
("ARM Ltd", 0xD21, 0x13, 0x4A13) => Some(PartInfo::new("Cortex-M33 ETM", PeripheralType::Etm)),
("ARM Ltd", 0xD21, 0x11, 0x0000) => Some(PartInfo::new("Cortex-M33 TPIU", PeripheralType::Tpiu)),
("ARM Ltd", 0xD21, 0x14, 0x1A14) => Some(PartInfo::new("Cortex-M33 CTI", PeripheralType::Cti)),
("ARM Ltd", 0x9A3, 0x13, 0x0000) => Some(PartInfo::new("Cortex-M0 MTB", PeripheralType::Mtb)),
("Atmel", 0xCD0, 1, 0) => Some(PartInfo::new("Atmel DSU", PeripheralType::Custom)),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct PartInfo {
name: &'static str,
peripheral_type: PeripheralType,
}
impl PartInfo {
pub const fn new(name: &'static str, peripheral_type: PeripheralType) -> Self {
Self {
name,
peripheral_type,
}
}
pub const fn name(&self) -> &'static str {
self.name
}
pub const fn peripheral_type(&self) -> PeripheralType {
self.peripheral_type
}
}
impl std::fmt::Display for PartInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {}", self.name, self.peripheral_type)
}
}
#[non_exhaustive]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PeripheralType {
Tpiu,
Itm,
Dwt,
Scs,
Fbp,
Bpu,
Etm,
Etb,
Rom,
Swo,
TraceFunnel,
Stm,
Tsgen,
Tmc,
Mtb,
Cti,
Custom,
}
impl std::fmt::Display for PeripheralType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PeripheralType::Tpiu => write!(f, "Tpiu (Trace Port Interface Unit)"),
PeripheralType::Itm => write!(f, "Itm (Instrumentation Trace Module)"),
PeripheralType::Dwt => write!(f, "Dwt (Data Watchpoint and Trace)"),
PeripheralType::Scs => write!(f, "Scs (System Control Space)"),
PeripheralType::Fbp => write!(f, "Fbp (Flash Patch and Breakpoint)"),
PeripheralType::Bpu => write!(f, "Bpu (Breakpoint Unit)"),
PeripheralType::Etm => write!(f, "Etm (Embedded Trace)"),
PeripheralType::Etb => write!(f, "Etb (Trace Buffer)"),
PeripheralType::Rom => write!(f, "Rom"),
PeripheralType::Swo => write!(f, "Swo (Single Wire Output)"),
PeripheralType::Stm => write!(f, "Stm (System Trace Macrocell)"),
PeripheralType::TraceFunnel => write!(f, "Trace Funnel"),
PeripheralType::Tsgen => write!(f, "Tsgen (Time Stamp Generator)"),
PeripheralType::Tmc => write!(f, "Tmc (Trace Memory Controller)"),
PeripheralType::Mtb => write!(f, "MTB (Micro Trace Buffer)"),
PeripheralType::Cti => write!(f, "CTI (Cross Trigger Interface)"),
PeripheralType::Custom => write!(f, "(Non-standard peripheral)"),
}
}
}