summaryrefslogtreecommitdiff
path: root/src/oracle/gpio_ref.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/oracle/gpio_ref.c')
-rw-r--r--src/oracle/gpio_ref.c116
1 files changed, 116 insertions, 0 deletions
diff --git a/src/oracle/gpio_ref.c b/src/oracle/gpio_ref.c
new file mode 100644
index 0000000..b4070a2
--- /dev/null
+++ b/src/oracle/gpio_ref.c
@@ -0,0 +1,116 @@
+/* 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);
+}