Move esp-riscv-rt into esp-hal (#578)

* Move esp-riscv-rt into esp-hal

* Add changelog entry
This commit is contained in:
Scott Mabin 2023-06-07 16:15:47 +01:00 committed by GitHub
parent aef0ff4e45
commit 8361ca308c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 727 additions and 1 deletions

View File

@ -312,6 +312,23 @@ jobs:
- name: check esp32s3-hal (async, i2c)
run: cd esp32s3-hal/ && cargo check --example=embassy_i2c --features=embassy,embassy-time-timg0,async
esp-riscv-rt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@v1
with:
target: riscv32imac-unknown-none-elf, riscv32imc-unknown-none-elf
toolchain: nightly
components: rust-src
- uses: Swatinem/rust-cache@v2
- name: Check esp-riscv-rt (imc)
run: cd esp-riscv-rt/ && cargo check --target=riscv32imc-unknown-none-elf -Zbuild-std=core
- name: Check esp-riscv-rt (imac)
run: cd esp-riscv-rt/ && cargo check --target=riscv32imac-unknown-none-elf -Zbuild-std=core
# --------------------------------------------------------------------------
# MSRV

View File

@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix Async GPIO not disabling interupts on chips with multiple banks (#572)
- Add unified field-based efuse access
- Add `timer_interrupt` example in ESP32-H2 and refactor `clk_src` configuration (#576)
- Move `esp-riscv-rt` into esp-hal (#578)
### Changed

View File

@ -38,7 +38,7 @@ embassy-time = { version = "0.1.1", features = ["nightly"], optional = tru
embassy-futures = { version = "0.1.0", optional = true }
# RISC-V
esp-riscv-rt = { version = "0.3.0", optional = true }
esp-riscv-rt = { version = "0.3.0", path = "../esp-riscv-rt", optional = true }
riscv-atomic-emulation-trap = { version = "0.4.0", optional = true }
# Xtensa

26
esp-riscv-rt/Cargo.toml Normal file
View File

@ -0,0 +1,26 @@
[package]
name = "esp-riscv-rt"
version = "0.3.0"
edition = "2021"
rust-version = "1.60"
description = "Minimal runtime / startup for RISC-V CPUs from Espressif"
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
keywords = ["esp32", "riscv", "runtime", "startup"]
categories = ["embedded", "no-std"]
[dependencies]
riscv = "0.10.1"
riscv-rt-macros = "0.2.0"
[dev-dependencies]
panic-halt = "0.2.0"
[features]
has-mie-mip = []
zero-bss = []
zero-rtc-fast-bss = []
init-data = []
init-rw-text = []
init-rtc-fast-data = []
init-rtc-fast-text = []

32
esp-riscv-rt/README.md Normal file
View File

@ -0,0 +1,32 @@
# esp-riscv-rt
[![Crates.io](https://img.shields.io/crates/v/esp-riscv-rt?color=C96329&logo=Rust&style=flat-square)](https://crates.io/crates/esp-riscv-rt)
[![docs.rs](https://img.shields.io/docsrs/esp-riscv-rt?color=C96329&logo=rust&style=flat-square)](https://docs.rs/esp-riscv-rt)
![MSRV](https://img.shields.io/badge/MSRV-1.60-blue?style=flat-square)
![Crates.io](https://img.shields.io/crates/l/esp-riscv-rt?style=flat-square)
> Minimal runtime / startup for RISC-V CPUs from Espressif.
Much of the code in this repository originated in the [rust-embedded/riscv-rt](https://github.com/rust-embedded/riscv-rt) repository.
## [Documentation](https://docs.rs/crate/esp-riscv-rt)
## Minimum Supported Rust Version (MSRV)
This crate is guaranteed to compile on stable Rust 1.60 and up. It _might_
compile with older versions but that may change in any new patch release.
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
any additional terms or conditions.

6
esp-riscv-rt/build.rs Normal file
View File

@ -0,0 +1,6 @@
use std::{env, path::PathBuf};
fn main() {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
println!("cargo:rustc-link-search={}", out_dir.display());
}

644
esp-riscv-rt/src/lib.rs Normal file
View File

@ -0,0 +1,644 @@
//! Minimal startup / runtime for RISC-V CPUs from Espressif
//!
//! # Minimum Supported Rust Version (MSRV)
//!
//! This crate is guaranteed to compile on stable Rust 1.60 and up. It *might*
//! compile with older versions but that may change in any new patch release.
//!
//! # Features
//!
//! This crate provides:
//!
//! - Before main initialization of the `.bss` and `.data` sections controlled
//! by features
//! - `#[entry]` to declare the entry point of the program
// NOTE: Adapted from riscv-rt/src/lib.rs
#![no_std]
use core::arch::global_asm;
pub use riscv;
use riscv::register::{
mcause,
mtvec::{self, TrapMode},
};
pub use riscv_rt_macros::{entry, pre_init};
pub use self::Interrupt as interrupt;
#[export_name = "error: esp-riscv-rt appears more than once in the dependency graph"]
#[doc(hidden)]
pub static __ONCE__: () = ();
extern "C" {
// Boundaries of the .bss section
static mut _bss_end: u32;
static mut _bss_start: u32;
// Boundaries of the .data section
static mut _data_end: u32;
static mut _data_start: u32;
// Initial values of the .data section (stored in Flash)
static _sidata: u32;
}
/// Rust entry point (_start_rust)
///
/// Zeros bss section, initializes data section and calls main. This function
/// never returns.
#[link_section = ".init.rust"]
#[export_name = "_start_rust"]
pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! {
extern "Rust" {
// This symbol will be provided by the user via `#[entry]`
fn main(a0: usize, a1: usize, a2: usize) -> !;
fn __post_init();
fn _setup_interrupts();
}
__post_init();
_setup_interrupts();
main(a0, a1, a2);
}
/// Registers saved in trap handler
#[allow(missing_docs)]
#[derive(Debug, Default, Clone, Copy)]
#[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,
pub s0: usize,
pub s1: usize,
pub s2: usize,
pub s3: usize,
pub s4: usize,
pub s5: usize,
pub s6: usize,
pub s7: usize,
pub s8: usize,
pub s9: usize,
pub s10: usize,
pub s11: usize,
pub gp: usize,
pub tp: usize,
pub sp: usize,
pub pc: usize,
pub mstatus: usize,
pub mcause: usize,
pub mtval: usize,
}
/// Trap entry point rust (_start_trap_rust)
///
/// `scause`/`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 unsafe extern "C" fn start_trap_rust(trap_frame: *const TrapFrame) {
extern "C" {
fn ExceptionHandler(trap_frame: &TrapFrame);
fn DefaultHandler();
}
unsafe {
let cause = mcause::read();
if cause.is_exception() {
ExceptionHandler(&*trap_frame)
} else if cause.code() < __INTERRUPTS.len() {
let h = &__INTERRUPTS[cause.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,
}
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 {
pub handler: unsafe extern "C" fn(),
pub reserved: usize,
}
#[doc(hidden)]
#[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,
},
];
#[doc(hidden)]
#[no_mangle]
#[rustfmt::skip]
pub unsafe extern "Rust" fn default_post_init() {}
/// Default implementation of `_setup_interrupts` that sets `mtvec`/`stvec` to a
/// trap handler address.
#[doc(hidden)]
#[no_mangle]
#[rustfmt::skip]
pub unsafe extern "Rust" fn default_setup_interrupts() {
extern "C" {
fn _start_trap();
}
mtvec::write(_start_trap as usize, TrapMode::Direct);
}
/// Parse cfg attributes inside a global_asm call.
macro_rules! cfg_global_asm {
{@inner, [$($x:tt)*], } => {
global_asm!{$($x)*}
};
(@inner, [$($x:tt)*], #[cfg($meta:meta)] $asm:literal, $($rest:tt)*) => {
#[cfg($meta)]
cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*}
#[cfg(not($meta))]
cfg_global_asm!{@inner, [$($x)*], $($rest)*}
};
{@inner, [$($x:tt)*], $asm:literal, $($rest:tt)*} => {
cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*}
};
{$($asms:tt)*} => {
cfg_global_asm!{@inner, [], $($asms)*}
};
}
cfg_global_asm! {
r#"
/*
Entry point of all programs (_start).
It initializes DWARF call frame information, the stack pointer, the
frame pointer (needed for closures to work in start_rust) and the global
pointer. Then it calls _start_rust.
*/
.section .init, "ax"
.global _start
_start:
/* Jump to the absolute address defined by the linker script. */
lui ra, %hi(_abs_start)
jr %lo(_abs_start)(ra)
_abs_start:
.option norelax
.cfi_startproc
.cfi_undefined ra
"#,
#[cfg(feature = "has-mie-mip")]
r#"
csrw mie, 0
csrw mip, 0
"#,
#[cfg(feature = "zero-bss")]
r#"
la a0, _bss_start
la a1, _bss_end
mv a3, x0
1:
sw a3, 0(a0)
addi a0, a0, 4
blt a0, a1, 1b
"#,
#[cfg(feature = "zero-rtc-fast-bss")]
r#"
la a0, _rtc_fast_bss_start
la a1, _rtc_fast_bss_end
mv a3, x0
1:
sw a3, 0(a0)
addi a0, a0, 4
blt a0, a1, 1b
"#,
#[cfg(feature = "init-data")]
r#"
la a0, _data_start
la a1, _data_end
la a2, _sidata
1:
lw a3, 0(a2)
sw a3, 0(a0)
addi a0, a0, 4
addi a2, a2, 4
blt a0, a1, 1b
"#,
#[cfg(feature = "init-rw-text")]
r#"
la a0, _srwtext
la a1, _erwtext
la a2, _irwtext
1:
lw a3, 0(a2)
sw a3, 0(a0)
addi a0, a0, 4
addi a2, a2, 4
blt a0, a1, 1b
"#,
#[cfg(feature = "init-rtc-fast-data")]
r#"
la a0, _rtc_fast_data_start
la a1, _rtc_fast_data_end
la a2, _irtc_fast_data
1:
lw a3, 0(a2)
sw a3, 0(a0)
addi a0, a0, 4
addi a2, a2, 4
blt a0, a1, 1b
"#,
#[cfg(feature = "init-rtc-fast-text")]
r#"
la a0, _srtc_fast_text
la a1, _ertc_fast_text
la a2, _irtc_fast_text
1:
lw a3, 0(a2)
sw a3, 0(a0)
addi a0, a0, 4
addi a2, a2, 4
blt a0, a1, 1b
"#,
r#"
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
la gp, __global_pointer$
.option pop
// Check hart ID
csrr t2, mhartid
lui t0, %hi(_max_hart_id)
add t0, t0, %lo(_max_hart_id)
bgtu t2, t0, abort
// Allocate stacks
la sp, _stack_start
lui t0, %hi(_hart_stack_size)
add t0, t0, %lo(_hart_stack_size)
beqz t2, 2f // Jump if single-hart
mv t1, t2
mv t3, t0
1:
add t0, t0, t3
addi t1, t1, -1
bnez t1, 1b
2:
sub sp, sp, t0
// Set frame pointer
add s0, sp, zero
jal zero, _start_rust
.cfi_endproc
/*
Trap entry points (_start_trap, _start_trapN for N in 1..=31)
The default implementation saves all registers to the stack and calls
_start_trap_rust, then restores all saved registers before `mret`
*/
.section .trap, "ax"
.weak _start_trap
.weak _start_trap1
.weak _start_trap2
.weak _start_trap3
.weak _start_trap4
.weak _start_trap5
.weak _start_trap6
.weak _start_trap7
.weak _start_trap8
.weak _start_trap9
.weak _start_trap10
.weak _start_trap11
.weak _start_trap12
.weak _start_trap13
.weak _start_trap14
.weak _start_trap15
.weak _start_trap16
.weak _start_trap17
.weak _start_trap18
.weak _start_trap19
.weak _start_trap20
.weak _start_trap21
.weak _start_trap22
.weak _start_trap23
.weak _start_trap24
.weak _start_trap25
.weak _start_trap26
.weak _start_trap27
.weak _start_trap28
.weak _start_trap29
.weak _start_trap30
.weak _start_trap31
_start_trap1:
_start_trap2:
_start_trap3:
_start_trap4:
_start_trap5:
_start_trap6:
_start_trap7:
_start_trap8:
_start_trap9:
_start_trap10:
_start_trap11:
_start_trap12:
_start_trap13:
_start_trap14:
_start_trap15:
_start_trap16:
_start_trap17:
_start_trap18:
_start_trap19:
_start_trap20:
_start_trap21:
_start_trap22:
_start_trap23:
_start_trap24:
_start_trap25:
_start_trap26:
_start_trap27:
_start_trap28:
_start_trap29:
_start_trap30:
_start_trap31:
_start_trap:
addi sp, sp, -40*4
sw ra, 0*4(sp)
sw t0, 1*4(sp)
sw t1, 2*4(sp)
sw t2, 3*4(sp)
sw t3, 4*4(sp)
sw t4, 5*4(sp)
sw t5, 6*4(sp)
sw t6, 7*4(sp)
sw a0, 8*4(sp)
sw a1, 9*4(sp)
sw a2, 10*4(sp)
sw a3, 11*4(sp)
sw a4, 12*4(sp)
sw a5, 13*4(sp)
sw a6, 14*4(sp)
sw a7, 15*4(sp)
sw s0, 16*4(sp)
sw s1, 17*4(sp)
sw s2, 18*4(sp)
sw s3, 19*4(sp)
sw s4, 20*4(sp)
sw s5, 21*4(sp)
sw s6, 22*4(sp)
sw s7, 23*4(sp)
sw s8, 24*4(sp)
sw s9, 25*4(sp)
sw s10, 26*4(sp)
sw s11, 27*4(sp)
sw gp, 28*4(sp)
sw tp, 29*4(sp)
csrrs t1, mepc, x0
sw t1, 31*4(sp)
csrrs t1, mstatus, x0
sw t1, 32*4(sp)
csrrs t1, mcause, x0
sw t1, 33*4(sp)
csrrs t1, mtval, x0
sw t1, 34*4(sp)
addi s0, sp, 40*4
sw s0, 30*4(sp)
add a0, sp, zero
jal ra, _start_trap_rust_hal
lw t1, 31*4(sp)
csrrw x0, mepc, t1
lw t1, 32*4(sp)
csrrw x0, mstatus, t1
lw ra, 0*4(sp)
lw t0, 1*4(sp)
lw t1, 2*4(sp)
lw t2, 3*4(sp)
lw t3, 4*4(sp)
lw t4, 5*4(sp)
lw t5, 6*4(sp)
lw t6, 7*4(sp)
lw a0, 8*4(sp)
lw a1, 9*4(sp)
lw a2, 10*4(sp)
lw a3, 11*4(sp)
lw a4, 12*4(sp)
lw a5, 13*4(sp)
lw a6, 14*4(sp)
lw a7, 15*4(sp)
lw s0, 16*4(sp)
lw s1, 17*4(sp)
lw s2, 18*4(sp)
lw s3, 19*4(sp)
lw s4, 20*4(sp)
lw s5, 21*4(sp)
lw s6, 22*4(sp)
lw s7, 23*4(sp)
lw s8, 24*4(sp)
lw s9, 25*4(sp)
lw s10, 26*4(sp)
lw s11, 27*4(sp)
lw gp, 28*4(sp)
lw tp, 29*4(sp)
lw sp, 30*4(sp)
# SP was restored from the original SP
mret
/* Make sure there is an abort when linking */
.section .text.abort
.globl abort
abort:
j abort
/*
Interrupt vector table (_vector_table)
*/
.section .trap, "ax"
.weak _vector_table
.type _vector_table, @function
.option push
.balign 0x100
.option norelax
.option norvc
_vector_table:
j _start_trap
j _start_trap1
j _start_trap2
j _start_trap3
j _start_trap4
j _start_trap5
j _start_trap6
j _start_trap7
j _start_trap8
j _start_trap9
j _start_trap10
j _start_trap11
j _start_trap12
j _start_trap13
j _start_trap14
j _start_trap15
j _start_trap16
j _start_trap17
j _start_trap18
j _start_trap19
j _start_trap20
j _start_trap21
j _start_trap22
j _start_trap23
j _start_trap24
j _start_trap25
j _start_trap26
j _start_trap27
j _start_trap28
j _start_trap29
j _start_trap30
j _start_trap31
.option pop
"#,
}