-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathloopback_test.c
More file actions
297 lines (264 loc) · 10.4 KB
/
Copy pathloopback_test.c
File metadata and controls
297 lines (264 loc) · 10.4 KB
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/*
* loopback_test.c
*
* UART Loopback Test
* ------------------
* Transmits 0x12 and 0x34 through the TX FIFO service algorithm
* (12.6.2.3), loops them back into the RX FIFO (via the simulated
* core model below), receives them via the RX FIFO service algorithm
* (12.6.2.4), compares, and prints PASS or FAIL.
*
* Two build targets:
*
* SIMULATED (default, runs on host PC, no hardware needed):
* gcc -Wall -Wextra -std=c11 \
* axi_uart_lite.c loopback_test.c -o loopback_test
* ./loopback_test
*
* REAL HARDWARE (bare-metal cross-compile):
* <cross-gcc> -DREAL_HARDWARE -DUART_BASEADDR=0xYOURBASEADDR \
* -Wall -Wextra -std=c11 \
* axi_uart_lite.c loopback_test.c -o loopback_test.elf
* (Requires a physical TX->RX loopback wire on the UART port.)
*
* Register map used (from uart_registers.sv):
* 0x00 RX_FIFO (pop on read)
* 0x04 TX_FIFO (push on byte-lane[0] write)
* 0x08 STAT_REG [0]=RX_VALID [1]=RX_FULL [2]=TX_EMPTY [3]=TX_FULL
* [5]=OVERRUN [6]=FRAME [7]=PARITY
* 0x0C CTRL_REG [3]=TX_INTR_EN [4]=RX_INTR_EN
* [5]=RST_TX(self-clr) [6]=RST_RX(self-clr)
*/
#include <stdio.h>
#include <string.h>
#include "axi_uart_lite.h"
/* ------------------------------------------------------------------ */
/* Build selector */
/* ------------------------------------------------------------------ */
#ifndef REAL_HARDWARE
#define SIMULATED_BUILD 1
#endif
/* ================================================================== */
/* SIMULATED CORE MODEL */
/* (only compiled when REAL_HARDWARE is not defined) */
/* ================================================================== */
#ifdef SIMULATED_BUILD
/* A register-behavior-level model of the RTL, matching the exact
* STAT_REG bit positions and FIFO push/pop semantics from
* uart_registers.sv. Not a cycle-accurate waveform simulation. */
#define SIM_FIFO_DEPTH 16
typedef struct {
uint8_t rx_fifo[SIM_FIFO_DEPTH];
uint32_t rx_head, rx_tail, rx_count;
uint8_t tx_fifo[SIM_FIFO_DEPTH];
uint32_t tx_head, tx_tail, tx_count;
uint8_t overrun_err;
uint8_t frame_err;
uint8_t parity_err;
uint8_t rx_intr_en;
uint8_t tx_intr_en;
} sim_core_t;
static sim_core_t g_core;
static uint8_t g_reg_space[16]; /* 4 x 4-byte registers */
/* Recompute STAT_REG from current model state and write into the
* memory region the driver reads via raw pointer. */
static void sim_sync_stat(void)
{
uint32_t stat = 0u;
if (g_core.rx_count > 0u) stat |= UART_STAT_RX_VALID_DATA;
if (g_core.rx_count == SIM_FIFO_DEPTH) stat |= UART_STAT_RX_FIFO_FULL;
if (g_core.tx_count == 0u) stat |= UART_STAT_TX_FIFO_EMPTY;
if (g_core.tx_count == SIM_FIFO_DEPTH) stat |= UART_STAT_TX_FIFO_FULL;
if (g_core.rx_intr_en) stat |= UART_STAT_RX_INTR_EN;
if (g_core.overrun_err) stat |= UART_STAT_OVERRUN_ERR;
if (g_core.frame_err) stat |= UART_STAT_FRAME_ERR;
if (g_core.parity_err) stat |= UART_STAT_PARITY_ERR;
*(volatile uint32_t *)(g_reg_space + UART_REG_STAT) = stat;
}
/* Present the head of the RX FIFO at the RX_FIFO offset so the
* driver's raw pointer read sees the correct next byte. */
static void sim_sync_rxfifo(void)
{
uint8_t b = (g_core.rx_count > 0u) ? g_core.rx_fifo[g_core.rx_tail] : 0u;
*(volatile uint32_t *)(g_reg_space + UART_REG_RX_FIFO) = b;
}
/* Advance the RX FIFO model after the driver pops a byte. */
static void sim_pop_rx(void)
{
if (g_core.rx_count > 0u) {
g_core.rx_tail = (g_core.rx_tail + 1u) % SIM_FIFO_DEPTH;
g_core.rx_count--;
}
}
/* Fold a driver write to TX_FIFO into the model. */
static void sim_apply_tx_write(void)
{
uint8_t byte = (uint8_t)(*(volatile uint32_t *)(g_reg_space + UART_REG_TX_FIFO));
if (g_core.tx_count < SIM_FIFO_DEPTH) {
g_core.tx_fifo[g_core.tx_head] = byte;
g_core.tx_head = (g_core.tx_head + 1u) % SIM_FIFO_DEPTH;
g_core.tx_count++;
}
}
/* Fold a driver write to CTRL_REG into the model. */
static void sim_apply_ctrl_write(void)
{
uint32_t ctrl = *(volatile uint32_t *)(g_reg_space + UART_REG_CTRL);
if (ctrl & UART_CTRL_RST_TX_FIFO) {
g_core.tx_head = g_core.tx_tail = g_core.tx_count = 0u;
}
if (ctrl & UART_CTRL_RST_RX_FIFO) {
g_core.rx_head = g_core.rx_tail = g_core.rx_count = 0u;
g_core.overrun_err = 0u;
g_core.frame_err = 0u;
g_core.parity_err = 0u;
}
g_core.rx_intr_en = (ctrl & UART_CTRL_RX_INTR_EN) ? 1u : 0u;
g_core.tx_intr_en = (ctrl & UART_CTRL_TX_INTR_EN) ? 1u : 0u;
}
/* Loopback: move every byte in the TX FIFO into the RX FIFO, in
* order. This models a physical TX->RX wire or internal loopback mux. */
static void sim_loopback_tx_to_rx(void)
{
while (g_core.tx_count > 0u) {
uint8_t b = g_core.tx_fifo[g_core.tx_tail];
g_core.tx_tail = (g_core.tx_tail + 1u) % SIM_FIFO_DEPTH;
g_core.tx_count--;
if (g_core.rx_count < SIM_FIFO_DEPTH) {
g_core.rx_fifo[g_core.rx_head] = b;
g_core.rx_head = (g_core.rx_head + 1u) % SIM_FIFO_DEPTH;
g_core.rx_count++;
} else {
g_core.overrun_err = 1u;
}
}
sim_sync_stat();
}
/*
* Pre-fold the soft-reset outcome into the model before calling
* uart_init(), because uart_init() issues several sequential CTRL
* writes internally and reads STAT_REG back on the SAME call. On
* real hardware STAT_REG updates combinatorially on every CTRL write;
* in our static-memory model we bridge this gap by pre-reflecting the
* fully-reset state before handing control to the driver.
*/
static void sim_apply_init_reset(void)
{
/* Mirrors what uart_init() will do: clear, RST_TX, RST_RX. */
g_core.tx_head = g_core.tx_tail = g_core.tx_count = 0u;
g_core.rx_head = g_core.rx_tail = g_core.rx_count = 0u;
g_core.overrun_err = 0u;
g_core.frame_err = 0u;
g_core.parity_err = 0u;
g_core.rx_intr_en = 0u;
g_core.tx_intr_en = 0u;
sim_sync_stat();
}
#endif /* SIMULATED_BUILD */
/* ================================================================== */
/* Main loopback test */
/* ================================================================== */
int main(void)
{
uart_dev_t dev;
uint8_t tx_q_mem[64];
uint8_t rx_q_mem[64];
uart_status_t st;
const uint8_t tx_data[2] = {0x12u, 0x34u};
uint8_t rx_data[2] = {0u, 0u};
int32_t sent_total = 0;
int32_t recv_total = 0;
#ifdef SIMULATED_BUILD
/* -------------------------------------------------------------- */
/* Simulated setup */
/* -------------------------------------------------------------- */
uintptr_t base = (uintptr_t)g_reg_space;
memset(&g_core, 0, sizeof(g_core));
memset(g_reg_space, 0, sizeof(g_reg_space));
/* Pre-resolve the reset outcome so uart_init's internal STAT_REG
* readback sees a correct post-reset state immediately. */
sim_apply_init_reset();
st = uart_init(&dev, base, 0, 0,
tx_q_mem, sizeof(tx_q_mem),
rx_q_mem, sizeof(rx_q_mem));
sim_apply_ctrl_write();
sim_sync_stat();
#else /* REAL_HARDWARE */
/* -------------------------------------------------------------- */
/* Real hardware setup */
/* -------------------------------------------------------------- */
st = uart_init(&dev, (uintptr_t)UART_BASEADDR, 0, 0,
tx_q_mem, sizeof(tx_q_mem),
rx_q_mem, sizeof(rx_q_mem));
#endif
if (st != UART_SUCCESS) {
printf("FAIL (init error: %d)\n", (int)st);
return 1;
}
/* ---------------------------------------------------------------- */
/* Transmit 0x12 then 0x34 via TX FIFO Service (12.6.2.3) */
/* ---------------------------------------------------------------- */
printf("Transmitting: 0x%02X 0x%02X\n", tx_data[0], tx_data[1]);
for (int i = 0; i < 2; i++) {
#ifdef SIMULATED_BUILD
sim_sync_stat();
#endif
int32_t sent = uart_tx_fifo_service(&dev, &tx_data[i], 1);
if (sent == 1) {
#ifdef SIMULATED_BUILD
sim_apply_tx_write();
sim_sync_stat();
#endif
sent_total++;
}
}
/* ---------------------------------------------------------------- */
/* Loopback: TX FIFO -> RX FIFO */
/* ---------------------------------------------------------------- */
#ifdef SIMULATED_BUILD
sim_loopback_tx_to_rx();
#else
/*
* On real hardware the UART serialises and deserialises the bytes
* over actual TX/RX pins. Ensure a loopback wire connects TX to RX
* on your board. Poll for the bytes to arrive in the RX FIFO.
*/
{
uint32_t timeout = 1000000u; /* adjust for your baud rate */
while (!(uart_read_stat(&dev) & UART_STAT_RX_VALID_DATA) && timeout > 0u) {
timeout--;
}
if (timeout == 0u) {
printf("FAIL (timeout waiting for loopback bytes)\n");
uart_shutdown(&dev);
return 1;
}
}
#endif
/* ---------------------------------------------------------------- */
/* Receive via RX FIFO Service (12.6.2.4) */
/* ---------------------------------------------------------------- */
for (int i = 0; i < 2; i++) {
#ifdef SIMULATED_BUILD
sim_sync_stat();
sim_sync_rxfifo();
#endif
int32_t got = uart_rx_fifo_service(&dev, &rx_data[i], 1);
if (got == 1) {
#ifdef SIMULATED_BUILD
sim_pop_rx();
sim_sync_stat();
#endif
recv_total++;
}
}
printf("Received: 0x%02X 0x%02X\n", rx_data[0], rx_data[1]);
/* ---------------------------------------------------------------- */
/* Compare and report */
/* ---------------------------------------------------------------- */
int match = (sent_total == 2) && (recv_total == 2) &&
(memcmp(tx_data, rx_data, sizeof(tx_data)) == 0);
printf("%s\n", match ? "PASS" : "FAIL");
uart_shutdown(&dev);
return match ? 0 : 1;
}