diff --git a/.gitignore b/.gitignore index 693699042..83e099870 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ /target -**/*.rs.bk +ulp/ulp_start.o Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index 0ab3323db..684a50ae2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "esp-idf-hal" -version = "0.17.0" +version = "0.17.1" authors = ["sapir ", "Ivan Markov "] edition = "2018" categories = ["embedded", "hardware-support"] diff --git a/build.rs b/build.rs index 4b0a86743..a4f18732e 100644 --- a/build.rs +++ b/build.rs @@ -6,4 +6,17 @@ fn main() { let mcu = "esp32s2"; println!("cargo:rustc-cfg={}", mcu); + + #[cfg(feature = "ulp")] + { + let ulp_dir = std::env::current_dir().unwrap().join("ulp"); + + println!("cargo:rustc-link-search={}", ulp_dir.display()); + + println!("cargo:rustc-link-lib=static=ulp_start"); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed={}", ulp_dir.join("libulp_start.a").display()); + println!("cargo:rerun-if-changed={}", ulp_dir.join("ulp_link.x").display()); + } } diff --git a/src/ulp.rs b/src/ulp.rs index 3eedc4bd2..1a6994f96 100644 --- a/src/ulp.rs +++ b/src/ulp.rs @@ -8,6 +8,8 @@ mod reg; pub mod sys; #[cfg(feature = "ulp")] pub mod delay; +#[cfg(feature = "ulp")] +pub mod start; #[cfg(not(feature = "ulp"))] #[cfg(any(esp32, esp32s2, esp32s3))] diff --git a/src/ulp/start.rs b/src/ulp/start.rs new file mode 100644 index 000000000..2b4381750 --- /dev/null +++ b/src/ulp/start.rs @@ -0,0 +1,175 @@ +//! Minimal startup / runtime for ESP32-SXX RISC-V ULPs +//! Adapted from riscv-rt/src/lib.rs + +#![deny(missing_docs)] + +use super::sys::cpu; + +#[export_name = "error: ulp_start appears more than once in the dependency graph"] +#[doc(hidden)] +pub static __ONCE__: () = (); + +/// Rust entry point (_start_rust) +/// +/// Calls main. This function never returns. +#[link_section = ".start.rust"] +#[export_name = "_start_rust"] +pub unsafe extern "C" fn start_rust() -> ! { + #[rustfmt::skip] + extern "Rust" { + // This symbol will be provided by the user + fn main(); + } + + cpu::rescue_from_monitor(); + + main(); + + cpu::shutdown(); +} + +/// Registers saved in trap handler +#[allow(missing_docs)] +#[repr(C)] +pub struct TrapFrame { + pub ra: usize, + pub t0: usize, + pub t1: usize, + pub t2: usize, + pub t3: usize, + pub t4: usize, + pub t5: usize, + pub t6: usize, + pub a0: usize, + pub a1: usize, + pub a2: usize, + pub a3: usize, + pub a4: usize, + pub a5: usize, + pub a6: usize, + pub a7: usize, +} + +/// Trap entry point rust (_start_trap_rust) +/// +/// `mcause` is read to determine the cause of the trap. XLEN-1 bit indicates +/// if it's an interrupt or an exception. The result is examined and ExceptionHandler +/// or one of the core interrupt handlers is called. +#[link_section = ".trap.rust"] +#[export_name = "_start_trap_rust"] +pub extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) { + // use riscv::register::mcause; + + extern "C" { + fn ExceptionHandler(trap_frame: &TrapFrame); + #[allow(dead_code)] + fn DefaultHandler(); + } + + unsafe { + // let cause = mcause::read(); + // if cause.is_exception() { + ExceptionHandler(&*trap_frame) + // } else { + // let code = cause.code(); + // if code < __INTERRUPTS.len() { + // let h = &__INTERRUPTS[code]; + // if h.reserved == 0 { + // DefaultHandler(); + // } else { + // (h.handler)(); + // } + // } else { + // DefaultHandler(); + // } + // } + } +} + +#[doc(hidden)] +#[no_mangle] +#[allow(unused_variables, non_snake_case)] +pub fn DefaultExceptionHandler(trap_frame: &TrapFrame) -> ! { + loop { + // Prevent this from turning into a UDF instruction + // see rust-lang/rust#28728 for details + continue; + } +} + +#[doc(hidden)] +#[no_mangle] +#[allow(unused_variables, non_snake_case)] +pub fn DefaultInterruptHandler() { + loop { + // Prevent this from turning into a UDF instruction + // see rust-lang/rust#28728 for details + continue; + } +} + +/* Interrupts */ +#[doc(hidden)] +pub enum Interrupt { + UserSoft, + SupervisorSoft, + MachineSoft, + UserTimer, + SupervisorTimer, + MachineTimer, + UserExternal, + SupervisorExternal, + MachineExternal, +} + +pub use self::Interrupt as interrupt; + +extern "C" { + fn UserSoft(); + fn SupervisorSoft(); + fn MachineSoft(); + fn UserTimer(); + fn SupervisorTimer(); + fn MachineTimer(); + fn UserExternal(); + fn SupervisorExternal(); + fn MachineExternal(); +} + +#[doc(hidden)] +pub union Vector { + handler: unsafe extern "C" fn(), + reserved: usize, +} + +#[doc(hidden)] +#[allow(dead_code)] +#[no_mangle] +pub static __INTERRUPTS: [Vector; 12] = [ + Vector { handler: UserSoft }, + Vector { + handler: SupervisorSoft, + }, + Vector { reserved: 0 }, + Vector { + handler: MachineSoft, + }, + Vector { handler: UserTimer }, + Vector { + handler: SupervisorTimer, + }, + Vector { reserved: 0 }, + Vector { + handler: MachineTimer, + }, + Vector { + handler: UserExternal, + }, + Vector { + handler: SupervisorExternal, + }, + Vector { reserved: 0 }, + Vector { + handler: MachineExternal, + }, +]; diff --git a/ulp/libulp_start.a b/ulp/libulp_start.a new file mode 100644 index 000000000..d3f858baf Binary files /dev/null and b/ulp/libulp_start.a differ diff --git a/ulp/ulp_link.x b/ulp/ulp_link.x new file mode 100644 index 000000000..5ef59c427 --- /dev/null +++ b/ulp/ulp_link.x @@ -0,0 +1,88 @@ +PROVIDE(_ram_size = 2K); + +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); + +ENTRY(reset_vector) + +MEMORY +{ + ram(RW) : ORIGIN = 0, LENGTH = _ram_size +} + +PROVIDE(_stext = ORIGIN(ram)); + +SECTIONS +{ + .text.dummy (NOLOAD) : + { + /* This section is intended to make _stext address work */ + . = ABSOLUTE(_stext); + } > ram + + .text _stext : + { + KEEP(*(.init)); // Default reset vector must link to offset 0x0 + KEEP(*(.start.rust)); + KEEP(*(.trap.rust)); + + *(.text .text.*); + } > ram + + .rodata ALIGN(4): + { + *(.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); + } > ram + + .data ALIGN(4): + { + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + . = ALIGN(4); + } > ram + + .bss ALIGN(4) : + { + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(4); + } > ram + + /* 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) + } + + _stack_top = ORIGIN(ram) + LENGTH(ram); +} \ No newline at end of file diff --git a/ulp/ulp_start.S b/ulp/ulp_start.S new file mode 100644 index 000000000..cc33001ea --- /dev/null +++ b/ulp/ulp_start.S @@ -0,0 +1,117 @@ +// NOTE: Adapted from riscv-rt/asm.S +#define REGBYTES (1 << 2) + +.section .init, "ax" + .global reset_vector + .global irq_vector + +// The reset vector, jumps to startup code +reset_vector: + j _start + +// Interrupt handler +.option push +.option norelax // To prevent an unsupported R_RISCV_ALIGN relocation from being generated +.balign 16 +irq_vector: + addi sp, sp, -16*REGBYTES + + sw ra, 0*REGBYTES(sp) + sw t0, 1*REGBYTES(sp) + sw t1, 2*REGBYTES(sp) + sw t2, 3*REGBYTES(sp) + sw t3, 4*REGBYTES(sp) + sw t4, 5*REGBYTES(sp) + sw t5, 6*REGBYTES(sp) + sw t6, 7*REGBYTES(sp) + sw a0, 8*REGBYTES(sp) + sw a1, 9*REGBYTES(sp) + sw a2, 10*REGBYTES(sp) + sw a3, 11*REGBYTES(sp) + sw a4, 12*REGBYTES(sp) + sw a5, 13*REGBYTES(sp) + sw a6, 14*REGBYTES(sp) + sw a7, 15*REGBYTES(sp) + + add a0, sp, zero + jal ra, _start_trap_rust + + lw ra, 0*REGBYTES(sp) + lw t0, 1*REGBYTES(sp) + lw t1, 2*REGBYTES(sp) + lw t2, 3*REGBYTES(sp) + lw t3, 4*REGBYTES(sp) + lw t4, 5*REGBYTES(sp) + lw t5, 6*REGBYTES(sp) + lw t6, 7*REGBYTES(sp) + lw a0, 8*REGBYTES(sp) + lw a1, 9*REGBYTES(sp) + lw a2, 10*REGBYTES(sp) + lw a3, 11*REGBYTES(sp) + lw a4, 12*REGBYTES(sp) + lw a5, 13*REGBYTES(sp) + lw a6, 14*REGBYTES(sp) + lw a7, 15*REGBYTES(sp) + + addi sp, sp, 16*REGBYTES + ret +.option pop + +_start: + .cfi_startproc + .cfi_undefined ra + + li x1, 0 + li x2, 0 + li x3, 0 + li x4, 0 + li x5, 0 + li x6, 0 + li x7, 0 + li x8, 0 + li x9, 0 + li x10,0 + li x11,0 + li x12,0 + li x13,0 + li x14,0 + li x15,0 + li x16,0 + li x17,0 + li x18,0 + li x19,0 + li x20,0 + li x21,0 + li x22,0 + li x23,0 + li x24,0 + li x25,0 + li x26,0 + li x27,0 + li x28,0 + li x29,0 + li x30,0 + li x31,0 + + .option push + .option norelax // To prevent an unsupported R_RISCV_ALIGN relocation from being generated + la gp, __global_pointer$ + .option pop + + // Allocate stack + la sp, _stack_top + + // Set frame pointer + add s0, sp, zero + + jal zero, _start_rust + + .cfi_endproc + +loop: + j loop + +// Make sure there is an abort when linking +.globl abort +abort: + j abort diff --git a/ulp/ulp_start_assemble.ps1 b/ulp/ulp_start_assemble.ps1 new file mode 100644 index 000000000..c3925474c --- /dev/null +++ b/ulp/ulp_start_assemble.ps1 @@ -0,0 +1,7 @@ +# remove existing blob because otherwise this will append object file to the old blob +Remove-Item -Force ulp_start.a + +riscv32-esp-elf-gcc -Desp_ulp -ggdb3 -fdebug-prefix-map=$(pwd)=/ulp_start -c -mabi=ilp32 -march=rv32imc ulp_start.S -o ulp_start.o +riscv32-esp-elf-ar crs libulp_start.a ulp_start.o + +Remove-Item ulp_start.o diff --git a/ulp/ulp_start_assemble.sh b/ulp/ulp_start_assemble.sh new file mode 100755 index 000000000..4e58d00cd --- /dev/null +++ b/ulp/ulp_start_assemble.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -euxo pipefail + +# remove existing blob because otherwise this will append object file to the old blob +rm -f ulp_start.a + +riscv32-esp-elf-gcc -Desp_ulp -ggdb3 -fdebug-prefix-map=$(pwd)=/ulp_start -c -mabi=ilp32 -march=rv32imc ulp_start.S -o ulp_start.o +riscv32-esp-elf-ar crs libulp_start.a ulp_start.o + +rm ulp_start.o