1 /*
2  * Copyright (c) 2006-2025, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*
8  * Refer to PDMA driver implementation and hardware specifications
9  * This is a PDMA (Peripheral DMA) device test routine. The program tests:
10  * - Channel allocation/release functionality
11  * - DMA transfer from DDR to UART (TX)
12  * - DMA transfer from UART to DDR (RX)
13  *
14  * "test_pdma_tx" tests DDR-to-UART transmission:
15  * - Should display 3 identical rows of data on screen
16  * - Each row consists of 62 'x' characters
17  * - Total output should contain exactly 186 'x' characters
18  * - Data is transferred from DDR to UART0 TX FIFO via PDMA
19  *
20  * "test_pdma_rx" tests UART-to-DDR reception:
21  * - After "Send test data by keyboard input within 2 seconds" prompt appears:
22  *   - User has 2 seconds to input data via keyboard
23  *   - Input will be captured via UART0 RX FIFO to DDR through PDMA
24  * - Test will display first 16 bytes of received DDR data
25  * - Verify output matches partial input data
26  */
27 
28 #include <rtthread.h>
29 #include <rtdevice.h>
30 #include "utest.h"
31 #include <mmu.h>
32 #include "board.h"
33 #include "drv_pdma.h"
34 
35 #define UART0_IRQ K230_IRQ_UART0
36 
37 #define CACHE_LINE_SIZE 64
38 
39 typedef enum
40 {
41     TEST_PDMA_EVENT_NONE,
42     TEST_PDMA_EVENT_COMPLETE,
43     TEST_PDMA_EVENT_TIMEOUT
44 } test_pdma_event_t;
45 
46 static rt_event_t test_pdma_event = RT_NULL;
47 
test_pdma_call_back(rt_uint8_t ch,rt_bool_t is_done)48 void test_pdma_call_back(rt_uint8_t ch, rt_bool_t is_done)
49 {
50     /* Send completion or timeout event based on callback status */
51     test_pdma_event_t event_type = is_done ? TEST_PDMA_EVENT_COMPLETE : TEST_PDMA_EVENT_TIMEOUT;
52     rt_event_send(test_pdma_event, event_type);
53 }
54 
test_pdma_request()55 void test_pdma_request()
56 {
57     rt_uint8_t ch;
58     rt_err_t err;
59 
60     /* Test channel allocation for all available channels */
61     for (rt_uint8_t i = 0; i < PDMA_CH_MAX; i++)
62     {
63         err = k230_pdma_request_channel(&ch);
64         uassert_int_equal(err, RT_EOK);
65     }
66 
67     /* Should fail when all channels are allocated */
68     err = k230_pdma_request_channel(&ch);
69     uassert_int_equal(err, -RT_EBUSY);
70 
71     /* Release channel 0 and test re-allocation */
72     err = k230_pdma_release_channel(0);
73     uassert_int_equal(err, RT_EOK);
74 
75     err = k230_pdma_request_channel(&ch);
76     uassert_int_equal(err, RT_EOK);
77 
78     /* Cleanup: release all channels */
79     for (rt_uint8_t i = 0; i < PDMA_CH_MAX; i++)
80     {
81         err = k230_pdma_release_channel(i);
82         uassert_int_equal(err, RT_EOK);
83     }
84 }
85 
86 /* Test DMA transfer from DDR to UART output */
test_pdma_tx()87 void test_pdma_tx()
88 {
89     rt_uint8_t ch;
90     rt_err_t err;
91     rt_uint32_t recv_event;
92     rt_uint32_t len = 192;
93 
94     /* For software-managed DMA cache coherency, ensure buffer start address and size are cache-line aligned */
95     uint8_t *buf = rt_malloc_align(len, CACHE_LINE_SIZE);
96     void *buf_pa = rt_kmem_v2p(buf);
97 
98     for (int i = 0; i < 192; i++)
99     {
100         if ((i + 2) % 64 == 0)
101         {
102             buf[i] = '\r';
103         }
104         else if ((i + 1) % 64 == 0)
105         {
106             buf[i] = '\n';
107         }
108         else
109         {
110             buf[i] = 'x';
111         }
112     }
113 
114     rt_event_control(test_pdma_event, RT_IPC_CMD_RESET, NULL);
115 
116     /* Configure DMA transfer */
117     err = k230_pdma_request_channel(&ch);
118     uassert_int_equal(err, RT_EOK);
119 
120     usr_pdma_cfg_t pdma_cfg;
121     /* Configure DMA parameters */
122     pdma_cfg.device = UART0_TX;
123     pdma_cfg.src_addr = buf_pa;
124     pdma_cfg.dst_addr = (rt_uint8_t *)UART0_BASE_ADDR;
125     pdma_cfg.line_size = len;
126 
127     /* Set channel configuration */
128     pdma_cfg.pdma_ch_cfg.ch_src_type = CONTINUE;
129     pdma_cfg.pdma_ch_cfg.ch_dev_hsize = PSBYTE1;
130     pdma_cfg.pdma_ch_cfg.ch_dat_endian = PDEFAULT;
131     pdma_cfg.pdma_ch_cfg.ch_dev_blen = PBURST_LEN_16;
132     pdma_cfg.pdma_ch_cfg.ch_priority = 7;
133     pdma_cfg.pdma_ch_cfg.ch_dev_tout = 0xFFF;
134 
135     err = k230_pdma_set_callback(ch, test_pdma_call_back);
136     uassert_int_equal(err, RT_EOK);
137 
138     err = k230_pdma_config(ch, &pdma_cfg);
139     uassert_int_equal(err, RT_EOK);
140 
141     /* Start transfer and wait for completion */
142     err = k230_pdma_start(ch);
143     uassert_int_equal(err, RT_EOK);
144 
145     err = rt_event_recv(test_pdma_event,
146                        TEST_PDMA_EVENT_COMPLETE | TEST_PDMA_EVENT_TIMEOUT,
147                        RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
148                        RT_WAITING_FOREVER,
149                        &recv_event);
150     uassert_int_equal(err, RT_EOK);
151     uassert_int_equal(recv_event, TEST_PDMA_EVENT_COMPLETE);
152 
153     /* Cleanup */
154     err = k230_pdma_stop(ch);
155     uassert_int_equal(err, RT_EOK);
156 
157     err = k230_pdma_release_channel(ch);
158     uassert_int_equal(err, RT_EOK);
159 
160     rt_free_align(buf);
161     LOG_I("PDMA TX test completed successfully");
162 }
163 
164 /* Test DMA transfer from UART RX FIFO to DDR */
test_pdma_rx()165 void test_pdma_rx()
166 {
167     rt_uint8_t ch;
168     rt_err_t err;
169     rt_uint32_t recv_event;
170     rt_uint32_t len = 16;
171 
172     uint8_t *buf = rt_malloc_align(64, CACHE_LINE_SIZE);
173     void *buf_pa = rt_kmem_v2p(buf);
174 
175     /* Reset event before starting test */
176     rt_event_control(test_pdma_event, RT_IPC_CMD_RESET, NULL);
177 
178     /* Request DMA channel */
179     err = k230_pdma_request_channel(&ch);
180     uassert_int_equal(err, RT_EOK);
181 
182     /* Configure DMA parameters */
183     usr_pdma_cfg_t pdma_cfg;
184     pdma_cfg.device = UART0_RX;
185     pdma_cfg.src_addr = (rt_uint8_t *)UART0_BASE_ADDR;
186     pdma_cfg.dst_addr = buf_pa;
187     pdma_cfg.line_size = len;
188 
189     /* Set channel configuration */
190     pdma_cfg.pdma_ch_cfg.ch_src_type = FIXED;
191     pdma_cfg.pdma_ch_cfg.ch_dev_hsize = PSBYTE1;
192     pdma_cfg.pdma_ch_cfg.ch_dat_endian = PDEFAULT;
193     pdma_cfg.pdma_ch_cfg.ch_dev_blen = PBURST_LEN_16;
194     pdma_cfg.pdma_ch_cfg.ch_priority = 7;
195     pdma_cfg.pdma_ch_cfg.ch_dev_tout = 0xFFF;
196 
197     /* Set callback and configure DMA */
198     err = k230_pdma_set_callback(ch, test_pdma_call_back);
199     uassert_int_equal(err, RT_EOK);
200 
201     err = k230_pdma_config(ch, &pdma_cfg);
202     uassert_int_equal(err, RT_EOK);
203 
204     LOG_I("Send test data by keyboard input within 2 seconds (to UART receive buffer)");
205 
206     /* Setup 2 second timeout */
207     rt_tick_t timeout = RT_TICK_PER_SECOND * 2;
208     rt_tick_t start_tick = rt_tick_get();
209 
210     /* Mask UART0 interrupt to prevent FIFO access by ISR */
211     rt_hw_interrupt_mask(UART0_IRQ);
212 
213     /* Wait for timeout period */
214     while (RT_TRUE)
215     {
216         if (rt_tick_get_delta(start_tick) >= timeout)
217         {
218             break;
219         }
220     }
221 
222     /* Start DMA transfer */
223     k230_pdma_start(ch);
224 
225     /* Wait for transfer completion event */
226     err = rt_event_recv(test_pdma_event,
227                        TEST_PDMA_EVENT_COMPLETE | TEST_PDMA_EVENT_TIMEOUT,
228                        RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
229                        RT_WAITING_FOREVER,
230                        &recv_event);
231     uassert_int_equal(err, RT_EOK);
232     uassert_int_equal(recv_event, TEST_PDMA_EVENT_COMPLETE);
233 
234     rt_hw_interrupt_umask(UART0_IRQ);
235 
236     err = k230_pdma_stop(ch);
237     uassert_int_equal(err, RT_EOK);
238 
239     err = k230_pdma_release_channel(ch);
240     uassert_int_equal(err, RT_EOK);
241 
242     LOG_I("Got: %.*s", len, buf);
243 
244     rt_free_align(buf);
245 }
246 
utest_tc_init(void)247 static rt_err_t utest_tc_init(void)
248 {
249     test_pdma_event = (rt_event_t)rt_malloc(sizeof(struct rt_event));
250     if (test_pdma_event == RT_NULL)
251     {
252         LOG_E("Failed to allocate memory for pdma_event!");
253         return -RT_ENOMEM;
254     }
255 
256     if (rt_event_init(test_pdma_event, "pdma_event", RT_IPC_FLAG_FIFO) != RT_EOK)
257     {
258         LOG_E("Failed to init pdma_event!");
259         rt_free(test_pdma_event);
260         return -RT_ERROR;
261     }
262 
263     LOG_I("PDMA event initialized successfully!");
264     return RT_EOK;
265 }
266 
utest_tc_cleanup(void)267 static rt_err_t utest_tc_cleanup(void)
268 {
269     rt_free(test_pdma_event);
270 
271     /* Check and release all DMA channels */
272     for (rt_uint8_t ch = 0; ch < PDMA_CH_MAX; ch++)
273     {
274         rt_err_t err = k230_pdma_release_channel(ch);
275 
276         /* Channel was successfully released now - means it wasn't properly released in test case */
277         if (err == RT_EOK)
278         {
279             LOG_W("PDMA channel %d was not released in test case!", ch);
280         }
281         /* Channel release failed with error other than -RT_EINVAL (unexpected error) */
282         else if (err != -RT_EINVAL)
283         {
284             LOG_I("PDMA channel %d release failed: %d", ch, err);
285         }
286         /* -RT_EINVAL means channel was already released (normal case) - no action needed */
287     }
288 
289     return RT_EOK;
290 }
291 
test_pdma()292 void test_pdma()
293 {
294     UTEST_UNIT_RUN(test_pdma_request);
295     UTEST_UNIT_RUN(test_pdma_tx);
296     UTEST_UNIT_RUN(test_pdma_rx);
297 }
298 
299 UTEST_TC_EXPORT(test_pdma, "pdma", utest_tc_init, utest_tc_cleanup, 10);
300