diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-25 12:40:53 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-25 12:46:51 -0300 |
| commit | f5f8068fac59b4f16046c2022c2fc7c7e447ef4c (patch) | |
| tree | 2731a3ed4e51cae09e184e25778eded5fc37d1f5 /src/oracle/timg_ref.c | |
| download | esp32p4-f5f8068fac59b4f16046c2022c2fc7c7e447ef4c.tar.gz esp32p4-f5f8068fac59b4f16046c2022c2fc7c7e447ef4c.zip | |
zig-p4: pure-Zig ESP32-P4 toolchain
build.zig generates the linker script and drives Zig's own LLD; tools/image.zig
turns the ELF into a flashable image and tools/{rom,serial}.zig speak the mask
ROM loader over the UART. No CMake, ninja, idf.py, esptool, or external linker.
src/soc.zig is a comptime register model over ESP-IDF's own *_reg.h headers;
src/hal/ adds peripheral sequences; src/io/ implements std.Io for the chip;
src/oracle/ diffs this HAL against ESP-IDF's on the die.
Diffstat (limited to 'src/oracle/timg_ref.c')
| -rw-r--r-- | src/oracle/timg_ref.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/src/oracle/timg_ref.c b/src/oracle/timg_ref.c new file mode 100644 index 0000000..38bccc8 --- /dev/null +++ b/src/oracle/timg_ref.c @@ -0,0 +1,197 @@ +/* The reference implementation for the timer groups and their watchdogs, which is ESP-IDF's own. + * + * Thin external-linkage wrappers over `timer_ll.h`, `mwdt_ll.h` and `timg_ll.h`, so Zig can call + * IDF's `static inline` functions and the differential harness can run both implementations in one + * image on one boot. There is no logic here: anything clever would be a third implementation to + * doubt. + * + * Two things about the watchdog wrappers are deliberate. The write-protect dance is *inside* each + * wrapper (`mwdt_ll_write_protect_disable` ... `mwdt_ll_write_protect_enable`) because that is what + * IDF's callers do - `mwdt_ll_config_stage` itself will silently do nothing if protection is on - + * and because our side does the same thing through `timg.unlock`/`release`. The two sides have to be + * the same operation, key register included, or comparing WDTWPROTECT afterwards means nothing. + * And nothing here ever calls `mwdt_ll_enable` on group 0: TIMG0 hosts the watchdog the rest of the + * system depends on not firing. + */ + +/* IDF's clock and reset LL functions are shadowed by a wrapper macro referencing + * `__DECLARE_RCC_ATOMIC_ENV` / `__DECLARE_RCC_RC_ATOMIC_ENV`, identifiers IDF never defines + * anywhere: their purpose is to make an unguarded call fail to compile, because the only legal + * caller holds a FreeRTOS spinlock. There is no FreeRTOS here and core 1 is held in reset at + * power-on, so declaring the names is exactly as safe as the spinlock would be - and it is what + * IDF's own bootloader does (bootloader_support/src/bootloader_console.c:53). */ +static int __DECLARE_RCC_ATOMIC_ENV __attribute__((unused)); +static int __DECLARE_RCC_RC_ATOMIC_ENV __attribute__((unused)); + +#include "hal/timer_ll.h" +#include "hal/mwdt_ll.h" +#include "hal/timg_ll.h" +#include "soc/timer_group_struct.h" + +static timg_dev_t *grp(int group) +{ + return TIMER_LL_GET_HW(group); +} + +/* ------------------------------------------------------------------ general purpose timer */ + +void oracle_timg_set_divider(int group, unsigned timer, unsigned divider) +{ + timer_ll_set_clock_prescale(grp(group), timer, divider); +} + +void oracle_timg_set_direction_up(int group, unsigned timer, int up) +{ + timer_ll_set_count_direction(grp(group), timer, up ? GPTIMER_COUNT_UP : GPTIMER_COUNT_DOWN); +} + +void oracle_timg_set_auto_reload(int group, unsigned timer, int en) +{ + timer_ll_enable_auto_reload(grp(group), timer, en != 0); +} + +void oracle_timg_enable_counter(int group, unsigned timer, int en) +{ + timer_ll_enable_counter(grp(group), timer, en != 0); +} + +void oracle_timg_enable_alarm(int group, unsigned timer, int en) +{ + timer_ll_enable_alarm(grp(group), timer, en != 0); +} + +void oracle_timg_set_alarm_value(int group, unsigned timer, unsigned long long value) +{ + timer_ll_set_alarm_value(grp(group), timer, value); +} + +void oracle_timg_set_reload_value(int group, unsigned timer, unsigned long long value) +{ + timer_ll_set_reload_value(grp(group), timer, value); +} + +unsigned long long oracle_timg_get_reload_value(int group, unsigned timer) +{ + return timer_ll_get_reload_value(grp(group), timer); +} + +void oracle_timg_trigger_soft_reload(int group, unsigned timer) +{ + timer_ll_trigger_soft_reload(grp(group), timer); +} + +/* The latch-then-read sequence: `timer_ll_trigger_soft_capture` writes TxUPDATE and spins until the + * hardware clears it, and only then is the TxHI/TxLO pair meaningful. Exposed as one call because + * that is how our `timg.read` expresses it, and splitting it would compare halves of a sequence. */ +unsigned long long oracle_timg_read_counter(int group, unsigned timer) +{ + timer_ll_trigger_soft_capture(grp(group), timer); + return timer_ll_get_counter_value(grp(group), timer); +} + +void oracle_timg_set_clock_source_xtal(int group, unsigned timer) +{ + timer_ll_set_clock_source(group, timer, GPTIMER_CLK_SRC_XTAL); +} + +void oracle_timg_set_clock_source_pll80m(int group, unsigned timer) +{ + timer_ll_set_clock_source(group, timer, GPTIMER_CLK_SRC_PLL_F80M); +} + +void oracle_timg_enable_timer_clock(int group, unsigned timer, int en) +{ + timer_ll_enable_clock(group, timer, en != 0); +} + +void oracle_timg_enable_bus_clock(int group, int en) +{ + timg_ll_enable_bus_clock(group, en != 0); +} + +/* Pulses the group's reset bit and then clears WDT_FLASHBOOT_MOD_EN, which the reset re-arms + * (timg_ll.h:51-71). The clearing is the interesting half: leave it out and the board reboots a + * moment later with nothing on the console to explain it. */ +void oracle_timg_reset_register(int group) +{ + timg_ll_reset_register(group); +} + +/* --------------------------------------------------------------------------------- watchdog */ + +void oracle_mwdt_set_stage(int group, unsigned stage, unsigned timeout, unsigned action) +{ + mwdt_ll_write_protect_disable(grp(group)); + mwdt_ll_config_stage(grp(group), (wdt_stage_t)stage, timeout, (wdt_stage_action_t)action); + mwdt_ll_write_protect_enable(grp(group)); +} + +void oracle_mwdt_disable_stage(int group, unsigned stage) +{ + mwdt_ll_write_protect_disable(grp(group)); + mwdt_ll_disable_stage(grp(group), stage); + mwdt_ll_write_protect_enable(grp(group)); +} + +void oracle_mwdt_set_prescaler(int group, unsigned prescaler) +{ + mwdt_ll_write_protect_disable(grp(group)); + mwdt_ll_set_prescaler(grp(group), prescaler); + mwdt_ll_write_protect_enable(grp(group)); +} + +void oracle_mwdt_set_cpu_reset_length(int group, unsigned length) +{ + mwdt_ll_write_protect_disable(grp(group)); + mwdt_ll_set_cpu_reset_length(grp(group), (wdt_reset_sig_length_t)length); + mwdt_ll_write_protect_enable(grp(group)); +} + +void oracle_mwdt_set_sys_reset_length(int group, unsigned length) +{ + mwdt_ll_write_protect_disable(grp(group)); + mwdt_ll_set_sys_reset_length(grp(group), (wdt_reset_sig_length_t)length); + mwdt_ll_write_protect_enable(grp(group)); +} + +void oracle_mwdt_set_flashboot_en(int group, int en) +{ + mwdt_ll_write_protect_disable(grp(group)); + mwdt_ll_set_flashboot_en(grp(group), en != 0); + mwdt_ll_write_protect_enable(grp(group)); +} + +void oracle_mwdt_set_enabled(int group, int en) +{ + mwdt_ll_write_protect_disable(grp(group)); + if (en) { + mwdt_ll_enable(grp(group)); + } else { + mwdt_ll_disable(grp(group)); + } + mwdt_ll_write_protect_enable(grp(group)); +} + +void oracle_mwdt_feed(int group) +{ + mwdt_ll_write_protect_disable(grp(group)); + mwdt_ll_feed(grp(group)); + mwdt_ll_write_protect_enable(grp(group)); +} + +/* The two halves of the protection dance on their own, so a case can check that our key value and + * IDF's are the same word rather than only that a guarded sequence ends up locked. */ +void oracle_mwdt_write_protect_disable(int group) +{ + mwdt_ll_write_protect_disable(grp(group)); +} + +void oracle_mwdt_write_protect_enable(int group) +{ + mwdt_ll_write_protect_enable(grp(group)); +} + +int oracle_mwdt_is_enabled(int group) +{ + return mwdt_ll_check_if_enabled(grp(group)) ? 1 : 0; +} |
