参考
- 嵌入式终端AtShell.csdn
- zynq中axi_dma的四个实验-第1课:PL搭建.csdn
- zynq中axi_dma的四个实验-第2课:Vitis裸机测试.csdn
- zynq中axi_dma的四个实验-第3课:Linux寄存器方案驱动测试.csdn
- zynq中axi_dma的四个实验-第4课:Linux dmaproxy方案驱动测试.csdn
- zynq中axi_dma的sg模式采集–PL搭建.csdn
- zynq中axi_dma的sg模式采集–裸机测试.csdn
约定
1共有10个BD 21. 采集流的 1 行 = 22 个 u32 3 格式为 v0 = 行号 v1 ~ v21 = 连续数据 4 第 0 行: 0 1 2 ... 21 5 第 1 行: 1 22 23 ... 42 6 72. 1 个 BD = 8 行 = 704 Bytes 8 10 个 BD = 80 行 = 7040 Bytes 9 103. axidma_capture_one_frame_v0() 11 只打印每个 BD 的 v0,用于快速检查: 12 BD 顺序、行号连续性、BD 边界是否正确。 13 144. axidma_capture_one_frame() 15 打印一个完整 BD 的 8 行 × 22 个数据, 16 用于检查 BD 内的完整数据是否正确。 17
目录
1PS D:\myr6\vitis_prj\hello\src> ls 2 3 4 目录: D:\myr6\vitis_prj\hello\src 5 6 7Mode LastWriteTime Length Name 8---- ------------- ------ ---- 9-a---- 2026/9/4 12:46 25508 AtShell.cpp 10-a---- 2026/9/4 12:49 9330 AtShell.h 11-a---- 2026/9/4 13:30 13908 at_app_axi_dma_sg.cpp 12-a---- 2026/9/4 13:34 14658 at_app_axi_dma_sg_cycle.cpp 13-a---- 2026/8/4 21:53 5323 bsp.cpp 14-a---- 2026/8/4 21:53 517 bsp.h 15-a---- 2026/6/23 22:45 1631 FifoBuffer.cpp 16-a---- 2026/6/23 22:45 510 FifoBuffer.h 17-a---- 2026/6/23 22:45 6303 lscript.ld 18-a---- 2026/9/4 14:11 616 main.cpp 19-a---- 2026/6/23 22:45 101 README.txt 20-a---- 2026/6/23 22:45 36 Xilinx.spec 21 22
AtShell
bsp和 FifoBuffer
main.cpp
1#include "bsp.h" 2#include "AtShell.h" 3 4extern int at_app_axi_dma_sg_cycle_read(int argc, char** argv); 5extern int at_app_axi_dma_sg_read(int argc, char** argv); 6 7void at_app_init(void) 8{ 9 AT_SHELL_EXPORT(sg_cycle_read, sg_cycle_read, at_app_axi_dma_sg_cycle_read,0); 10 AT_SHELL_EXPORT(sg_read, sg_read, at_app_axi_dma_sg_read,0); 11} 12 13 14int main() 15{ 16 BspInit(); 17 at_init(Bsp_shell_write); 18 at_app_init(); 19 at_show_version(); 20 while (1) { 21 uint32_t ms = BspGetMillis(); 22 int len = BspUartRead((uint8_t *)AT_m_buf, -1); 23 at_import((uint8_t *)AT_m_buf, len, ms); 24 } 25 26 return 0; 27} 28 29
at_app_axi_dma_sg.cpp
sg模式
1#include "AtShell.h" 2#include "bsp.h" 3#include "xaxidma.h" 4#include "xaxidma_hw.h" 5#include "xil_cache.h" 6#include "xil_io.h" 7#include "xparameters.h" 8#include "xstatus.h" 9#include <stdint.h> 10#include <string.h> 11 12// ----------------------------------------------------------------------------- 13// 普通 SG 模式(非循环) 14// DMA 完成 BD 后自动停止,软件必须重新分配并提交已完成的 BD。 15// cmd=0 : 打印所有已完成 BD 的 v0(十进制),每 BD 一行。 16// cmd=1 : 仅打印最新完成的 BD 的完整行数据(22 个 word),每行十六进制。 17// ----------------------------------------------------------------------------- 18 19#define AXIS_LINE_WORD_COUNT 22u 20#define AXIS_LINES_PER_BD 8u 21#define AXIS_BD_COUNT 10u 22#define AXIS_LINE_BYTE_COUNT (AXIS_LINE_WORD_COUNT * sizeof(uint32_t)) 23#define AXIS_BD_WORD_COUNT (AXIS_LINE_WORD_COUNT * AXIS_LINES_PER_BD) 24#define AXIS_BD_BYTE_COUNT (AXIS_BD_WORD_COUNT * sizeof(uint32_t)) 25#define RX_BD_COUNT AXIS_BD_COUNT 26#define RX_BD_SPACE_BYTES XAxiDma_BdRingMemCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT, RX_BD_COUNT) 27 28static XAxiDma s_axis_dma; 29static int s_axis_dma_ready = 0; 30static XAxiDma_BdRing *s_rx_ring = NULL; 31static uint8_t s_rx_bd_space[RX_BD_SPACE_BYTES] __attribute__((aligned(XAXIDMA_BD_MINIMUM_ALIGNMENT))); 32static uint32_t s_axis_rx_buf[AXIS_BD_WORD_COUNT * RX_BD_COUNT] __attribute__((aligned(64))); 33static uint32_t s_frame_seq = 0; 34 35static void axidma_print_s2mm_diag(const char *tag) 36{ 37 uint32_t sr = XAxiDma_ReadReg(s_axis_dma.RegBase, 38 XAXIDMA_RX_OFFSET + XAXIDMA_SR_OFFSET); 39 AT_printf("%s: S2MM_SR=0x%08X ring_err=0x%08X\r\n", 40 tag, sr, (unsigned int)XAxiDma_BdRingGetError(s_rx_ring)); 41} 42 43static void axidma_reset_runtime(void) 44{ 45 XAxiDma_Reset(&s_axis_dma); 46 while (!XAxiDma_ResetIsDone(&s_axis_dma)) { } 47 s_axis_dma_ready = 0; 48 s_rx_ring = NULL; 49} 50 51// 打印完整行数据(22 个 word,十六进制) 52static void axidma_dump_bd_lines(const uint32_t *words, uint32_t line_count) 53{ 54 for (uint32_t line = 0u; line < line_count; line++) { 55 AT_printf("%04u :", (unsigned int)line); 56 for (uint32_t word = 0u; word < AXIS_LINE_WORD_COUNT; word++) { 57 AT_printf(" %08X", (unsigned int)words[line * AXIS_LINE_WORD_COUNT + word]); 58 } 59 AT_printf("\r\n"); 60 } 61} 62 63// 初始化:创建 BD ring,分配全部 10 个 BD,提交并启动(不使能循环) 64static int at_app_axi_full_dma_init(void) 65{ 66 if (s_axis_dma_ready) return XST_SUCCESS; 67 68 XAxiDma_Config *cfg = XAxiDma_LookupConfig(XPAR_AXI_DMA_0_DEVICE_ID); 69 if (cfg == NULL) { 70 AT_printf("ERR: AXI DMA config not found\r\n"); 71 return XST_FAILURE; 72 } 73 74 int status = XAxiDma_CfgInitialize(&s_axis_dma, cfg); 75 if (status != XST_SUCCESS) { 76 AT_printf("ERR: AXI DMA init failed: %d\r\n", status); 77 return status; 78 } 79 80 if (!XAxiDma_HasSg(&s_axis_dma)) { 81 AT_printf("ERR: AXI DMA must use SG mode\r\n"); 82 return XST_FAILURE; 83 } 84 if (!s_axis_dma.HasS2Mm) { 85 AT_printf("ERR: AXI DMA S2MM channel disabled\r\n"); 86 return XST_FAILURE; 87 } 88 89 s_rx_ring = XAxiDma_GetRxRing(&s_axis_dma); 90 if (s_rx_ring == NULL) { 91 AT_printf("ERR: RX ring not found\r\n"); 92 axidma_reset_runtime(); 93 return XST_FAILURE; 94 } 95 96 XAxiDma_BdRingIntDisable(s_rx_ring, XAXIDMA_IRQ_ALL_MASK); 97 XAxiDma_IntrDisable(&s_axis_dma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA); 98 99 status = XAxiDma_BdRingCreate(s_rx_ring, 100 (UINTPTR)s_rx_bd_space, 101 (UINTPTR)s_rx_bd_space, 102 XAXIDMA_BD_MINIMUM_ALIGNMENT, 103 RX_BD_COUNT); 104 if (status != XST_SUCCESS) { 105 AT_printf("ERR: RX bd ring create failed: %d\r\n", status); 106 axidma_reset_runtime(); 107 return status; 108 } 109 110 XAxiDma_Bd bd_template; 111 XAxiDma_BdClear(&bd_template); 112 status = XAxiDma_BdRingClone(s_rx_ring, &bd_template); 113 if (status != XST_SUCCESS) { 114 AT_printf("ERR: RX bd ring clone failed: %d\r\n", status); 115 axidma_reset_runtime(); 116 return status; 117 } 118 119 status = XAxiDma_BdRingCheck(s_rx_ring); 120 if (status != XST_SUCCESS) { 121 AT_printf("ERR: RX bd ring check failed: %d\r\n", status); 122 axidma_reset_runtime(); 123 return status; 124 } 125 126 int free_count = XAxiDma_BdRingGetFreeCnt(s_rx_ring); 127 if (free_count < (int)RX_BD_COUNT) { 128 AT_printf("ERR: RX bd ring free BDs not enough\r\n"); 129 axidma_reset_runtime(); 130 return XST_FAILURE; 131 } 132 133 XAxiDma_Bd *bd_ptr = NULL; 134 status = XAxiDma_BdRingAlloc(s_rx_ring, RX_BD_COUNT, &bd_ptr); 135 if (status != XST_SUCCESS) { 136 AT_printf("ERR: RX bd alloc failed: %d\r\n", status); 137 axidma_reset_runtime(); 138 return status; 139 } 140 141 XAxiDma_Bd *bd_cur = bd_ptr; 142 for (uint32_t i = 0u; i < RX_BD_COUNT; i++) { 143 UINTPTR buf_addr = (UINTPTR)&s_axis_rx_buf[i * AXIS_BD_WORD_COUNT]; 144 status = XAxiDma_BdSetBufAddr(bd_cur, buf_addr); 145 if (status != XST_SUCCESS) { 146 AT_printf("ERR: set buffer addr failed: %d\r\n", status); 147 axidma_reset_runtime(); 148 return status; 149 } 150 status = XAxiDma_BdSetLength(bd_cur, AXIS_BD_BYTE_COUNT, s_rx_ring->MaxTransferLen); 151 if (status != XST_SUCCESS) { 152 AT_printf("ERR: set length failed: %d\r\n", status); 153 axidma_reset_runtime(); 154 return status; 155 } 156 XAxiDma_BdSetCtrl(bd_cur, 0); 157 XAxiDma_BdSetId(bd_cur, (UINTPTR)(i + 1)); 158 bd_cur = (XAxiDma_Bd *)XAxiDma_BdRingNext(s_rx_ring, bd_cur); 159 } 160 161 Xil_DCacheInvalidateRange((INTPTR)s_axis_rx_buf, sizeof(s_axis_rx_buf)); 162 163 // 提交全部 BD(普通模式,不使能循环) 164 status = XAxiDma_BdRingToHw(s_rx_ring, RX_BD_COUNT, bd_ptr); 165 if (status != XST_SUCCESS) { 166 AT_printf("ERR: submit to hw failed: %d\r\n", status); 167 axidma_reset_runtime(); 168 return status; 169 } 170 171 // 启动 DMA(完成后自动停止) 172 status = XAxiDma_BdRingStart(s_rx_ring); 173 if (status != XST_SUCCESS) { 174 AT_printf("ERR: start failed: %d\r\n", status); 175 axidma_reset_runtime(); 176 return status; 177 } 178 179 s_axis_dma_ready = 1; 180 return XST_SUCCESS; 181} 182 183// 重新提交已完成的 BD(公共函数,用于 cmd=0 和 cmd=1) 184static int axidma_resubmit_completed_bds(int bd_count, XAxiDma_Bd *bd_ptr) 185{ 186 if (bd_count <= 0) return XST_SUCCESS; 187 188 // 1. 释放已完成 BD 189 int status = XAxiDma_BdRingFree(s_rx_ring, bd_count, bd_ptr); 190 if (status != XST_SUCCESS) { 191 AT_printf("ERR: free BD failed: %d\r\n", status); 192 axidma_reset_runtime(); 193 return status; 194 } 195 196 // 2. 检查 free list 中可用数量 197 int free_cnt = XAxiDma_BdRingGetFreeCnt(s_rx_ring); 198 if (free_cnt < bd_count) { 199 AT_printf("ERR: free cnt %d < %d, resetting\r\n", free_cnt, bd_count); 200 axidma_reset_runtime(); 201 return XST_FAILURE; 202 } 203 204 // 3. 重新分配 bd_count 个 BD 205 XAxiDma_Bd *new_bd_ptr = NULL; 206 status = XAxiDma_BdRingAlloc(s_rx_ring, bd_count, &new_bd_ptr); 207 if (status != XST_SUCCESS) { 208 AT_printf("ERR: alloc BD failed: %d\r\n", status); 209 axidma_reset_runtime(); 210 return status; 211 } 212 213 // 4. 设置新分配的 BD(使用全局缓冲区的前 bd_count 个) 214 XAxiDma_Bd *new_cur = new_bd_ptr; 215 for (uint32_t i = 0; i < (uint32_t)bd_count; i++) { 216 UINTPTR buf_addr = (UINTPTR)&s_axis_rx_buf[i * AXIS_BD_WORD_COUNT]; 217 status = XAxiDma_BdSetBufAddr(new_cur, buf_addr); 218 if (status != XST_SUCCESS) { 219 AT_printf("ERR: set buf addr failed: %d\r\n", status); 220 axidma_reset_runtime(); 221 return status; 222 } 223 status = XAxiDma_BdSetLength(new_cur, AXIS_BD_BYTE_COUNT, s_rx_ring->MaxTransferLen); 224 if (status != XST_SUCCESS) { 225 AT_printf("ERR: set length failed: %d\r\n", status); 226 axidma_reset_runtime(); 227 return status; 228 } 229 XAxiDma_BdSetCtrl(new_cur, 0); 230 XAxiDma_BdSetId(new_cur, (UINTPTR)(i + 1)); 231 new_cur = (XAxiDma_Bd *)XAxiDma_BdRingNext(s_rx_ring, new_cur); 232 } 233 234 // 5. 提交这些 BD 到硬件 235 status = XAxiDma_BdRingToHw(s_rx_ring, bd_count, new_bd_ptr); 236 if (status != XST_SUCCESS) { 237 AT_printf("ERR: re-submit BD failed: %d\r\n", status); 238 axidma_reset_runtime(); 239 return status; 240 } 241 242 // 6. 重启 DMA 243 status = XAxiDma_BdRingStart(s_rx_ring); 244 if (status != XST_SUCCESS) { 245 AT_printf("WARN: restart DMA returned %d\r\n", status); 246 axidma_reset_runtime(); 247 return status; 248 } 249 250 return XST_SUCCESS; 251} 252 253// cmd=0: 打印所有已完成 BD 的 v0(十进制),每 BD 一行 254static int axidma_capture_one_frame_v0(void) 255{ 256 XAxiDma_Bd *bd_ptr = NULL; 257 int bd_count = XAxiDma_BdRingFromHw(s_rx_ring, XAXIDMA_ALL_BDS, &bd_ptr); 258 259 if (bd_count <= 0) { 260 uint32_t sr = XAxiDma_ReadReg(s_axis_dma.RegBase, 261 XAXIDMA_RX_OFFSET + XAXIDMA_SR_OFFSET); 262 if ((sr & XAXIDMA_ERR_ALL_MASK) != 0u) { 263 AT_printf("ERR: AXI DMA S2MM fault\r\n"); 264 axidma_print_s2mm_diag("AXI DMA fault"); 265 axidma_reset_runtime(); 266 return XST_FAILURE; 267 } 268 AT_printf("dma_read: no frame ready\r\n"); 269 return XST_SUCCESS; 270 } 271 272 AT_printf("dma_read frame=%u completed_bd=%d/%u\r\n", 273 (unsigned int)s_frame_seq++, bd_count, (unsigned int)RX_BD_COUNT); 274 275 XAxiDma_Bd *bd_cur = bd_ptr; 276 for (int i = 0; i < bd_count; i++) { 277 UINTPTR bd_id = XAxiDma_BdGetId(bd_cur); 278 UINTPTR buf_addr = XAxiDma_BdGetBufAddr(bd_cur); 279 uint32_t actual_len = XAxiDma_BdGetActualLength(bd_cur, s_rx_ring->MaxTransferLen); 280 uint32_t line_count = actual_len / AXIS_LINE_BYTE_COUNT; 281 if (line_count > AXIS_LINES_PER_BD) line_count = AXIS_LINES_PER_BD; 282 283 Xil_DCacheInvalidateRange((INTPTR)buf_addr, AXIS_BD_BYTE_COUNT); 284 const uint32_t *bd_words = (const uint32_t *)buf_addr; 285 286 AT_printf("BD %u:", (unsigned int)bd_id); 287 for (uint32_t line = 0; line < line_count; line++) { 288 uint32_t v0 = bd_words[line * AXIS_LINE_WORD_COUNT]; 289 AT_printf(" %u", (unsigned int)v0); 290 } 291 AT_printf("\r\n"); 292 293 if (i < bd_count - 1) { 294 bd_cur = (XAxiDma_Bd *)XAxiDma_BdRingNext(s_rx_ring, bd_cur); 295 } 296 } 297 298 // 重新提交所有已完成 BD 299 return axidma_resubmit_completed_bds(bd_count, bd_ptr); 300} 301 302// cmd=1: 仅打印最新完成的 BD 的完整行数据(22 个 word,十六进制) 303static int axidma_capture_one_frame_full(void) 304{ 305 XAxiDma_Bd *bd_ptr = NULL; 306 int bd_count = XAxiDma_BdRingFromHw(s_rx_ring, XAXIDMA_ALL_BDS, &bd_ptr); 307 308 if (bd_count <= 0) { 309 uint32_t sr = XAxiDma_ReadReg(s_axis_dma.RegBase, 310 XAXIDMA_RX_OFFSET + XAXIDMA_SR_OFFSET); 311 if ((sr & XAXIDMA_ERR_ALL_MASK) != 0u) { 312 AT_printf("ERR: AXI DMA S2MM fault\r\n"); 313 axidma_print_s2mm_diag("AXI DMA fault"); 314 axidma_reset_runtime(); 315 return XST_FAILURE; 316 } 317 AT_printf("dma_read: no frame ready\r\n"); 318 return XST_SUCCESS; 319 } 320 321 // 取链表最后一个(最新完成) 322 XAxiDma_Bd *latest_bd = bd_ptr; 323 XAxiDma_Bd *bd_cur = bd_ptr; 324 for (int i = 1; i < bd_count; i++) { 325 bd_cur = (XAxiDma_Bd *)XAxiDma_BdRingNext(s_rx_ring, bd_cur); 326 latest_bd = bd_cur; 327 } 328 329 UINTPTR latest_bd_id = XAxiDma_BdGetId(latest_bd); 330 UINTPTR latest_buf_addr = XAxiDma_BdGetBufAddr(latest_bd); 331 uint32_t actual_len = XAxiDma_BdGetActualLength(latest_bd, s_rx_ring->MaxTransferLen); 332 uint32_t line_count = actual_len / AXIS_LINE_BYTE_COUNT; 333 if (line_count > AXIS_LINES_PER_BD) line_count = AXIS_LINES_PER_BD; 334 335 Xil_DCacheInvalidateRange((INTPTR)latest_buf_addr, AXIS_BD_BYTE_COUNT); 336 const uint32_t *bd_words = (const uint32_t *)latest_buf_addr; 337 338 if (actual_len != AXIS_BD_BYTE_COUNT) { 339 AT_printf("WARN: RX length actual=%u expected=%u\r\n", 340 (unsigned int)actual_len, (unsigned int)AXIS_BD_BYTE_COUNT); 341 } 342 343 AT_printf("dma_read frame=%u completed_bd=%d/%u latest_bd=%u actual=%u\r\n", 344 (unsigned int)s_frame_seq++, bd_count, 345 (unsigned int)RX_BD_COUNT, 346 (unsigned int)latest_bd_id, 347 (unsigned int)actual_len); 348 axidma_dump_bd_lines(bd_words, line_count); 349 350 // 重新提交所有已完成 BD(包括最新及其他) 351 return axidma_resubmit_completed_bds(bd_count, bd_ptr); 352} 353 354// 对外接口:支持两个子命令 355// s_macManage 0 -> 打印所有 BD 的 v0(十进制) 356// s_macManage 1 -> 打印最新 BD 的完整行数据(十六进制) 357int at_app_axi_dma_sg_read(int argc, char** argv) 358{ 359 if (!s_axis_dma_ready) { 360 int status = at_app_axi_full_dma_init(); 361 Xil_DCacheDisable(); // 调试用,可移除 362 if (status != XST_SUCCESS) return status; 363 } 364 365 if (argc == 1) { 366 AT_printf("Usage: s_macManage <cmd>\r\n"); 367 AT_printf(" cmd=0 : print v0 of all completed BDs (decimal)\r\n"); 368 AT_printf(" cmd=1 : print full lines of latest BD (hex)\r\n"); 369 return 0; 370 } 371 372 int cmd = at_param_to_int(1); 373 if (cmd == 0) { 374 return axidma_capture_one_frame_v0(); 375 } else if (cmd == 1) { 376 return axidma_capture_one_frame_full(); 377 } else { 378 AT_printf("Invalid cmd: %d\r\n", cmd); 379 return XST_FAILURE; 380 } 381} 382
at_app_axi_dma_sg_cycle.cpp
sg循环模式
1#include "AtShell.h" 2#include "bsp.h" 3#include "xaxidma.h" 4#include "xaxidma_hw.h" 5#include "xil_cache.h" 6#include "xil_io.h" 7#include "xparameters.h" 8#include "xstatus.h" 9#include <stdint.h> 10#include <string.h> 11 12// ----------------------------------------------------------------------------- 13// 数据布局说明 14// 1) 这里的“一行”固定为 22 个 u32,也就是 88 字节,这 22 个 u32 对应 PL 端一次完整采样帧中的一行数据。 15// 2) 一个 BD 负责承载 8 行数据,因此单个 BD 的有效数据量是, 8 * 22 * 4 = 704 字节。 16// 3) 这里使用 10 个 BD 组成循环接收环。DMA 持续工作时,会把新收到的,数据写入下一个 BD 对应的 buffer,形成连续不断的采集链路。 17// 4) 每次调用 axidma_capture_one_frame() 时,只从完成链表中挑出最新的 18// 1 个 BD 来打印,这样串口输出会更清晰,也更适合循环 DMA 调试。 19// ----------------------------------------------------------------------------- 20 21#define AXIS_LINE_WORD_COUNT 22u 22#define AXIS_LINES_PER_BD 8u 23#define AXIS_BD_COUNT 10u 24#define AXIS_LINE_BYTE_COUNT (AXIS_LINE_WORD_COUNT * sizeof(uint32_t)) 25#define AXIS_BD_WORD_COUNT (AXIS_LINE_WORD_COUNT * AXIS_LINES_PER_BD) 26#define AXIS_BD_BYTE_COUNT (AXIS_BD_WORD_COUNT * sizeof(uint32_t)) 27#define RX_BD_COUNT AXIS_BD_COUNT 28#define RX_BD_SPACE_BYTES XAxiDma_BdRingMemCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT, RX_BD_COUNT) 29 30static XAxiDma s_axis_dma; 31static int s_axis_dma_ready = 0; 32static XAxiDma_BdRing *s_rx_ring = NULL; 33static uint8_t s_rx_bd_space[RX_BD_SPACE_BYTES] __attribute__((aligned(XAXIDMA_BD_MINIMUM_ALIGNMENT))); 34// RX 端缓冲区按 BD 切成 10 份, 35// 每份都对应一个独立的接收 buffer。 36static uint32_t s_axis_rx_buf[AXIS_BD_WORD_COUNT * RX_BD_COUNT] __attribute__((aligned(64))); 37static uint32_t s_frame_seq = 0; 38 39static void axidma_print_s2mm_diag(const char *tag) 40{ 41 uint32_t sr = XAxiDma_ReadReg(s_axis_dma.RegBase, 42 XAXIDMA_RX_OFFSET + XAXIDMA_SR_OFFSET); 43 44 AT_printf("%s: S2MM_SR=0x%08X ring_err=0x%08X\r\n", 45 tag, 46 sr, 47 (unsigned int)XAxiDma_BdRingGetError(s_rx_ring)); 48} 49 50 51// 将一个 BD 的有效载荷按“行”打印出来。 52// 每行固定 22 个 32-bit word,输出格式是: 53// 0000 : v0 v1 ... v21 54static void axidma_dump_bd_lines(const uint32_t *words, uint32_t line_count) 55{ 56 for (uint32_t line = 0u; line < line_count; line++) { 57 AT_printf("%04u :", (unsigned int)line); 58 for (uint32_t word = 0u; word < AXIS_LINE_WORD_COUNT; word++) { 59 AT_printf(" %08X", (unsigned int)words[line * AXIS_LINE_WORD_COUNT + word]); 60 } 61 AT_printf("\r\n"); 62 } 63} 64 65// DMA 出现错误后,先复位硬件和软件状态,避免残留错误影响下一次采集。 66static void axidma_reset_runtime(void) 67{ 68 XAxiDma_Reset(&s_axis_dma); 69 while (!XAxiDma_ResetIsDone(&s_axis_dma)) { 70 } 71 s_axis_dma_ready = 0; 72 s_rx_ring = NULL; 73} 74 75// 初始化阶段负责把 RX BD ring、缓冲区和 DMA 通道一次性准备好。 76// 只要这个函数成功返回,后续 read 命令就可以直接轮询已完成 BD。 77static int at_app_axi_full_dma_init(void) 78{ 79 if (s_axis_dma_ready) { 80 return XST_SUCCESS; 81 } 82 83 // 从 BSP 生成的设备表里找到 AXI DMA 配置,再交给驱动初始化。 84 XAxiDma_Config *cfg = XAxiDma_LookupConfig(XPAR_AXI_DMA_0_DEVICE_ID); 85 if (cfg == NULL) { 86 AT_printf("ERR: AXI DMA config not found\r\n"); 87 return XST_FAILURE; 88 } 89 90 int status = XAxiDma_CfgInitialize(&s_axis_dma, cfg); 91 if (status != XST_SUCCESS) { 92 AT_printf("ERR: AXI DMA init failed: %d\r\n", status); 93 return status; 94 } 95 96 // 这里依赖 SG/BD Ring 工作方式,并且只使用 S2MM 接收通道。 97 if (!XAxiDma_HasSg(&s_axis_dma)) { 98 AT_printf("ERR: AXI DMA must use SG mode\r\n"); 99 return XST_FAILURE; 100 } 101 102 if (!s_axis_dma.HasS2Mm) { 103 AT_printf("ERR: AXI DMA S2MM channel disabled\r\n"); 104 return XST_FAILURE; 105 } 106 107 s_rx_ring = XAxiDma_GetRxRing(&s_axis_dma); 108 if (s_rx_ring == NULL) { 109 AT_printf("ERR: RX ring not found\r\n"); 110 axidma_reset_runtime(); 111 return XST_FAILURE; 112 } 113 114 // 这个命令走轮询路径,不依赖中断,因此把 RX ring 中断先关掉。 115 XAxiDma_BdRingIntDisable(s_rx_ring, XAXIDMA_IRQ_ALL_MASK); 116 XAxiDma_IntrDisable(&s_axis_dma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA); 117 118 // 在预分配的 s_rx_bd_space 里创建 BD ring,后续所有 BD 都从这里取。 119 status = XAxiDma_BdRingCreate(s_rx_ring, 120 (UINTPTR)s_rx_bd_space, 121 (UINTPTR)s_rx_bd_space, 122 XAXIDMA_BD_MINIMUM_ALIGNMENT, 123 RX_BD_COUNT); 124 if (status != XST_SUCCESS) { 125 AT_printf("ERR: RX bd ring create failed: %d\r\n", status); 126 axidma_reset_runtime(); 127 return status; 128 } 129 130 // 先构造一个空模板,再把整个 ring 复制成统一的默认状态。 131 XAxiDma_Bd bd_template; 132 XAxiDma_BdClear(&bd_template); 133 status = XAxiDma_BdRingClone(s_rx_ring, &bd_template); 134 if (status != XST_SUCCESS) { 135 AT_printf("ERR: RX bd ring clone failed: %d\r\n", status); 136 axidma_reset_runtime(); 137 return status; 138 } 139 140 // 让驱动自检一下 BD ring 的结构是否正确,避免后面 DMA 运行时才暴露问题。 141 status = XAxiDma_BdRingCheck(s_rx_ring); 142 if (status != XST_SUCCESS) { 143 AT_printf("ERR: RX bd ring check failed: %d\r\n", status); 144 axidma_reset_runtime(); 145 return status; 146 } 147 148 // 确认 free list 里有足够的 BD,一次性把整套接收环都挂到硬件上。 149 int free_count = XAxiDma_BdRingGetFreeCnt(s_rx_ring); 150 if (free_count < (int)RX_BD_COUNT) { 151 AT_printf("ERR: RX bd ring free BDs not enough\r\n"); 152 axidma_reset_runtime(); 153 return XST_FAILURE; 154 } 155 156 XAxiDma_Bd *bd_ptr = NULL; 157 status = XAxiDma_BdRingAlloc(s_rx_ring, RX_BD_COUNT, &bd_ptr); 158 if (status != XST_SUCCESS) { 159 AT_printf("ERR: RX bd alloc failed: %d\r\n", status); 160 axidma_reset_runtime(); 161 return status; 162 } 163 164 XAxiDma_Bd *bd_cur = bd_ptr; 165 // 给每个 BD 绑定独立 buffer,避免多个 BD 互相覆盖同一块内存。 166 for (uint32_t i = 0u; i < RX_BD_COUNT; i++) { 167 UINTPTR buf_addr = (UINTPTR)&s_axis_rx_buf[i * AXIS_BD_WORD_COUNT]; 168 169 status = XAxiDma_BdSetBufAddr(bd_cur, buf_addr); 170 if (status != XST_SUCCESS) { 171 AT_printf("ERR: RX set buffer addr failed: %d\r\n", status); 172 axidma_reset_runtime(); 173 return status; 174 } 175 176 status = XAxiDma_BdSetLength(bd_cur, 177 AXIS_BD_BYTE_COUNT, 178 s_rx_ring->MaxTransferLen); 179 if (status != XST_SUCCESS) { 180 AT_printf("ERR: RX set length failed: %d\r\n", status); 181 axidma_reset_runtime(); 182 return status; 183 } 184 185 XAxiDma_BdSetCtrl(bd_cur, 0); 186 // ID 直接写成 1-based 的 BD 编号,后面打印时更直观。 187 XAxiDma_BdSetId(bd_cur, (UINTPTR)(i + 1u)); 188 bd_cur = (XAxiDma_Bd *)XAxiDma_BdRingNext(s_rx_ring, bd_cur); 189 } 190 191 // 把 CPU 侧缓存失效掉,避免后面把旧数据当成 DMA 新写入的数据。 192 Xil_DCacheInvalidateRange((INTPTR)s_axis_rx_buf, sizeof(s_axis_rx_buf)); 193 194 // 将整条 BD 链提交给硬件,DMA 从这一刻开始可以向这些 buffer 写数据。 195 status = XAxiDma_BdRingToHw(s_rx_ring, RX_BD_COUNT, bd_ptr); 196 if (status != XST_SUCCESS) { 197 AT_printf("ERR: RX submit to hw failed: %d\r\n", status); 198 axidma_reset_runtime(); 199 return status; 200 } 201 202 // 打开 cyclic 模式,让 DMA 在 BD ring 里循环运行,不会在单轮结束后停住。 203 XAxiDma_BdRingEnableCyclicDMA(s_rx_ring); 204 status = XAxiDma_SelectCyclicMode(&s_axis_dma, XAXIDMA_DEVICE_TO_DMA, 1); 205 if (status != XST_SUCCESS) { 206 AT_printf("ERR: RX cyclic enable failed: %d\r\n", status); 207 axidma_reset_runtime(); 208 return status; 209 } 210 211 // 正式启动 RX ring,S2MM 通道开始接收 PL 端送来的 AXI-Stream 数据。 212 status = XAxiDma_BdRingStart(s_rx_ring); 213 if (status != XST_SUCCESS) { 214 AT_printf("ERR: RX start failed: %d\r\n", status); 215 axidma_reset_runtime(); 216 return status; 217 } 218 219 s_axis_dma_ready = 1; 220 return XST_SUCCESS; 221} 222 223 224static int axidma_capture_one_frame_v0(void) 225{ 226 XAxiDma_Bd *bd_ptr = NULL; 227 int bd_count = XAxiDma_BdRingFromHw(s_rx_ring, XAXIDMA_ALL_BDS, &bd_ptr); 228 229 if (bd_count <= 0) { 230 uint32_t sr = XAxiDma_ReadReg(s_axis_dma.RegBase, 231 XAXIDMA_RX_OFFSET + XAXIDMA_SR_OFFSET); 232 if ((sr & XAXIDMA_ERR_ALL_MASK) != 0u) { 233 AT_printf("ERR: AXI DMA S2MM fault\r\n"); 234 axidma_print_s2mm_diag("AXI DMA fault"); 235 axidma_reset_runtime(); 236 return XST_FAILURE; 237 } 238 AT_printf("dma_read: no frame ready\r\n"); 239 return XST_SUCCESS; 240 } 241 242 // 打印摘要 243 AT_printf("dma_read frame=%u completed_bd=%d/%u\r\n", 244 (unsigned int)s_frame_seq++, 245 bd_count, 246 (unsigned int)RX_BD_COUNT); 247 248 // 遍历所有完成的BD 249 XAxiDma_Bd *bd_cur = bd_ptr; 250 for (int i = 0; i < bd_count; i++) { 251 UINTPTR bd_id = XAxiDma_BdGetId(bd_cur); 252 UINTPTR buf_addr = XAxiDma_BdGetBufAddr(bd_cur); 253 uint32_t actual_len = XAxiDma_BdGetActualLength(bd_cur, s_rx_ring->MaxTransferLen); 254 uint32_t line_count = actual_len / AXIS_LINE_BYTE_COUNT; 255 if (line_count > AXIS_LINES_PER_BD) line_count = AXIS_LINES_PER_BD; 256 257 // 失效缓存 258 Xil_DCacheInvalidateRange((INTPTR)buf_addr, AXIS_BD_BYTE_COUNT); 259 260 const uint32_t *bd_words = (const uint32_t *)buf_addr; 261 262 // 打印此BD的v0值:一行 263 AT_printf("BD %u:", (unsigned int)bd_id); 264 for (uint32_t line = 0; line < line_count; line++) { 265 uint32_t v0 = bd_words[line * AXIS_LINE_WORD_COUNT]; 266 AT_printf(" %u", (unsigned int)v0); 267 } 268 AT_printf("\r\n"); 269 270 // 移到下一个BD 271 bd_cur = (XAxiDma_Bd *)XAxiDma_BdRingNext(s_rx_ring, bd_cur); 272 } 273 274 return XST_SUCCESS; 275} 276 277 278// 读取阶段只做三件事: 279// 1) 从硬件侧取回已经完成的 BD; 280// 2) 失效该 BD 对应的 cache; 281// 3) 按“行号 : v0 ... v21”格式打印出来。 282static int axidma_capture_one_frame(void) 283{ 284 XAxiDma_Bd *bd_ptr = NULL; 285 // 从硬件侧取回已经完成的 BD。这里先尽量把当前完成链表里的 BD 都取出来。 286 int bd_count = XAxiDma_BdRingFromHw(s_rx_ring, XAXIDMA_ALL_BDS, &bd_ptr); 287 288 // 没有拿到完成 BD 时,先区分“暂时还没到帧”还是“DMA 已经出错”。 289 if (bd_count <= 0) { 290 uint32_t sr = XAxiDma_ReadReg(s_axis_dma.RegBase, 291 XAXIDMA_RX_OFFSET + XAXIDMA_SR_OFFSET); 292 if ((sr & XAXIDMA_ERR_ALL_MASK) != 0u) { 293 AT_printf("ERR: AXI DMA S2MM fault\r\n"); 294 axidma_print_s2mm_diag("AXI DMA fault"); 295 axidma_reset_runtime(); 296 return XST_FAILURE; 297 } 298 299 AT_printf("dma_read: no frame ready\r\n"); 300 return XST_SUCCESS; 301 } 302 303 // 如果一次返回了多个 BD,就取链表里的最后一个作为“最新完成的 BD”。 304 // 这样每次命令只打印一个 BD,串口输出会更规整。 305 XAxiDma_Bd *latest_bd = bd_ptr; 306 XAxiDma_Bd *bd_cur = bd_ptr; 307 for (int i = 1; i < bd_count; i++) { 308 bd_cur = (XAxiDma_Bd *)XAxiDma_BdRingNext(s_rx_ring, bd_cur); 309 latest_bd = bd_cur; 310 } 311 312 // 取出这个 BD 的元信息:编号、buffer 地址和实际接收长度。 313 UINTPTR latest_bd_id = XAxiDma_BdGetId(latest_bd); 314 UINTPTR latest_buf_addr = XAxiDma_BdGetBufAddr(latest_bd); 315 uint32_t actual_len = XAxiDma_BdGetActualLength(latest_bd, s_rx_ring->MaxTransferLen); 316 uint32_t line_count = actual_len / AXIS_LINE_BYTE_COUNT; 317 const uint32_t *bd_words = (const uint32_t *)latest_buf_addr; 318 319 // 失效这个 BD 对应的 cache,确保后面打印到的是 DMA 刚写进内存的新数据。 320 Xil_DCacheInvalidateRange((INTPTR)latest_buf_addr, AXIS_BD_BYTE_COUNT); 321 322 if (line_count > AXIS_LINES_PER_BD) { 323 line_count = AXIS_LINES_PER_BD; 324 } 325 326 // 正常情况下,一个 BD 的长度应该刚好等于整块 BD 缓冲区大小。 327 if (actual_len != AXIS_BD_BYTE_COUNT) { 328 AT_printf("WARN: RX length actual=%u expected=%u\r\n", 329 (unsigned int)actual_len, 330 (unsigned int)AXIS_BD_BYTE_COUNT); 331 } 332 333 // 先打印一个简短摘要,方便快速知道这次读到了哪一个 BD。 334 AT_printf("dma_read frame=%u completed_bd=%d/%u latest_bd=%u actual=%u\r\n", 335 (unsigned int)s_frame_seq++, 336 bd_count, 337 (unsigned int)RX_BD_COUNT, 338 (unsigned int)latest_bd_id, 339 (unsigned int)actual_len); 340 // 最后把这个 BD 的 8 行数据逐行打印出来。 341 axidma_dump_bd_lines(bd_words, line_count); 342 343 return XST_SUCCESS; 344} 345 346 347// read 命令的入口:第一次调用时完成初始化,后续直接读取并打印最新 BD。 348int at_app_axi_dma_sg_cycle_read(int argc, char** argv) 349{ 350 351 if (!s_axis_dma_ready) { 352 int status = at_app_axi_full_dma_init(); 353 // 为了简化这个调试命令,这里关闭 DCache,避免缓存一致性影响观察结果。 354 Xil_DCacheDisable(); 355 if (status != XST_SUCCESS) { 356 return status; 357 } 358 } 359 360 if (argc == 1) { 361 at_printf("Usage: s_macManage <cmd>\r\n"); 362 at_printf(" cmd=0 : axidma_capture_one_frame_v0\r\n"); 363 at_printf(" cmd=1 : axidma_capture_one_frame\r\n"); 364 return 0; 365 } 366 int cmd = at_param_to_int(1); 367 if(cmd==0){ 368 return axidma_capture_one_frame_v0(); 369 } 370 if(cmd==1){ 371 return axidma_capture_one_frame(); 372 } 373} 374 375
测试
1$:0 2AtShell commands: 3 0.help - list cmd 4 1.clean - clean screen 5 2.sg_cycle_read - sg_cycle_read 6 3.sg_read - sg_read 7 8$:3 0 9dma_read frame=13 completed_bd=10/10 10BD 1: 552 553 554 555 556 557 558 559 11BD 2: 560 561 562 563 564 565 566 567 12BD 3: 568 569 570 571 572 573 574 575 13BD 4: 576 577 578 579 580 581 582 583 14BD 5: 584 585 586 587 588 589 590 591 15BD 6: 592 593 594 595 596 597 598 599 16BD 7: 600 601 602 603 604 605 606 607 17BD 8: 608 609 610 611 612 613 614 615 18BD 9: 616 617 618 619 620 621 622 623 19BD 10: 624 625 626 627 628 629 630 631 20 21
《zynq中axi_dma的sg模式采集--裸机测试(还有bug)》 是转载文章,点击查看原文。