From 545f997b071d2c9a99ccadc51fe337c2c12e4440 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Tue, 15 Feb 2022 10:59:30 +0100 Subject: [PATCH] Support ESP32S3 --- .github/workflows/ci.yml | 2 +- esp-hal-common/src/interrupt/xtensa.rs | 35 +++--- esp32s3-hal/.cargo/config.toml | 2 +- esp32s3-hal/build.rs | 12 ++ esp32s3-hal/esp32s3.x | 110 +++++++++++++++++ esp32s3-hal/examples/hello_world.rs | 27 ++++ esp32s3-hal/examples/ram.rs | 91 ++++++++++++++ esp32s3-hal/hal-defaults.x | 7 ++ esp32s3-hal/memory.x | 163 +++++++++++++++++++++++++ esp32s3-hal/rom.x | 2 + esp32s3-hal/src/lib.rs | 88 ++++++++++++- 11 files changed, 522 insertions(+), 17 deletions(-) create mode 100644 esp32s3-hal/esp32s3.x create mode 100644 esp32s3-hal/examples/hello_world.rs create mode 100644 esp32s3-hal/examples/ram.rs create mode 100644 esp32s3-hal/hal-defaults.x diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 193252824..a1fa41224 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - chip: [esp32, esp32s2] + chip: [esp32, esp32s2, esp32s3] steps: - uses: actions/checkout@v2 - uses: esp-rs/xtensa-toolchain@afb2ca0c7eefb637832d240ae357b820a1edc2c7 diff --git a/esp-hal-common/src/interrupt/xtensa.rs b/esp-hal-common/src/interrupt/xtensa.rs index eba47acb9..8f4608c0a 100644 --- a/esp-hal-common/src/interrupt/xtensa.rs +++ b/esp-hal-common/src/interrupt/xtensa.rs @@ -172,37 +172,44 @@ unsafe fn core1_interrupt_peripheral() -> *const crate::pac::interrupt_core1::Re crate::pac::INTERRUPT_CORE1::ptr() } -#[xtensa_lx_rt::interrupt(1)] -fn _level1_interrupt() { +#[no_mangle] +#[link_section = ".rwtext"] +fn __level1_interrupt() { unsafe { level1_interrupt() }; } -#[xtensa_lx_rt::interrupt(2)] -fn _level2_interrupt() { +#[no_mangle] +#[link_section = ".rwtext"] +fn __level2_interrupt() { unsafe { level2_interrupt() }; } -#[xtensa_lx_rt::interrupt(3)] -fn _level3_interrupt() { +#[no_mangle] +#[link_section = ".rwtext"] +fn __level3_interrupt() { unsafe { level3_interrupt() }; } -#[xtensa_lx_rt::interrupt(4)] -fn _level4_interrupt() { +#[no_mangle] +#[link_section = ".rwtext"] +fn __level4_interrupt() { unsafe { level4_interrupt() }; } -#[xtensa_lx_rt::interrupt(5)] -fn _level5_interrupt() { +#[no_mangle] +#[link_section = ".rwtext"] +fn __level5_interrupt() { unsafe { level5_interrupt() }; } -#[xtensa_lx_rt::interrupt(6)] -fn _level6_interrupt() { +#[no_mangle] +#[link_section = ".rwtext"] +fn __level6_interrupt() { unsafe { level6_interrupt() }; } -#[xtensa_lx_rt::interrupt(7)] -fn _level7_interrupt() { +#[no_mangle] +#[link_section = ".rwtext"] +fn __level7_interrupt() { unsafe { level7_interrupt() }; } diff --git a/esp32s3-hal/.cargo/config.toml b/esp32s3-hal/.cargo/config.toml index 6dae2c109..9b1b1e675 100644 --- a/esp32s3-hal/.cargo/config.toml +++ b/esp32s3-hal/.cargo/config.toml @@ -4,7 +4,7 @@ runner = "xtensa-esp32s3-elf-gdb -q -x xtensa.gdb" [build] rustflags = [ "-C", "link-arg=-nostartfiles", - "-C", "link-arg=-Wl,-Tlink.x", + "-C", "link-arg=-Wl,-Tesp32s3.x", ] target = "xtensa-esp32s3-none-elf" diff --git a/esp32s3-hal/build.rs b/esp32s3-hal/build.rs index bd5affeec..970b42d9f 100644 --- a/esp32s3-hal/build.rs +++ b/esp32s3-hal/build.rs @@ -13,6 +13,18 @@ fn main() { .write_all(include_bytes!("rom.x")) .unwrap(); + File::create(out.join("hal-defaults.x")) + .unwrap() + .write_all(include_bytes!("hal-defaults.x")) + .unwrap(); + + File::create(out.join("esp32s3.x")) + .unwrap() + .write_all(include_bytes!("esp32s3.x")) + .unwrap(); + + println!("cargo:rustc-link-arg=-Thal-defaults.x"); + println!("cargo:rustc-link-search={}", out.display()); // Only re-run the build script when memory.x is changed, diff --git a/esp32s3-hal/esp32s3.x b/esp32s3-hal/esp32s3.x new file mode 100644 index 000000000..cc727c93c --- /dev/null +++ b/esp32s3-hal/esp32s3.x @@ -0,0 +1,110 @@ + +/* before memory.x to allow override */ +ENTRY(Reset) + +INCLUDE memory.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 + +SECTIONS { + .text : ALIGN(4) + { + _stext = .; + . = ALIGN (4); + _text_start = ABSOLUTE(.); + . = ALIGN (4); + *(.literal .text .literal.* .text.*) + _text_end = ABSOLUTE(.); + _etext = .; + } > ROTEXT + + .rodata_dummy (NOLOAD) : + { + /* This dummy section represents the .flash.text section but in RODATA. + * Thus, it must have its alignment and (at least) its size. + */ + + /* Start at the same alignment constraint than .flash.text */ + + . = ALIGN(ALIGNOF(.text)); + + /* Create an empty gap as big as .text section */ + + . = SIZEOF(.text); + + /* Prepare the alignment of the section above. Few bytes (0x20) must be + * added for the mapping header. + */ + + . = ALIGN(0x10000) + 0x20; + _rodata_reserved_start = .; + } > RODATA + + .rodata : ALIGN(4) + { + _rodata_start = ABSOLUTE(.); + . = ALIGN (4); + *(.rodata .rodata.*) + _rodata_end = ABSOLUTE(.); + } > RODATA + + + .rwtext : ALIGN(4) + { + . = ALIGN (4); + *(.rwtext.literal .rwtext .rwtext.literal.* .rwtext.*) + } > RWTEXT + + .rwdata_dummy (NOLOAD) : + { + /* This dummy section represents the .rwtext section but in RWDATA. + * Thus, it must have its alignment and (at least) its size. + */ + + /* Start at the same alignment constraint than .flash.text */ + + . = ALIGN(ALIGNOF(.rwtext)); + + /* Create an empty gap as big as .text section */ + + . = SIZEOF(.rwtext); + + /* Prepare the alignment of the section above. Few bytes (0x20) must be + * added for the mapping header. + */ + + . = ALIGN(0x10000) + 0x20; + _rwdata_reserved_start = .; + } > RWDATA + + .data : ALIGN(4) + { + _data_start = ABSOLUTE(.); + . = ALIGN (4); + *(.data .data.*) + _data_end = ABSOLUTE(.); + } > RWDATA AT > RODATA + + /* LMA of .data */ + _sidata = LOADADDR(.data); + + .bss (NOLOAD) : ALIGN(4) + { + _bss_start = ABSOLUTE(.); + . = ALIGN (4); + *(.bss .bss.* COMMON) + _bss_end = ABSOLUTE(.); + } > RWDATA + + .noinit (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + *(.noinit .noinit.*) + } > RWDATA + +} \ No newline at end of file diff --git a/esp32s3-hal/examples/hello_world.rs b/esp32s3-hal/examples/hello_world.rs new file mode 100644 index 000000000..d3c1d9054 --- /dev/null +++ b/esp32s3-hal/examples/hello_world.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] + +use core::fmt::Write; + +use esp32s3_hal::{pac::Peripherals, prelude::*, Serial, Timer}; +use nb::block; +use panic_halt as _; +use xtensa_lx_rt::entry; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + + let mut timer0 = Timer::new(peripherals.TIMG0); + let mut serial0 = Serial::new(peripherals.UART0).unwrap(); + + // Disable watchdog timer + timer0.disable(); + + timer0.start(40_000_000u64); + + loop { + writeln!(serial0, "Hello world!").unwrap(); + block!(timer0.wait()).unwrap(); + } +} diff --git a/esp32s3-hal/examples/ram.rs b/esp32s3-hal/examples/ram.rs new file mode 100644 index 000000000..0376355f2 --- /dev/null +++ b/esp32s3-hal/examples/ram.rs @@ -0,0 +1,91 @@ +#![no_std] +#![no_main] + +use core::fmt::Write; + +use esp32s3_hal::{ + pac::{Peripherals, UART0}, + prelude::*, + ram, + Serial, + Timer, +}; +use nb::block; +use panic_halt as _; +use xtensa_lx_rt::entry; + +#[ram(rtc_fast)] +static mut SOME_INITED_DATA: [u8; 2] = [0xaa, 0xbb]; + +#[ram(rtc_fast, uninitialized)] +static mut SOME_UNINITED_DATA: [u8; 2] = [0; 2]; + +#[ram(rtc_fast, zeroed)] +static mut SOME_ZEROED_DATA: [u8; 8] = [0; 8]; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + + let mut timer0 = Timer::new(peripherals.TIMG0); + let mut serial0 = Serial::new(peripherals.UART0).unwrap(); + + // Disable watchdog timer + timer0.disable(); + + timer0.start(10_000_000u64); + + writeln!( + serial0, + "IRAM function located at {:p}", + function_in_ram as *const () + ) + .unwrap(); + unsafe { + writeln!(serial0, "SOME_INITED_DATA {:x?}", SOME_INITED_DATA).unwrap(); + writeln!(serial0, "SOME_UNINITED_DATA {:x?}", SOME_UNINITED_DATA).unwrap(); + writeln!(serial0, "SOME_ZEROED_DATA {:x?}", SOME_ZEROED_DATA).unwrap(); + + SOME_INITED_DATA[0] = 0xff; + SOME_ZEROED_DATA[0] = 0xff; + + writeln!(serial0, "SOME_INITED_DATA {:x?}", SOME_INITED_DATA).unwrap(); + writeln!(serial0, "SOME_UNINITED_DATA {:x?}", SOME_UNINITED_DATA).unwrap(); + writeln!(serial0, "SOME_ZEROED_DATA {:x?}", SOME_ZEROED_DATA).unwrap(); + + if SOME_UNINITED_DATA[0] != 0 { + SOME_UNINITED_DATA[0] = 0; + SOME_UNINITED_DATA[1] = 0; + } + + if SOME_UNINITED_DATA[1] == 0xff { + SOME_UNINITED_DATA[1] = 0; + } + + writeln!(serial0, "Counter {}", SOME_UNINITED_DATA[1]).unwrap(); + SOME_UNINITED_DATA[1] += 1; + } + + writeln!( + serial0, + "RTC_FAST function located at {:p}", + function_in_rtc_ram as *const () + ) + .unwrap(); + writeln!(serial0, "Result {}", function_in_rtc_ram()).unwrap(); + + loop { + function_in_ram(&mut serial0); + block!(timer0.wait()).unwrap(); + } +} + +#[ram] +fn function_in_ram(serial0: &mut Serial) { + writeln!(serial0, "Hello world!").unwrap(); +} + +#[ram(rtc_fast)] +fn function_in_rtc_ram() -> u32 { + 42 +} diff --git a/esp32s3-hal/hal-defaults.x b/esp32s3-hal/hal-defaults.x new file mode 100644 index 000000000..f58999823 --- /dev/null +++ b/esp32s3-hal/hal-defaults.x @@ -0,0 +1,7 @@ +PROVIDE(level1_interrupt = DefaultHandler); +PROVIDE(level2_interrupt = DefaultHandler); +PROVIDE(level3_interrupt = DefaultHandler); +PROVIDE(level4_interrupt = DefaultHandler); +PROVIDE(level5_interrupt = DefaultHandler); +PROVIDE(level6_interrupt = DefaultHandler); +PROVIDE(level7_interrupt = DefaultHandler); diff --git a/esp32s3-hal/memory.x b/esp32s3-hal/memory.x index e69de29bb..4f763a1c5 100644 --- a/esp32s3-hal/memory.x +++ b/esp32s3-hal/memory.x @@ -0,0 +1,163 @@ +/* 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 +{ + vectors_seg ( RX ) : ORIGIN = 0x40370000 + RESERVE_DRAM, len = 1k + 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 = 0x42000020, len = 4M - 0x20 + drom_seg ( R ) : ORIGIN = 0x3C000020, len = 4M - 0x20 + + + /* 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 /* ??? */ +} + +/* map generic regions to output sections */ +INCLUDE "alias.x" + +/* esp32 specific regions */ +SECTIONS { + .rtc_fast.text : { + . = ALIGN(4); + *(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*) + } > rtc_fast_iram_seg AT > RODATA + + /* + This section is required to skip rtc.text area because rtc_iram_seg and + rtc_data_seg are reflect the same address space on different buses. + */ + .rtc_fast.dummy (NOLOAD) : + { + _rtc_dummy_start = ABSOLUTE(.); /* needed to make section proper size */ + . = SIZEOF(.rtc_fast.text); + _rtc_dummy_end = ABSOLUTE(.); /* needed to make section proper size */ + } > rtc_fast_dram_seg + + + .rtc_fast.data : + { + . = ALIGN(4); + _rtc_fast_data_start = ABSOLUTE(.); + *(.rtc_fast.data .rtc_fast.data.*) + _rtc_fast_data_end = ABSOLUTE(.); + } > rtc_fast_dram_seg AT > RODATA + + .rtc_fast.bss (NOLOAD) : + { + . = ALIGN(4); + _rtc_fast_bss_start = ABSOLUTE(.); + *(.rtc_fast.bss .rtc_fast.bss.*) + _rtc_fast_bss_end = ABSOLUTE(.); + } > rtc_fast_dram_seg + + .rtc_fast.noinit (NOLOAD) : + { + . = ALIGN(4); + *(.rtc_fast.noinit .rtc_fast.noinit.*) + } > rtc_fast_dram_seg + + + .rtc_slow.text : { + . = ALIGN(4); + *(.rtc_slow.literal .rtc_slow.text .rtc_slow.literal.* .rtc_slow.text.*) + } > rtc_slow_seg AT > RODATA + + .rtc_slow.data : + { + . = ALIGN(4); + _rtc_slow_data_start = ABSOLUTE(.); + *(.rtc_slow.data .rtc_slow.data.*) + _rtc_slow_data_end = ABSOLUTE(.); + } > rtc_slow_seg AT > RODATA + + .rtc_slow.bss (NOLOAD) : + { + . = ALIGN(4); + _rtc_slow_bss_start = ABSOLUTE(.); + *(.rtc_slow.bss .rtc_slow.bss.*) + _rtc_slow_bss_end = ABSOLUTE(.); + } > rtc_slow_seg + + .rtc_slow.noinit (NOLOAD) : + { + . = ALIGN(4); + *(.rtc_slow.noinit .rtc_slow.noinit.*) + } > rtc_slow_seg + + .external.data : + { + _external_data_start = ABSOLUTE(.); + . = ALIGN(4); + *(.external.data .external.data.*) + _external_data_end = ABSOLUTE(.); + } > psram_seg AT > RODATA + + .external.bss (NOLOAD) : + { + _external_bss_start = ABSOLUTE(.); + . = ALIGN(4); + *(.external.bss .external.bss.*) + _external_bss_end = ABSOLUTE(.); + } > psram_seg + + .external.noinit (NOLOAD) : + { + . = ALIGN(4); + *(.external.noinit .external.noinit.*) + } > psram_seg + + /* must be last segment using psram_seg */ + .external_heap_start (NOLOAD) : + { + . = ALIGN (4); + _external_heap_start = ABSOLUTE(.); + } > psram_seg +} + +_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" diff --git a/esp32s3-hal/rom.x b/esp32s3-hal/rom.x index af743b0cb..e70d54ac4 100644 --- a/esp32s3-hal/rom.x +++ b/esp32s3-hal/rom.x @@ -2,3 +2,5 @@ REGION_ALIAS("ROTEXT", irom_seg); REGION_ALIAS("RWTEXT", iram_seg); REGION_ALIAS("RODATA", drom_seg); REGION_ALIAS("RWDATA", dram_seg); + +PROVIDE( rom_config_instruction_cache_mode = 0x40001a1c ); diff --git a/esp32s3-hal/src/lib.rs b/esp32s3-hal/src/lib.rs index f56ba41af..90d05d1d4 100644 --- a/esp32s3-hal/src/lib.rs +++ b/esp32s3-hal/src/lib.rs @@ -1,4 +1,90 @@ #![no_std] pub use embedded_hal as ehal; -pub use esp_hal_common::{pac, prelude, Delay, Serial, Timer}; +pub use esp_hal_common::{pac, prelude, ram, Delay, Serial, Timer}; + +#[no_mangle] +extern "C" fn DefaultHandler(_level: u32, _interrupt: pac::Interrupt) {} + +#[cfg(feature = "rt")] +#[doc(hidden)] +#[link_section = ".rwtext"] +pub unsafe fn configure_cpu_caches() { + // this is just the bare minimum we need to run code from flash + // consider implementing more advanced configurations + // see https://github.com/apache/incubator-nuttx/blob/master/arch/xtensa/src/esp32s3/esp32s3_start.c + + extern "C" { + fn rom_config_instruction_cache_mode( + cfg_cache_size: u32, + cfg_cache_ways: u8, + cfg_cache_line_size: u8, + ); + } + + // ideally these should be configurable + const CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE: u32 = 0x4000; // ESP32S3_INSTRUCTION_CACHE_16KB + const CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS: u8 = 8; // ESP32S3_INSTRUCTION_CACHE_8WAYS + const CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE: u8 = 32; // ESP32S3_INSTRUCTION_CACHE_LINE_32B + + // Configure the mode of instruction cache: cache size, cache line size. + rom_config_instruction_cache_mode( + CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE, + CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS, + CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE, + ); +} + +/// Function initializes ESP32S3 specific memories (RTC slow and fast) and +/// then calls original Reset function +/// +/// ENTRY point is defined in memory.x +/// *Note: the pre_init function is called in the original reset handler +/// after the initializations done in this function* +#[cfg(feature = "rt")] +#[doc(hidden)] +#[no_mangle] +#[link_section = ".rwtext"] +pub unsafe extern "C" fn ESP32Reset() -> ! { + configure_cpu_caches(); + + // These symbols come 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; + + 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); + + // copying data from flash to various data segments is done by the bootloader + // initialization to zero needs to be done by the application + + // 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); + + // continue with default reset handler + xtensa_lx_rt::Reset(); +} + +/// The ESP32 has a first stage bootloader that handles loading program data +/// into the right place therefore we skip loading it again. +#[doc(hidden)] +#[no_mangle] +#[rustfmt::skip] +pub extern "Rust" fn __init_data() -> bool { + false +} + +fn _gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 { + int_enable as u8 + | ((nmi_enable as u8) << 1) + | (int_enable as u8) << 2 + | ((nmi_enable as u8) << 3) +}