#![no_std]
pub mod gpio;
use rivet_arch_riscv as _;
esp_bootloader_esp_idf::esp_app_desc!();
const CPU_HZ: u32 = 80_000_000;
const WDT_WKEY_UNLOCK: u32 = 0x50D8_3AA1;
const WDT_WKEY_LOCK: u32 = 0;
const SWD_WKEY_UNLOCK: u32 = 0x50D8_3AA1;
const SWD_WKEY_LOCK: u32 = 0;
pub fn disable_watchdogs() {
unsafe {
let lp_wdt = &*esp32c6::LP_WDT::ptr();
lp_wdt.wdtwprotect().write(|w| w.bits(WDT_WKEY_UNLOCK));
lp_wdt.wdtconfig0().write(|w| w.bits(0));
lp_wdt.wdtwprotect().write(|w| w.bits(WDT_WKEY_LOCK));
lp_wdt.swd_wprotect().write(|w| w.swd_wkey().bits(SWD_WKEY_UNLOCK));
lp_wdt.swd_conf().write(|w| w.swd_auto_feed_en().set_bit());
lp_wdt.swd_wprotect().write(|w| w.swd_wkey().bits(SWD_WKEY_LOCK));
let timg0 = &*esp32c6::TIMG0::ptr();
timg0.wdtwprotect().write(|w| w.wdt_wkey().bits(WDT_WKEY_UNLOCK));
timg0.wdtconfig0().write(|w| w.bits(0));
timg0.wdtwprotect().write(|w| w.wdt_wkey().bits(WDT_WKEY_LOCK));
}
}
#[no_mangle]
extern "Rust" fn __rivet_board_init() {
disable_watchdogs();
unsafe {
systimer_init();
}
}
#[no_mangle]
unsafe extern "Rust" fn __rivet_board_console_write(ptr: *const u8, len: usize) {
let bytes = unsafe { core::slice::from_raw_parts(ptr, len) };
rivet::critical::enter(|| {
let uart0 = unsafe { &*esp32c6::UART0::ptr() };
for &b in bytes {
while uart0.status().read().txfifo_cnt().bits() >= 124 {
core::hint::spin_loop();
}
uart0.fifo().write(|w| unsafe { w.rxfifo_rd_byte().bits(b) });
}
});
}
#[no_mangle]
extern "Rust" fn __rivet_board_console_kick_tx() {
}
unsafe fn systimer_init() {
unsafe {
let systimer = &*esp32c6::SYSTIMER::ptr();
systimer.conf().modify(|_, w| w.timer_unit0_work_en().set_bit());
}
}
fn systimer_ticks() -> u64 {
unsafe {
let systimer = &*esp32c6::SYSTIMER::ptr();
systimer.unit_op(0).write(|w| w.update().set_bit());
while !systimer.unit_op(0).read().value_valid().bit_is_set() {}
let unit_value = systimer.unit_value(0);
let mut lo_prev = unit_value.lo().read().bits();
loop {
let lo = lo_prev;
let hi = unit_value.hi().read().bits();
lo_prev = unit_value.lo().read().bits();
if lo == lo_prev {
break ((hi as u64) << 32) | lo as u64;
}
}
}
}
#[no_mangle]
extern "Rust" fn __rivet_board_now_us() -> u64 {
systimer_ticks() / 16
}
#[no_mangle]
extern "Rust" fn __rivet_arch_cycle_count() -> u64 {
systimer_ticks()
}
#[no_mangle]
extern "Rust" fn __rivet_board_reset() -> ! {
let deadline = __rivet_board_now_us().wrapping_add(20_000);
while __rivet_board_now_us() < deadline {
core::hint::spin_loop();
}
unsafe {
let lp_wdt = &*esp32c6::LP_WDT::ptr();
lp_wdt.wdtwprotect().write(|w| w.bits(WDT_WKEY_UNLOCK));
lp_wdt.wdtconfig(0).write(|w| w.hold().bits(1));
lp_wdt.wdtconfig0().write(|w| {
w.wdt_pause_in_slp().set_bit();
w.wdt_cpu_reset_length().bits(7);
w.wdt_sys_reset_length().bits(7);
w.wdt_stg0().bits(4); w.wdt_en().set_bit()
});
lp_wdt.wdtwprotect().write(|w| w.bits(WDT_WKEY_LOCK));
}
loop {
core::hint::spin_loop();
}
}
#[no_mangle]
extern "Rust" fn __rivet_board_exit(code: u32) -> ! {
if code == 0 {
rivet::console::write_str("RIVET_EXIT_OK\n");
} else {
rivet::console::write_str("RIVET_EXIT_FAIL code=");
print_dec(code as usize);
rivet::console::write_str("\n");
}
rivet::console::flush_sync();
let deadline = __rivet_board_now_us().wrapping_add(20_000);
while __rivet_board_now_us() < deadline {
core::hint::spin_loop();
}
loop {
core::hint::spin_loop();
}
}
fn print_dec(mut n: usize) {
if n == 0 {
rivet::console::write_str("0");
return;
}
let mut digits = [0u8; 10];
let mut i = 0;
while n > 0 {
digits[i] = b'0' + (n % 10) as u8;
n /= 10;
i += 1;
}
let mut buf = [0u8; 10];
for j in 0..i {
buf[j] = digits[i - 1 - j];
}
if let Ok(s) = core::str::from_utf8(&buf[..i]) {
rivet::console::write_str(s);
}
}
#[no_mangle]
extern "Rust" fn __rivet_board_cpu_hz() -> u32 {
CPU_HZ
}
#[no_mangle]
extern "Rust" fn __rivet_board_wdt_init(period_us: u32) {
rivet_bsp_support::sw_watchdog::init(period_us, __rivet_board_now_us());
}
#[no_mangle]
extern "Rust" fn __rivet_board_wdt_feed() {
rivet_bsp_support::sw_watchdog::feed(__rivet_board_now_us());
}
#[no_mangle]
extern "Rust" fn __rivet_board_wdt_check() {
if rivet_bsp_support::sw_watchdog::expired(__rivet_board_now_us()) {
rivet::console::write_str("RIVET WATCHDOG TIMEOUT\n");
rivet::port::board::reset();
}
}
const SOURCE_FROM_CPU_INTR0: usize = 22;
const SOURCE_SYSTIMER_TARGET0: usize = 57;
const LINE_TICK: u32 = 16;
const LINE_RESCHED: u32 = 17;
static TICK_PERIOD_TICKS: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
static MATRIX_CONFIGURED: core::sync::atomic::AtomicBool =
core::sync::atomic::AtomicBool::new(false);
fn configure_interrupt_matrix() {
if MATRIX_CONFIGURED.swap(true, core::sync::atomic::Ordering::AcqRel) {
return;
}
unsafe {
let systimer = &*esp32c6::SYSTIMER::ptr();
systimer.int_clr().write(|w| w.target0().clear_bit_by_one());
let intpri_ack = &*esp32c6::INTPRI::ptr();
intpri_ack.cpu_intr_from_cpu(0).write(|w| w.cpu_intr().clear_bit());
let core0 = &*esp32c6::INTERRUPT_CORE0::ptr();
core0
.core_0_intr_map(SOURCE_SYSTIMER_TARGET0)
.write(|w| w.bits(LINE_TICK));
core0
.core_0_intr_map(SOURCE_FROM_CPU_INTR0)
.write(|w| w.bits(LINE_RESCHED));
let plic_mx = &*esp32c6::PLIC_MX::ptr();
plic_mx.mxint_pri(LINE_TICK as usize).write(|w| w.cpu_mxint_pri().bits(4));
plic_mx.mxint_pri(LINE_RESCHED as usize).write(|w| w.cpu_mxint_pri().bits(4));
plic_mx.mxint_thresh().write(|w| w.cpu_mxint_thresh().bits(0));
plic_mx.mxint_enable().modify(|r, w| {
w.cpu_mxint_enable().bits(r.cpu_mxint_enable().bits() | (1 << LINE_TICK) | (1 << LINE_RESCHED))
});
core::arch::asm!(
"csrs mie, {0}",
in(reg) (1u32 << LINE_TICK) | (1u32 << LINE_RESCHED),
);
}
}
#[no_mangle]
extern "Rust" fn __rivet_board_tick_start(hz: u32) {
configure_interrupt_matrix();
let period_ticks = (16_000_000u32 / hz).max(1);
TICK_PERIOD_TICKS.store(period_ticks, core::sync::atomic::Ordering::Relaxed);
unsafe {
let systimer = &*esp32c6::SYSTIMER::ptr();
systimer.target_conf(0).write(|w| {
w.period().bits(period_ticks);
w.period_mode().set_bit();
w.timer_unit_sel().clear_bit()
});
systimer.comp_load(0).write(|w| w.load().set_bit());
systimer.conf().modify(|_, w| w.target0_work_en().set_bit());
systimer.int_ena().modify(|_, w| w.target0().set_bit());
}
}
#[no_mangle]
extern "Rust" fn __rivet_board_on_async_trap(code: usize, resume_sp: usize) -> usize {
if code == LINE_TICK as usize {
unsafe {
let systimer = &*esp32c6::SYSTIMER::ptr();
systimer.int_clr().write(|w| w.target0().clear_bit_by_one());
}
rivet::timer::poll_timers(__rivet_board_now_us());
rivet::watchdog::on_tick();
return rivet::preempt::on_tick(resume_sp);
}
if code == LINE_RESCHED as usize {
unsafe {
let intpri = &*esp32c6::INTPRI::ptr();
intpri.cpu_intr_from_cpu(0).write(|w| w.cpu_intr().clear_bit());
}
#[cfg(feature = "latency-histograms")]
rivet::latency::record(
rivet::latency::Kind::IrqEntry,
(rivet::port::arch::cycle_count() as u32)
.wrapping_sub(RESCHEDULE_REQUESTED_AT.load(core::sync::atomic::Ordering::Relaxed))
as u64,
);
return rivet::preempt::on_tick(resume_sp);
}
if (FIRST_IRQ_LINE as usize..FIRST_IRQ_LINE as usize + N_IRQ_LINES).contains(&code) {
let source = IRQ_LINE_SOURCE[code - FIRST_IRQ_LINE as usize]
.load(core::sync::atomic::Ordering::Acquire);
if source != u32::MAX {
rivet::irq::dispatch(source);
}
}
resume_sp
}
#[cfg(feature = "latency-histograms")]
static RESCHEDULE_REQUESTED_AT: core::sync::atomic::AtomicU32 =
core::sync::atomic::AtomicU32::new(0);
#[no_mangle]
extern "Rust" fn __rivet_arch_request_reschedule() {
#[cfg(feature = "latency-histograms")]
RESCHEDULE_REQUESTED_AT.store(
rivet::port::arch::cycle_count() as u32,
core::sync::atomic::Ordering::Relaxed,
);
configure_interrupt_matrix();
unsafe {
let intpri = &*esp32c6::INTPRI::ptr();
intpri.cpu_intr_from_cpu(0).write(|w| w.cpu_intr().set_bit());
}
}
#[no_mangle]
extern "Rust" fn __rivet_arch_request_reschedule_on(_hart: usize) {
__rivet_arch_request_reschedule();
}
const FIRST_IRQ_LINE: u32 = 18;
const N_IRQ_LINES: usize = 6;
static IRQ_LINE_SOURCE: [core::sync::atomic::AtomicU32; N_IRQ_LINES] =
[const { core::sync::atomic::AtomicU32::new(u32::MAX) }; N_IRQ_LINES];
const LOGICAL_TO_SOURCE: [usize; 1] = [43 ];
fn irq_line_for(irq_id: u32) -> Option<u32> {
for (i, slot) in IRQ_LINE_SOURCE.iter().enumerate() {
if slot.load(core::sync::atomic::Ordering::Acquire) == irq_id {
return Some(FIRST_IRQ_LINE + i as u32);
}
}
for (i, slot) in IRQ_LINE_SOURCE.iter().enumerate() {
if slot
.compare_exchange(
u32::MAX,
irq_id,
core::sync::atomic::Ordering::AcqRel,
core::sync::atomic::Ordering::Acquire,
)
.is_ok()
{
return Some(FIRST_IRQ_LINE + i as u32);
}
}
None
}
#[no_mangle]
extern "Rust" fn __rivet_board_irq_enable(irq_id: u32) {
configure_interrupt_matrix();
let Some(line) = irq_line_for(irq_id) else {
return;
};
let Some(&source) = LOGICAL_TO_SOURCE.get(irq_id as usize) else {
return;
};
unsafe {
(&*esp32c6::INTERRUPT_CORE0::ptr())
.core_0_intr_map(source)
.write(|w| w.bits(line));
let plic_mx = &*esp32c6::PLIC_MX::ptr();
plic_mx.mxint_pri(line as usize).write(|w| w.cpu_mxint_pri().bits(4));
plic_mx.mxint_enable().modify(|r, w| {
w.cpu_mxint_enable().bits(r.cpu_mxint_enable().bits() | (1 << line))
});
core::arch::asm!("csrs mie, {0}", in(reg) 1u32 << line);
}
}
#[no_mangle]
extern "Rust" fn __rivet_board_irq_disable(irq_id: u32) {
let Some(line) = irq_line_for(irq_id) else {
return;
};
unsafe {
(&*esp32c6::PLIC_MX::ptr()).mxint_enable().modify(|r, w| {
w.cpu_mxint_enable().bits(r.cpu_mxint_enable().bits() & !(1 << line))
});
}
}
#[no_mangle]
extern "Rust" fn __rivet_board_irq_set_priority(irq_id: u32, priority: u8) {
let Some(line) = irq_line_for(irq_id) else {
return;
};
let pri = ((priority as u32) & 0xF).max(1);
unsafe {
(&*esp32c6::PLIC_MX::ptr())
.mxint_pri(line as usize)
.write(|w| w.cpu_mxint_pri().bits(pri as u8));
}
}
pub mod irq {
pub const UART0: u32 = 0;
}