From a6c9a6fcdcaa40d6c1dd1509f774ee4e6c3fab59 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 8 Apr 2025 11:08:25 +0200 Subject: [PATCH] continue CAN support --- va416xx-hal/Cargo.toml | 1 + va416xx-hal/src/can.rs | 1 - va416xx-hal/src/can/mod.rs | 67 ++++++++ va416xx-hal/src/can/regs.rs | 298 ++++++++++++++++++++++++++++++++++++ va416xx-hal/src/clock.rs | 29 ++++ va416xx-hal/src/i2c.rs | 19 +-- va416xx-hal/src/lib.rs | 36 +++-- va416xx-hal/src/spi.rs | 7 +- va416xx-hal/src/uart/mod.rs | 10 +- 9 files changed, 430 insertions(+), 38 deletions(-) delete mode 100644 va416xx-hal/src/can.rs create mode 100644 va416xx-hal/src/can/mod.rs create mode 100644 va416xx-hal/src/can/regs.rs diff --git a/va416xx-hal/Cargo.toml b/va416xx-hal/Cargo.toml index 8386371..25e33f2 100644 --- a/va416xx-hal/Cargo.toml +++ b/va416xx-hal/Cargo.toml @@ -25,6 +25,7 @@ typenum = "1" bitflags = "2" bitbybit = "1.3" arbitrary-int = "1.3" +derive-mmio = "0.4" fugit = "0.3" delegate = ">=0.12, <=0.13" heapless = "0.8" diff --git a/va416xx-hal/src/can.rs b/va416xx-hal/src/can.rs deleted file mode 100644 index 8b13789..0000000 --- a/va416xx-hal/src/can.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/va416xx-hal/src/can/mod.rs b/va416xx-hal/src/can/mod.rs new file mode 100644 index 0000000..618a4d4 --- /dev/null +++ b/va416xx-hal/src/can/mod.rs @@ -0,0 +1,67 @@ +use arbitrary_int::{u2, u3, u4, u7}; + +use crate::{clock::Clocks, enable_peripheral_clock, PeripheralSelect}; + +pub mod regs; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum CanId { + Can0 = 0, + Can1 = 1, +} + +impl CanId {} + +pub struct Can { + id: CanId, +} + +pub trait Instance { + const ID: CanId; + const PERIPH_SEL: PeripheralSelect; +} + +impl Instance for va416xx::Can0 { + const ID: CanId = CanId::Can0; + const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0; +} + +impl Instance for va416xx::Can1 { + const ID: CanId = CanId::Can1; + const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1; +} + +pub struct ClockConfig { + prescaler: u7, + tseg1: u4, + tseg2: u3, + sjw: u2, +} + +impl ClockConfig { + /// New clock configuration from the raw configuration values. + pub fn new(prescaler: u7, tseg1: u4, tseg2: u3, sjw: u2) -> Self { + Self { + prescaler, + tseg1, + tseg2, + sjw, + } + } +} + +impl Can { + pub fn new(_can: CanI, clk_config: ClockConfig) -> Self { + enable_peripheral_clock(CanI::PERIPH_SEL); + let id = CanI::ID; + let mut regs = if id == CanId::Can0 { + unsafe { regs::Can::new_mmio_fixed_0() } + } else { + unsafe { regs::Can::new_mmio_fixed_1() } + }; + for i in 0..15 { + regs.msg_buf_block_mut(i).reset(); + } + Self { id } + } +} diff --git a/va416xx-hal/src/can/regs.rs b/va416xx-hal/src/can/regs.rs new file mode 100644 index 0000000..7fe6060 --- /dev/null +++ b/va416xx-hal/src/can/regs.rs @@ -0,0 +1,298 @@ +//! Custom register definitions for the CAN register block to circumvent PAC API / SVD +//! shortcomings. + +use arbitrary_int::{u2, u3, u4, u7}; + +pub const CAN_0_BASE: usize = 0x4001_4000; +pub const CAN_1_BASE: usize = 0x4001_4400; + +#[derive(Debug)] +#[bitbybit::bitenum(u4)] +pub enum BufferStatus { + RxNotActive = 0b0000, + RxReady = 0b0001, + RxBusy0 = 0b0010, + RxFull = 0b0100, + RxBusy1 = 0b0101, + RxOverrun = 0b0110, + RxBusy2 = 0b0111, + TxNotActive = 0b1000, + TxRtr = 0b1010, + TxOnce = 0b1100, + TxBusy0 = 0b1101, + TxOnceRtr = 0b1110, + TxBusy2 = 0b1111, +} + +/// Status control register for individual message buffers. +#[bitbybit::bitfield(u32)] +pub struct BufStatusAndControl { + /// Data length code. + #[bits(12..=15, rw)] + dlc: u4, + #[bits(4..=7, rw)] + priority: u4, + #[bits(0..=3, rw)] + status: Option, +} + +#[derive(Debug)] +pub struct Data16Bit(arbitrary_int::UInt); + +impl Data16Bit { + pub fn new(value: u16) -> Self { + Self(value.into()) + } + pub fn value(&self) -> u16 { + self.0.value() as u16 + } + + pub fn write(&mut self, value: u16) { + self.0 = value.into(); + } +} + +#[derive(derive_mmio::Mmio)] +#[repr(C)] +pub struct CanMsgBuf { + stat_ctrl: BufStatusAndControl, + timestamp: Data16Bit, + data3: Data16Bit, + data2: Data16Bit, + data1: Data16Bit, + data0: Data16Bit, + id0: Data16Bit, + id1: Data16Bit, +} + +impl MmioCanMsgBuf<'_> { + pub fn reset(&mut self) { + self.write_stat_ctrl(BufStatusAndControl::new_with_raw_value(0)); + self.write_timestamp(Data16Bit::new(0)); + self.write_data3(Data16Bit::new(0)); + self.write_data2(Data16Bit::new(0)); + self.write_data1(Data16Bit::new(0)); + self.write_data0(Data16Bit::new(0)); + self.write_id1(Data16Bit::new(0)); + self.write_id0(Data16Bit::new(0)); + } +} + +#[bitbybit::bitenum(u1, exhaustive = true)] +#[derive(Debug)] +pub enum PinLogicLevel { + DominantIsZero = 0b0, + DominantIsOne = 0b1, +} + +#[bitbybit::bitenum(u1, exhaustive = true)] +#[derive(Debug)] +pub enum ErrorInterruptType { + /// EIPND bit is set on every error. + EveryError = 0b0, + /// EIPND bit is only set if error state changes as a result of a receive or transmit + /// error counter increment. + ErrorOnRxTxCounterChange = 0b1, +} + +#[bitbybit::bitenum(u1, exhaustive = true)] +#[derive(Debug)] +pub enum DataDirection { + FirstByteAtHighestAddr = 0b0, + LastByteAtHighestAddr = 0b1, +} + +#[bitbybit::bitfield(u32)] +pub struct Control { + #[bit(11, rw)] + error_interrupt_type: ErrorInterruptType, + /// Enables special diagnostics features of the CAN like LO, IGNACK, LOOPBACK, INTERNAL. + #[bit(10, rw)] + diag_enable: bool, + /// CANTX and CANRX pins are internally connected to each other. + #[bit(9, rw)] + internal: bool, + /// All messages sent by the CAN controller can also be received by a CAN buffer with a + /// matching buffer ID. + #[bit(8, rw)] + loopback: bool, + /// IGNACK feature. The CAN does not expect to receive an ACK bit. + #[bit(7, rw)] + ignore_ack: bool, + /// LO feature. The CAN is only configured as a receiver. + #[bit(6, rw)] + listen_only: bool, + #[bit(5, rw)] + data_dir: DataDirection, + #[bit(4, rw)] + timestamp_enable: bool, + #[bit(3, rw)] + bufflock: bool, + #[bit(2, rw)] + tx_logic_level: PinLogicLevel, + #[bit(1, rw)] + rx_logic_level: PinLogicLevel, + #[bit(0, rw)] + enable: bool, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct TimingConfig { + #[bits(0..=2, rw)] + tseg2: u3, + #[bits(3..=6, rw)] + tseg1: u4, + #[bits(7..=8, rw)] + sync_jump_width: u2, + #[bits(9..=15, rw)] + prescaler: u7, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct InterruptEnable { + #[bit(15, rw)] + error: bool, + #[bit(0, rw)] + buffer: [bool; 15], +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct InterruptClear { + #[bit(15, w)] + error: bool, + #[bit(0, w)] + buffer: [bool; 15], +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct InterruptPending { + #[bit(15, r)] + error: bool, + #[bit(0, r)] + buffer: [bool; 15], +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct ErrorCounter { + #[bits(0..=7, r)] + transmit: u8, + #[bits(8..=15, r)] + receive: u8, +} + +#[derive(derive_mmio::Mmio)] +#[repr(C)] +pub struct Can { + #[mmio(inner)] + cmb0: CanMsgBuf, + #[mmio(inner)] + cmb1: CanMsgBuf, + #[mmio(inner)] + cmb2: CanMsgBuf, + #[mmio(inner)] + cmb3: CanMsgBuf, + #[mmio(inner)] + cmb4: CanMsgBuf, + #[mmio(inner)] + cmb5: CanMsgBuf, + #[mmio(inner)] + cmb6: CanMsgBuf, + #[mmio(inner)] + cmb7: CanMsgBuf, + #[mmio(inner)] + cmb8: CanMsgBuf, + #[mmio(inner)] + cmb9: CanMsgBuf, + #[mmio(inner)] + cmb10: CanMsgBuf, + #[mmio(inner)] + cmb11: CanMsgBuf, + #[mmio(inner)] + cmb12: CanMsgBuf, + #[mmio(inner)] + cmb13: CanMsgBuf, + #[mmio(inner)] + cmb14: CanMsgBuf, + #[mmio(inner)] + hcmb: CanMsgBuf, + control: Control, + timing: TimingConfig, + /// Global mask extension. + gmskx: u32, + /// Global mask base. + gmskb: u32, + /// Basic mask extension. + bmskx: u32, + /// Basic mask base. + bmskb: u32, + ien: InterruptEnable, + #[mmio(PureRead)] + ipnd: InterruptPending, + #[mmio(Write)] + iclr: InterruptClear, + /// Interrupt Code Enable Register. + icen: InterruptEnable, + #[mmio(PureRead)] + status_pending: u32, + #[mmio(PureRead)] + error_counter: ErrorCounter, + #[mmio(PureRead)] + diag: u32, + #[mmio(PureRead)] + timer: u32, +} + +impl Can { + /// Create a new CAN MMIO instance for peripheral 0. + /// + /// # Safety + /// + /// This API can be used to potentially create a driver to the same peripheral structure + /// from multiple threads. The user must ensure that concurrent accesses are safe and do not + /// interfere with each other. + pub const unsafe fn new_mmio_fixed_0() -> MmioCan<'static> { + Self::new_mmio_at(CAN_0_BASE) + } + + /// Create a new CAN MMIO instance for peripheral 1. + /// + /// # Safety + /// + /// This API can be used to potentially create a driver to the same peripheral structure + /// from multiple threads. The user must ensure that concurrent accesses are safe and do not + /// interfere with each other. + pub const unsafe fn new_mmio_fixed_1() -> MmioCan<'static> { + Self::new_mmio_at(CAN_1_BASE) + } +} + +impl MmioCan<'_> { + // TODO: It would be nice if derive-mmio could generate this for us.. + pub fn msg_buf_block_mut(&mut self, idx: usize) -> MmioCanMsgBuf<'static> { + assert!(idx < 16, "invalid index for CAN message buffer"); + match idx { + 0 => unsafe { self.steal_cmb0() }, + 1 => unsafe { self.steal_cmb1() }, + 2 => unsafe { self.steal_cmb2() }, + 3 => unsafe { self.steal_cmb3() }, + 4 => unsafe { self.steal_cmb4() }, + 5 => unsafe { self.steal_cmb5() }, + 6 => unsafe { self.steal_cmb6() }, + 7 => unsafe { self.steal_cmb7() }, + 8 => unsafe { self.steal_cmb8() }, + 9 => unsafe { self.steal_cmb9() }, + 10 => unsafe { self.steal_cmb10() }, + 11 => unsafe { self.steal_cmb11() }, + 12 => unsafe { self.steal_cmb12() }, + 13 => unsafe { self.steal_cmb13() }, + 14 => unsafe { self.steal_cmb14() }, + 15 => unsafe { self.steal_hcmb() }, + _ => unreachable!(), + } + } +} diff --git a/va416xx-hal/src/clock.rs b/va416xx-hal/src/clock.rs index a8712f8..bc5a25d 100644 --- a/va416xx-hal/src/clock.rs +++ b/va416xx-hal/src/clock.rs @@ -396,16 +396,45 @@ impl Clocks { } /// Returns the frequency of the APB0 which is equal to the system clock. + /// + /// This clock is the reference clock for the following peripherals: + /// + /// - Ethernet + /// - SpaceWire + /// - IRQ Router + /// - DMA + /// - Clock Generator pub const fn apb0(&self) -> Hertz { self.sysclk() } /// Returns system clock divied by 2. + /// + /// This clock is the reference clock for the following peripherals: + /// + /// - Timer[15:0] + /// - UART2 + /// - SPI + /// - I2C + /// - CAN + /// - GPIO + /// - IOCONFIG + /// - System Config pub const fn apb1(&self) -> Hertz { self.apb1 } /// Returns system clock divied by 4. + /// + /// This clock is the reference clock for the following peripherals: + /// + /// - Timer[23:16] + /// - TRNG + /// - UART[1:0] + /// - DAC + /// - ADC + /// - Watchdog + /// - Utility pub const fn apb2(&self) -> Hertz { self.apb2 } diff --git a/va416xx-hal/src/i2c.rs b/va416xx-hal/src/i2c.rs index a4403de..2d71c45 100644 --- a/va416xx-hal/src/i2c.rs +++ b/va416xx-hal/src/i2c.rs @@ -3,7 +3,9 @@ //! ## Examples //! //! - [PEB1 accelerometer example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/peb1-accelerometer.rs) -use crate::{clock::Clocks, pac, time::Hertz, typelevel::Sealed, PeripheralSelect, SyscfgExt as _}; +use crate::{ + clock::Clocks, enable_peripheral_clock, pac, time::Hertz, typelevel::Sealed, PeripheralSelect, +}; use core::{marker::PhantomData, ops::Deref}; use embedded_hal::i2c::{self, Operation, SevenBitAddress, TenBitAddress}; @@ -318,13 +320,12 @@ impl I2cBase { impl I2cBase { pub fn new( i2c: I2c, - syscfg: &mut pac::Sysconfig, clocks: &Clocks, speed_mode: I2cSpeed, ms_cfg: Option<&MasterConfig>, sl_cfg: Option<&SlaveConfig>, ) -> Result { - syscfg.enable_peripheral_clock(I2c::PERIPH_SEL); + enable_peripheral_clock(I2c::PERIPH_SEL); let mut i2c_base = I2cBase { i2c, @@ -476,13 +477,12 @@ pub struct I2cMaster { impl I2cMaster { pub fn new( i2c: I2c, - sys_cfg: &mut pac::Sysconfig, cfg: MasterConfig, clocks: &Clocks, speed_mode: I2cSpeed, ) -> Result { Ok(I2cMaster { - i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, Some(&cfg), None)?, + i2c_base: I2cBase::new(i2c, clocks, speed_mode, Some(&cfg), None)?, addr: PhantomData, } .enable_master()) @@ -737,13 +737,12 @@ pub struct I2cSlave { impl I2cSlave { fn new_generic( i2c: I2c, - sys_cfg: &mut pac::Sysconfig, cfg: SlaveConfig, clocks: &Clocks, speed_mode: I2cSpeed, ) -> Result { Ok(I2cSlave { - i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, None, Some(&cfg))?, + i2c_base: I2cBase::new(i2c, clocks, speed_mode, None, Some(&cfg))?, addr: PhantomData, } .enable_slave()) @@ -884,7 +883,6 @@ impl I2cSlave { /// Create a new I2C slave for seven bit addresses pub fn new( i2c: I2c, - sys_cfg: &mut pac::Sysconfig, cfg: SlaveConfig, clocks: &Clocks, speed_mode: I2cSpeed, @@ -892,18 +890,17 @@ impl I2cSlave { if let I2cAddress::TenBit(_) = cfg.addr { return Err(InitError::WrongAddrMode); } - Ok(Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)?) + Ok(Self::new_generic(i2c, cfg, clocks, speed_mode)?) } } impl I2cSlave { pub fn new_ten_bit_addr( i2c: I2c, - sys_cfg: &mut pac::Sysconfig, cfg: SlaveConfig, clocks: &Clocks, speed_mode: I2cSpeed, ) -> Result { - Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode) + Self::new_generic(i2c, cfg, clocks, speed_mode) } } diff --git a/va416xx-hal/src/lib.rs b/va416xx-hal/src/lib.rs index cbf354c..5ee22e5 100644 --- a/va416xx-hal/src/lib.rs +++ b/va416xx-hal/src/lib.rs @@ -97,39 +97,43 @@ pub enum PeripheralSelect { pub type PeripheralClock = PeripheralSelect; #[inline(always)] -pub fn enable_peripheral_clock(syscfg: &mut pac::Sysconfig, clock: PeripheralSelect) { - syscfg +pub fn enable_peripheral_clock(clock: PeripheralSelect) { + // Safety: Only bit of peripheral is modified. + unsafe { pac::Sysconfig::steal() } .peripheral_clk_enable() .modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) }); } #[inline(always)] -pub fn disable_peripheral_clock(syscfg: &mut pac::Sysconfig, clock: PeripheralSelect) { - syscfg +pub fn disable_peripheral_clock(clock: PeripheralSelect) { + // Safety: Only bit of peripheral is modified. + unsafe { pac::Sysconfig::steal() } .peripheral_clk_enable() .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) }); } #[inline(always)] -pub fn assert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) { - syscfg +pub fn assert_periph_reset(periph: PeripheralSelect) { + // Safety: Only reset bit of peripheral is modified. + unsafe { pac::Sysconfig::steal() } .peripheral_reset() .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << periph as u8)) }); } #[inline(always)] -pub fn deassert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) { - syscfg +pub fn deassert_periph_reset(periph: PeripheralSelect) { + // Safety: Only rest bit of peripheral is modified. + unsafe { pac::Sysconfig::steal() } .peripheral_reset() .modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph as u8)) }); } #[inline(always)] -fn assert_periph_reset_for_two_cycles(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) { - assert_periph_reset(syscfg, periph); +fn assert_periph_reset_for_two_cycles(periph: PeripheralSelect) { + assert_periph_reset(periph); cortex_m::asm::nop(); cortex_m::asm::nop(); - deassert_periph_reset(syscfg, periph); + deassert_periph_reset(periph); } #[derive(Debug, Eq, Copy, Clone, PartialEq)] @@ -190,27 +194,27 @@ pub trait SyscfgExt { impl SyscfgExt for pac::Sysconfig { #[inline(always)] fn enable_peripheral_clock(&mut self, clock: PeripheralClock) { - enable_peripheral_clock(self, clock) + enable_peripheral_clock(clock) } #[inline(always)] fn disable_peripheral_clock(&mut self, clock: PeripheralClock) { - disable_peripheral_clock(self, clock) + disable_peripheral_clock(clock) } #[inline(always)] fn assert_periph_reset(&mut self, clock: PeripheralSelect) { - assert_periph_reset(self, clock) + assert_periph_reset(clock) } #[inline(always)] fn deassert_periph_reset(&mut self, clock: PeripheralSelect) { - deassert_periph_reset(self, clock) + deassert_periph_reset(clock) } #[inline(always)] fn assert_periph_reset_for_two_cycles(&mut self, periph: PeripheralSelect) { - assert_periph_reset_for_two_cycles(self, periph) + assert_periph_reset_for_two_cycles(periph) } } diff --git a/va416xx-hal/src/spi.rs b/va416xx-hal/src/spi.rs index e8f06fb..e46fa2f 100644 --- a/va416xx-hal/src/spi.rs +++ b/va416xx-hal/src/spi.rs @@ -21,7 +21,7 @@ use crate::{ pac, time::Hertz, typelevel::{NoneT, Sealed}, - PeripheralSelect, SyscfgExt as _, + PeripheralSelect, }; #[cfg(not(feature = "va41628"))] @@ -1041,15 +1041,14 @@ where /// to be done once. /// * `syscfg` - Can be passed optionally to enable the peripheral clock pub fn new( - syscfg: &mut pac::Sysconfig, clocks: &crate::clock::Clocks, spi: SpiI, pins: (Sck, Miso, Mosi), spi_cfg: SpiConfig, ) -> Self { - crate::enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL); + crate::enable_peripheral_clock(SpiI::PERIPH_SEL); // This is done in the C HAL. - syscfg.assert_periph_reset_for_two_cycles(SpiI::PERIPH_SEL); + crate::assert_periph_reset_for_two_cycles(SpiI::PERIPH_SEL); let SpiConfig { clk, init_mode, diff --git a/va416xx-hal/src/uart/mod.rs b/va416xx-hal/src/uart/mod.rs index 99f942d..4727639 100644 --- a/va416xx-hal/src/uart/mod.rs +++ b/va416xx-hal/src/uart/mod.rs @@ -18,7 +18,7 @@ use fugit::RateExtU32; use crate::clock::Clocks; use crate::gpio::PF13; use crate::time::Hertz; -use crate::{disable_nvic_interrupt, enable_nvic_interrupt, PeripheralSelect, SyscfgExt as _}; +use crate::{disable_nvic_interrupt, enable_nvic_interrupt, PeripheralSelect}; use crate::{ gpio::{ AltFunc1, AltFunc2, AltFunc3, Pin, PA2, PA3, PB14, PB15, PC14, PC4, PC5, PD11, PD12, PE2, @@ -630,15 +630,14 @@ impl, RxPinInst: RxPin, UartInstanc Uart { pub fn new( - syscfg: &mut va416xx::Sysconfig, uart: UartInstance, pins: (TxPinInst, RxPinInst), config: impl Into, clocks: &Clocks, ) -> Self { - crate::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL); + crate::enable_peripheral_clock(UartInstance::PERIPH_SEL); // This is done in the C HAL. - syscfg.assert_periph_reset_for_two_cycles(UartInstance::PERIPH_SEL); + crate::assert_periph_reset_for_two_cycles(UartInstance::PERIPH_SEL); Uart { inner: UartBase { uart, @@ -651,13 +650,12 @@ impl, RxPinInst: RxPin, UartInstanc } pub fn new_with_clock_freq( - syscfg: &mut va416xx::Sysconfig, uart: UartInstance, pins: (TxPinInst, RxPinInst), config: impl Into, clock: impl Into, ) -> Self { - crate::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL); + crate::enable_peripheral_clock(UartInstance::PERIPH_SEL); Uart { inner: UartBase { uart,