/* UART's reference implementation: ESP-IDF's own `uart_ll.h`, given external linkage. * * No logic here. Anything clever in this file would be a third implementation to doubt, and the * whole point of the differential is that one side is unmodified IDF. * * Two IDF-specific notes: * * - Several of these LL functions are shadowed by a macro that references * `__DECLARE_RCC_ATOMIC_ENV`, an identifier IDF never defines anywhere, so that an unguarded call * fails to compile: HP_SYS_CLKRST's PERI_CLK_CTRL and SOC_CLK_CTRL registers are shared with * unrelated peripherals and 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 as safe as the spinlock would be, * and it is what IDF's own bootloader does (bootloader_console.c:53). * - `uart_ll_set_sclk` and `uart_ll_set_baudrate` are *function-like macros* wrapping * `uart_ll_set_sclk` / `_uart_ll_set_baudrate` (uart_ll.h:477-481, 578-586). Calling the * underscored inner function directly would skip the very guard above, so these wrappers call * through the macro. For `set_sclk` the macro and the function share a name, which works only * because the macro is defined after the function. */ static int __DECLARE_RCC_ATOMIC_ENV __attribute__((unused)); #include "hal/uart_ll.h" #include "soc/uart_struct.h" /* Nothing here touches UART0. Resetting UART0 clears UART_CLKDIV, and UART0 is this board's * console: the output turns to garbage mid-character and the board takes a watchdog reset with * nothing readable left to say why. Measured on this board. The suite drives UART1. */ static inline uart_dev_t *dev(unsigned num) { return UART_LL_GET_HW(num); } /* ---------------------------------------------------------------------- clocks, reset, baud */ /* Only the operations uart_cases.zig actually pairs are wrapped. A wrapper with no case behind it * is unreachable code that looks like coverage - and for `uart_ll_reset_register` in particular it * would be a loaded gun, since the harness restores this block through its own reset-bit path and * calling that function with num = 0 kills the console. */ /* The whole point of the UART suite: IDF's baud-rate arithmetic, which picks an integer pre-divider * and then a 12-bit divider with a 4-bit fraction, and commits through REG_UPDATE. Returns false on * the rates it cannot represent - which is a real outcome, not an error path, since a 12-bit divider * cannot reach every baud from every source clock. uart_ll.h:532-588. */ int oracle_uart_set_baudrate(unsigned num, unsigned baud, unsigned sclk_freq) { return _uart_ll_set_baudrate(UART_LL_GET_HW(num), baud, sclk_freq) ? 1 : 0; } void oracle_uart_enable_bus_clock(unsigned num, int enable) { uart_ll_enable_bus_clock((uart_port_t)num, enable != 0); } void oracle_uart_sclk_enable(unsigned num) { uart_ll_sclk_enable(dev(num)); } /* `sel` is the soc_module_clk_t value, not the raw field encoding: UART_SCLK_XTAL, * UART_SCLK_RTC, UART_SCLK_PLL_F80M. The mapping from those to the 2-bit field is the part of * uart_ll_set_sclk under test. */ /* The clock source, named on the C side rather than passed as a number. * * `UART_SCLK_XTAL` is an enumerator of `soc_module_clk_t`, not a small ordinal: clk_tree_defs.h:280 * defines it as SOC_MOD_CLK_XTAL, whose value is whatever position it happens to occupy in a * chip-wide enum. Passing 0 from Zig - which is what a first version did - lands on an unmatched * case in `_uart_ll_set_sclk`, whose default is HAL_ASSERT(false); compiled at assertion level 0 * that is `__builtin_unreachable()`, so the select is left at a value with no clock behind it and * the very next `uart_ll_update` spins forever on a commit that cannot land. The harness reached the * first UART case and went silent, which looks exactly like a crash. * * Keeping the enumerator on this side of the boundary removes the class of bug entirely. */ void oracle_uart_set_sclk_xtal(unsigned num) { uart_ll_set_sclk(UART_LL_GET_HW(num), UART_SCLK_XTAL); } void oracle_uart_set_sclk_pll(unsigned num) { uart_ll_set_sclk(UART_LL_GET_HW(num), UART_SCLK_PLL_F80M); } void oracle_uart_set_sclk(unsigned num, unsigned sel) { uart_ll_set_sclk(dev(num), (soc_module_clk_t)sel); } /* -------------------------------------------------------------------------------- data format */ void oracle_uart_set_data_bit_num(unsigned num, unsigned bits) { uart_ll_set_data_bit_num(dev(num), (uart_word_length_t)bits); } void oracle_uart_set_stop_bits(unsigned num, unsigned stop) { uart_ll_set_stop_bits(dev(num), (uart_stop_bits_t)stop); } void oracle_uart_set_parity(unsigned num, unsigned parity) { uart_ll_set_parity(dev(num), (uart_parity_t)parity); } /* ---------------------------------------------------------------------------------------- FIFO */ void oracle_uart_txfifo_rst(unsigned num) { uart_ll_txfifo_rst(dev(num)); } void oracle_uart_rxfifo_rst(unsigned num) { uart_ll_rxfifo_rst(dev(num)); } /* ------------------------------------------------------------------------- loopback and update */ void oracle_uart_set_loop_back(unsigned num, int enable) { uart_ll_set_loop_back(dev(num), enable != 0); } void oracle_uart_update(unsigned num) { uart_ll_update(dev(num)); } /* ---------------------------------------------------------------------------------- pin routing */ /* IDF routes UART pins through esp_rom_gpio_connect_*_signal / gpio_ll, not through uart_ll, so the * reference for pin routing is the GPIO LL - the same functions gpio_ref.c wraps, called with this * UART's signal indices. Kept here rather than in gpio_ref.c because the *signal index* is the part * under test, and it belongs to the UART. */ #include "hal/gpio_ll.h" #include "soc/gpio_struct.h" /* The signal indices themselves, which are not in any *_ll.h - and are the whole point of these two * wrappers: the Zig side reads the same macros through translate-c. */ #include "soc/gpio_sig_map.h" void oracle_uart_route_tx(unsigned num, unsigned pin) { const unsigned sig[5] = { UART0_TXD_PAD_OUT_IDX, UART1_TXD_PAD_OUT_IDX, UART2_TXD_PAD_OUT_IDX, UART3_TXD_PAD_OUT_IDX, UART4_TXD_PAD_OUT_IDX, }; gpio_ll_set_output_signal_matrix_source(&GPIO, pin, sig[num], false); gpio_ll_set_output_enable_ctrl(&GPIO, pin, true, false); } void oracle_uart_route_rx(unsigned num, unsigned pin) { const unsigned sig[5] = { UART0_RXD_PAD_IN_IDX, UART1_RXD_PAD_IN_IDX, UART2_RXD_PAD_IN_IDX, UART3_RXD_PAD_IN_IDX, UART4_RXD_PAD_IN_IDX, }; gpio_ll_input_enable(&GPIO, pin); gpio_ll_set_input_signal_matrix_source(&GPIO, sig[num], pin, false); }