From a9293e68a633a94564a59b1deabf177f2d96784b Mon Sep 17 00:00:00 2001 From: Mirabellensaft Date: Mon, 20 Feb 2023 17:58:48 +0100 Subject: [PATCH] Cleaned up lib --- down-the-stack/dk_template/src/errata.rs | 13 -- down-the-stack/dk_template/src/lib.rs | 167 ++------------ down-the-stack/dk_template/src/peripheral.rs | 3 - down-the-stack/dk_template/src/usbd.rs | 231 ------------------- 4 files changed, 17 insertions(+), 397 deletions(-) delete mode 100644 down-the-stack/dk_template/src/errata.rs delete mode 100644 down-the-stack/dk_template/src/peripheral.rs delete mode 100644 down-the-stack/dk_template/src/usbd.rs diff --git a/down-the-stack/dk_template/src/errata.rs b/down-the-stack/dk_template/src/errata.rs deleted file mode 100644 index cb26d96..0000000 --- a/down-the-stack/dk_template/src/errata.rs +++ /dev/null @@ -1,13 +0,0 @@ -/// USBD cannot be enabled -pub unsafe fn e187a() { - (0x4006_EC00 as *mut u32).write_volatile(0x9375); - (0x4006_ED14 as *mut u32).write_volatile(3); - (0x4006_EC00 as *mut u32).write_volatile(0x9375); -} - -/// USBD cannot be enabled -pub unsafe fn e187b() { - (0x4006_EC00 as *mut u32).write_volatile(0x9375); - (0x4006_ED14 as *mut u32).write_volatile(0); - (0x4006_EC00 as *mut u32).write_volatile(0x9375); -} diff --git a/down-the-stack/dk_template/src/lib.rs b/down-the-stack/dk_template/src/lib.rs index dbaf2b0..5884013 100644 --- a/down-the-stack/dk_template/src/lib.rs +++ b/down-the-stack/dk_template/src/lib.rs @@ -7,38 +7,23 @@ use core::{ ops, fmt, - sync::atomic::{self, AtomicU32, Ordering}, + sync::atomic::{self, Ordering}, time::Duration, }; -use cortex_m::{asm, peripheral::NVIC}; +use cortex_m::asm; use embedded_hal::digital::v2::{OutputPin as _, StatefulOutputPin}; -pub use hal::ieee802154; pub use hal::pac::{ - interrupt, Interrupt, NVIC_PRIO_BITS, RTC0, UARTE1, uarte0::{ - baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}}; + UARTE1, uarte0::{ + baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}}; use hal::{ - clocks::{self, Clocks}, gpio::{p0, Level, Output, Input, PullUp, Pin, Port, PushPull}, - rtc::{Rtc, RtcInterrupt}, timer::OneShot, prelude::InputPin, }; use defmt; use defmt_rtt as _; // global logger - -use crate::{ - peripheral::{POWER, USBD}, - usbd::Ep0In, - -}; - -mod errata; -pub mod peripheral; -pub mod usbd; - - /// Components on the board pub struct Board { /// LEDs @@ -49,15 +34,6 @@ pub struct Board { // --- Exercise --- 🔼 /// Timer pub timer: Timer, - - /// Radio interface - pub radio: ieee802154::Radio<'static>, - /// USBD (Universal Serial Bus Device) peripheral - pub usbd: USBD, - /// POWER (Power Supply) peripheral - pub power: POWER, - /// USB control endpoint 0 - pub ep0in: Ep0In, // --- Exercise --- 🔽 /// uarte interface pub uarte: Uarte, @@ -86,11 +62,7 @@ impl Led { pub fn on(&mut self) { defmt::trace!( "setting P{}.{} low (LED on)", - if self.inner.port() == Port::Port1 { - '1' - } else { - '0' - }, + port_as_char(&self.inner.port()), self.inner.pin() ); @@ -102,11 +74,7 @@ impl Led { pub fn off(&mut self) { defmt::trace!( "setting P{}.{} high (LED off)", - if self.inner.port() == Port::Port1 { - '1' - } else { - '0' - }, + port_as_char(&self.inner.port()), self.inner.pin() ); @@ -170,7 +138,8 @@ impl Timer { pub fn wait(&mut self, duration: Duration) { defmt::trace!("blocking for {:?} ...", duration); - // 1 cycle = 1 microsecond + // 1 cycle = 1 microsecond because the underlying HAL driver + // always sets the timer to 1 MHz. const NANOS_IN_ONE_MICRO: u32 = 1_000; let subsec_micros = duration.subsec_nanos() / NANOS_IN_ONE_MICRO; if subsec_micros != 0 { @@ -223,7 +192,7 @@ impl fmt::Write for Uarte { fn write_str(&mut self, s: &str) -> fmt::Result { // Copy all data into an on-stack buffer so we never try to EasyDMA from // flash. - let buf = &mut [0; 16][..]; + let mut buf: [u8; 16] = [0; 16]; for block in s.as_bytes().chunks(16) { buf[..block.len()].copy_from_slice(block); self.inner.write(&buf[..block.len()]).map_err(|_| fmt::Error)?; @@ -238,42 +207,6 @@ impl fmt::Write for Uarte { /// This return an `Err`or if called more than once pub fn init() -> Result { if let Some(periph) = hal::pac::Peripherals::take() { - // NOTE(static mut) this branch runs at most once - static mut EP0IN_BUF: [u8; 64] = [0; 64]; - static mut CLOCKS: Option< - Clocks, - > = None; - - defmt::debug!("Initializing the board"); - - let clocks = Clocks::new(periph.CLOCK); - let clocks = clocks.enable_ext_hfosc(); - let clocks = clocks.set_lfclk_src_external(clocks::LfOscConfiguration::NoExternalNoBypass); - let clocks = clocks.start_lfclk(); - let _clocks = clocks.enable_ext_hfosc(); - // extend lifetime to `'static` - let clocks = unsafe { CLOCKS.get_or_insert(_clocks) }; - - defmt::debug!("Clocks configured"); - - let mut rtc = Rtc::new(periph.RTC0, 0).unwrap(); - rtc.enable_interrupt(RtcInterrupt::Overflow, None); - rtc.enable_counter(); - // NOTE(unsafe) because this crate defines the `#[interrupt] fn RTC0` interrupt handler, - // RTIC cannot manage that interrupt (trying to do so results in a linker error). Thus it - // is the task of this crate to mask/unmask the interrupt in a safe manner. - // - // Because the RTC0 interrupt handler does *not* access static variables through a critical - // section (that disables interrupts) this `unmask` operation cannot break critical sections - // and thus won't lead to undefined behavior (e.g. torn reads/writes) - // - // the preceding `enable_conuter` method consumes the `rtc` value. This is a semantic move - // of the RTC0 peripheral from this function (which can only be called at most once) to the - // interrupt handler (where the peripheral is accessed without any synchronization - // mechanism) - unsafe { NVIC::unmask(Interrupt::RTC0) }; - - defmt::debug!("RTC started"); let pins = p0::Parts::new(periph.P0); @@ -308,18 +241,6 @@ pub fn init() -> Result { let uarte = hal::uarte::Uarte::new(periph.UARTE0, pins, Parity::INCLUDED, Baudrate::BAUD115200); // --- Exercise --- 🔼 - // Radio - let radio = { - let mut radio = ieee802154::Radio::init(periph.RADIO, clocks); - - // set TX power to its maximum value - radio.set_txpower(ieee802154::TxPower::Pos8dBm); - defmt::debug!( - "Radio initialized and configured with TX power set to the maximum value" - ); - radio - }; - Ok(Board { leds: Leds { led_1: Led { inner: led_1 }, @@ -336,12 +257,9 @@ pub fn init() -> Result { b_4: Button { inner: b_4}, }, // --- Exercise --- 🔼 - radio, + timer: Timer { inner: timer }, - usbd: periph.USBD, - power: periph.POWER, - ep0in: unsafe { Ep0In::new(&mut EP0IN_BUF) }, - + // --- Exercise --- 🔽 uarte: Uarte { inner: uarte }, // --- Exercise --- 🔼 @@ -351,19 +269,6 @@ pub fn init() -> Result { } } -// Counter of OVERFLOW events -- an OVERFLOW occurs every (1<<24) ticks -static OVERFLOWS: AtomicU32 = AtomicU32::new(0); - -// NOTE this will run at the highest priority, higher priority than RTIC tasks -#[interrupt] -fn RTC0() { - let curr = OVERFLOWS.load(Ordering::Relaxed); - OVERFLOWS.store(curr + 1, Ordering::Relaxed); - - // clear the EVENT register - unsafe { core::mem::transmute::<_, RTC0>(()).events_ovrflw.reset() } -} - /// Exits the application when the program is executed through the `probe-run` Cargo runner pub fn exit() -> ! { unsafe { @@ -384,49 +289,11 @@ pub fn exit() -> ! { } } -/// Returns the time elapsed since the call to the `dk::init` function -/// -/// The clock that is read to compute this value has a resolution of 30 microseconds. -/// -/// Calling this function before calling `dk::init` will return a value of `0` nanoseconds. -pub fn uptime() -> Duration { - // here we are going to perform a 64-bit read of the number of ticks elapsed - // - // a 64-bit load operation cannot performed in a single instruction so the operation can be - // preempted by the RTC0 interrupt handler (which increases the OVERFLOWS counter) - // - // the loop below will load both the lower and upper parts of the 64-bit value while preventing - // the issue of mixing a low value with an "old" high value -- note that, due to interrupts, an - // arbitrary amount of time may elapse between the `hi1` load and the `low` load - let overflows = &OVERFLOWS as *const AtomicU32 as *const u32; - let ticks = loop { - unsafe { - // NOTE volatile is used to order these load operations among themselves - let hi1 = overflows.read_volatile(); - let low = core::mem::transmute::<_, RTC0>(()) - .counter - .read() - .counter() - .bits(); - let hi2 = overflows.read_volatile(); +// Helper functions - if hi1 == hi2 { - break u64::from(low) | (u64::from(hi1) << 24); - } - } - }; - - // 2**15 ticks = 1 second - let freq = 1 << 15; - let secs = ticks / freq; - // subsec ticks - let ticks = (ticks % freq) as u32; - // one tick is equal to `1e9 / 32768` nanos - // the fraction can be reduced to `1953125 / 64` - // which can be further decomposed as `78125 * (5 / 4) * (5 / 4) * (1 / 4)`. - // Doing the operation this way we can stick to 32-bit arithmetic without overflowing the value - // at any stage - let nanos = - (((ticks % 32768).wrapping_mul(78125) >> 2).wrapping_mul(5) >> 2).wrapping_mul(5) >> 2; - Duration::new(secs, nanos as u32) +fn port_as_char(port: &Port) -> char { + match port { + Port::Port0 => '0', + Port::Port1 => '1', + } } diff --git a/down-the-stack/dk_template/src/peripheral.rs b/down-the-stack/dk_template/src/peripheral.rs deleted file mode 100644 index bfec1a7..0000000 --- a/down-the-stack/dk_template/src/peripheral.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Low level access to the nRF52840 peripheral - -pub use hal::pac::{POWER, USBD}; diff --git a/down-the-stack/dk_template/src/usbd.rs b/down-the-stack/dk_template/src/usbd.rs deleted file mode 100644 index ae77157..0000000 --- a/down-the-stack/dk_template/src/usbd.rs +++ /dev/null @@ -1,231 +0,0 @@ -//! USBD peripheral - -use core::sync::atomic::{self, Ordering}; - -use crate::{ - errata, - peripheral::{POWER, USBD}, -}; - -/// Endpoint IN 0 -pub struct Ep0In { - buffer: &'static mut [u8; 64], - busy: bool, -} - -impl Ep0In { - /// # Safety - /// Must be created at most once (singleton) - pub(crate) unsafe fn new(buffer: &'static mut [u8; 64]) -> Self { - Self { - buffer, - busy: false, - } - } - - /// Starts a data transfer over endpoint 0 - /// - /// # Panics - /// - /// - This function panics if the last transfer was not finished by calling the `end` function - /// - This function panics if `bytes` is larger than the maximum packet size (64 bytes) - pub fn start(&mut self, bytes: &[u8], usbd: &USBD) { - assert!(!self.busy, "EP0IN: last transfer has not completed"); - assert!( - bytes.len() <= self.buffer.len(), - "EP0IN: multi-packet data transfers are not supported" - ); - - let n = bytes.len(); - self.buffer[..n].copy_from_slice(bytes); - - // use a "shortcut" to issue a status stage after the data transfer is complete - usbd.shorts - .modify(|_, w| w.ep0datadone_ep0status().set_bit()); - usbd.epin0 - .maxcnt - .write(|w| unsafe { w.maxcnt().bits(n as u8) }); - usbd.epin0 - .ptr - .write(|w| unsafe { w.ptr().bits(self.buffer.as_ptr() as u32) }); - - self.busy = true; - - defmt::println!("EP0IN: start {}B transfer", n); - - // start DMA transfer - dma_start(); - usbd.tasks_startepin[0].write(|w| w.tasks_startepin().set_bit()); - } - - /// Completes a data transfer - /// - /// This function must be called after the EP0DATADONE event is raised - /// - /// # Panics - /// - /// This function panics if called before `start` or before the EP0DATADONE event is raised by - /// the hardware - pub fn end(&mut self, usbd: &USBD) { - if usbd.events_ep0datadone.read().bits() == 0 { - panic!("Ep0In.end called before the EP0DATADONE event was raised"); - } else { - // DMA transfer complete - dma_end(); - usbd.events_ep0datadone.reset(); - - self.busy = false; - defmt::println!("EP0IN: transfer done"); - } - } -} - -// memory barrier to synchronize the start of a DMA transfer (which will run in parallel) with the -// caller's memory operations -// -// This function call *must* be *followed* by a memory *store* operation. Memory operations that -// *precede* this function call will *not* be moved, by the compiler or the instruction pipeline, to -// *after* the function call. -fn dma_start() { - atomic::fence(Ordering::Release); -} - -// memory barrier to synchronize the end of a DMA transfer (which ran in parallel) to the caller's -// memory operations -// -// This function call *must* be *preceded* by a memory *load* operation. Memory operations that -// *follow* this function call will *not* be moved, by the compiler or the instruction pipeline, to -// *before* the function call. -fn dma_end() { - atomic::fence(Ordering::Acquire); -} - -/// Initializes the USBD peripheral -// NOTE will be called from user code; at that point the high frequency clock source has already -// been configured to use to the external crystal -// Reference: section 6.35.4 of the nRF52840 Product Specification -pub fn init(power: POWER, usbd: &USBD) { - let mut once = true; - - // wait until the USB cable has been connected - while power.events_usbdetected.read().bits() == 0 { - if once { - defmt::println!("waiting for USB connection on port J3"); - once = false; - } - - continue; - } - power.events_usbdetected.reset(); - - // workaround silicon bug - unsafe { errata::e187a() } - // enable the USB peripheral - usbd.enable.write(|w| w.enable().set_bit()); - - // wait for the peripheral to signal it has reached the READY state - while usbd.eventcause.read().ready().bit_is_clear() { - continue; - } - // write 1 to clear the flag - usbd.eventcause.write(|w| w.ready().set_bit()); - - // if EVENTCAUSE is all zeroes then also clear the USBEVENT register - if usbd.eventcause.read().bits() == 0 { - usbd.events_usbevent.reset(); - } - - // complete the silicon bug workaround - unsafe { errata::e187b() } - - // also need to wait for the USB power supply regulator to stabilize - while power.events_usbpwrrdy.read().bits() == 0 { - continue; - } - power.events_usbpwrrdy.reset(); - - // before returning unmask the relevant interrupts - usbd.intenset.write(|w| { - w.ep0datadone().set_bit(); - w.ep0setup().set_bit(); - w.usbreset().set_bit() - }); - - // enable the D+ line pull-up - usbd.usbpullup.write(|w| w.connect().set_bit()); -} - -/// Stalls endpoint 0 -pub fn ep0stall(usbd: &USBD) { - usbd.tasks_ep0stall.write(|w| w.tasks_ep0stall().set_bit()); -} - -/// USBD.EVENTS registers mapped to an enum -#[derive(Debug, defmt::Format)] -pub enum Event { - /// `EVENTS_USBRESET` register was active - UsbReset, - - /// `EVENTS_EP0DATADONE` register was active - UsbEp0DataDone, - - /// `EVENTS_EP0SETUP` register was active - UsbEp0Setup, -} - -/// Returns the next unhandled USB event; returns none if there's no event to handle -/// -/// NOTE this function will clear the corresponding the EVENT register (*) so the caller should -/// handle the returned event properly. Expect for USBEVENT and EP0DATADONE -pub fn next_event(usbd: &USBD) -> Option { - if usbd.events_usbreset.read().bits() != 0 { - usbd.events_usbreset.reset(); - - return Some(Event::UsbReset); - } - - if usbd.events_ep0datadone.read().bits() != 0 { - // this will be cleared by the `Ep0In.end` method - // usbd.events_ep0datadone.reset(); - - return Some(Event::UsbEp0DataDone); - } - - if usbd.events_ep0setup.read().bits() != 0 { - usbd.events_ep0setup.reset(); - - return Some(Event::UsbEp0Setup); - } - - None -} - -/// Reads the BMREQUESTTYPE register and returns the 8-bit BMREQUESTTYPE component of a setup packet -pub fn bmrequesttype(usbd: &USBD) -> u8 { - // read the 32-bit register and extract the least significant byte - // (the alternative is to read the 3 bitfields of the register and merge them into one byte) - usbd.bmrequesttype.read().bits() as u8 -} - -/// Reads the BREQUEST register and returns the 8-bit BREQUEST component of a setup packet -pub fn brequest(usbd: &USBD) -> u8 { - usbd.brequest.read().brequest().bits() -} - -/// Reads the WLENGTHL and WLENGTHH registers and returns the 16-bit WLENGTH component of a setup packet -pub fn wlength(usbd: &USBD) -> u16 { - u16::from(usbd.wlengthl.read().wlengthl().bits()) - | u16::from(usbd.wlengthh.read().wlengthh().bits()) << 8 -} - -/// Reads the WINDEXL and WINDEXH registers and returns the 16-bit WINDEX component of a setup packet -pub fn windex(usbd: &USBD) -> u16 { - u16::from(usbd.windexl.read().windexl().bits()) - | u16::from(usbd.windexh.read().windexh().bits()) << 8 -} - -/// Reads the WVALUEL and WVALUEH registers and returns the 16-bit WVALUE component of a setup packet -pub fn wvalue(usbd: &USBD) -> u16 { - u16::from(usbd.wvaluel.read().wvaluel().bits()) - | u16::from(usbd.wvalueh.read().wvalueh().bits()) << 8 -}