183 lines
5.7 KiB
Rust
183 lines
5.7 KiB
Rust
#![no_std]
|
|
#![no_main]
|
|
|
|
//! Bring-up probe for ESP32-P4 SYSTIMER (base 0x500E_2000).
|
|
//!
|
|
//! Validates that we can read the 64-bit free-running counter UNIT0 via the
|
|
//! snapshot dance (WT bit 30 in UNIT0_OP, poll bit 29, then read VAL_HI/LO).
|
|
//! Prints DATE register, CONF, several timed snapshots, and an estimated tick
|
|
//! rate by comparing two reads spaced by a long busy-wait.
|
|
//!
|
|
//! Prerequisites assumed by HW reset state:
|
|
//! - SYSTIMER bus clock: HP_SYS_CLKRST.SOC_CLK_CTRL2 bit 23 default = 1.
|
|
//! - SYSTIMER reset: HP_SYS_CLKRST.HP_RST_EN1 bit 5 default = 0 (not in reset).
|
|
//! - SYSTIMER UNIT0: CONF.timer_unit0_work_en (bit 30) default = 1.
|
|
//! Expected source: XTAL 40 MHz / 2.5 = 16 MHz tick (62.5 ns/tick).
|
|
|
|
use core::fmt::{self, Write};
|
|
use core::hint::spin_loop;
|
|
use core::ptr::{read_volatile, write_volatile};
|
|
|
|
use panic_halt as _;
|
|
use riscv_rt::entry;
|
|
|
|
const UART0_BASE: usize = 0x500C_A000;
|
|
const UART_FIFO_REG: *mut u32 = UART0_BASE as *mut u32;
|
|
const UART_STATUS_REG: *const u32 = (UART0_BASE + 0x1C) as *const u32;
|
|
const UART_TXFIFO_CNT_SHIFT: u32 = 16;
|
|
const UART_TXFIFO_CNT_MASK: u32 = 0xFF << UART_TXFIFO_CNT_SHIFT;
|
|
const UART_TXFIFO_CAPACITY: u32 = 128;
|
|
|
|
const SYSTIMER_BASE: usize = 0x500E_2000;
|
|
const SYSTIMER_CONF: *mut u32 = SYSTIMER_BASE as *mut u32;
|
|
const SYSTIMER_UNIT0_OP: *mut u32 = (SYSTIMER_BASE + 0x04) as *mut u32;
|
|
const SYSTIMER_UNIT0_VAL_HI: *const u32 = (SYSTIMER_BASE + 0x40) as *const u32;
|
|
const SYSTIMER_UNIT0_VAL_LO: *const u32 = (SYSTIMER_BASE + 0x44) as *const u32;
|
|
const SYSTIMER_DATE: *const u32 = (SYSTIMER_BASE + 0xFC) as *const u32;
|
|
|
|
const HP_CLKRST_SOC_CLK_CTRL2: *const u32 = 0x500E_601C as *const u32;
|
|
const HP_CLKRST_PERI_CLK_CTRL21: *const u32 = 0x500E_6098 as *const u32;
|
|
const HP_CLKRST_HP_RST_EN1: *const u32 = 0x500E_60C4 as *const u32;
|
|
|
|
const UNIT_OP_UPDATE_BIT: u32 = 1 << 30;
|
|
const UNIT_OP_VALID_BIT: u32 = 1 << 29;
|
|
|
|
struct Uart0;
|
|
|
|
impl Uart0 {
|
|
fn write_byte(&mut self, byte: u8) {
|
|
while txfifo_count() >= UART_TXFIFO_CAPACITY {
|
|
spin_loop();
|
|
}
|
|
unsafe {
|
|
write_volatile(UART_FIFO_REG, byte as u32);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Write for Uart0 {
|
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
for byte in s.bytes() {
|
|
if byte == b'\n' {
|
|
self.write_byte(b'\r');
|
|
}
|
|
self.write_byte(byte);
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn txfifo_count() -> u32 {
|
|
let status = unsafe { read_volatile(UART_STATUS_REG) };
|
|
(status & UART_TXFIFO_CNT_MASK) >> UART_TXFIFO_CNT_SHIFT
|
|
}
|
|
|
|
fn delay_cycles(spins: usize) {
|
|
for _ in 0..spins {
|
|
spin_loop();
|
|
}
|
|
}
|
|
|
|
fn flush_uart() {
|
|
while txfifo_count() != 0 {
|
|
spin_loop();
|
|
}
|
|
}
|
|
|
|
/// Atomic 64-bit read of UNIT0 via the snapshot dance.
|
|
fn read_unit0() -> u64 {
|
|
unsafe {
|
|
write_volatile(SYSTIMER_UNIT0_OP, UNIT_OP_UPDATE_BIT);
|
|
// Poll value_valid; it is set once the snapshot is committed.
|
|
while read_volatile(SYSTIMER_UNIT0_OP) & UNIT_OP_VALID_BIT == 0 {
|
|
spin_loop();
|
|
}
|
|
let hi = read_volatile(SYSTIMER_UNIT0_VAL_HI) as u64;
|
|
let lo = read_volatile(SYSTIMER_UNIT0_VAL_LO) as u64;
|
|
(hi << 32) | lo
|
|
}
|
|
}
|
|
|
|
#[entry]
|
|
fn main() -> ! {
|
|
let mut uart = Uart0;
|
|
let _ = writeln!(uart, "\n=== systimer_probe alive ===");
|
|
flush_uart();
|
|
|
|
let date = unsafe { read_volatile(SYSTIMER_DATE) };
|
|
let conf = unsafe { read_volatile(SYSTIMER_CONF) };
|
|
let soc_clk_ctrl2 = unsafe { read_volatile(HP_CLKRST_SOC_CLK_CTRL2) };
|
|
let peri_clk_ctrl21 = unsafe { read_volatile(HP_CLKRST_PERI_CLK_CTRL21) };
|
|
let hp_rst_en1 = unsafe { read_volatile(HP_CLKRST_HP_RST_EN1) };
|
|
|
|
let _ = writeln!(uart, "SYSTIMER_DATE = 0x{:08X}", date);
|
|
let _ = writeln!(uart, "SYSTIMER_CONF = 0x{:08X}", conf);
|
|
let _ = writeln!(
|
|
uart,
|
|
" decoded: clk_en={} unit0_work_en={} unit1_work_en={} target0_en={}",
|
|
(conf >> 31) & 1,
|
|
(conf >> 30) & 1,
|
|
(conf >> 29) & 1,
|
|
(conf >> 24) & 1,
|
|
);
|
|
let _ = writeln!(
|
|
uart,
|
|
"SOC_CLK_CTRL2 = 0x{:08X} (bit23 SYSTIMER_APB_CLK_EN={})",
|
|
soc_clk_ctrl2,
|
|
(soc_clk_ctrl2 >> 23) & 1,
|
|
);
|
|
let _ = writeln!(
|
|
uart,
|
|
"PERI_CLK_CTRL21 = 0x{:08X} (bit29 CLK_SRC_SEL={} 0=XTAL,1=RC_FAST)",
|
|
peri_clk_ctrl21,
|
|
(peri_clk_ctrl21 >> 29) & 1,
|
|
);
|
|
let _ = writeln!(
|
|
uart,
|
|
"HP_RST_EN1 = 0x{:08X} (bit5 RST_EN_STIMER={})",
|
|
hp_rst_en1,
|
|
(hp_rst_en1 >> 5) & 1,
|
|
);
|
|
flush_uart();
|
|
|
|
let _ = writeln!(uart, "--- 5 snapshots, ~5ms apart ---");
|
|
flush_uart();
|
|
let mut prev = 0u64;
|
|
for i in 0..5 {
|
|
let v = read_unit0();
|
|
let _ = writeln!(uart, " t[{}] = {:>14} (delta = {})", i, v, v - prev);
|
|
flush_uart();
|
|
prev = v;
|
|
delay_cycles(50_000);
|
|
}
|
|
|
|
// Linearity sweep — three different busy-wait lengths. If counter rate is
|
|
// constant, ticks/spin should match across all three. The previous run
|
|
// measured ~44 ticks per spin_loop iter at espflash --ram boot (CPU not
|
|
// boosted past XTAL by IDF system_init).
|
|
for spins in [50_000usize, 200_000, 1_000_000] {
|
|
let t0 = read_unit0();
|
|
delay_cycles(spins);
|
|
let t1 = read_unit0();
|
|
let dt = t1 - t0;
|
|
let per_spin = dt / spins as u64;
|
|
let _ = writeln!(
|
|
uart,
|
|
" spins={:>8} delta_ticks={:>10} ticks/spin={}",
|
|
spins, dt, per_spin
|
|
);
|
|
flush_uart();
|
|
}
|
|
let _ = writeln!(
|
|
uart,
|
|
" expected systimer rate = XTAL 40 MHz / 2.5 = 16 MHz (62.5 ns/tick)"
|
|
);
|
|
flush_uart();
|
|
|
|
let _ = writeln!(uart, "=== systimer_probe done ===");
|
|
flush_uart();
|
|
loop {
|
|
spin_loop();
|
|
}
|
|
}
|