1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
/* 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);
}
|