diff --git a/esp32s3-hal/Cargo.toml b/esp32s3-hal/Cargo.toml index 0d2c14af6..e706fa51a 100644 --- a/esp32s3-hal/Cargo.toml +++ b/esp32s3-hal/Cargo.toml @@ -29,6 +29,7 @@ embedded-hal = { version = "0.2", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.8" } xtensa-lx = { version = "0.7", features = ["esp32s3"] } xtensa-lx-rt = { version = "0.12", features = ["esp32s3"], optional = true } +r0 = { version = "1.0.0", optional = true } [dependencies.esp-hal-common] path = "../esp-hal-common" @@ -43,6 +44,7 @@ esp-println = { version = "0.2.0", features = ["esp32s3"] } [features] default = ["rt"] +direct-boot = ["r0"] eh1 = ["esp-hal-common/eh1"] rt = ["xtensa-lx-rt/esp32s3"] ufmt = ["esp-hal-common/ufmt"] diff --git a/esp32s3-hal/build.rs b/esp32s3-hal/build.rs index 72225eec4..d5b4221cd 100644 --- a/esp32s3-hal/build.rs +++ b/esp32s3-hal/build.rs @@ -1,5 +1,6 @@ use std::{env, fs::File, io::Write, path::PathBuf}; +#[cfg(not(feature = "direct-boot"))] fn main() { // Put the linker script somewhere the linker can find it let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); @@ -34,3 +35,39 @@ fn main() { // instead of when any part of the source code changes. println!("cargo:rerun-if-changed=memory.x"); } + +#[cfg(feature = "direct-boot")] +fn main() { + // Put the linker script somewhere the linker can find it + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("ld/db-memory.x")) + .unwrap(); + + File::create(out.join("alias.x")) + .unwrap() + .write_all(include_bytes!("ld/rom.x")) + .unwrap(); + + File::create(out.join("hal-defaults.x")) + .unwrap() + .write_all(include_bytes!("ld/hal-defaults.x")) + .unwrap(); + + File::create(out.join("esp32s3.x")) + .unwrap() + .write_all(include_bytes!("ld/db-esp32s3.x")) + .unwrap(); + + File::create(out.join("linkall.x")) + .unwrap() + .write_all(include_bytes!("ld/linkall.x")) + .unwrap(); + + println!("cargo:rustc-link-search={}", out.display()); + + // Only re-run the build script when memory.x is changed, + // instead of when any part of the source code changes. + println!("cargo:rerun-if-changed=memory.x"); +} diff --git a/esp32s3-hal/ld/db-esp32s3.x b/esp32s3-hal/ld/db-esp32s3.x new file mode 100644 index 000000000..28c8727be --- /dev/null +++ b/esp32s3-hal/ld/db-esp32s3.x @@ -0,0 +1,300 @@ +/* before memory.x to allow override */ +ENTRY(Reset) + +INCLUDE memory.x + +/* map generic regions to output sections */ +INCLUDE "alias.x" + +_external_ram_start = ABSOLUTE(ORIGIN(psram_seg)); +_external_ram_end = ABSOLUTE(ORIGIN(psram_seg)+LENGTH(psram_seg)); + +_heap_end = ABSOLUTE(ORIGIN(dram_seg))+LENGTH(dram_seg)+LENGTH(reserved_for_boot_seg) - 2*STACK_SIZE; +_text_heap_end = ABSOLUTE(ORIGIN(iram_seg)+LENGTH(iram_seg)); +_external_heap_end = ABSOLUTE(ORIGIN(psram_seg)+LENGTH(psram_seg)); + +_stack_start_cpu1 = _heap_end; +_stack_end_cpu1 = _stack_start_cpu1 + STACK_SIZE; +_stack_start_cpu0 = _stack_end_cpu1; +_stack_end_cpu0 = _stack_start_cpu0 + STACK_SIZE; + +EXTERN(DefaultHandler); + +INCLUDE "device.x" + +/* after memory.x to allow override */ +PROVIDE(__pre_init = DefaultPreInit); +PROVIDE(__zero_bss = default_mem_hook); +PROVIDE(__init_data = default_mem_hook); + +/*INCLUDE exception.x*/ +/* exception vector for the ESP32, requiring high priority interrupts and register window support */ + +/* high level exception/interrupt routines, which can be override with Rust functions */ +PROVIDE(__exception = __default_exception); +PROVIDE(__user_exception = __default_user_exception); +PROVIDE(__double_exception = __default_double_exception); +PROVIDE(__level_1_interrupt = __default_interrupt); +PROVIDE(__level_2_interrupt = __default_interrupt); +PROVIDE(__level_3_interrupt = __default_interrupt); +PROVIDE(__level_4_interrupt = __default_interrupt); +PROVIDE(__level_5_interrupt = __default_interrupt); +PROVIDE(__level_6_interrupt = __default_interrupt); +PROVIDE(__level_7_interrupt = __default_interrupt); + +/* low level exception/interrupt, which must be overridden using naked functions */ +PROVIDE(__naked_user_exception = __default_naked_exception); +PROVIDE(__naked_kernel_exception = __default_naked_exception); +PROVIDE(__naked_double_exception = __default_naked_double_exception); +PROVIDE(__naked_level_2_interrupt = __default_naked_level_2_interrupt); +PROVIDE(__naked_level_3_interrupt = __default_naked_level_3_interrupt); +PROVIDE(__naked_level_4_interrupt = __default_naked_level_4_interrupt); +PROVIDE(__naked_level_5_interrupt = __default_naked_level_5_interrupt); +PROVIDE(__naked_level_6_interrupt = __default_naked_level_6_interrupt); +PROVIDE(__naked_level_7_interrupt = __default_naked_level_7_interrupt); + +/* needed to force inclusion of the vectors */ +EXTERN(__default_exception); +EXTERN(__default_double_exception); +EXTERN(__default_interrupt); + +EXTERN(__default_naked_exception); +EXTERN(__default_naked_double_exception); +EXTERN(__default_naked_level_2_interrupt); +EXTERN(__default_naked_level_3_interrupt); +EXTERN(__default_naked_level_4_interrupt); +EXTERN(__default_naked_level_5_interrupt); +EXTERN(__default_naked_level_6_interrupt); +EXTERN(__default_naked_level_7_interrupt); + +SECTIONS { + .pre_header (NOLOAD) : AT(0) + { + . = . + 0x400; + } + + .header ORIGIN(ROTEXT) : AT(0x400) + { + LONG(0xaedb041d) + LONG(0xaedb041d) + } + + .text ORIGIN(ROTEXT) + 0x408 : AT(0x408) + { + _stext = .; + . = ALIGN (4); + _text_start = ABSOLUTE(.); + . = ALIGN (4); + KEEP(*(.init)); + *(.literal .text .literal.* .text.*) + . = ALIGN (4); + _text_end = ABSOLUTE(.); + _etext = .; + } + _text_size = _etext - _stext; + + .rodata ORIGIN(RODATA) + 0x408 + _text_size : AT(_text_size + SIZEOF(.header) + SIZEOF(.pre_header)) + { + _rodata_start = ABSOLUTE(.); + . = ALIGN (4); + *(.rodata .rodata.*) + . = ALIGN (4); + _rodata_end = ABSOLUTE(.); + } + + .rwtext ORIGIN(RWTEXT) + 0x408 + _text_size + SIZEOF(.rodata) : + AT(_text_size + SIZEOF(.header) + SIZEOF(.pre_header) + SIZEOF(.rodata)) + { + _irwtext = ORIGIN(RODATA) + 0x408 + _text_size + SIZEOF(.rodata); + _srwtext = .; + + . = ALIGN (4); + + . = ALIGN(0x1000); + _init_start = ABSOLUTE(.); + KEEP(*(.WindowOverflow4.text)); + . = ALIGN(64); + KEEP(*(.WindowUnderflow4.text)); + . = ALIGN(64); + KEEP(*(.WindowOverflow8.text)); + . = ALIGN(64); + KEEP(*(.WindowUnderflow8.text)); + . = ALIGN(64); + KEEP(*(.WindowOverflow12.text)); + . = ALIGN(64); + KEEP(*(.WindowUnderflow12.text)); + . = ALIGN(64); + KEEP(*(.Level2InterruptVector.text)); + . = ALIGN(64); + KEEP(*(.Level3InterruptVector.text)); + . = ALIGN(64); + KEEP(*(.Level4InterruptVector.text)); + . = ALIGN(64); + KEEP(*(.Level5InterruptVector.text)); + . = ALIGN(64); + KEEP(*(.DebugExceptionVector.text)); + . = ALIGN(64); + KEEP(*(.NMIExceptionVector.text)); + . = ALIGN(64); + KEEP(*(.KernelExceptionVector.text)); + . = ALIGN(64); + KEEP(*(.UserExceptionVector.text)); + . = ALIGN(128); + KEEP(*(.DoubleExceptionVector.text)); + . = ALIGN(0x400); + + _init_end = ABSOLUTE(.); + + *(.rwtext.literal .rwtext .rwtext.literal.* .rwtext.*) + + . = ALIGN (4); + _erwtext = .; + } + + .data ORIGIN(RWDATA) : + AT(_text_size + SIZEOF(.header) + SIZEOF(.pre_header) + SIZEOF(.rodata) + SIZEOF(.rwtext)) + { + _data_start = ABSOLUTE(.); + . = ALIGN (4); + *(.data .data.*) + . = ALIGN (4); + _data_end = ABSOLUTE(.); + } + + + /* LMA of .data */ + _sidata = ORIGIN(RODATA) + _text_size + SIZEOF(.header) + SIZEOF(.pre_header) + SIZEOF(.rodata) + SIZEOF(.rwtext); + + .bss (NOLOAD) : ALIGN(4) + { + _bss_start = ABSOLUTE(.); + . = ALIGN (4); + *(.bss .bss.* COMMON) + . = ALIGN (4); + _bss_end = ABSOLUTE(.); + } > RWDATA + + .noinit (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + *(.noinit .noinit.*) + . = ALIGN (4); + } > RWDATA + + .rtc_fast.text ORIGIN(rtc_fast_iram_seg) : + AT(_text_size + SIZEOF(.header) + SIZEOF(.pre_header) + SIZEOF(.rodata) + SIZEOF(.rwtext) ) + { + . = ALIGN(4); + _rtc_fast_text_start = ABSOLUTE(.); + *(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*) + . = ALIGN(4); + _rtc_fast_text_end = ABSOLUTE(.); + } + _irtc_fast_text = ORIGIN(RODATA) + _text_size + SIZEOF(.header) + SIZEOF(.pre_header) + SIZEOF(.rodata) + SIZEOF(.rwtext); + + .rtc_fast.data ORIGIN(rtc_fast_dram_seg) + SIZEOF(.rtc_fast.text) : + AT(_text_size + SIZEOF(.header) + SIZEOF(.pre_header) + SIZEOF(.rodata) + SIZEOF(.rwtext) + SIZEOF(.rtc_fast.text) ) + { + . = ALIGN(4); + _rtc_fast_data_start = ABSOLUTE(.); + *(.rtc_fast.data .rtc_fast.data.*) + . = ALIGN(4); + _rtc_fast_data_end = ABSOLUTE(.); + } + _irtc_fast_data = ORIGIN(RODATA) + _text_size + SIZEOF(.header) + SIZEOF(.pre_header) + SIZEOF(.rodata) + SIZEOF(.rwtext) + SIZEOF(.rtc_fast.text); + + .rtc_fast.bss ORIGIN(rtc_fast_dram_seg) + SIZEOF(.rtc_fast.text) + SIZEOF(.rtc_fast.data) (NOLOAD) : + AT(_text_size + SIZEOF(.header) + SIZEOF(.pre_header) + SIZEOF(.rodata) + + SIZEOF(.rwtext) + SIZEOF(.rtc_fast.text) + SIZEOF(.rtc_fast.data)) + { + . = ALIGN(4); + _rtc_fast_bss_start = ABSOLUTE(.); + *(.rtc_fast.bss .rtc_fast.bss.*) + . = ALIGN (4); + _rtc_fast_bss_end = ABSOLUTE(.); + } + + .rtc_fast.noinit ORIGIN(rtc_fast_dram_seg) + SIZEOF(.rtc_fast.text) + SIZEOF(.rtc_fast.data) + SIZEOF(.rtc_fast.bss) (NOLOAD) : + { + . = ALIGN(4); + *(.rtc_fast.noinit .rtc_fast.noinit.*) + . = ALIGN (4); + } + + .rtc_slow.text ORIGIN(rtc_slow_seg) : + AT(_text_size + SIZEOF(.header) + SIZEOF(.pre_header) + SIZEOF(.rodata) + SIZEOF(.rwtext) + + SIZEOF(.rtc_fast.text) + SIZEOF(.rtc_fast.data) + SIZEOF(.rtc_fast.bss)) + { + . = ALIGN(4); + _rtc_slow_text_start = ABSOLUTE(.); + *(.rtc_slow.literal .rtc_slow.text .rtc_slow.literal.* .rtc_slow.text.*) + . = ALIGN(4); + _rtc_slow_text_end = ABSOLUTE(.); + } + _irtc_slow_text = ORIGIN(RODATA) + _text_size + SIZEOF(.header) + SIZEOF(.pre_header) + SIZEOF(.rodata) + SIZEOF(.rwtext) + + SIZEOF(.rtc_fast.text) + SIZEOF(.rtc_fast.data) + SIZEOF(.rtc_fast.bss); + + .rtc_slow.data ORIGIN(rtc_slow_seg) + SIZEOF(.rtc_slow.text) : + AT(_text_size + SIZEOF(.header) + SIZEOF(.pre_header) + SIZEOF(.rodata) + SIZEOF(.rwtext) + + SIZEOF(.rtc_fast.text) + SIZEOF(.rtc_fast.data) + SIZEOF(.rtc_fast.bss) + SIZEOF(.rtc_slow.text)) + { + . = ALIGN(4); + _rtc_slow_data_start = ABSOLUTE(.); + *(.rtc_slow.data .rtc_slow.data.*) + . = ALIGN(4); + _rtc_slow_data_end = ABSOLUTE(.); + } + _irtc_slow_data = ORIGIN(RODATA) + _text_size + SIZEOF(.header) + SIZEOF(.pre_header) + SIZEOF(.rodata) + SIZEOF(.rwtext) + + SIZEOF(.rtc_fast.text) + SIZEOF(.rtc_fast.data) + SIZEOF(.rtc_fast.bss) + SIZEOF(.rtc_slow.text); + + .rtc_slow.bss ORIGIN(rtc_slow_seg) + SIZEOF(.rtc_slow.text) + SIZEOF(.rtc_slow.data) (NOLOAD) : + AT(_text_size + SIZEOF(.header) + SIZEOF(.pre_header) + SIZEOF(.rodata) + SIZEOF(.rwtext) + + SIZEOF(.rtc_fast.text) + SIZEOF(.rtc_fast.data) + SIZEOF(.rtc_fast.bss) + SIZEOF(.rtc_slow.text) + SIZEOF(.rtc_slow.data)) + { + . = ALIGN(4); + _rtc_slow_bss_start = ABSOLUTE(.); + *(.rtc_slow.bss .rtc_slow.bss.*) + . = ALIGN (4); + _rtc_slow_bss_end = ABSOLUTE(.); + } + + .rtc_slow.noinit ORIGIN(rtc_slow_seg) + SIZEOF(.rtc_slow.text) + SIZEOF(.rtc_slow.data) + SIZEOF(.rtc_slow.bss) (NOLOAD) : + { + . = ALIGN(4); + *(.rtc_slow.noinit .rtc_slow.noinit.*) + . = ALIGN (4); + } + + .external.data : + { + _external_data_start = ABSOLUTE(.); + . = ALIGN(4); + *(.external.data .external.data.*) + . = ALIGN (4); + _external_data_end = ABSOLUTE(.); + } > psram_seg AT > RODATA + + .external.bss (NOLOAD) : + { + _external_bss_start = ABSOLUTE(.); + . = ALIGN(4); + *(.external.bss .external.bss.*) + . = ALIGN (4); + _external_bss_end = ABSOLUTE(.); + } > psram_seg + + .external.noinit (NOLOAD) : + { + . = ALIGN(4); + *(.external.noinit .external.noinit.*) + . = ALIGN (4); + } > psram_seg + + /* must be last segment using psram_seg */ + .external_heap_start (NOLOAD) : + { + . = ALIGN (4); + _external_heap_start = ABSOLUTE(.); + . = ALIGN (4); + } > psram_seg +} diff --git a/esp32s3-hal/ld/db-memory.x b/esp32s3-hal/ld/db-memory.x new file mode 100644 index 000000000..9d0099ea5 --- /dev/null +++ b/esp32s3-hal/ld/db-memory.x @@ -0,0 +1,44 @@ +/* override entry point */ +ENTRY(ESP32Reset) + +/* reserved at the start of DRAM */ +RESERVE_DRAM = 0x8000; + +/* reserved at the start of the RTC memories for use by the ULP processor */ +RESERVE_RTC_FAST = 0; +RESERVE_RTC_SLOW = 0; + +/* define stack size for both cores */ +STACK_SIZE = 8k; + +/* Specify main memory areas */ +MEMORY +{ + iram_seg ( RX ) : ORIGIN = 0x40370400 + RESERVE_DRAM, len = 328k - 0x400 + + dram_seg ( RW ) : ORIGIN = 0x3FC80000 + RESERVE_DRAM, len = 328k - RESERVE_DRAM + reserved_for_boot_seg : ORIGIN = 0x3FFDC200, len = 144k /* ???? SRAM1; reserved for static ROM usage; can be used for heap */ + + /* external flash + The 0x20 offset is a convenience for the app binary image generation. + Flash cache has 64KB pages. The .bin file which is flashed to the chip + has a 0x18 byte file header, and each segment has a 0x08 byte segment + header. Setting this offset makes it simple to meet the flash cache MMU's + constraint that (paddr % 64KB == vaddr % 64KB).) + */ + irom_seg ( RX ) : ORIGIN = 0x42000000, len = 4M + drom_seg ( R ) : ORIGIN = 0x3C000000, len = 4M + + + /* RTC fast memory (executable). Persists over deep sleep. Only for core 0 (PRO_CPU) */ + rtc_fast_iram_seg(RWX) : ORIGIN = 0x600fe000, len = 8k + + /* RTC fast memory (same block as above), viewed from data bus. Only for core 0 (PRO_CPU) */ + rtc_fast_dram_seg(RW) : ORIGIN = 0x600fe000 + RESERVE_RTC_FAST, len = 8k - RESERVE_RTC_FAST + + /* RTC slow memory (data accessible). Persists over deep sleep. */ + rtc_slow_seg(RW) : ORIGIN = 0x50000000 + RESERVE_RTC_SLOW, len = 8k - RESERVE_RTC_SLOW + + /* external memory, including data and text */ + psram_seg(RWX) : ORIGIN = 0x3F500000, len = 0xA80000 /* ??? */ +} diff --git a/esp32s3-hal/src/lib.rs b/esp32s3-hal/src/lib.rs index dd8bcfa9e..c758b98ee 100644 --- a/esp32s3-hal/src/lib.rs +++ b/esp32s3-hal/src/lib.rs @@ -1,4 +1,6 @@ #![no_std] +#![cfg_attr(feature = "direct-boot", feature(naked_functions))] +#![cfg_attr(feature = "direct-boot", feature(asm_experimental_arch))] pub use embedded_hal as ehal; pub use esp_hal_common::{ @@ -34,6 +36,118 @@ pub mod gpio; #[no_mangle] extern "C" fn DefaultHandler(_level: u32, _interrupt: pac::Interrupt) {} +#[cfg(all(feature = "rt", feature = "direct-boot"))] +#[doc(hidden)] +#[no_mangle] +#[link_section = ".init"] +#[naked] +unsafe extern "C" fn init() { + core::arch::asm!("call0 startup_direct_boot", options(noreturn)); +} + +#[cfg(all(feature = "rt", feature = "direct-boot"))] +#[doc(hidden)] +#[no_mangle] +pub unsafe fn startup_direct_boot() -> ! { + // These symbols are from `memory.x` + extern "C" { + static mut _rtc_fast_bss_start: u32; + static mut _rtc_fast_bss_end: u32; + + static mut _rtc_slow_bss_start: u32; + static mut _rtc_slow_bss_end: u32; + + // Boundaries of the .rtc_fast.text section + static mut _rtc_fast_text_start: u32; + static mut _rtc_fast_text_end: u32; + static mut _irtc_fast_text: u32; + + // Boundaries of the .rtc_fast.data section + static mut _rtc_fast_data_start: u32; + static mut _rtc_fast_data_end: u32; + static mut _irtc_fast_data: u32; + + // Boundaries of the .rtc_slow.text section + static mut _rtc_slow_text_start: u32; + static mut _rtc_slow_text_end: u32; + static mut _irtc_slow_text: u32; + + // Boundaries of the .rtc_slow.data section + static mut _rtc_slow_data_start: u32; + static mut _rtc_slow_data_end: u32; + static mut _irtc_slow_data: u32; + + static mut _stack_end_cpu0: u32; + } + + // set stack pointer to end of memory: no need to retain stack up to this point + xtensa_lx::set_stack_pointer(&mut _stack_end_cpu0); + + // copy rtc data from flash to destinations + r0::init_data( + &mut _rtc_fast_data_start, + &mut _rtc_fast_data_end, + &_irtc_fast_data, + ); + + r0::init_data( + &mut _rtc_fast_text_start, + &mut _rtc_fast_text_end, + &_irtc_fast_text, + ); + + r0::init_data( + &mut _rtc_slow_data_start, + &mut _rtc_slow_data_end, + &_irtc_slow_data, + ); + + r0::init_data( + &mut _rtc_slow_text_start, + &mut _rtc_slow_text_end, + &_irtc_slow_text, + ); + + // Initialize RTC RAM + xtensa_lx_rt::zero_bss(&mut _rtc_fast_bss_start, &mut _rtc_fast_bss_end); + xtensa_lx_rt::zero_bss(&mut _rtc_slow_bss_start, &mut _rtc_slow_bss_end); + + // first of all copy rwtext + extern "C" { + // Boundaries of the .iram section + static mut _srwtext: u32; + static mut _erwtext: u32; + static mut _irwtext: u32; + } + r0::init_data(&mut _srwtext, &mut _erwtext, &_irwtext); + + // do some configurations for compatability with the 2nd stage bootloader + // this is a workaround and ideally we should deal with these settings in other + // places + (&*crate::pac::TIMG0::PTR) + .int_ena_timers + .modify(|_, w| w.t0_int_ena().set_bit().t1_int_ena().set_bit()); + (&*crate::pac::TIMG1::PTR) + .int_ena_timers + .modify(|_, w| w.t0_int_ena().set_bit().t1_int_ena().set_bit()); + + (&*crate::pac::RTC_CNTL::PTR) + .swd_wprotect + .write(|w| w.bits(0x8f1d312a)); + (&*crate::pac::RTC_CNTL::PTR) + .swd_conf + .modify(|_, w| w.swd_disable().set_bit()); + (&*crate::pac::RTC_CNTL::PTR) + .swd_wprotect + .write(|w| w.bits(0)); + + (&*crate::pac::SYSTEM::PTR) + .sysclk_conf + .modify(|_, w| w.soc_clk_sel().bits(1)); + + xtensa_lx_rt::Reset(); +} + #[cfg(feature = "rt")] #[doc(hidden)] #[link_section = ".rwtext"] @@ -107,7 +221,13 @@ pub unsafe extern "C" fn ESP32Reset() -> ! { #[no_mangle] #[rustfmt::skip] pub extern "Rust" fn __init_data() -> bool { - false + #[cfg(feature = "direct-boot")] + let res = true; + + #[cfg(not(feature = "direct-boot"))] + let res = false; + + res } fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 {