diff --git a/embassy-hal-internal/src/atomic_ring_buffer.rs b/embassy-hal-internal/src/atomic_ring_buffer.rs index 34ceac852..40ad9dc7a 100644 --- a/embassy-hal-internal/src/atomic_ring_buffer.rs +++ b/embassy-hal-internal/src/atomic_ring_buffer.rs @@ -478,8 +478,12 @@ mod tests { #[test] fn zero_len() { + let mut b = [0; 0]; + let rb = RingBuffer::new(); unsafe { + rb.init(b.as_mut_ptr(), b.len()); + assert_eq!(rb.is_empty(), true); assert_eq!(rb.is_full(), true); diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 8ef68490b..c753d046c 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -52,6 +52,7 @@ trait SealedInstance { #[allow(unused)] fn regs() -> crate::pac::adc::Adc; #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] + #[allow(unused)] fn common_regs() -> crate::pac::adccommon::AdcCommon; #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] fn state() -> &'static State; diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index 9ac10e714..29b11735e 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs @@ -47,6 +47,7 @@ trait SealedInstance { } /// Enable global interrupt + #[allow(unused)] fn enable_irq(&self) { Self::regs().csr().modify(|v| v.set_ien(true)) } diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 398c3298f..c25ac4d24 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -969,17 +969,6 @@ fn finish_dma(regs: Regs) { }); } -trait RegsExt { - fn dr_ptr<W>(&self) -> *mut W; -} - -impl RegsExt for Regs { - fn dr_ptr<W>(&self) -> *mut W { - let dr = self.dr(); - dr.as_ptr() as *mut W - } -} - pub(crate) trait SealedInstance { const REGS: Regs; } diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index cf7a986d5..4882afd3e 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -177,9 +177,10 @@ embassy_time_queue_driver::timer_queue_impl!(static QUEUE: Queue = Queue::new()) #[cfg(test)] #[cfg(feature = "mock-driver")] mod tests { - use core::cell::Cell; - use core::task::{RawWaker, RawWakerVTable, Waker}; - use std::rc::Rc; + use core::sync::atomic::{AtomicBool, Ordering}; + use core::task::Waker; + use std::sync::Arc; + use std::task::Wake; use serial_test::serial; @@ -188,42 +189,26 @@ mod tests { use crate::{Duration, Instant}; struct TestWaker { - pub awoken: Rc<Cell<bool>>, - pub waker: Waker, + pub awoken: AtomicBool, } - impl TestWaker { - fn new() -> Self { - let flag = Rc::new(Cell::new(false)); - - const VTABLE: RawWakerVTable = RawWakerVTable::new( - |data: *const ()| { - unsafe { - Rc::increment_strong_count(data as *const Cell<bool>); - } - - RawWaker::new(data as _, &VTABLE) - }, - |data: *const ()| unsafe { - let data = data as *const Cell<bool>; - data.as_ref().unwrap().set(true); - Rc::decrement_strong_count(data); - }, - |data: *const ()| unsafe { - (data as *const Cell<bool>).as_ref().unwrap().set(true); - }, - |data: *const ()| unsafe { - Rc::decrement_strong_count(data); - }, - ); - - let raw = RawWaker::new(Rc::into_raw(flag.clone()) as _, &VTABLE); - - Self { - awoken: flag.clone(), - waker: unsafe { Waker::from_raw(raw) }, - } + impl Wake for TestWaker { + fn wake(self: Arc<Self>) { + self.awoken.store(true, Ordering::Relaxed); } + + fn wake_by_ref(self: &Arc<Self>) { + self.awoken.store(true, Ordering::Relaxed); + } + } + + fn test_waker() -> (Arc<TestWaker>, Waker) { + let arc = Arc::new(TestWaker { + awoken: AtomicBool::new(false), + }); + let waker = Waker::from(arc.clone()); + + (arc, waker) } fn setup() { @@ -249,11 +234,11 @@ mod tests { assert_eq!(queue_len(), 0); - let waker = TestWaker::new(); + let (flag, waker) = test_waker(); - QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); + QUEUE.schedule_wake(Instant::from_secs(1), &waker); - assert!(!waker.awoken.get()); + assert!(!flag.awoken.load(Ordering::Relaxed)); assert_eq!(queue_len(), 1); } @@ -262,23 +247,23 @@ mod tests { fn test_schedule_same() { setup(); - let waker = TestWaker::new(); + let (_flag, waker) = test_waker(); - QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); + QUEUE.schedule_wake(Instant::from_secs(1), &waker); assert_eq!(queue_len(), 1); - QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); + QUEUE.schedule_wake(Instant::from_secs(1), &waker); assert_eq!(queue_len(), 1); - QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); + QUEUE.schedule_wake(Instant::from_secs(100), &waker); assert_eq!(queue_len(), 1); - let waker2 = TestWaker::new(); + let (_flag2, waker2) = test_waker(); - QUEUE.schedule_wake(Instant::from_secs(100), &waker2.waker); + QUEUE.schedule_wake(Instant::from_secs(100), &waker2); assert_eq!(queue_len(), 2); } @@ -288,21 +273,21 @@ mod tests { fn test_trigger() { setup(); - let waker = TestWaker::new(); + let (flag, waker) = test_waker(); - QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); + QUEUE.schedule_wake(Instant::from_secs(100), &waker); - assert!(!waker.awoken.get()); + assert!(!flag.awoken.load(Ordering::Relaxed)); MockDriver::get().advance(Duration::from_secs(99)); - assert!(!waker.awoken.get()); + assert!(!flag.awoken.load(Ordering::Relaxed)); assert_eq!(queue_len(), 1); MockDriver::get().advance(Duration::from_secs(1)); - assert!(waker.awoken.get()); + assert!(flag.awoken.load(Ordering::Relaxed)); assert_eq!(queue_len(), 0); } @@ -312,18 +297,18 @@ mod tests { fn test_immediate_trigger() { setup(); - let waker = TestWaker::new(); + let (flag, waker) = test_waker(); - QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); + QUEUE.schedule_wake(Instant::from_secs(100), &waker); MockDriver::get().advance(Duration::from_secs(50)); - let waker2 = TestWaker::new(); + let (flag2, waker2) = test_waker(); - QUEUE.schedule_wake(Instant::from_secs(40), &waker2.waker); + QUEUE.schedule_wake(Instant::from_secs(40), &waker2); - assert!(!waker.awoken.get()); - assert!(waker2.awoken.get()); + assert!(!flag.awoken.load(Ordering::Relaxed)); + assert!(flag2.awoken.load(Ordering::Relaxed)); assert_eq!(queue_len(), 1); } @@ -333,30 +318,31 @@ mod tests { setup(); for i in 1..super::QUEUE_SIZE { - let waker = TestWaker::new(); + let (flag, waker) = test_waker(); - QUEUE.schedule_wake(Instant::from_secs(310), &waker.waker); + QUEUE.schedule_wake(Instant::from_secs(310), &waker); assert_eq!(queue_len(), i); - assert!(!waker.awoken.get()); + assert!(!flag.awoken.load(Ordering::Relaxed)); } - let first_waker = TestWaker::new(); + let (flag, waker) = test_waker(); - QUEUE.schedule_wake(Instant::from_secs(300), &first_waker.waker); + QUEUE.schedule_wake(Instant::from_secs(300), &waker); assert_eq!(queue_len(), super::QUEUE_SIZE); - assert!(!first_waker.awoken.get()); + assert!(!flag.awoken.load(Ordering::Relaxed)); - let second_waker = TestWaker::new(); + let (flag2, waker2) = test_waker(); - QUEUE.schedule_wake(Instant::from_secs(305), &second_waker.waker); + QUEUE.schedule_wake(Instant::from_secs(305), &waker2); assert_eq!(queue_len(), super::QUEUE_SIZE); - assert!(first_waker.awoken.get()); + assert!(flag.awoken.load(Ordering::Relaxed)); - QUEUE.schedule_wake(Instant::from_secs(320), &TestWaker::new().waker); + let (_flag3, waker3) = test_waker(); + QUEUE.schedule_wake(Instant::from_secs(320), &waker3); assert_eq!(queue_len(), super::QUEUE_SIZE); - assert!(second_waker.awoken.get()); + assert!(flag2.awoken.load(Ordering::Relaxed)); } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2f5d17069..57185e217 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.77" +channel = "1.78" components = [ "rust-src", "rustfmt", "llvm-tools" ] targets = [ "thumbv7em-none-eabi", diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml index 38fb2deec..94eda3c09 100644 --- a/tests/riscv32/Cargo.toml +++ b/tests/riscv32/Cargo.toml @@ -11,8 +11,8 @@ embassy-executor = { version = "0.5.0", path = "../../embassy-executor", feature embassy-time = { version = "0.3.0", path = "../../embassy-time" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -riscv-rt = "0.11" -riscv = { version = "0.10", features = ["critical-section-single-hart"] } +riscv-rt = "0.12.2" +riscv = { version = "0.11.1", features = ["critical-section-single-hart"] } [profile.dev] diff --git a/tests/riscv32/link.x b/tests/riscv32/link.x new file mode 100644 index 000000000..4076b0c68 --- /dev/null +++ b/tests/riscv32/link.x @@ -0,0 +1,214 @@ +/* # EMBASSY notes + This file is a workaround for https://github.com/rust-embedded/riscv/issues/196 + Remove when fixed upstream. +*/ +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut _heap_size }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol if not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- In this linker script, you may find symbols that look like `${...}` (e.g., `4`). + These are wildcards used by the `build.rs` script to adapt to different target particularities. + Check `build.rs` for more details about these symbols. + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all `4`-byte aligned. These alignments are assumed by the RAM + initialization routine. There's also a second benefit: `4`-byte aligned boundaries + means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +PROVIDE(_stext = ORIGIN(REGION_TEXT)); +PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(InstructionMisaligned = ExceptionHandler); +PROVIDE(InstructionFault = ExceptionHandler); +PROVIDE(IllegalInstruction = ExceptionHandler); +PROVIDE(Breakpoint = ExceptionHandler); +PROVIDE(LoadMisaligned = ExceptionHandler); +PROVIDE(LoadFault = ExceptionHandler); +PROVIDE(StoreMisaligned = ExceptionHandler); +PROVIDE(StoreFault = ExceptionHandler);; +PROVIDE(UserEnvCall = ExceptionHandler); +PROVIDE(SupervisorEnvCall = ExceptionHandler); +PROVIDE(MachineEnvCall = ExceptionHandler); +PROVIDE(InstructionPageFault = ExceptionHandler); +PROVIDE(LoadPageFault = ExceptionHandler); +PROVIDE(StorePageFault = ExceptionHandler); + +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +/* # Start trap function override + By default uses the riscv crates default trap handler + but by providing the `_start_trap` symbol external crates can override. +*/ +PROVIDE(_start_trap = default_start_trap); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > REGION_TEXT + + .text _stext : + { + /* Put reset handler first in .text section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + *(.trap); + *(.trap.rust); + *(.text.abort); + *(.text .text.*); + } > REGION_TEXT + + .eh_frame : { KEEP(*(.eh_frame)) } > REGION_TEXT + .eh_frame_hdr : { *(.eh_frame_hdr) } > REGION_TEXT + + .rodata : ALIGN(4) + { + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + } > REGION_RODATA + + .data : ALIGN(4) + { + _sidata = LOADADDR(.data); + _sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(4); + _edata = .; + } > REGION_DATA AT > REGION_RODATA + + .bss (NOLOAD) : ALIGN(4) + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(4); + _ebss = .; + } > REGION_BSS + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > REGION_HEAP + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > REGION_STACK + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } +} + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_DATA) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_DATA must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_STACK) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 4 == 0 && _edata % 4 == 0, " +BUG(riscv-rt): .data is not 4-byte aligned"); + +ASSERT(_sidata % 4 == 0, " +BUG(riscv-rt): the LMA of .data is not 4-byte aligned"); + +ASSERT(_sbss % 4 == 0 && _ebss % 4 == 0, " +BUG(riscv-rt): .bss is not 4-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), " +ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region. +Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */