Add basic interrupt support for ESP32C3 and ESP32

This commit is contained in:
bjoernQ 2022-01-23 12:10:45 +01:00 committed by Jesse Braham
parent ef5d1ac7f4
commit cac30b7544
20 changed files with 907 additions and 10 deletions

View File

@ -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 }
xtensa-lx-rt = { version = "0.7", features = ["lx6"], optional = true }
procmacros = { path = "../esp-hal-procmacros", package = "esp-hal-procmacros" }
# IMPORTANT:
# Each supported device MUST have its PAC included below along with a
@ -27,7 +28,10 @@ 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", "procmacros/rtc_slow"]
esp32c3 = ["esp32c3_pac", "esp32c3_pac/rt", "riscv"]
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
esp32 = ["esp32_pac", "esp32_pac/rt", "xtensa-lx/lx6", "xtensa-lx-rt/lx6", "procmacros/rtc_slow", "dual_core"]
esp32c3 = ["esp32c3_pac", "esp32c3_pac/rt", "riscv", "single_core"]
esp32s2 = ["esp32s2_pac", "esp32s2_pac/rt", "xtensa-lx/lx6", "xtensa-lx-rt/lx6", "procmacros/rtc_slow", "single_core"] # FIXME
esp32s3 = ["esp32s3_pac", "esp32s3_pac/rt", "xtensa-lx/lx6", "xtensa-lx-rt/lx6", "procmacros/rtc_slow", "dual_core"] # FIXME
single_core = []
dual_core = []

View File

@ -330,10 +330,12 @@ macro_rules! impl_input {
_ => {}
}
}
// a crate using this macro needs to provide gpio_intr_enable
unsafe {
(&*GPIO::ptr()).pin[$pin_num].modify(|_, w|
w
.pin_int_ena().bits(int_enable as u8 | ((nmi_enable as u8) << 1))
.pin_int_ena().bits(crate::gpio_intr_enable(int_enable, nmi_enable))
.pin_int_type().bits(event as u8)
.pin_wakeup_enable().bit(wake_up_from_light_sleep)
);

View File

@ -0,0 +1,295 @@
use riscv::register::mcause;
use crate::{pac::Interrupt, Cpu};
// User code shouldn't usually take the mutable TrapFrame or the TrapFrame in
// general. However this makes things like preemtive multitasking easier in
// future
extern "C" {
fn interrupt1(frame: &mut TrapFrame);
fn interrupt2(frame: &mut TrapFrame);
fn interrupt3(frame: &mut TrapFrame);
fn interrupt4(frame: &mut TrapFrame);
fn interrupt5(frame: &mut TrapFrame);
fn interrupt6(frame: &mut TrapFrame);
fn interrupt7(frame: &mut TrapFrame);
fn interrupt8(frame: &mut TrapFrame);
fn interrupt9(frame: &mut TrapFrame);
fn interrupt10(frame: &mut TrapFrame);
fn interrupt11(frame: &mut TrapFrame);
fn interrupt12(frame: &mut TrapFrame);
fn interrupt13(frame: &mut TrapFrame);
fn interrupt14(frame: &mut TrapFrame);
fn interrupt15(frame: &mut TrapFrame);
fn interrupt16(frame: &mut TrapFrame);
fn interrupt17(frame: &mut TrapFrame);
fn interrupt18(frame: &mut TrapFrame);
fn interrupt19(frame: &mut TrapFrame);
fn interrupt20(frame: &mut TrapFrame);
fn interrupt21(frame: &mut TrapFrame);
fn interrupt22(frame: &mut TrapFrame);
fn interrupt23(frame: &mut TrapFrame);
fn interrupt24(frame: &mut TrapFrame);
fn interrupt25(frame: &mut TrapFrame);
fn interrupt26(frame: &mut TrapFrame);
fn interrupt27(frame: &mut TrapFrame);
fn interrupt28(frame: &mut TrapFrame);
fn interrupt29(frame: &mut TrapFrame);
fn interrupt30(frame: &mut TrapFrame);
fn interrupt31(frame: &mut TrapFrame);
}
/// Interrupt kind
pub enum InterruptKind {
/// Level interrupt
Level,
/// Edge interrupt
Edge,
}
/// Enumeration of available CPU interrupts.
/// It is possible to create a handler for each of the interrupts. (e.g. `interrupt3`)
pub enum CpuInterrupt {
Interrupt1 = 1,
Interrupt2,
Interrupt3,
Interrupt4,
Interrupt5,
Interrupt6,
Interrupt7,
Interrupt8,
Interrupt9,
Interrupt10,
Interrupt11,
Interrupt12,
Interrupt13,
Interrupt14,
Interrupt15,
Interrupt16,
Interrupt17,
Interrupt18,
Interrupt19,
Interrupt20,
Interrupt21,
Interrupt22,
Interrupt23,
Interrupt24,
Interrupt25,
Interrupt26,
Interrupt27,
Interrupt28,
Interrupt29,
Interrupt30,
Interrupt31,
}
/// Interrupt priority levels.
pub enum Priority {
None,
Priority1,
Priority2,
Priority3,
Priority4,
Priority5,
Priority6,
Priority7,
Priority8,
Priority9,
Priority10,
Priority11,
Priority12,
Priority13,
Priority14,
Priority15,
}
/// Enable and assign a peripheral interrupt to an CPU interrupt.
pub fn enable(_core: Cpu, interrupt: Interrupt, which: CpuInterrupt) {
unsafe {
let interrupt_number = interrupt as isize;
let cpu_interrupt_number = which as isize;
let intr = &*crate::pac::INTERRUPT_CORE0::ptr();
let intr_map_base = intr.mac_intr_map.as_ptr();
intr_map_base
.offset(interrupt_number)
.write_volatile(cpu_interrupt_number as u32);
// enable interrupt
intr.cpu_int_enable
.write(|w| w.bits(1 << cpu_interrupt_number));
}
}
/// Disable the given peripheral interrupt.
pub fn disable(_core: Cpu, interrupt: Interrupt) {
unsafe {
let interrupt_number = interrupt as isize;
let intr = &*crate::pac::INTERRUPT_CORE0::ptr();
let intr_map_base = intr.mac_intr_map.as_ptr();
intr_map_base.offset(interrupt_number).write_volatile(0);
}
}
/// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
pub fn set_kind(_core: Cpu, which: CpuInterrupt, kind: InterruptKind) {
unsafe {
let intr = &*crate::pac::INTERRUPT_CORE0::ptr();
let cpu_interrupt_number = which as isize;
let interrupt_type = match kind {
InterruptKind::Level => 0,
InterruptKind::Edge => 1,
};
intr.cpu_int_type.modify(|r, w| {
w.bits(
r.bits() & !(1 << cpu_interrupt_number) | (interrupt_type << cpu_interrupt_number),
)
});
}
}
/// Set the priority level of an CPU interrupt
pub fn set_priority(_core: Cpu, which: CpuInterrupt, priority: Priority) {
unsafe {
let intr = &*crate::pac::INTERRUPT_CORE0::ptr();
let cpu_interrupt_number = which as isize;
let intr_prio_base = intr.cpu_int_pri_0.as_ptr();
intr_prio_base
.offset(cpu_interrupt_number as isize)
.write_volatile(priority as u32);
}
}
/// Clear a CPU interrupt
pub fn clear(_core: Cpu, which: CpuInterrupt) {
unsafe {
let cpu_interrupt_number = which as isize;
let intr = &*crate::pac::INTERRUPT_CORE0::ptr();
intr.cpu_int_clear
.write(|w| w.bits(1 << cpu_interrupt_number));
}
}
/// Get status of peripheral interrupts
pub fn get_status(_core: Cpu) -> u128 {
unsafe {
((*crate::pac::INTERRUPT_CORE0::ptr())
.intr_status_reg_0
.read()
.bits() as u128)
| ((*crate::pac::INTERRUPT_CORE0::ptr())
.intr_status_reg_1
.read()
.bits() as u128)
<< 32
}
}
// TODO should this be aligned with Atomic Emulation Trap Handler in future?
/// Registers saved in trap handler
#[doc(hidden)]
#[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,
}
/// # Safety
///
/// This function is called from an assembly trap handler.
#[doc(hidden)]
#[link_section = ".trap.rust"]
#[export_name = "_start_trap_rust_hal"]
pub unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
extern "C" {
pub fn _start_trap_rust(trap_frame: *const TrapFrame);
pub fn DefaultHandler();
}
let cause = mcause::read();
if cause.is_exception() {
_start_trap_rust(trap_frame);
} else {
let code = riscv::register::mcause::read().code();
match code {
1 => interrupt1(trap_frame.as_mut().unwrap()),
2 => interrupt2(trap_frame.as_mut().unwrap()),
3 => interrupt3(trap_frame.as_mut().unwrap()),
4 => interrupt4(trap_frame.as_mut().unwrap()),
5 => interrupt5(trap_frame.as_mut().unwrap()),
6 => interrupt6(trap_frame.as_mut().unwrap()),
7 => interrupt7(trap_frame.as_mut().unwrap()),
8 => interrupt8(trap_frame.as_mut().unwrap()),
9 => interrupt9(trap_frame.as_mut().unwrap()),
10 => interrupt10(trap_frame.as_mut().unwrap()),
11 => interrupt11(trap_frame.as_mut().unwrap()),
12 => interrupt12(trap_frame.as_mut().unwrap()),
13 => interrupt13(trap_frame.as_mut().unwrap()),
14 => interrupt14(trap_frame.as_mut().unwrap()),
16 => interrupt16(trap_frame.as_mut().unwrap()),
15 => interrupt15(trap_frame.as_mut().unwrap()),
17 => interrupt17(trap_frame.as_mut().unwrap()),
18 => interrupt18(trap_frame.as_mut().unwrap()),
19 => interrupt19(trap_frame.as_mut().unwrap()),
20 => interrupt20(trap_frame.as_mut().unwrap()),
21 => interrupt21(trap_frame.as_mut().unwrap()),
22 => interrupt22(trap_frame.as_mut().unwrap()),
23 => interrupt23(trap_frame.as_mut().unwrap()),
24 => interrupt24(trap_frame.as_mut().unwrap()),
25 => interrupt25(trap_frame.as_mut().unwrap()),
26 => interrupt26(trap_frame.as_mut().unwrap()),
27 => interrupt27(trap_frame.as_mut().unwrap()),
28 => interrupt28(trap_frame.as_mut().unwrap()),
29 => interrupt29(trap_frame.as_mut().unwrap()),
30 => interrupt30(trap_frame.as_mut().unwrap()),
31 => interrupt31(trap_frame.as_mut().unwrap()),
_ => DefaultHandler(),
};
}
}
#[doc(hidden)]
#[no_mangle]
pub fn _setup_interrupts() {
extern "C" {
static _vector_table: *const u32;
}
unsafe {
let vec_table = &_vector_table as *const _ as usize;
riscv::register::mtvec::write(vec_table, riscv::register::mtvec::TrapMode::Vectored);
};
}

View File

@ -0,0 +1,144 @@
use crate::{pac::Interrupt, Cpu};
extern "C" {
fn level1_interrupt();
fn level2_interrupt();
fn level3_interrupt();
fn level4_interrupt();
fn level5_interrupt();
fn level6_interrupt();
fn level7_interrupt();
}
/// Enumeration of available CPU interrupts
/// It's possible to create one handler per priority level. (e.g `level1_interrupt`)
#[allow(unused)]
pub enum CpuInterrupt {
Interrupt0LevelPriority1 = 0,
Interrupt1LevelPriority1,
Interrupt2LevelPriority1,
Interrupt3LevelPriority1,
Interrupt4LevelPriority1,
Interrupt5LevelPriority1,
Interrupt6Timer0Priority1,
Interrupt7SoftwarePriority1,
Interrupt8LevelPriority1,
Interrupt9LevelPriority1,
Interrupt10EdgePriority1,
Interrupt11ProfilingPriority3,
Interrupt12LevelPriority1,
Interrupt13LevelPriority1,
Interrupt14NmiPriority7,
Interrupt15Timer1Priority3,
Interrupt16Timer2Priority3,
Interrupt17LevelPriority1,
Interrupt18LevelPriority1,
Interrupt19LevelPriority2,
Interrupt20LevelPriority2,
Interrupt21LevelPriority2,
Interrupt22EdgePriority3,
Interrupt23LevelPriority3,
Interrupt24LevelPriority4,
Interrupt25LevelPriority4,
Interrupt26LevelPriority5,
Interrupt27LevelPriority3,
Interrupt28EdgePriority4,
Interrupt29SoftwarePriority3,
Interrupt30EdgePriority4,
Interrupt31EdgePriority5,
}
/// Enable and assign a peripheral interrupt to an CPU interrupt.
pub fn enable(core: Cpu, interrupt: Interrupt, which: CpuInterrupt) {
unsafe {
let interrupt_number = interrupt as isize;
let cpu_interrupt_number = which as isize;
let intr = &*crate::pac::DPORT::ptr();
let intr_map_base = match core {
Cpu::ProCpu => intr.pro_mac_intr_map.as_ptr(),
#[cfg(feature = "dual_core")]
Cpu::AppCpu => intr.app_mac_intr_map.as_ptr(),
#[cfg(feature = "single_core")]
Cpu::AppCpu => intr.pro_mac_intr_map.as_ptr(),
};
intr_map_base
.offset(interrupt_number)
.write_volatile(cpu_interrupt_number as u32);
}
}
/// Disable the given peripheral interrupt.
pub fn disable(core: Cpu, interrupt: Interrupt) {
unsafe {
let interrupt_number = interrupt as isize;
let intr = &*crate::pac::DPORT::ptr();
let intr_map_base = match core {
Cpu::ProCpu => intr.pro_mac_intr_map.as_ptr(),
#[cfg(feature = "dual_core")]
Cpu::AppCpu => intr.app_mac_intr_map.as_ptr(),
#[cfg(feature = "single_core")]
Cpu::AppCpu => intr.pro_mac_intr_map.as_ptr(),
};
intr_map_base.offset(interrupt_number).write_volatile(0);
}
}
/// Clear the given CPU interrupt
pub fn clear(_core: Cpu, which: CpuInterrupt) {
unsafe {
xtensa_lx::interrupt::clear(1 << which as u32);
}
}
/// Get status of peripheral interrupts
pub fn get_status(core: Cpu) -> u128 {
unsafe {
match core {
Cpu::ProCpu => {
((*crate::pac::DPORT::ptr()).pro_intr_status_0.read().bits() as u128)
| ((*crate::pac::DPORT::ptr()).pro_intr_status_1.read().bits() as u128) << 32
| ((*crate::pac::DPORT::ptr()).pro_intr_status_2.read().bits() as u128) << 64
}
Cpu::AppCpu => {
((*crate::pac::DPORT::ptr()).app_intr_status_0.read().bits() as u128)
| ((*crate::pac::DPORT::ptr()).app_intr_status_1.read().bits() as u128) << 32
| ((*crate::pac::DPORT::ptr()).app_intr_status_2.read().bits() as u128) << 64
}
}
}
}
#[xtensa_lx_rt::interrupt(1)]
fn _level1_interrupt() {
unsafe { level1_interrupt() };
}
#[xtensa_lx_rt::interrupt(2)]
fn _level2_interrupt() {
unsafe { level2_interrupt() };
}
#[xtensa_lx_rt::interrupt(3)]
fn _level3_interrupt() {
unsafe { level3_interrupt() };
}
#[xtensa_lx_rt::interrupt(4)]
fn _level4_interrupt() {
unsafe { level4_interrupt() };
}
#[xtensa_lx_rt::interrupt(5)]
fn _level5_interrupt() {
unsafe { level5_interrupt() };
}
#[xtensa_lx_rt::interrupt(6)]
fn _level6_interrupt() {
unsafe { level6_interrupt() };
}
#[xtensa_lx_rt::interrupt(7)]
fn _level7_interrupt() {
unsafe { level7_interrupt() };
}

View File

@ -29,14 +29,26 @@ pub use esp32s3_pac as pac;
pub mod delay;
pub mod gpio;
#[cfg_attr(feature = "esp32", path = "interrupt/xtensa.rs")]
#[cfg_attr(feature = "esp32c3", path = "interrupt/riscv.rs")]
pub mod interrupt;
pub mod prelude;
pub mod serial;
pub mod timer;
pub use delay::Delay;
pub use gpio::*;
pub use interrupt::*;
use procmacros;
pub use procmacros::ram;
pub use serial::Serial;
pub use timer::Timer;
use procmacros;
pub use procmacros::ram;
/// Enumeration of CPU cores
/// The actual number of available cores depends on the target.
pub enum Cpu {
/// The fist core
ProCpu = 0,
/// The second core
AppCpu,
}

16
esp32-hal/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
"rust-analyzer.cargo.features": [],
"rust-analyzer.cargo.allFeatures": false,
"editor.formatOnSave": true,
"rust-analyzer.checkOnSave.allTargets": false,
"rust-analyzer.checkOnSave.allFeatures": false,
"rust-analyzer.cargo.runBuildScripts": false,
"rust-analyzer.checkOnSave.overrideCommand": [
"cargo",
"check",
"--message-format=json",
"-Z",
"build-std=core",
"--examples"
]
}

View File

@ -13,6 +13,13 @@ 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();
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,

View File

@ -0,0 +1,95 @@
#![no_std]
#![no_main]
use core::{cell::RefCell, fmt::Write};
use esp32_hal::{
gpio::{Gpio0, IO},
pac::{self, Peripherals, UART0},
prelude::*,
Delay,
Serial,
Timer,
};
use esp_hal_common::{
gpio::{Event, Pin},
interrupt,
Cpu,
Input,
PullDown,
};
use panic_halt as _;
use xtensa_lx::mutex::{Mutex, SpinLockMutex};
use xtensa_lx_rt::entry;
static mut SERIAL: SpinLockMutex<RefCell<Option<Serial<UART0>>>> =
SpinLockMutex::new(RefCell::new(None));
static mut BUTTON: SpinLockMutex<RefCell<Option<Gpio0<Input<PullDown>>>>> =
SpinLockMutex::new(RefCell::new(None));
#[entry]
fn main() -> ! {
let peripherals = Peripherals::take().unwrap();
// Disable the TIMG watchdog timer.
let mut timer0 = Timer::new(peripherals.TIMG0);
let serial0 = Serial::new(peripherals.UART0).unwrap();
timer0.disable();
// Set GPIO15 as an output, and set its state high initially.
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
let mut led = io.pins.gpio15.into_push_pull_output();
let mut button = io.pins.gpio0.into_pull_down_input();
button.listen(Event::FallingEdge);
unsafe {
(&SERIAL).lock(|data| (*data).replace(Some(serial0)));
(&BUTTON).lock(|data| (*data).replace(Some(button)));
}
interrupt::enable(
Cpu::ProCpu,
pac::Interrupt::GPIO,
interrupt::CpuInterrupt::Interrupt1LevelPriority1,
);
led.set_high().unwrap();
// Initialize the Delay peripheral, and use it to toggle the LED state in a
// loop.
let mut delay = Delay::new();
unsafe {
xtensa_lx::interrupt::enable();
}
loop {
led.toggle().unwrap();
delay.delay_ms(500u32);
}
}
#[no_mangle]
pub fn level1_interrupt() {
unsafe {
(&SERIAL).lock(|data| {
let mut serial = data.borrow_mut();
let serial = serial.as_mut().unwrap();
writeln!(serial, "Interrupt").ok();
});
}
interrupt::clear(
Cpu::ProCpu,
interrupt::CpuInterrupt::Interrupt1LevelPriority1,
);
unsafe {
(&BUTTON).lock(|data| {
let mut button = data.borrow_mut();
let button = button.as_mut().unwrap();
button.clear_interrupt();
});
}
}

7
esp32-hal/hal-defaults.x Normal file
View File

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

View File

@ -7,7 +7,7 @@ pub use self::gpio::IO;
pub mod gpio;
pub use esp_hal_common::ram;
pub use esp_hal_common::{interrupt, ram, Cpu};
#[no_mangle]
extern "C" fn DefaultHandler(_level: u32, _interrupt: pac::Interrupt) {}
@ -55,3 +55,10 @@ pub unsafe extern "C" fn ESP32Reset() -> ! {
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)
}

View File

