/* The reference implementation, which is ESP-IDF's own. * * ESP-IDF's `*_ll.h` headers are `static inline` functions over the same registers this project's * Zig HAL drives. Compiled by Zig's clang for riscv32-freestanding they link into the same image as * the Zig code, which is what makes a differential test possible at all: one binary, one boot, one * set of clocks, both implementations, and the diff taken on the die. * * These wrappers exist only to give the inline functions external linkage so Zig can call them. * There is no logic here - anything clever in this file would be a third implementation to doubt. */ /* IDF's clock and reset LL functions are shadowed by a wrapper macro that references * `__DECLARE_RCC_ATOMIC_ENV`, an identifier IDF never defines anywhere; its purpose is to make an * unguarded call fail to compile, because the only legal caller holds a spinlock. There is no * FreeRTOS here, and core 1 is held in reset at power-on, so declaring the name 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 declares a dummy local for the same reason). */ static int __DECLARE_RCC_ATOMIC_ENV __attribute__((unused)); #include "hal/gpio_ll.h" #include "soc/gpio_struct.h" #include "soc/io_mux_struct.h" /* Whether this translation unit was built with the ROM path switched on. The harness prints it, so * that a differential run can never silently be "my registers versus the mask ROM". */ int oracle_gpio_uses_rom_api(void) { #if HAL_CONFIG(GPIO_USE_ROM_API) return 1; #else return 0; #endif } void oracle_gpio_set_level(unsigned pin, unsigned level) { gpio_ll_set_level(&GPIO, pin, level); } int oracle_gpio_get_level(unsigned pin) { return gpio_ll_get_level(&GPIO, pin); } void oracle_gpio_output_enable(unsigned pin) { gpio_ll_output_enable(&GPIO, pin); } void oracle_gpio_output_disable(unsigned pin) { gpio_ll_output_disable(&GPIO, pin); } void oracle_gpio_input_enable(unsigned pin) { gpio_ll_input_enable(&GPIO, pin); } void oracle_gpio_input_disable(unsigned pin) { gpio_ll_input_disable(&GPIO, pin); } void oracle_gpio_func_sel(unsigned pin, unsigned func) { gpio_ll_func_sel(&GPIO, pin, func); } void oracle_gpio_set_drive(unsigned pin, unsigned strength) { gpio_ll_set_drive_capability(&GPIO, pin, (gpio_drive_cap_t)strength); } void oracle_gpio_pullup_en(unsigned pin) { gpio_ll_pullup_en(&GPIO, pin); } void oracle_gpio_pullup_dis(unsigned pin) { gpio_ll_pullup_dis(&GPIO, pin); } void oracle_gpio_pulldown_en(unsigned pin) { gpio_ll_pulldown_en(&GPIO, pin); } void oracle_gpio_pulldown_dis(unsigned pin) { gpio_ll_pulldown_dis(&GPIO, pin); } /* Open drain, which lives in the GPIO block's own per-pin register (GPIO_PINn_PAD_DRIVER) rather * than in the IO MUX pad register - a different register file for the same pad. The I2C HAL needs it * because that bus is wired-AND, and a pin left push-pull shorts a shared bus against another * device's driver. One bit, and expensive to get wrong. */ void oracle_gpio_od_enable(unsigned pin) { gpio_ll_od_enable(&GPIO, pin); } void oracle_gpio_od_disable(unsigned pin) { gpio_ll_od_disable(&GPIO, pin); } /* Route a peripheral signal to a pad through the GPIO matrix. This is the one GPIO operation with a * real sequence rather than a single field write, and therefore the one where a write-trace * comparison can find something a state comparison cannot. */ void oracle_gpio_matrix_out(unsigned pin, unsigned signal) { gpio_ll_set_output_signal_matrix_source(&GPIO, pin, signal, false); gpio_ll_set_output_enable_ctrl(&GPIO, pin, true, false); }