diff --git a/.github/bors.toml b/.github/bors.toml index 63d883fd..0cf10c79 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -3,7 +3,7 @@ delete_merged_branches = true required_approvals = 1 status = [ "ci-linux (stable)", - "ci-linux (1.38.0)", + "ci-linux (1.40.0)", "rustfmt", "clippy", ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72890855..86000548 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: include: # Test MSRV - - rust: 1.38.0 + - rust: 1.40.0 # Test nightly but don't fail - rust: nightly diff --git a/README.md b/README.md index 6bd8aedd..6011ab2b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This project is developed and maintained by the [Cortex-M team][team]. ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.38 and up. It might compile with older versions but that may change in any new patch release. +This crate is guaranteed to compile on stable Rust 1.40 and up. It might compile with older versions but that may change in any new patch release. ## License diff --git a/src/lib.rs b/src/lib.rs index 6a736924..a2677503 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,7 @@ //! //! # Minimum Supported Rust Version (MSRV) //! -//! This crate is guaranteed to compile on stable Rust 1.38 and up. It *might* +//! This crate is guaranteed to compile on stable Rust 1.40 and up. It *might* //! compile with older versions but that may change in any new patch release. #![cfg_attr(feature = "inline-asm", feature(asm))] diff --git a/src/peripheral/dcb.rs b/src/peripheral/dcb.rs index 5689cb4f..056150bc 100644 --- a/src/peripheral/dcb.rs +++ b/src/peripheral/dcb.rs @@ -25,6 +25,10 @@ impl DCB { /// `peripheral::DWT` cycle counter to work properly. /// As by STM documentation, this flag is not reset on /// soft-reset, only on power reset. + /// + /// Note: vendor-specific registers may have to be set to completely + /// enable tracing. For example, on the STM32F401RE, `TRACE_MODE` + /// and `TRACE_IOEN` must be configured in `DBGMCU_CR` register. #[inline] pub fn enable_trace(&mut self) { // set bit 24 / TRCENA diff --git a/src/peripheral/dwt.rs b/src/peripheral/dwt.rs index 9e8e6385..11dd5455 100644 --- a/src/peripheral/dwt.rs +++ b/src/peripheral/dwt.rs @@ -5,12 +5,13 @@ use volatile_register::WO; use volatile_register::{RO, RW}; use crate::peripheral::DWT; +use bitfield::bitfield; /// Register block #[repr(C)] pub struct RegisterBlock { /// Control - pub ctrl: RW, + pub ctrl: RW, /// Cycle Count #[cfg(not(armv6m))] pub cyccnt: RW, @@ -50,6 +51,21 @@ pub struct RegisterBlock { pub lsr: RO, } +bitfield! { + /// Control register. + #[repr(C)] + #[derive(Copy, Clone)] + pub struct Ctrl(u32); + cyccntena, set_cyccntena: 0; + pcsamplena, set_pcsamplena: 12; + exctrcena, set_exctrcena: 16; + noprfcnt, _: 24; + nocyccnt, _: 25; + noexttrig, _: 26; + notrcpkt, _: 27; + u8, numcomp, _: 31, 28; +} + /// Comparator #[repr(C)] pub struct Comparator { @@ -58,58 +74,57 @@ pub struct Comparator { /// Comparator Mask pub mask: RW, /// Comparator Function - pub function: RW, + pub function: RW, reserved: u32, } -// DWT CTRL register fields -const NUMCOMP_OFFSET: u32 = 28; -const NOTRCPKT: u32 = 1 << 27; -const NOEXTTRIG: u32 = 1 << 26; -const NOCYCCNT: u32 = 1 << 25; -const NOPRFCNT: u32 = 1 << 24; -const CYCCNTENA: u32 = 1 << 0; +bitfield! { + #[repr(C)] + #[derive(Copy, Clone)] + /// Comparator FUNCTIONn register. + pub struct Function(u32); + u8, function, set_function: 3, 0; + emitrange, set_emitrange: 5; + cycmatch, set_cycmatch: 7; + datavmatch, set_datavmatch: 8; + matched, _: 24; +} impl DWT { /// Number of comparators implemented /// /// A value of zero indicates no comparator support. #[inline] - pub fn num_comp() -> u8 { - // NOTE(unsafe) atomic read with no side effects - unsafe { ((*Self::ptr()).ctrl.read() >> NUMCOMP_OFFSET) as u8 } + pub fn num_comp(&self) -> u8 { + self.ctrl.read().numcomp() } /// Returns `true` if the the implementation supports sampling and exception tracing #[cfg(not(armv6m))] #[inline] - pub fn has_exception_trace() -> bool { - // NOTE(unsafe) atomic read with no side effects - unsafe { (*Self::ptr()).ctrl.read() & NOTRCPKT == 0 } + pub fn has_exception_trace(&self) -> bool { + !self.ctrl.read().notrcpkt() } /// Returns `true` if the implementation includes external match signals #[cfg(not(armv6m))] #[inline] - pub fn has_external_match() -> bool { - // NOTE(unsafe) atomic read with no side effects - unsafe { (*Self::ptr()).ctrl.read() & NOEXTTRIG == 0 } + pub fn has_external_match(&self) -> bool { + !self.ctrl.read().noexttrig() } /// Returns `true` if the implementation supports a cycle counter #[cfg(not(armv6m))] #[inline] - pub fn has_cycle_counter() -> bool { - // NOTE(unsafe) atomic read with no side effects - unsafe { (*Self::ptr()).ctrl.read() & NOCYCCNT == 0 } + pub fn has_cycle_counter(&self) -> bool { + !self.ctrl.read().nocyccnt() } /// Returns `true` if the implementation the profiling counters #[cfg(not(armv6m))] #[inline] - pub fn has_profiling_counter() -> bool { - // NOTE(unsafe) atomic read with no side effects - unsafe { (*Self::ptr()).ctrl.read() & NOPRFCNT == 0 } + pub fn has_profiling_counter(&self) -> bool { + !self.ctrl.read().noprfcnt() } /// Enables the cycle counter @@ -123,22 +138,55 @@ impl DWT { #[cfg(not(armv6m))] #[inline] pub fn enable_cycle_counter(&mut self) { - unsafe { self.ctrl.modify(|r| r | CYCCNTENA) } + unsafe { + self.ctrl.modify(|mut r| { + r.set_cyccntena(true); + r + }); + } } - /// Disables the cycle counter + /// Returns `true` if the cycle counter is enabled #[cfg(not(armv6m))] #[inline] - pub fn disable_cycle_counter(&mut self) { - unsafe { self.ctrl.modify(|r| r & !CYCCNTENA) } + pub fn cycle_counter_enabled(&self) -> bool { + self.ctrl.read().cyccntena() } - /// Returns `true` if the cycle counter is enabled + /// Enables exception tracing #[cfg(not(armv6m))] #[inline] - pub fn cycle_counter_enabled() -> bool { - // NOTE(unsafe) atomic read with no side effects - unsafe { (*Self::ptr()).ctrl.read() & CYCCNTENA != 0 } + pub fn enable_exception_tracing(&mut self) { + unsafe { + self.ctrl.modify(|mut r| { + r.set_exctrcena(true); + r + }); + } + } + + /// Disables exception tracing + #[cfg(not(armv6m))] + #[inline] + pub fn disable_exception_tracing(&mut self) { + unsafe { + self.ctrl.modify(|mut r| { + r.set_exctrcena(false); + r + }); + } + } + + /// Whether to periodically generate PC samples + #[cfg(not(armv6m))] + #[inline] + pub fn enable_pc_samples(&mut self, bit: bool) { + unsafe { + self.ctrl.modify(|mut r| { + r.set_pcsamplena(bit); + r + }); + } } /// Returns the current clock cycle count @@ -266,3 +314,111 @@ impl DWT { unsafe { self.foldcnt.write(count as u32) } } } + +/// Whether the comparator should match on read, write or read/write operations. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum AccessType { + /// Generate packet only when matched adress is read from. + ReadOnly, + /// Generate packet only when matched adress is written to. + WriteOnly, + /// Generate packet when matched adress is both read from and written to. + ReadWrite, +} + +/// The sequence of packet(s) that should be emitted on comparator match. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum EmitOption { + /// Emit only trace data value packet. + Data, + /// Emit only trace address packet. + Address, + /// Emit only trace PC value packet + /// + /// *NOTE* only compatible with [AccessType::ReadWrite]. + PC, + /// Emit trace address and data value packets. + AddressData, + /// Emit trace PC value and data value packets. + PCData, +} + +/// Settings for address matching +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub struct ComparatorAddressSettings { + /// The address to match against. + pub address: u32, + /// The address mask to match against. + pub mask: u32, + /// What sequence of packet(s) to emit on comparator match. + pub emit: EmitOption, + /// Whether to match on read, write or read/write operations. + pub access_type: AccessType, +} + +/// The available functions of a DWT comparator. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[non_exhaustive] +pub enum ComparatorFunction { + /// Compare accessed memory addresses. + Address(ComparatorAddressSettings), +} + +/// Possible error values returned on [Comparator::configure]. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[non_exhaustive] +pub enum DwtError { + /// Invalid combination of [AccessType] and [EmitOption]. + InvalidFunction, +} + +impl Comparator { + /// Configure the function of the comparator + #[allow(clippy::missing_inline_in_public_items)] + pub fn configure(&self, settings: ComparatorFunction) -> Result<(), DwtError> { + match settings { + ComparatorFunction::Address(settings) => unsafe { + // FUNCTION, EMITRANGE + // See Table C1-14 + let (function, emit_range) = match (&settings.access_type, &settings.emit) { + (AccessType::ReadOnly, EmitOption::Data) => (0b1100, false), + (AccessType::ReadOnly, EmitOption::Address) => (0b1100, true), + (AccessType::ReadOnly, EmitOption::AddressData) => (0b1110, true), + (AccessType::ReadOnly, EmitOption::PCData) => (0b1110, false), + + (AccessType::WriteOnly, EmitOption::Data) => (0b1101, false), + (AccessType::WriteOnly, EmitOption::Address) => (0b1101, true), + (AccessType::WriteOnly, EmitOption::AddressData) => (0b1111, true), + (AccessType::WriteOnly, EmitOption::PCData) => (0b1111, false), + + (AccessType::ReadWrite, EmitOption::Data) => (0b0010, false), + (AccessType::ReadWrite, EmitOption::Address) => (0b0001, true), + (AccessType::ReadWrite, EmitOption::AddressData) => (0b0010, true), + (AccessType::ReadWrite, EmitOption::PCData) => (0b0011, false), + + (AccessType::ReadWrite, EmitOption::PC) => (0b0001, false), + (_, EmitOption::PC) => return Err(DwtError::InvalidFunction), + }; + + self.function.modify(|mut r| { + r.set_function(function); + r.set_emitrange(emit_range); + + // don't compare data value + r.set_datavmatch(false); + + // don't compare cycle counter value + // NOTE: only needed for comparator 0, but is SBZP. + r.set_cycmatch(false); + + r + }); + + self.comp.write(settings.address); + self.mask.write(settings.mask); + }, + } + + Ok(()) + } +} diff --git a/src/peripheral/itm.rs b/src/peripheral/itm.rs index c0d560f5..4d0aa220 100644 --- a/src/peripheral/itm.rs +++ b/src/peripheral/itm.rs @@ -7,6 +7,9 @@ use core::ptr; use volatile_register::{RO, RW, WO}; +use crate::peripheral::ITM; +use bitfield::bitfield; + /// Register block #[repr(C)] pub struct RegisterBlock { @@ -20,7 +23,7 @@ pub struct RegisterBlock { pub tpr: RW, reserved2: [u32; 15], /// Trace Control - pub tcr: RW, + pub tcr: RW, reserved3: [u32; 75], /// Lock Access pub lar: WO, @@ -28,6 +31,22 @@ pub struct RegisterBlock { pub lsr: RO, } +bitfield! { + /// Trace Control Register. + #[repr(C)] + #[derive(Copy, Clone)] + pub struct Tcr(u32); + itmena, set_itmena: 0; + tsena, set_tsena: 1; + syncena, set_synena: 2; + txena, set_txena: 3; + swoena, set_swoena: 4; + u8, tsprescale, set_tsprescale: 9, 8; + u8, gtsfreq, set_gtsfreq: 11, 10; + u8, tracebusid, set_tracebusid: 22, 16; + busy, _: 23; +} + /// Stimulus Port pub struct Stim { register: UnsafeCell, @@ -69,3 +88,106 @@ impl Stim { unsafe { ptr::read_volatile(self.register.get()) & 0b11 != 0 } } } + +/// The possible local timestamp options. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum LocalTimestampOptions { + /// Disable local timestamps. + Disabled, + /// Enable local timestamps and use no prescaling. + Enabled, + /// Enable local timestamps and set the prescaler to divide the + /// reference clock by 4. + EnabledDiv4, + /// Enable local timestamps and set the prescaler to divide the + /// reference clock by 16. + EnabledDiv16, + /// Enable local timestamps and set the prescaler to divide the + /// reference clock by 64. + EnabledDiv64, +} + +/// The possible global timestamp options. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum GlobalTimestampOptions { + /// Disable global timestamps. + Disabled, + /// Generate a global timestamp approximately every 128 cycles. + Every128Cycles, + /// Generate a global timestamp approximately every 8921 cycles. + Every8192Cycles, + /// Generate a global timestamp after every packet, if the output FIFO is empty. + EveryPacket, +} + +/// The possible clock sources for timestamp counters. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum TimestampClkSrc { + /// Clock timestamp counters using the system processor clock. + SystemClock, + /// Clock timestamp counters using the asynchronous clock from the + /// TPIU interface. + /// + /// NOTE: The timestamp counter is held in reset while the output + /// line is idle. + AsyncTPIU, +} + +/// Available settings for the ITM peripheral. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub struct ITMSettings { + /// Whether to enable ITM. + pub enable: bool, + /// Whether DWT packets should be forwarded to ITM. + pub forward_dwt: bool, + /// The local timestamp options that should be applied. + pub local_timestamps: LocalTimestampOptions, + /// The global timestamp options that should be applied. + pub global_timestamps: GlobalTimestampOptions, + /// The trace bus ID to use when multi-trace sources are in use. + /// `None` specifies that only a single trace source is in use and + /// has the same effect as `Some(0)`. + pub bus_id: Option, + /// The clock that should increase timestamp counters. + pub timestamp_clk_src: TimestampClkSrc, +} + +impl ITM { + /// Removes the software lock on the ITM. + #[inline] + pub fn unlock(&mut self) { + // NOTE(unsafe) atomic write to a stateless, write-only register + unsafe { self.lar.write(0xC5AC_CE55) } + } + + /// Configures the ITM with the passed [ITMSettings]. + #[inline] + pub fn configure(&mut self, settings: ITMSettings) { + unsafe { + self.tcr.modify(|mut r| { + r.set_itmena(settings.enable); + r.set_tsena(settings.local_timestamps != LocalTimestampOptions::Disabled); + r.set_txena(settings.forward_dwt); + r.set_tsprescale(match settings.local_timestamps { + LocalTimestampOptions::Disabled | LocalTimestampOptions::Enabled => 0b00, + LocalTimestampOptions::EnabledDiv4 => 0b10, + LocalTimestampOptions::EnabledDiv16 => 0b10, + LocalTimestampOptions::EnabledDiv64 => 0b11, + }); + r.set_gtsfreq(match settings.global_timestamps { + GlobalTimestampOptions::Disabled => 0b00, + GlobalTimestampOptions::Every128Cycles => 0b01, + GlobalTimestampOptions::Every8192Cycles => 0b10, + GlobalTimestampOptions::EveryPacket => 0b11, + }); + r.set_swoena(match settings.timestamp_clk_src { + TimestampClkSrc::SystemClock => false, + TimestampClkSrc::AsyncTPIU => true, + }); + r.set_tracebusid(settings.bus_id.unwrap_or(0)); + + r + }); + } + } +} diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index 5c5e7ce6..081aa0ab 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -253,6 +253,7 @@ impl AC { } /// Cache and branch predictor maintenance operations +#[allow(clippy::upper_case_acronyms)] pub struct CBP { _marker: PhantomData<*const ()>, } @@ -289,6 +290,7 @@ impl ops::Deref for CBP { } /// CPUID +#[allow(clippy::upper_case_acronyms)] pub struct CPUID { _marker: PhantomData<*const ()>, } @@ -316,6 +318,7 @@ impl ops::Deref for CPUID { } /// Debug Control Block +#[allow(clippy::upper_case_acronyms)] pub struct DCB { _marker: PhantomData<*const ()>, } @@ -343,6 +346,7 @@ impl ops::Deref for DCB { } /// Data Watchpoint and Trace unit +#[allow(clippy::upper_case_acronyms)] pub struct DWT { _marker: PhantomData<*const ()>, } @@ -370,6 +374,7 @@ impl ops::Deref for DWT { } /// Flash Patch and Breakpoint unit +#[allow(clippy::upper_case_acronyms)] pub struct FPB { _marker: PhantomData<*const ()>, } @@ -399,6 +404,7 @@ impl ops::Deref for FPB { } /// Floating Point Unit +#[allow(clippy::upper_case_acronyms)] pub struct FPU { _marker: PhantomData<*const ()>, } @@ -433,6 +439,7 @@ impl ops::Deref for FPU { /// `actlr`. It's called the "implementation control block" in the ARMv8-M /// standard, but earlier standards contained the registers, just without a /// name. +#[allow(clippy::upper_case_acronyms)] pub struct ICB { _marker: PhantomData<*const ()>, } @@ -467,6 +474,7 @@ impl ops::DerefMut for ICB { } /// Instrumentation Trace Macrocell +#[allow(clippy::upper_case_acronyms)] pub struct ITM { _marker: PhantomData<*const ()>, } @@ -504,6 +512,7 @@ impl ops::DerefMut for ITM { } /// Memory Protection Unit +#[allow(clippy::upper_case_acronyms)] pub struct MPU { _marker: PhantomData<*const ()>, } @@ -531,6 +540,7 @@ impl ops::Deref for MPU { } /// Nested Vector Interrupt Controller +#[allow(clippy::upper_case_acronyms)] pub struct NVIC { _marker: PhantomData<*const ()>, } @@ -558,6 +568,7 @@ impl ops::Deref for NVIC { } /// Security Attribution Unit +#[allow(clippy::upper_case_acronyms)] pub struct SAU { _marker: PhantomData<*const ()>, } @@ -587,6 +598,7 @@ impl ops::Deref for SAU { } /// System Control Block +#[allow(clippy::upper_case_acronyms)] pub struct SCB { _marker: PhantomData<*const ()>, } @@ -614,6 +626,7 @@ impl ops::Deref for SCB { } /// SysTick: System Timer +#[allow(clippy::upper_case_acronyms)] pub struct SYST { _marker: PhantomData<*const ()>, } @@ -641,6 +654,7 @@ impl ops::Deref for SYST { } /// Trace Port Interface Unit +#[allow(clippy::upper_case_acronyms)] pub struct TPIU { _marker: PhantomData<*const ()>, } diff --git a/src/peripheral/tpiu.rs b/src/peripheral/tpiu.rs index 11cb79e9..3ff5f550 100644 --- a/src/peripheral/tpiu.rs +++ b/src/peripheral/tpiu.rs @@ -4,6 +4,9 @@ use volatile_register::{RO, RW, WO}; +use crate::peripheral::TPIU; +use bitfield::bitfield; + /// Register block #[repr(C)] pub struct RegisterBlock { @@ -16,10 +19,10 @@ pub struct RegisterBlock { pub acpr: RW, reserved1: [u32; 55], /// Selected Pin Control - pub sppr: RW, + pub sppr: RW, reserved2: [u32; 132], /// Formatter and Flush Control - pub ffcr: RW, + pub ffcr: RW, reserved3: [u32; 810], /// Lock Access pub lar: WO, @@ -27,5 +30,131 @@ pub struct RegisterBlock { pub lsr: RO, reserved4: [u32; 4], /// TPIU Type - pub _type: RO, + pub _type: RO, +} + +bitfield! { + /// Formatter and flush control register. + #[repr(C)] + #[derive(Clone, Copy)] + pub struct Ffcr(u32); + enfcont, set_enfcont: 1; +} + +bitfield! { + /// TPIU Type Register. + #[repr(C)] + #[derive(Clone, Copy)] + pub struct Type(u32); + u8, fifosz, _: 8, 6; + ptinvalid, _: 9; + mancvalid, _: 10; + nrzvalid, _: 11; +} + +bitfield! { + /// Selected pin protocol register. + #[repr(C)] + #[derive(Clone, Copy)] + pub struct Sppr(u32); + u8, txmode, set_txmode: 1, 0; +} + +/// The available protocols for the trace output. +#[repr(u8)] +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum TraceProtocol { + /// Parallel trace port mode + Parallel = 0b00, + /// Asynchronous SWO, using Manchester encoding + AsyncSWOManchester = 0b01, + /// Asynchronous SWO, using NRZ encoding + AsyncSWONRZ = 0b10, +} +impl core::convert::TryFrom for TraceProtocol { + type Error = (); + + /// Tries to convert from a `TXMODE` field value. Fails if the set mode is + /// unknown (and thus unpredictable). + #[inline] + fn try_from(value: u8) -> Result { + match value { + x if x == Self::Parallel as u8 => Ok(Self::Parallel), + x if x == Self::AsyncSWOManchester as u8 => Ok(Self::AsyncSWOManchester), + x if x == Self::AsyncSWONRZ as u8 => Ok(Self::AsyncSWONRZ), + _ => Err(()), // unknown and unpredictable mode + } + } +} + +/// The SWO options supported by the TPIU. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub struct SWOSupports { + /// Whether UART/NRZ encoding is supported for SWO. + nrz_encoding: bool, + /// Whether Manchester encoding is supported for SWO. + manchester_encoding: bool, + /// Whether parallel trace port operation is supported. + parallel_operation: bool, + /// The minimum implemented FIFO queue size of the TPIU for trace data. + min_queue_size: u8, +} + +impl TPIU { + /// Sets the prescaler value for a wanted baud rate of the Serial + /// Wire Output (SWO) in relation to a given asynchronous refernce + /// clock rate. + #[inline] + pub fn set_swo_baud_rate(&mut self, ref_clk_rate: u32, baud_rate: u32) { + unsafe { + self.acpr.write((ref_clk_rate / baud_rate) - 1); + } + } + + /// The used protocol for the trace output. Return `None` if an + /// unknown (and thus unpredicable mode) is configured by means + /// other than + /// [`trace_output_protocol`](Self::set_trace_output_protocol). + #[inline] + pub fn trace_output_protocol(&self) -> Option { + use core::convert::TryInto; + self.sppr.read().txmode().try_into().ok() + } + + /// Sets the used protocol for the trace output. + #[inline] + pub fn set_trace_output_protocol(&mut self, proto: TraceProtocol) { + unsafe { + self.sppr.modify(|mut r| { + r.set_txmode(proto as u8); + r + }); + } + } + + /// Whether to enable the formatter. If disabled, only ITM and DWT + /// trace sources are passed through. Data from the ETM is + /// discarded. + #[inline] + pub fn enable_continuous_formatting(&mut self, bit: bool) { + unsafe { + self.ffcr.modify(|mut r| { + r.set_enfcont(bit); + r + }); + } + } + + /// Reads the supported trace output modes and the minimum size of + /// the TPIU FIFO queue for trace data. + #[inline] + pub fn swo_supports() -> SWOSupports { + let _type = unsafe { (*Self::ptr())._type.read() }; + SWOSupports { + nrz_encoding: _type.nrzvalid(), + manchester_encoding: _type.mancvalid(), + parallel_operation: !_type.ptinvalid(), + min_queue_size: _type.fifosz(), + } + } }