@ -4,3 +4,6 @@ rustflags = [
[build]
target = "riscv32imc-unknown-none-elf"
[unstable]
build-std = [ "core" ]

16
esp32c3-hal/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
"rust-analyzer.cargo.features": [],
"rust-analyzer.cargo.allFeatures": false,
"editor.formatOnSave": true,
"rust-analyzer.checkOnSave.allTargets": false,
"rust-analyzer.checkOnSave.allFeatures": false,
"rust-analyzer.cargo.runBuildScripts": false,
"rust-analyzer.checkOnSave.overrideCommand": [
"cargo",
"check",
"--message-format=json",
"-Z",
"build-std=core",
"--examples"
]
}

View File

@ -43,6 +43,9 @@ git = "https://github.com/MabezDev/riscv-rt"
rev = "6b55e4aa3895924e31bcd151f2f0ab840836fa07"
optional = true
[build-dependencies]
riscv-target = "0.1.2"
[dev-dependencies]
panic-halt = "0.2"

101
esp32c3-hal/asm.S Normal file
View File

@ -0,0 +1,101 @@
#define STORE sw
#define LOAD lw
#define LOG_REGBYTES 2
#define REGBYTES (1 << LOG_REGBYTES)
/*
Trap entry point (_start_trap)
Saves registers and calls _start_trap_rust_hal,
restores registers and then returns.
*/
.section .trap, "ax"
.global _start_trap_hal
.option norelax
.align 6
_start_trap_hal:
addi sp, sp, -32*REGBYTES
STORE ra, 0*REGBYTES(sp)
STORE t0, 1*REGBYTES(sp)
STORE t1, 2*REGBYTES(sp)
STORE t2, 3*REGBYTES(sp)
STORE t3, 4*REGBYTES(sp)
STORE t4, 5*REGBYTES(sp)
STORE t5, 6*REGBYTES(sp)
STORE t6, 7*REGBYTES(sp)
STORE a0, 8*REGBYTES(sp)
STORE a1, 9*REGBYTES(sp)
STORE a2, 10*REGBYTES(sp)
STORE a3, 11*REGBYTES(sp)
STORE a4, 12*REGBYTES(sp)
STORE a5, 13*REGBYTES(sp)
STORE a6, 14*REGBYTES(sp)
STORE a7, 15*REGBYTES(sp)
STORE s0, 16*REGBYTES(sp)
STORE s1, 17*REGBYTES(sp)
STORE s2, 18*REGBYTES(sp)
STORE s3, 19*REGBYTES(sp)
STORE s4, 20*REGBYTES(sp)
STORE s5, 21*REGBYTES(sp)
STORE s6, 22*REGBYTES(sp)
STORE s7, 23*REGBYTES(sp)
STORE s8, 24*REGBYTES(sp)
STORE s9, 25*REGBYTES(sp)
STORE s10, 26*REGBYTES(sp)
STORE s11, 27*REGBYTES(sp)
STORE gp, 28*REGBYTES(sp)
STORE tp, 29*REGBYTES(sp)
addi s0, sp, 32*REGBYTES
STORE s0, 30*REGBYTES(sp)
add a0, sp, zero
jal ra, _start_trap_rust_hal
LOAD ra, 0*REGBYTES(sp)
LOAD t0, 1*REGBYTES(sp)
LOAD t1, 2*REGBYTES(sp)
LOAD t2, 3*REGBYTES(sp)
LOAD t3, 4*REGBYTES(sp)
LOAD t4, 5*REGBYTES(sp)
LOAD t5, 6*REGBYTES(sp)
LOAD t6, 7*REGBYTES(sp)
LOAD a0, 8*REGBYTES(sp)
LOAD a1, 9*REGBYTES(sp)
LOAD a2, 10*REGBYTES(sp)
LOAD a3, 11*REGBYTES(sp)
LOAD a4, 12*REGBYTES(sp)
LOAD a5, 13*REGBYTES(sp)
LOAD a6, 14*REGBYTES(sp)
LOAD a7, 15*REGBYTES(sp)
LOAD s0, 16*REGBYTES(sp)
LOAD s1, 17*REGBYTES(sp)
LOAD s2, 18*REGBYTES(sp)
LOAD s3, 19*REGBYTES(sp)
LOAD s4, 20*REGBYTES(sp)
LOAD s5, 21*REGBYTES(sp)
LOAD s6, 22*REGBYTES(sp)
LOAD s7, 23*REGBYTES(sp)
LOAD s8, 24*REGBYTES(sp)
LOAD s9, 25*REGBYTES(sp)
LOAD s10, 26*REGBYTES(sp)
LOAD s11, 27*REGBYTES(sp)
LOAD gp, 28*REGBYTES(sp)
LOAD tp, 29*REGBYTES(sp)
LOAD sp, 30*REGBYTES(sp)
# SP was restored from the original SP
mret
.section .trap, "ax"
.balign 0x100
.global _vector_table
.type _vector_table, @function
_vector_table:
.option push
.option norvc
.rept 31
j _start_trap_hal
.endr

