diff --git a/Cargo.lock b/Cargo.lock index d68143696..6c5afa2cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1780,6 +1780,7 @@ dependencies = [ "esp-hal-procmacros", "esp-metadata", "esp-riscv-rt", + "esp-synopsys-usb-otg", "esp32", "esp32c2", "esp32c3", @@ -1795,7 +1796,9 @@ dependencies = [ "riscv", "serde", "strum", + "usb-device", "void", + "xtensa-lx", "xtensa-lx-rt", ] @@ -1869,6 +1872,19 @@ dependencies = [ "riscv-rt-macros", ] +[[package]] +name = "esp-synopsys-usb-otg" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8938451cb19032f13365328ea66ab38c8d16deecdf322067442297110eb74468" +dependencies = [ + "critical-section", + "embedded-hal 0.2.7", + "ral-registers", + "usb-device", + "vcell", +] + [[package]] name = "esp-wifi" version = "0.8.0" @@ -2000,6 +2016,14 @@ dependencies = [ "riot-rs-rt", ] +[[package]] +name = "espressif-esp32-s3-wroom-1" +version = "0.1.0" +dependencies = [ + "riot-rs-debug", + "riot-rs-rt", +] + [[package]] name = "example-random" version = "0.1.0" @@ -3498,6 +3522,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" +[[package]] +name = "ral-registers" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46b71a9d9206e8b46714c74255adcaea8b11e0350c1d8456165073c3f75fc81a" + [[package]] name = "rand" version = "0.8.5" @@ -3632,6 +3662,7 @@ dependencies = [ "cfg-if", "cortex-m", "defmt", + "esp-hal", ] [[package]] @@ -3642,6 +3673,7 @@ dependencies = [ "cfg-if", "dwm1001", "espressif-esp32-c6-devkitc-1", + "espressif-esp32-s3-wroom-1", "linkme", "microbit", "microbit-v2", diff --git a/laze-project.yml b/laze-project.yml index e32bb8b77..86fad66b5 100644 --- a/laze-project.yml +++ b/laze-project.yml @@ -12,8 +12,6 @@ contexts: - ?defmt env: RUSTFLAGS: - - "-Clink-arg=--nmagic" - - "-Clink-arg=${LINK_ARG_PREFIX}--no-eh-frame-hdr" - --cfg builder=\"${builder}\" - --cfg context=\"${builder}\" - --cfg context=\"riot-rs\" @@ -63,7 +61,7 @@ contexts: pool: console always: true cmd: >- - cd ${relpath} && ${CARGO_ENV} cargo ${CARGO_ARGS} build --${PROFILE} ${FEATURES} + cd ${relpath} && ${CARGO_ENV} cargo ${CARGO_TOOLCHAIN} ${CARGO_ARGS} build --${PROFILE} ${FEATURES} && cp ${relroot}/${build-dir}/bin/${builder}/${app}/cargo/${RUSTC_TARGET}/${PROFILE}/${riot_binary} ${relroot}/${out} - name: GIT_DOWNLOAD @@ -77,13 +75,13 @@ contexts: cargo: cmd: - - cd ${relpath} && ${CARGO_ENV} cargo ${CARGO_ARGS} + - cd ${relpath} && ${CARGO_ENV} cargo ${CARGO_TOOLCHAIN} ${CARGO_ARGS} build: false run: build: false cmd: - - cd ${appdir} && ${CARGO_ENV} cargo ${CARGO_ARGS} run --${PROFILE} ${FEATURES} + - cd ${appdir} && ${CARGO_ENV} cargo ${CARGO_TOOLCHAIN} ${CARGO_ARGS} run --${PROFILE} ${FEATURES} cargo-test: cmd: @@ -92,7 +90,7 @@ contexts: debug: cmd: - - cd ${appdir} && ${CARGO_ENV} cargo ${CARGO_ARGS} run --${PROFILE} ${FEATURES} + - cd ${appdir} && ${CARGO_ENV} cargo ${CARGO_TOOLCHAIN} ${CARGO_ARGS} run --${PROFILE} ${FEATURES} build: false ignore_ctrl_c: true @@ -104,12 +102,12 @@ contexts: bloat: cmd: - - cd ${appdir} && ${CARGO_ENV} cargo ${CARGO_ARGS} bloat --${PROFILE} ${FEATURES} + - cd ${appdir} && ${CARGO_ENV} cargo ${CARGO_TOOLCHAIN} ${CARGO_ARGS} bloat --${PROFILE} ${FEATURES} build: false tree: cmd: - - cd ${appdir} && ${CARGO_ENV} cargo ${CARGO_ARGS} tree ${FEATURES} + - cd ${appdir} && ${CARGO_ENV} cargo ${CARGO_TOOLCHAIN} ${CARGO_ARGS} tree ${FEATURES} build: false flash: @@ -278,8 +276,6 @@ contexts: - --cfg context=\"esp\" # linkall first - -Clink-arg=-Tlinkall.x - - -Clink-arg=-Tlinkme-esp-fixup.x - - -Clink-arg=-Tlinkme.x # this might be needed for backtraces # - -C force-frame-pointers CARGO_ARGS: @@ -288,6 +284,8 @@ contexts: - name: esp32c3 parent: esp + selects: + - riscv env: RUSTFLAGS: - --cfg context=\"esp32c3\" @@ -296,12 +294,25 @@ contexts: - name: esp32c6 parent: esp + selects: + - riscv env: RUSTFLAGS: - --cfg context=\"esp32c6\" RUSTC_TARGET: riscv32imac-unknown-none-elf CARGO_TARGET_PREFIX: CARGO_TARGET_RISCV32IMAC_UNKNOWN_NONE_ELF + - name: esp32s3 + parent: esp + selects: + - xtensa + env: + CARGO_TOOLCHAIN: +esp + RUSTFLAGS: + - --cfg context=\"esp32s3\" + RUSTC_TARGET: xtensa-esp32s3-none-elf + CARGO_TARGET_PREFIX: CARGO_TARGET_XTENSA_ESP32S3_NONE_ELF + - name: stm32 help: STM32 support (based on embassy-stm32) parent: riot-rs @@ -358,6 +369,8 @@ modules: global: OBJCOPY: arm-none-eabi-objcopy RUSTFLAGS: + - -Clink-arg=--nmagic + - -Clink-arg=${LINK_ARG_PREFIX}--no-eh-frame-hdr - -Clink-arg=-Tlinkme.x - -Clink-arg=-Tlink.x - -Clink-arg=-Tdevice.x @@ -414,6 +427,18 @@ modules: RUSTFLAGS: - --cfg armv7m + - name: xtensa + env: + global: + RUSTFLAGS: + - --cfg context=\"xtensa\" + + - name: riscv + env: + global: + RUSTFLAGS: + - --cfg context=\"riscv\" + - name: rp-link-arg help: helper module that ensures link-rp.x is added behind cortex-m ld scripts env: @@ -630,6 +655,7 @@ modules: - nrf - rp - stm32 + - esp - name: wifi-esp context: @@ -776,6 +802,9 @@ builders: - name: espressif-esp32-c6-devkitc-1 parent: esp32c6 + - name: espressif-esp32-s3-wroom-1 + parent: esp32s3 + - name: nrf5340dk parent: nrf5340 diff --git a/src/riot-rs-bench/Cargo.toml b/src/riot-rs-bench/Cargo.toml index 5bcffaf36..75b519a65 100644 --- a/src/riot-rs-bench/Cargo.toml +++ b/src/riot-rs-bench/Cargo.toml @@ -15,3 +15,6 @@ defmt = { workspace = true, optional = true } [target.'cfg(context = "cortex-m")'.dependencies] cortex-m = { workspace = true, features = ["critical-section-single-core"] } + +[target.'cfg(context = "esp")'.dependencies] +esp-hal = { workspace = true } diff --git a/src/riot-rs-bench/src/esp.rs b/src/riot-rs-bench/src/esp.rs new file mode 100644 index 000000000..45cf14f6f --- /dev/null +++ b/src/riot-rs-bench/src/esp.rs @@ -0,0 +1,28 @@ +use esp_hal::{ + peripherals, + timer::systimer::{SystemTimer, Unit as _}, +}; + +use crate::Error; + +#[allow(missing_docs)] +pub fn benchmark ()>(iterations: usize, f: F) -> Result { + let mut systimer_periph = unsafe { peripherals::SYSTIMER::steal() }; + let timer = SystemTimer::new(&mut systimer_periph); + + // Reset counter of unit0, which is read in `SystemTimer::now()`. + timer.unit0.set_count(0); + + while SystemTimer::now() == 0 {} + + let before = SystemTimer::now(); + + for _ in 0..iterations { + f(); + } + + SystemTimer::now() + .checked_sub(before) + .map(|total| total as usize / iterations) + .ok_or(Error::SystemTimerWrapped) +} diff --git a/src/riot-rs-bench/src/lib.rs b/src/riot-rs-bench/src/lib.rs index 1f6a230bb..e58d1e7d6 100644 --- a/src/riot-rs-bench/src/lib.rs +++ b/src/riot-rs-bench/src/lib.rs @@ -9,8 +9,10 @@ cfg_if::cfg_if! { if #[cfg(context = "cortex-m")] { mod cortexm; use cortexm as bench; - } - else if #[cfg(context = "riot-rs")] { + } else if #[cfg(context = "esp")] { + mod esp; + use esp as bench; + } else if #[cfg(context = "riot-rs")] { // When run with laze but the architecture is not supported compile_error!("benchmarking is not supported for this architecture"); } else { diff --git a/src/riot-rs-boards/Cargo.toml b/src/riot-rs-boards/Cargo.toml index 93865777a..f65a6d990 100644 --- a/src/riot-rs-boards/Cargo.toml +++ b/src/riot-rs-boards/Cargo.toml @@ -16,6 +16,7 @@ riot-rs-rt = { path = "../riot-rs-rt" } ai-c3 = { optional = true, path = "ai-c3" } espressif-esp32-c6-devkitc-1 = { optional = true, path = "espressif-esp32-c6-devkitc-1" } +espressif-esp32-s3-wroom-1 = { optional = true, path = "espressif-esp32-s3-wroom-1" } dwm1001 = { optional = true, path = "dwm1001" } microbit = { optional = true, path = "microbit" } microbit-v2 = { optional = true, path = "microbit-v2" } diff --git a/src/riot-rs-boards/espressif-esp32-s3-wroom-1/Cargo.toml b/src/riot-rs-boards/espressif-esp32-s3-wroom-1/Cargo.toml new file mode 100644 index 000000000..6dcfe097b --- /dev/null +++ b/src/riot-rs-boards/espressif-esp32-s3-wroom-1/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "espressif-esp32-s3-wroom-1" +version = "0.1.0" +authors = ["Elena Frank "] +license.workspace = true +edition = "2021" + +[dependencies] +riot-rs-rt = { workspace = true, features = ["_esp32s3"] } +riot-rs-debug.workspace = true diff --git a/src/riot-rs-boards/espressif-esp32-s3-wroom-1/src/lib.rs b/src/riot-rs-boards/espressif-esp32-s3-wroom-1/src/lib.rs new file mode 100644 index 000000000..f2b338c79 --- /dev/null +++ b/src/riot-rs-boards/espressif-esp32-s3-wroom-1/src/lib.rs @@ -0,0 +1,7 @@ +#![no_std] + +use riot_rs_debug::log::debug; + +pub fn init() { + debug!("espressif-esp32-s3-wroom-1::init()"); +} diff --git a/src/riot-rs-boards/src/lib.rs b/src/riot-rs-boards/src/lib.rs index 37e836946..08739b75d 100644 --- a/src/riot-rs-boards/src/lib.rs +++ b/src/riot-rs-boards/src/lib.rs @@ -8,6 +8,8 @@ cfg_if! { pub use ai_c3 as board; } else if #[cfg(feature = "espressif-esp32-c6-devkitc-1")] { pub use espressif_esp32_c6_devkitc_1 as board; + } else if #[cfg(feature = "espressif-esp32-s3-wroom-1")] { + pub use espressif_esp32_s3_wroom_1 as board; } else if #[cfg(feature = "nrf52dk")] { pub use nrf52dk as board; } else if #[cfg(feature = "dwm1001")] { diff --git a/src/riot-rs-debug/Cargo.toml b/src/riot-rs-debug/Cargo.toml index 7db58f3f4..509239ea1 100644 --- a/src/riot-rs-debug/Cargo.toml +++ b/src/riot-rs-debug/Cargo.toml @@ -33,6 +33,9 @@ esp-println = { workspace = true, features = ["esp32c3"] } [target.'cfg(context = "esp32c6")'.dependencies] esp-println = { workspace = true, features = ["esp32c6"] } +[target.'cfg(context = "esp32s3")'.dependencies] +esp-println = { workspace = true, features = ["esp32s3"] } + [features] debug-console = [] defmt = ["dep:defmt", "esp-println?/defmt-espflash"] diff --git a/src/riot-rs-esp/Cargo.toml b/src/riot-rs-esp/Cargo.toml index 1482e78a9..d91b534bf 100644 --- a/src/riot-rs-esp/Cargo.toml +++ b/src/riot-rs-esp/Cargo.toml @@ -51,6 +51,15 @@ esp-wifi = { workspace = true, default-features = false, features = [ "esp32c6", ], optional = true } +[target.'cfg(context = "esp32s3")'.dependencies] +esp-hal = { workspace = true, features = ["esp32s3"] } +esp-hal-embassy = { workspace = true, default-features = false, features = [ + "esp32s3", +] } +esp-wifi = { workspace = true, default-features = false, features = [ + "esp32s3", +], optional = true } + [features] ## Enables GPIO interrupt support. external-interrupts = ["riot-rs-embassy-common/external-interrupts"] diff --git a/src/riot-rs-rt/Cargo.toml b/src/riot-rs-rt/Cargo.toml index 5c94b30c2..c407457a0 100644 --- a/src/riot-rs-rt/Cargo.toml +++ b/src/riot-rs-rt/Cargo.toml @@ -38,10 +38,11 @@ silent-panic = [] _panic-handler = [] # internal -# These two features are used by `build.rs`, which doesn't "see" context +# These features are used by `build.rs`, which doesn't "see" context # variables. _esp32c3 = [] _esp32c6 = [] +_esp32s3 = [] [dev-dependencies] riot-rs-boards = { path = "../riot-rs-boards" } diff --git a/src/riot-rs-rt/build.rs b/src/riot-rs-rt/build.rs index fcc8a4da3..058c357e9 100644 --- a/src/riot-rs-rt/build.rs +++ b/src/riot-rs-rt/build.rs @@ -8,13 +8,5 @@ fn main() { std::fs::copy("isr_stack.ld.in", out.join("isr_stack.x")).unwrap(); std::fs::copy("linkme.x", out.join("linkme.x")).unwrap(); - if env::var_os("CARGO_FEATURE__ESP32C3").is_some() { - std::fs::copy("linkme-esp32c3-fixup.x", out.join("linkme-esp-fixup.x")).unwrap(); - } - - if env::var_os("CARGO_FEATURE__ESP32C6").is_some() { - std::fs::copy("linkme-esp32c6-fixup.x", out.join("linkme-esp-fixup.x")).unwrap(); - } - println!("cargo:rustc-link-search={}", out.display()); } diff --git a/src/riot-rs-rt/linkme-esp32c3-fixup.x b/src/riot-rs-rt/linkme-esp32c3-fixup.x deleted file mode 100644 index e73dd91ed..000000000 --- a/src/riot-rs-rt/linkme-esp32c3-fixup.x +++ /dev/null @@ -1 +0,0 @@ -REGION_ALIAS("FLASH", IROM); diff --git a/src/riot-rs-rt/linkme-esp32c6-fixup.x b/src/riot-rs-rt/linkme-esp32c6-fixup.x deleted file mode 100644 index 352184123..000000000 --- a/src/riot-rs-rt/linkme-esp32c6-fixup.x +++ /dev/null @@ -1 +0,0 @@ -REGION_ALIAS("FLASH", ROM); diff --git a/src/riot-rs-threads/Cargo.toml b/src/riot-rs-threads/Cargo.toml index 7220f502c..b9e4fcdb4 100644 --- a/src/riot-rs-threads/Cargo.toml +++ b/src/riot-rs-threads/Cargo.toml @@ -26,6 +26,9 @@ esp-hal = { workspace = true, features = ["esp32c3"] } [target.'cfg(context = "esp32c6")'.dependencies] esp-hal = { workspace = true, features = ["esp32c6"] } +[target.'cfg(context = "esp32s3")'.dependencies] +esp-hal = { workspace = true, features = ["esp32s3"] } + [target.'cfg(context = "cortex-m")'.dependencies] # cortex-m specifics cortex-m.workspace = true diff --git a/src/riot-rs-threads/src/arch/mod.rs b/src/riot-rs-threads/src/arch/mod.rs index 9cd270e1f..eeff25352 100644 --- a/src/riot-rs-threads/src/arch/mod.rs +++ b/src/riot-rs-threads/src/arch/mod.rs @@ -28,12 +28,13 @@ cfg_if::cfg_if! { if #[cfg(context = "cortex-m")] { mod cortex_m; pub use cortex_m::Cpu; - } - else if #[cfg(any(context = "esp32c3", context = "esp32c6"))] { + } else if #[cfg(context = "riscv")] { mod riscv; pub use riscv::Cpu; - } - else { + } else if #[cfg(context = "xtensa")] { + mod xtensa; + pub use xtensa::Cpu; + } else { pub struct Cpu; impl Arch for Cpu { type ThreadData = (); diff --git a/src/riot-rs-threads/src/arch/riscv.rs b/src/riot-rs-threads/src/arch/riscv.rs index 413039940..7d36e2a67 100644 --- a/src/riot-rs-threads/src/arch/riscv.rs +++ b/src/riot-rs-threads/src/arch/riscv.rs @@ -42,7 +42,8 @@ impl Arch for Cpu { fn start_threading() { interrupt::disable(EspHalCpu::ProCpu, Interrupt::FROM_CPU_INTR1); Self::schedule(); - // TODO: handle unwrap error? + // Panics if `FROM_CPU_INTR1` is among `esp_hal::interrupt::RESERVED_INTERRUPTS`, + // which isn't the case. interrupt::enable(Interrupt::FROM_CPU_INTR1, interrupt::Priority::min()).unwrap(); } } diff --git a/src/riot-rs-threads/src/arch/xtensa.rs b/src/riot-rs-threads/src/arch/xtensa.rs new file mode 100644 index 000000000..329a5e899 --- /dev/null +++ b/src/riot-rs-threads/src/arch/xtensa.rs @@ -0,0 +1,141 @@ +use esp_hal::{ + interrupt, + peripherals::{Interrupt, SYSTEM}, + trapframe::TrapFrame, +}; + +use crate::{cleanup, Arch, THREADS}; + +pub struct Cpu; + +impl Arch for Cpu { + type ThreadData = TrapFrame; + const DEFAULT_THREAD_DATA: Self::ThreadData = default_trap_frame(); + + fn schedule() { + unsafe { + (&*SYSTEM::PTR) + .cpu_intr_from_cpu_1() + .modify(|_, w| w.cpu_intr_from_cpu_1().set_bit()); + } + } + + fn setup_stack(thread: &mut crate::thread::Thread, stack: &mut [u8], func: usize, arg: usize) { + let stack_start = stack.as_ptr() as usize; + let task_stack_ptr = stack_start + stack.len(); + // 16 byte alignment. + let stack_pos = task_stack_ptr - (task_stack_ptr % 0x10); + + thread.sp = stack_pos; + thread.data.A1 = stack_pos as u32; + thread.data.A6 = arg as u32; + // Usually A0 holds the return address. + // However, xtensa features so-called Windowed registers, which allow + // to shift the used registers when calling procedure. + // The xtensa-lx-rt does this when calling the exception handler using + // call4, which shifts the window by 4. + // See `xtensa_lx_rt::exception::asm::__default_naked_exception`. + // (At least that's what I assume is happening) + thread.data.A4 = cleanup as u32; + thread.data.PC = func as u32; + + // Copied from esp-wifi::preempt::preempt_xtensa + + // For windowed ABI set WOE and CALLINC (pretend task was 'call4'd). + thread.data.PS = 0x00040000 | (1 & 3) << 16; + } + + fn start_threading() { + interrupt::disable(esp_hal::Cpu::ProCpu, Interrupt::FROM_CPU_INTR1); + Self::schedule(); + // Panics if `FROM_CPU_INTR1` is among `esp_hal::interrupt::RESERVED_INTERRUPTS`, + // which isn't the case. + interrupt::enable(Interrupt::FROM_CPU_INTR1, interrupt::Priority::min()).unwrap(); + } +} + +const fn default_trap_frame() -> TrapFrame { + TrapFrame { + PC: 0, + PS: 0, + A0: 0, + A1: 0, + A2: 0, + A3: 0, + A4: 0, + A5: 0, + A6: 0, + A7: 0, + A8: 0, + A9: 0, + A10: 0, + A11: 0, + A12: 0, + A13: 0, + A14: 0, + A15: 0, + SAR: 0, + EXCCAUSE: 0, + EXCVADDR: 0, + LBEG: 0, + LEND: 0, + LCOUNT: 0, + THREADPTR: 0, + SCOMPARE1: 0, + BR: 0, + ACCLO: 0, + ACCHI: 0, + M0: 0, + M1: 0, + M2: 0, + M3: 0, + } +} + +/// Handler for software interrupt 0, which we use for context switching. +#[allow(non_snake_case)] +#[no_mangle] +extern "C" fn FROM_CPU_INTR1(trap_frame: &mut TrapFrame) { + unsafe { + // clear FROM_CPU_INTR1 + (&*SYSTEM::PTR) + .cpu_intr_from_cpu_1() + .modify(|_, w| w.cpu_intr_from_cpu_1().clear_bit()); + + sched(trap_frame) + } +} + +/// Probes the runqueue for the next thread and switches context if needed. +/// +/// # Safety +/// +/// This method might switch the current register state that is saved in the +/// `trap_frame`. +/// It should only be called from inside the trap handler that is responsible for +/// context switching. +unsafe fn sched(trap_frame: &mut TrapFrame) { + loop { + if THREADS.with_mut(|mut threads| { + let Some(next_pid) = threads.runqueue.get_next() else { + return false; + }; + + if let Some(current_pid) = threads.current_pid() { + if next_pid == current_pid { + return true; + } + threads.threads[usize::from(current_pid)].data = *trap_frame; + } + threads.current_thread = Some(next_pid); + *trap_frame = threads.threads[usize::from(next_pid)].data; + true + }) { + break; + } + // The esp-hal implementation of critical-section doesn't disable all interrupts. + // Thus we should release our hold on `THREADS` before we `waiti`, to prevent + // that another interrupt handler will try to borrow it while we still have it borrowed. + unsafe { core::arch::asm!("waiti 0") }; + } +} diff --git a/src/riot-rs-threads/src/lib.rs b/src/riot-rs-threads/src/lib.rs index ad3775a11..f60fe01bb 100644 --- a/src/riot-rs-threads/src/lib.rs +++ b/src/riot-rs-threads/src/lib.rs @@ -1,6 +1,7 @@ #![cfg_attr(not(test), no_std)] #![feature(naked_functions)] #![feature(used_with_arg)] +#![cfg_attr(target_arch = "xtensa", feature(asm_experimental_arch))] // Disable indexing lints for now, possible panics are documented or rely on internally-enforced // invariants #![allow(clippy::indexing_slicing)] diff --git a/tests/benchmarks/bench_sched_yield/laze.yml b/tests/benchmarks/bench_sched_yield/laze.yml index b8277bc23..ae6ddaa57 100644 --- a/tests/benchmarks/bench_sched_yield/laze.yml +++ b/tests/benchmarks/bench_sched_yield/laze.yml @@ -2,4 +2,5 @@ apps: - name: bench_sched_yield selects: - sw/benchmark + - executor-thread - ?release