From 3d266dd3f67a935da1f3fd6b90ffbca460afbfd7 Mon Sep 17 00:00:00 2001 From: Bjoern Quentin Date: Tue, 11 Jan 2022 09:33:33 +0100 Subject: [PATCH 1/2] Add .iram section for ESP32C3, make it possible to use direct-boot or normal-boot --- esp32c3-hal/.cargo/config.toml | 1 - esp32c3-hal/Cargo.toml | 2 + esp32c3-hal/bl-esp32c3-memory.x | 41 +++++++ esp32c3-hal/bl-riscv-link.x | 207 ++++++++++++++++++++++++++++++++ esp32c3-hal/build.rs | 25 ++++ esp32c3-hal/examples/ram.rs | 42 +++++++ esp32c3-hal/examples/serial.rs | 2 +- esp32c3-hal/riscv-link.x | 12 +- esp32c3-hal/src/lib.rs | 32 +++++ 9 files changed, 361 insertions(+), 3 deletions(-) create mode 100644 esp32c3-hal/bl-esp32c3-memory.x create mode 100644 esp32c3-hal/bl-riscv-link.x create mode 100644 esp32c3-hal/examples/ram.rs diff --git a/esp32c3-hal/.cargo/config.toml b/esp32c3-hal/.cargo/config.toml index c2b4f5e589f..ff7953215fe 100644 --- a/esp32c3-hal/.cargo/config.toml +++ b/esp32c3-hal/.cargo/config.toml @@ -1,6 +1,5 @@ [target.riscv32imc-unknown-none-elf] rustflags = [ - "-C", "link-arg=-Tesp32c3-link.x", ] [build] diff --git a/esp32c3-hal/Cargo.toml b/esp32c3-hal/Cargo.toml index ce640c8790f..0528abf26d4 100644 --- a/esp32c3-hal/Cargo.toml +++ b/esp32c3-hal/Cargo.toml @@ -32,6 +32,7 @@ embedded-hal = { version = "0.2", features = ["unproven"] } nb = "1.0" riscv = "0.7" void = { version = "1.0", default-features = false } +r0 = "1.0.0" [dependencies.esp-hal-common] path = "../esp-hal-common" @@ -48,3 +49,4 @@ panic-halt = "0.2" [features] default = ["rt"] rt = ["riscv-rt"] +normalboot = [] diff --git a/esp32c3-hal/bl-esp32c3-memory.x b/esp32c3-hal/bl-esp32c3-memory.x new file mode 100644 index 00000000000..3bb4f256716 --- /dev/null +++ b/esp32c3-hal/bl-esp32c3-memory.x @@ -0,0 +1,41 @@ +MEMORY +{ + /* + https://github.com/espressif/esptool/blob/master/esptool.py#L1919 + MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"], + [0x3C000000, 0x3C800000, "DROM"], + [0x3FC80000, 0x3FCE0000, "DRAM"], + [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"], + [0x3FF00000, 0x3FF20000, "DROM_MASK"], + [0x40000000, 0x40060000, "IROM_MASK"], + [0x42000000, 0x42800000, "IROM"], + [0x4037C000, 0x403E0000, "IRAM"], + [0x50000000, 0x50002000, "RTC_IRAM"], + [0x50000000, 0x50002000, "RTC_DRAM"], + [0x600FE000, 0x60100000, "MEM_INTERNAL2"]] + */ + /* 400K of on soc RAM, 16K reserved for cache */ + ICACHE : ORIGIN = 0x4037C000, LENGTH = 0x4000 + IRAM : ORIGIN = 0x4037C000 + 0x4000, LENGTH = 400K - 0x4000 + /* Data RAM */ + DRAM : ORIGIN = 0x3FC80000, LENGTH = 0x50000 + + + /* External flash */ + /* Instruction ROM */ + IROM : ORIGIN = 0x42000000 + 0x20, LENGTH = 0x400000 - 0x20 + /* Data ROM */ + /* + TODO IROM + DROM share the same phys address ... need to offset DROM data with len of .text + DROM : ORIGIN = 0x3C000000 + 0x20 + 0xa0000, LENGTH = 0x400000 - 0x20 + */ + DROM : ORIGIN = 0x3C000000, LENGTH = 0x400000 +} + +REGION_ALIAS("REGION_TEXT", IROM); +REGION_ALIAS("REGION_RODATA", DROM); + +REGION_ALIAS("REGION_DATA", DRAM); +REGION_ALIAS("REGION_BSS", DRAM); +REGION_ALIAS("REGION_HEAP", DRAM); +REGION_ALIAS("REGION_STACK", DRAM); diff --git a/esp32c3-hal/bl-riscv-link.x b/esp32c3-hal/bl-riscv-link.x new file mode 100644 index 00000000000..b13b1255d45 --- /dev/null +++ b/esp32c3-hal/bl-riscv-link.x @@ -0,0 +1,207 @@ +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(UserSoft = DefaultHandler); +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(UserTimer = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(UserExternal = 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 : + { + _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 .text.*); + _etext = .; + } > REGION_TEXT + + /** + * This dummy section represents the .text section but in rodata. + * Thus, it must have its alignement and (at least) its size. + */ + .text_dummy (NOLOAD): + { + /* Start at the same alignement constraint than .text */ + . = ALIGN(ALIGNOF(.text)); + /* Create an empty gap as big as .text section */ + . = . + SIZEOF(.text); + /* Prepare the alignement of the section above. Few bytes (0x20) must be + * added for the mapping header. */ + . = ALIGN(0x10000) + 0x20; + } > REGION_RODATA + + .rodata : ALIGN(4) + { + _srodata = .; + *(.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); + _erodata = .; + } > REGION_RODATA + + .iram : ALIGN(4) { + _iramdata = LOADADDR(.iram); + _siramdata = .; + *(.iram); + . = ALIGN(4); + _eiramdata = .; + } > IRAM + + /* similar as text_dummy */ + .ram_dummy (NOLOAD) : { + . = ALIGN(ALIGNOF(.iram)); + . = . + SIZEOF(.iram); + } > REGION_DATA + + .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 + + .bss (NOLOAD) : + { + _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.*)); + } + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +/* 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 | */ \ No newline at end of file diff --git a/esp32c3-hal/build.rs b/esp32c3-hal/build.rs index 042a451981f..102de955eeb 100644 --- a/esp32c3-hal/build.rs +++ b/esp32c3-hal/build.rs @@ -1,5 +1,6 @@ use std::{env, fs::File, io::Write, path::PathBuf}; +#[cfg(not(feature = "normalboot"))] fn main() { // Put the linker script somewhere the linker can find it let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); @@ -24,4 +25,28 @@ fn main() { // 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"); + println!("cargo:rustc-link-arg=-Tesp32c3-link.x"); +} + +#[cfg(feature = "normalboot")] +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!("bl-esp32c3-memory.x")) + .unwrap(); + + File::create(out.join("bl-riscv-link.x")) + .unwrap() + .write_all(include_bytes!("bl-riscv-link.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"); + println!("cargo:rustc-link-arg=-Tmemory.x"); + println!("cargo:rustc-link-arg=-Tbl-riscv-link.x"); } diff --git a/esp32c3-hal/examples/ram.rs b/esp32c3-hal/examples/ram.rs new file mode 100644 index 00000000000..a4c7335c389 --- /dev/null +++ b/esp32c3-hal/examples/ram.rs @@ -0,0 +1,42 @@ +#![no_std] +#![no_main] + +use core::fmt::Write; + +use esp32c3_hal::{ + pac::{self, UART0}, + prelude::*, + RtcCntl, + Serial, + Timer, +}; +use nb::block; +use panic_halt as _; +use riscv_rt::{entry}; + +#[entry] +fn main() -> ! { + let peripherals = pac::Peripherals::take().unwrap(); + let rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL); + let mut timer0 = Timer::new(peripherals.TIMG0); + let mut serial0 = Serial::new(peripherals.UART0).unwrap(); + + rtc_cntl.set_super_wdt_enable(false); + rtc_cntl.set_wdt_enable(false); + timer0.disable(); + + timer0.start(10_000_000u64); + + writeln!(serial0, "Function located at {:p}", say_hello as *const ()).unwrap(); + + loop { + say_hello(&mut serial0); + block!(timer0.wait()).unwrap(); + } +} + +#[link_section = ".iram"] +#[inline(never)] +fn say_hello(serial0: &mut Serial) { + writeln!(serial0, "Hello world!").unwrap(); +} diff --git a/esp32c3-hal/examples/serial.rs b/esp32c3-hal/examples/serial.rs index 963f0eb95a9..a7d7ec98b23 100644 --- a/esp32c3-hal/examples/serial.rs +++ b/esp32c3-hal/examples/serial.rs @@ -26,4 +26,4 @@ fn main() -> ! { writeln!(serial0, "Hello world!").unwrap(); block!(timer0.wait()).unwrap(); } -} +} \ No newline at end of file diff --git a/esp32c3-hal/riscv-link.x b/esp32c3-hal/riscv-link.x index 763baa91e53..cf1e7737401 100644 --- a/esp32c3-hal/riscv-link.x +++ b/esp32c3-hal/riscv-link.x @@ -73,7 +73,6 @@ SECTIONS _rodata_size = _erodata - _srodata + 8; .data ORIGIN(DRAM) : AT(_text_size + _rodata_size) { - _sidata = LOADADDR(.data); _sdata = .; /* Must be called __global_pointer$ for linker relaxations to work. */ PROVIDE(__global_pointer$ = . + 0x800); @@ -83,6 +82,7 @@ SECTIONS _edata = .; } > REGION_DATA + _data_size = _edata - _sdata + 8; .bss (NOLOAD) : { _sbss = .; @@ -91,6 +91,13 @@ SECTIONS _ebss = .; } > REGION_BSS + .iram ORIGIN(IRAM) + _data_size : AT(_text_size + _rodata_size + _data_size){ + _siramdata = .; + *(.iram); + . = ALIGN(4); + _eiramdata = .; + } > IRAM + /* fictitious region that represents the memory available for the heap */ .heap (NOLOAD) : { @@ -121,6 +128,9 @@ SECTIONS .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } } +PROVIDE(_sidata = _erodata + 8); +PROVIDE(_iramdata = /*LOADADDR(.iram) +*/ 0x3C000000 + _text_size + _rodata_size + _data_size); + /* 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"); diff --git a/esp32c3-hal/src/lib.rs b/esp32c3-hal/src/lib.rs index e9edbce5dd7..c61f9423812 100644 --- a/esp32c3-hal/src/lib.rs +++ b/esp32c3-hal/src/lib.rs @@ -7,4 +7,36 @@ pub mod rtc_cntl; pub mod gpio; +use riscv_rt::pre_init; pub use rtc_cntl::RtcCntl; + +extern "C" { + // Boundaries of the .iram section + static mut _siramdata: u32; + static mut _eiramdata: u32; + static mut _iramdata: u32; + + // Boundaries of the .bss section + static mut _ebss: u32; + static mut _sbss: u32; +} + +#[pre_init] +unsafe fn init() { + // no init data for iram when using normal boot - the boot loader does it for us + #[cfg(not(feature = "normalboot"))] + r0::init_data(&mut _siramdata, &mut _eiramdata, &_iramdata); +} + +#[export_name = "_mp_hook"] +pub extern "Rust" fn mp_hook() -> bool { + #[cfg(not(feature = "normalboot"))] + return true; + + // no init data when using normal boot - but we need to zero out BSS + unsafe { + r0::zero_bss(&mut _sbss, &mut _ebss); + } + + false +} From e07c3332abdb3d38c6a98dd4b3e6fbd8e686fed9 Mon Sep 17 00:00:00 2001 From: Bjoern Quentin Date: Thu, 13 Jan 2022 10:52:32 +0100 Subject: [PATCH 2/2] Add ram proc-macro --- esp-hal-common/Cargo.toml | 7 +- esp-hal-common/src/lib.rs | 3 + esp-hal-procmacros/Cargo.toml | 24 +++++ esp-hal-procmacros/src/lib.rs | 91 +++++++++++++++++++ esp32-hal/examples/ram.rs | 91 +++++++++++++++++++ esp32-hal/memory.x | 8 +- esp32-hal/src/lib.rs | 2 + esp32c3-hal/Cargo.toml | 2 +- esp32c3-hal/bl-esp32c3-memory.x | 11 ++- esp32c3-hal/bl-riscv-link.x | 44 ++++++--- esp32c3-hal/build.rs | 12 +-- .../{esp32c3-link.x => db-esp32c3-link.x} | 0 .../{esp32c3-memory.x => db-esp32c3-memory.x} | 8 +- esp32c3-hal/{riscv-link.x => db-riscv-link.x} | 45 +++++++-- esp32c3-hal/examples/ram.rs | 69 ++++++++++++-- esp32c3-hal/src/lib.rs | 43 +++++++-- 16 files changed, 403 insertions(+), 57 deletions(-) create mode 100644 esp-hal-procmacros/Cargo.toml create mode 100644 esp-hal-procmacros/src/lib.rs create mode 100644 esp32-hal/examples/ram.rs rename esp32c3-hal/{esp32c3-link.x => db-esp32c3-link.x} (100%) rename esp32c3-hal/{esp32c3-memory.x => db-esp32c3-memory.x} (83%) rename esp32c3-hal/{riscv-link.x => db-riscv-link.x} (79%) diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index 25417978f1b..dfa13ad1a8a 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -17,6 +17,7 @@ paste = "1.0" riscv = { version = "0.7", optional = true } void = { version = "1.0", default-features = false } xtensa-lx = { version = "0.4", optional = true } +procmacros = { path = "../esp-hal-procmacros", package = "esp-hal-procmacros" } # IMPORTANT: # Each supported device MUST have its PAC included below along with a # corresponding feature. @@ -26,7 +27,7 @@ esp32s2_pac = { package = "esp32s2", git = "https://github.com/jessebraham/esp32 esp32s3_pac = { package = "esp32s3", git = "https://github.com/jessebraham/esp32s3.git", branch = "develop", optional = true } [features] -esp32 = ["esp32_pac", "esp32_pac/rt", "xtensa-lx/lx6"] +esp32 = ["esp32_pac", "esp32_pac/rt", "xtensa-lx/lx6", "procmacros/rtc_slow"] esp32c3 = ["esp32c3_pac", "esp32c3_pac/rt", "riscv"] -esp32s2 = ["esp32s2_pac", "esp32s2_pac/rt", "xtensa-lx/lx6"] # FIXME -esp32s3 = ["esp32s3_pac", "esp32s3_pac/rt", "xtensa-lx/lx6"] # FIXME +esp32s2 = ["esp32s2_pac", "esp32s2_pac/rt", "xtensa-lx/lx6", "procmacros/rtc_slow"] # FIXME +esp32s3 = ["esp32s3_pac", "esp32s3_pac/rt", "xtensa-lx/lx6", "procmacros/rtc_slow"] # FIXME diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index 0ce1a65f69c..0918cff6611 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -37,3 +37,6 @@ pub use delay::Delay; pub use gpio::*; pub use serial::Serial; pub use timer::Timer; + +use procmacros; +pub use procmacros::ram; diff --git a/esp-hal-procmacros/Cargo.toml b/esp-hal-procmacros/Cargo.toml new file mode 100644 index 00000000000..4dda205dee2 --- /dev/null +++ b/esp-hal-procmacros/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "esp-hal-procmacros" +version = "0.1.0" +authors = [ + "Jesse Braham ", + "Björn Quentin ", +] +edition = "2021" +description = "Procedural macros for ESP-HAL" +repository = "https://github.com/esp-rs/esp-hal" +license = "MIT OR Apache-2.0" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0" +proc-macro2 = "1.0" +darling = "0.10" +syn = {version = "1.0", features = ["extra-traits", "full"]} +proc-macro-error = "1.0.4" + +[features] +rtc_slow = [] diff --git a/esp-hal-procmacros/src/lib.rs b/esp-hal-procmacros/src/lib.rs new file mode 100644 index 00000000000..2050cbb8676 --- /dev/null +++ b/esp-hal-procmacros/src/lib.rs @@ -0,0 +1,91 @@ +use darling::FromMeta; +use proc_macro::TokenStream; +use proc_macro::{self, Span}; +use proc_macro_error::{abort, proc_macro_error}; +use quote::quote; +use syn::{parse_macro_input, AttributeArgs}; + +#[derive(Debug, Default, FromMeta)] +#[darling(default)] +struct RamArgs { + rtc_fast: bool, + rtc_slow: bool, + uninitialized: bool, + zeroed: bool, +} + +/// This attribute allows placing statics and functions into ram. +/// +/// Options that can be specified are rtc_slow or rtc_fast to use the +/// RTC slow or RTC fast ram instead of the normal SRAM. +/// +/// The uninitialized option will skip initialization of the memory +/// (e.g. to persist it across resets or deep sleep mode for the RTC RAM) +/// +/// Not all targets support RTC slow ram. + +#[proc_macro_attribute] +#[proc_macro_error] +pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream { + let attr_args = parse_macro_input!(args as AttributeArgs); + + let RamArgs { + rtc_fast, + rtc_slow, + uninitialized, + zeroed, + } = match FromMeta::from_list(&attr_args) { + Ok(v) => v, + Err(e) => { + return e.write_errors().into(); + } + }; + + let item: syn::Item = syn::parse(input).expect("failed to parse input"); + + #[cfg(not(feature = "rtc_slow"))] + if rtc_slow { + abort!( + Span::call_site(), + "rtc_slow is not available for this target" + ); + } + + let is_fn = matches!(item, syn::Item::Fn(_)); + let section_name = match (is_fn, rtc_fast, rtc_slow, uninitialized, zeroed) { + (true, false, false, false, false) => Ok(".rwtext"), + (true, true, false, false, false) => Ok(".rtc_fast.text"), + (true, false, true, false, false) => Ok(".rtc_slow.text"), + + (false, false, false, false, false) => Ok(".data"), + + (false, true, false, false, false) => Ok(".rtc_fast.data"), + (false, true, false, true, false) => Ok(".rtc_fast.noinit"), + (false, true, false, false, true) => Ok(".rtc_fast.bss"), + + (false, false, true, false, false) => Ok(".rtc_slow.data"), + (false, false, true, true, false) => Ok(".rtc_slow.noinit"), + (false, false, true, false, true) => Ok(".rtc_slow.bss"), + + _ => Err(()), + }; + + let section = match (is_fn, section_name) { + (true, Ok(section_name)) => quote! { + #[link_section = #section_name] + #[inline(never)] // make certain function is not inlined + }, + (false, Ok(section_name)) => quote! { + #[link_section = #section_name] + }, + (_, Err(_)) => { + abort!(Span::call_site(), "Invalid combination of ram arguments"); + } + }; + + let output = quote! { + #section + #item + }; + output.into() +} diff --git a/esp32-hal/examples/ram.rs b/esp32-hal/examples/ram.rs new file mode 100644 index 00000000000..c21eef05fa0 --- /dev/null +++ b/esp32-hal/examples/ram.rs @@ -0,0 +1,91 @@ +#![no_std] +#![no_main] + +use core::fmt::Write; + +use esp32_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/esp32-hal/memory.x b/esp32-hal/memory.x index 0c4200e60d6..d3e0b57a662 100644 --- a/esp32-hal/memory.x +++ b/esp32-hal/memory.x @@ -78,16 +78,16 @@ SECTIONS { .rtc_fast.data : { - _rtc_fast_data_start = ABSOLUTE(.); . = 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) : { - _rtc_fast_bss_start = ABSOLUTE(.); . = ALIGN(4); + _rtc_fast_bss_start = ABSOLUTE(.); *(.rtc_fast.bss .rtc_fast.bss.*) _rtc_fast_bss_end = ABSOLUTE(.); } > rtc_fast_dram_seg @@ -106,16 +106,16 @@ SECTIONS { .rtc_slow.data : { - _rtc_slow_data_start = ABSOLUTE(.); . = 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) : { - _rtc_slow_bss_start = ABSOLUTE(.); . = ALIGN(4); + _rtc_slow_bss_start = ABSOLUTE(.); *(.rtc_slow.bss .rtc_slow.bss.*) _rtc_slow_bss_end = ABSOLUTE(.); } > rtc_slow_seg diff --git a/esp32-hal/src/lib.rs b/esp32-hal/src/lib.rs index 1043af72b89..09d9d1cb415 100644 --- a/esp32-hal/src/lib.rs +++ b/esp32-hal/src/lib.rs @@ -7,6 +7,8 @@ pub use self::gpio::IO; pub mod gpio; +pub use esp_hal_common::ram; + #[no_mangle] extern "C" fn DefaultHandler(_level: u32, _interrupt: pac::Interrupt) {} diff --git a/esp32c3-hal/Cargo.toml b/esp32c3-hal/Cargo.toml index 0528abf26d4..3b51ede7267 100644 --- a/esp32c3-hal/Cargo.toml +++ b/esp32c3-hal/Cargo.toml @@ -32,7 +32,7 @@ embedded-hal = { version = "0.2", features = ["unproven"] } nb = "1.0" riscv = "0.7" void = { version = "1.0", default-features = false } -r0 = "1.0.0" +r0 = "1.0.0" [dependencies.esp-hal-common] path = "../esp-hal-common" diff --git a/esp32c3-hal/bl-esp32c3-memory.x b/esp32c3-hal/bl-esp32c3-memory.x index 3bb4f256716..94148fdc125 100644 --- a/esp32c3-hal/bl-esp32c3-memory.x +++ b/esp32c3-hal/bl-esp32c3-memory.x @@ -16,6 +16,7 @@ MEMORY */ /* 400K of on soc RAM, 16K reserved for cache */ ICACHE : ORIGIN = 0x4037C000, LENGTH = 0x4000 + /* Instruction RAM */ IRAM : ORIGIN = 0x4037C000 + 0x4000, LENGTH = 400K - 0x4000 /* Data RAM */ DRAM : ORIGIN = 0x3FC80000, LENGTH = 0x50000 @@ -25,11 +26,10 @@ MEMORY /* Instruction ROM */ IROM : ORIGIN = 0x42000000 + 0x20, LENGTH = 0x400000 - 0x20 /* Data ROM */ - /* - TODO IROM + DROM share the same phys address ... need to offset DROM data with len of .text - DROM : ORIGIN = 0x3C000000 + 0x20 + 0xa0000, LENGTH = 0x400000 - 0x20 - */ DROM : ORIGIN = 0x3C000000, LENGTH = 0x400000 + + /* RTC fast memory (executable). Persists over deep sleep. */ + RTC_FAST : ORIGIN = 0x50000000, LENGTH = 0x2000 /*- ESP_BOOTLOADER_RESERVE_RTC*/ } REGION_ALIAS("REGION_TEXT", IROM); @@ -39,3 +39,6 @@ REGION_ALIAS("REGION_DATA", DRAM); REGION_ALIAS("REGION_BSS", DRAM); REGION_ALIAS("REGION_HEAP", DRAM); REGION_ALIAS("REGION_STACK", DRAM); + +REGION_ALIAS("REGION_RWTEXT", IRAM); +REGION_ALIAS("REGION_RTC_FAST", RTC_FAST); diff --git a/esp32c3-hal/bl-riscv-link.x b/esp32c3-hal/bl-riscv-link.x index b13b1255d45..ed74d03844b 100644 --- a/esp32c3-hal/bl-riscv-link.x +++ b/esp32c3-hal/bl-riscv-link.x @@ -91,18 +91,18 @@ SECTIONS _erodata = .; } > REGION_RODATA - .iram : ALIGN(4) { - _iramdata = LOADADDR(.iram); - _siramdata = .; - *(.iram); + .rwtext : ALIGN(4) { + _irwtext = LOADADDR(.rwtext); + _srwtext = .; + *(.rwtext); . = ALIGN(4); - _eiramdata = .; - } > IRAM + _erwtext = .; + } > REGION_RWTEXT /* similar as text_dummy */ .ram_dummy (NOLOAD) : { - . = ALIGN(ALIGNOF(.iram)); - . = . + SIZEOF(.iram); + . = ALIGN(ALIGNOF(.rwtext)); + . = . + SIZEOF(.rwtext); } > REGION_DATA .data : ALIGN(4) @@ -142,14 +142,28 @@ SECTIONS _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) : + .rtc_fast.text : ALIGN(4) { + *(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*) + } > REGION_RTC_FAST AT > REGION_RODATA + + .rtc_fast.data : ALIGN(4) + { + _rtc_fast_data_start = ABSOLUTE(.); + *(.rtc_fast.data .rtc_fast.data.*) + _rtc_fast_data_end = ABSOLUTE(.); + } > REGION_RTC_FAST AT > REGION_RODATA + + .rtc_fast.bss (NOLOAD) : ALIGN(4) + { + _rtc_fast_bss_start = ABSOLUTE(.); + *(.rtc_fast.bss .rtc_fast.bss.*) + _rtc_fast_bss_end = ABSOLUTE(.); + } > REGION_RTC_FAST + + .rtc_fast.noinit (NOLOAD) : ALIGN(4) { - KEEP(*(.got .got.*)); - } + *(.rtc_fast.noinit .rtc_fast.noinit.*) + } > REGION_RTC_FAST .eh_frame (INFO) : { KEEP(*(.eh_frame)) } .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } diff --git a/esp32c3-hal/build.rs b/esp32c3-hal/build.rs index 102de955eeb..b992fe241df 100644 --- a/esp32c3-hal/build.rs +++ b/esp32c3-hal/build.rs @@ -7,18 +7,18 @@ fn main() { File::create(out.join("memory.x")) .unwrap() - .write_all(include_bytes!("esp32c3-memory.x")) + .write_all(include_bytes!("db-esp32c3-memory.x")) .unwrap(); File::create(out.join("esp32c3-link.x")) .unwrap() - .write_all(include_bytes!("esp32c3-link.x")) + .write_all(include_bytes!("db-esp32c3-link.x")) .unwrap(); File::create(out.join("riscv-link.x")) .unwrap() - .write_all(include_bytes!("riscv-link.x")) - .unwrap(); + .write_all(include_bytes!("db-riscv-link.x")) + .unwrap(); println!("cargo:rustc-link-search={}", out.display()); @@ -37,10 +37,10 @@ fn main() { .write_all(include_bytes!("bl-esp32c3-memory.x")) .unwrap(); - File::create(out.join("bl-riscv-link.x")) + File::create(out.join("bl-riscv-link.x")) .unwrap() .write_all(include_bytes!("bl-riscv-link.x")) - .unwrap(); + .unwrap(); println!("cargo:rustc-link-search={}", out.display()); diff --git a/esp32c3-hal/esp32c3-link.x b/esp32c3-hal/db-esp32c3-link.x similarity index 100% rename from esp32c3-hal/esp32c3-link.x rename to esp32c3-hal/db-esp32c3-link.x diff --git a/esp32c3-hal/esp32c3-memory.x b/esp32c3-hal/db-esp32c3-memory.x similarity index 83% rename from esp32c3-hal/esp32c3-memory.x rename to esp32c3-hal/db-esp32c3-memory.x index 8aece714c8c..4ec8f7b80ce 100644 --- a/esp32c3-hal/esp32c3-memory.x +++ b/esp32c3-hal/db-esp32c3-memory.x @@ -27,6 +27,9 @@ MEMORY IROM : ORIGIN = 0x42000000, LENGTH = 0x400000 /* Data ROM */ DROM : ORIGIN = 0x3C000000, LENGTH = 0x400000 + + /* RTC fast memory (executable). Persists over deep sleep. */ + RTC_FAST : ORIGIN = 0x50000000, LENGTH = 0x2000 /*- ESP_BOOTLOADER_RESERVE_RTC*/ } @@ -36,4 +39,7 @@ REGION_ALIAS("REGION_RODATA", DROM); REGION_ALIAS("REGION_DATA", DRAM); REGION_ALIAS("REGION_BSS", DRAM); REGION_ALIAS("REGION_HEAP", DRAM); -REGION_ALIAS("REGION_STACK", DRAM); \ No newline at end of file +REGION_ALIAS("REGION_STACK", DRAM); + +REGION_ALIAS("REGION_RWTEXT", IRAM); +REGION_ALIAS("REGION_RTC_FAST", RTC_FAST); diff --git a/esp32c3-hal/riscv-link.x b/esp32c3-hal/db-riscv-link.x similarity index 79% rename from esp32c3-hal/riscv-link.x rename to esp32c3-hal/db-riscv-link.x index cf1e7737401..54209d169b0 100644 --- a/esp32c3-hal/riscv-link.x +++ b/esp32c3-hal/db-riscv-link.x @@ -91,12 +91,13 @@ SECTIONS _ebss = .; } > REGION_BSS - .iram ORIGIN(IRAM) + _data_size : AT(_text_size + _rodata_size + _data_size){ - _siramdata = .; - *(.iram); + .rwtext ORIGIN(REGION_RWTEXT) + _data_size : AT(_text_size + _rodata_size + _data_size){ + _srwtext = .; + *(.rwtext); . = ALIGN(4); - _eiramdata = .; - } > IRAM + _erwtext = .; + } > REGION_RWTEXT + _rwtext_size = _erwtext - _srwtext + 8; /* fictitious region that represents the memory available for the heap */ .heap (NOLOAD) : @@ -115,6 +116,36 @@ SECTIONS _sstack = .; } > REGION_STACK + .rtc_fast.text : AT(_text_size + _rodata_size + _data_size + _rwtext_size) { + _srtc_fast_text = .; + *(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*) + . = ALIGN(4); + _ertc_fast_text = .; + } > REGION_RTC_FAST + _fast_text_size = _ertc_fast_text - _srtc_fast_text + 8; + + .rtc_fast.data : AT(_text_size + _rodata_size + _data_size + _rwtext_size + _fast_text_size) + { + _rtc_fast_data_start = ABSOLUTE(.); + *(.rtc_fast.data .rtc_fast.data.*) + . = ALIGN(4); + _rtc_fast_data_end = ABSOLUTE(.); + } > REGION_RTC_FAST + _rtc_fast_data_size = _rtc_fast_data_end - _rtc_fast_data_start + 8; + + .rtc_fast.bss (NOLOAD) : ALIGN(4) + { + _rtc_fast_bss_start = ABSOLUTE(.); + *(.rtc_fast.bss .rtc_fast.bss.*) + . = ALIGN(4); + _rtc_fast_bss_end = ABSOLUTE(.); + } > REGION_RTC_FAST + + .rtc_fast.noinit (NOLOAD) : ALIGN(4) + { + *(.rtc_fast.noinit .rtc_fast.noinit.*) + } > REGION_RTC_FAST + /* 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 @@ -129,7 +160,9 @@ SECTIONS } PROVIDE(_sidata = _erodata + 8); -PROVIDE(_iramdata = /*LOADADDR(.iram) +*/ 0x3C000000 + _text_size + _rodata_size + _data_size); +PROVIDE(_irwtext = ORIGIN(DROM) + _text_size + _rodata_size + _data_size); +PROVIDE(_irtc_fast_text = ORIGIN(DROM) + _text_size + _rodata_size + _data_size + _rwtext_size); +PROVIDE(_irtc_fast_data = ORIGIN(DROM) + _text_size + _rodata_size + _data_size + _rwtext_size + _fast_text_size); /* Do not exceed this mark in the error messages above | */ ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " diff --git a/esp32c3-hal/examples/ram.rs b/esp32c3-hal/examples/ram.rs index a4c7335c389..f0d51cbab0d 100644 --- a/esp32c3-hal/examples/ram.rs +++ b/esp32c3-hal/examples/ram.rs @@ -6,37 +6,86 @@ use core::fmt::Write; use esp32c3_hal::{ pac::{self, UART0}, prelude::*, - RtcCntl, + ram, Serial, Timer, }; use nb::block; use panic_halt as _; -use riscv_rt::{entry}; +use riscv_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 = pac::Peripherals::take().unwrap(); - let rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL); let mut timer0 = Timer::new(peripherals.TIMG0); let mut serial0 = Serial::new(peripherals.UART0).unwrap(); - rtc_cntl.set_super_wdt_enable(false); - rtc_cntl.set_wdt_enable(false); + // don't disable WDTs ... we actually want it getting triggered in this example + timer0.disable(); timer0.start(10_000_000u64); - writeln!(serial0, "Function located at {:p}", say_hello as *const ()).unwrap(); + 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 { - say_hello(&mut serial0); + function_in_ram(&mut serial0); block!(timer0.wait()).unwrap(); } } -#[link_section = ".iram"] -#[inline(never)] -fn say_hello(serial0: &mut Serial) { +#[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/esp32c3-hal/src/lib.rs b/esp32c3-hal/src/lib.rs index 94a87a3b071..f5475275b06 100644 --- a/esp32c3-hal/src/lib.rs +++ b/esp32c3-hal/src/lib.rs @@ -2,34 +2,63 @@ pub use embedded_hal as ehal; pub use esp_hal_common::{pac, prelude, Delay, Serial, Timer}; +#[cfg(not(feature = "normalboot"))] use riscv_rt::pre_init; pub mod gpio; pub mod rtc_cntl; +pub use esp_hal_common::ram; + pub use self::{gpio::IO, rtc_cntl::RtcCntl}; extern "C" { // Boundaries of the .iram section - static mut _siramdata: u32; - static mut _eiramdata: u32; - static mut _iramdata: u32; + static mut _srwtext: u32; + static mut _erwtext: u32; + static mut _irwtext: u32; // Boundaries of the .bss section static mut _ebss: u32; static mut _sbss: u32; + + // Boundaries of the rtc .bss section + static mut _rtc_fast_bss_start: u32; + static mut _rtc_fast_bss_end: u32; + + // Boundaries of the .rtc_fast.text section + static mut _srtc_fast_text: u32; + static mut _ertc_fast_text: 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; } +#[cfg(not(feature = "normalboot"))] #[pre_init] +#[cfg(not(feature = "normalboot"))] unsafe fn init() { - // no init data for iram when using normal boot - the boot loader does it for us - #[cfg(not(feature = "normalboot"))] - r0::init_data(&mut _siramdata, &mut _eiramdata, &_iramdata); + r0::init_data(&mut _srwtext, &mut _erwtext, &_irwtext); + + r0::init_data( + &mut _rtc_fast_data_start, + &mut _rtc_fast_data_end, + &_irtc_fast_data, + ); + + r0::init_data(&mut _srtc_fast_text, &mut _ertc_fast_text, &_irtc_fast_text); } #[allow(unreachable_code)] #[export_name = "_mp_hook"] -pub extern "Rust" fn mp_hook() -> bool { +pub fn mp_hook() -> bool { + unsafe { + r0::zero_bss(&mut _rtc_fast_bss_start, &mut _rtc_fast_bss_end); + } + #[cfg(not(feature = "normalboot"))] return true;