3
esp32c3-hal/asm.bat Normal file
View File

@ -0,0 +1,3 @@
riscv32-esp-elf-gcc -ggdb3 -c -mabi=ilp32 -march=rv32i asm.S -o bin/esp32c3asm.o
riscv32-esp-elf-ar crs bin/asm_riscv32i-unknown-none-elf.a bin/esp32c3asm.o
del bin\esp32c3asm.o

Binary file not shown.

View File

@ -1,4 +1,11 @@
use std::{env, fs::File, io::Write, path::PathBuf};
use std::{
env,
fs::{self, File},
io::Write,
path::PathBuf,
};
use riscv_target::Target;
#[cfg(not(feature = "normalboot"))]
fn main() {
@ -26,6 +33,9 @@ fn main() {
// 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");
add_defaults();
prepare_trap();
}
#[cfg(feature = "normalboot")]
@ -49,4 +59,40 @@ fn main() {
println!("cargo:rerun-if-changed=memory.x");
println!("cargo:rustc-link-arg=-Tmemory.x");
println!("cargo:rustc-link-arg=-Tbl-riscv-link.x");
add_defaults();
prepare_trap();
}
fn add_defaults() {
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("hal-defaults.x"))
.unwrap()
.write_all(include_bytes!("hal-defaults.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
println!("cargo:rustc-link-arg=-Thal-defaults.x");
}
fn prepare_trap() {
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
let name = env::var("CARGO_PKG_NAME").unwrap();
let target = env::var("TARGET").unwrap();
if target.starts_with("riscv") {
let mut target = Target::from_target_str(&target);
target.retain_extensions("if");
let target = target.to_string();
fs::copy(
format!("bin/asm_{}.a", target),
out.join(format!("lib{}.a", name)),
)
.unwrap();
println!("cargo:rustc-link-lib=static={}", name);
println!("cargo:rustc-link-search={}", out.display());
}
}

View File

@ -0,0 +1,99 @@
#![no_std]
#![no_main]
use core::{cell::RefCell, fmt::Write};
use bare_metal::Mutex;
use esp32c3_hal::{
gpio::{Gpio9, IO},
pac::{self, Peripherals, UART0},
prelude::*,
Delay,
RtcCntl,
Serial,
Timer,
};
use esp_hal_common::{
interrupt::{self},
Cpu,
Event,
Input,
Pin,
PullDown,
};
use panic_halt as _;
use riscv_rt::entry;
static mut SERIAL: Mutex<RefCell<Option<Serial<UART0>>>> = Mutex::new(RefCell::new(None));
static mut BUTTON: Mutex<RefCell<Option<Gpio9<Input<PullDown>>>>> = Mutex::new(RefCell::new(None));
#[entry]
fn main() -> ! {
let peripherals = Peripherals::take().unwrap();
// Disable the watchdog timers. For the ESP32-C3, this includes the Super WDT,
// the RTC WDT, and the TIMG WDTs.
let rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL);
let mut timer0 = Timer::new(peripherals.TIMG0);
let mut timer1 = Timer::new(peripherals.TIMG1);
let serial0 = Serial::new(peripherals.UART0).unwrap();
rtc_cntl.set_super_wdt_enable(false);
rtc_cntl.set_wdt_enable(false);
timer0.disable();
timer1.disable();
// Set GPIO5 as an output
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
let mut led = io.pins.gpio5.into_push_pull_output();
// Set GPIO9 as an input
let mut button = io.pins.gpio9.into_pull_down_input();
button.listen(Event::FallingEdge);
riscv::interrupt::free(|_cs| unsafe {
SERIAL.get_mut().replace(Some(serial0));
BUTTON.get_mut().replace(Some(button));
});
interrupt::enable(
Cpu::ProCpu,
pac::Interrupt::GPIO,
interrupt::CpuInterrupt::Interrupt3,
);
interrupt::set_kind(
Cpu::ProCpu,
interrupt::CpuInterrupt::Interrupt3,
interrupt::InterruptKind::Level,
);
interrupt::set_priority(
Cpu::ProCpu,
interrupt::CpuInterrupt::Interrupt3,
interrupt::Priority::Priority1,
);
unsafe {
riscv::interrupt::enable();
}
let mut delay = Delay::new(peripherals.SYSTIMER);
loop {
led.toggle().unwrap();
delay.delay_ms(500u32);
}
}
#[no_mangle]
pub fn interrupt3() {
riscv::interrupt::free(|cs| unsafe {
let mut serial = SERIAL.borrow(*cs).borrow_mut();
let serial = serial.as_mut().unwrap();
let mut button = BUTTON.borrow(*cs).borrow_mut();
let button = button.as_mut().unwrap();
writeln!(serial, "Interrupt").ok();
interrupt::clear(Cpu::ProCpu, interrupt::CpuInterrupt::Interrupt3);
button.clear_interrupt();
});
}

View File

@ -0,0 +1,31 @@
PROVIDE(interrupt1 = DefaultHandler);
PROVIDE(interrupt2 = DefaultHandler);
PROVIDE(interrupt3 = DefaultHandler);
PROVIDE(interrupt4 = DefaultHandler);
PROVIDE(interrupt5 = DefaultHandler);
PROVIDE(interrupt6 = DefaultHandler);
PROVIDE(interrupt7 = DefaultHandler);
PROVIDE(interrupt8 = DefaultHandler);
PROVIDE(interrupt9 = DefaultHandler);
PROVIDE(interrupt10 = DefaultHandler);
PROVIDE(interrupt11 = DefaultHandler);
PROVIDE(interrupt12 = DefaultHandler);
PROVIDE(interrupt13 = DefaultHandler);
PROVIDE(interrupt14 = DefaultHandler);
PROVIDE(interrupt15 = DefaultHandler);
PROVIDE(interrupt16 = DefaultHandler);
PROVIDE(interrupt17 = DefaultHandler);
PROVIDE(interrupt18 = DefaultHandler);
PROVIDE(interrupt19 = DefaultHandler);
PROVIDE(interrupt20 = DefaultHandler);
PROVIDE(interrupt21 = DefaultHandler);
PROVIDE(interrupt22 = DefaultHandler);
PROVIDE(interrupt23 = DefaultHandler);
PROVIDE(interrupt24 = DefaultHandler);
PROVIDE(interrupt25 = DefaultHandler);
PROVIDE(interrupt26 = DefaultHandler);
PROVIDE(interrupt27 = DefaultHandler);
PROVIDE(interrupt28 = DefaultHandler);
PROVIDE(interrupt29 = DefaultHandler);
PROVIDE(interrupt30 = DefaultHandler);
PROVIDE(interrupt31 = DefaultHandler);

View File

@ -8,7 +8,7 @@ use riscv_rt::pre_init;
pub mod gpio;
pub mod rtc_cntl;
pub use esp_hal_common::ram;
pub use esp_hal_common::{interrupt, ram, Cpu};
pub use self::{gpio::IO, rtc_cntl::RtcCntl};
@ -40,6 +40,7 @@ extern "C" {
#[cfg(not(feature = "normalboot"))]
#[pre_init]
#[cfg(not(feature = "normalboot"))]
#[doc(hidden)]
unsafe fn init() {
r0::init_data(&mut _srwtext, &mut _erwtext, &_irwtext);
@ -54,6 +55,7 @@ unsafe fn init() {
#[allow(unreachable_code)]
#[export_name = "_mp_hook"]
#[doc(hidden)]
pub fn mp_hook() -> bool {
unsafe {
r0::zero_bss(&mut _rtc_fast_bss_start, &mut _rtc_fast_bss_end);
@ -69,3 +71,7 @@ pub fn mp_hook() -> bool {
false
}
fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 {
int_enable as u8 | ((nmi_enable as u8) << 1)
}