Add direct-boot support for ESP32-S3 (#107)

* Add direct-boot support for ESP32-S3
* Make sure to use correct alignments
* Only enable naked_functions and asm_experimental_arch when direct-boot feature is selected
This commit is contained in:
Björn Quentin 2022-07-20 16:14:19 +02:00 committed by GitHub
parent 147d8de988
commit 9fa1d1ecdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 504 additions and 1 deletions

View File

@ -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"]

View File

@ -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");
}

300
esp32s3-hal/ld/db-esp32s3.x Normal file
View File

@ -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
}

View File

@ -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 /* ??? */
}

View File

@ -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 {