告别AXI DMA!用PYNQ-Z2和BRAM实现PS与PL的轻量级数据交换(Vivado 2023.1实战)
2026/6/6 7:14:52 网站建设 项目流程

告别AXI DMA!用PYNQ-Z2和BRAM实现PS与PL的轻量级数据交换(Vivado 2023.1实战)

在嵌入式开发中,ZYNQ系列芯片的PS(Processing System)与PL(Programmable Logic)之间的数据交互一直是开发者关注的焦点。传统上,AXI DMA(Direct Memory Access)被广泛用于大数据量传输场景,但对于小数据量、不规则传输需求,AXI DMA显得过于复杂且资源占用高。本文将介绍一种更轻量级的替代方案——基于AXI BRAM控制器的PS与PL数据交互方法,特别适合PYNQ-Z2开发板和Vivado 2023.1环境下的快速原型开发。

1. 为什么选择BRAM而非DMA?

当处理小数据量(通常小于2MB)且传输模式不规则的场景时,AXI BRAM控制器提供了几个显著优势:

  • 低延迟:BRAM作为片上存储,访问延迟远低于通过DMA访问DDR内存
  • 简化设计:无需复杂的DMA配置和中断处理
  • 资源效率:节省了DMA控制器占用的逻辑资源和功耗
  • 实时性:数据立即可见,无需等待传输完成

典型适用场景

  • 传感器数据采集(如ADC读数)
  • 控制参数传递
  • 状态寄存器交换
  • 小批量图像/音频数据预处理

注意:当数据量超过BRAM容量或需要高带宽连续传输时,仍应优先考虑DMA方案

2. 硬件平台搭建(Vivado 2023.1)

2.1 工程创建与ZYNQ配置

  1. 新建Vivado工程,选择PYNQ-Z2板卡预设
  2. 创建Block Design,添加ZYNQ7 Processing System IP
  3. 关键配置项:
    # 使能M_AXI_GP0接口 set_property CONFIG.PCW_USE_M_AXI_GP0 {1} [get_bd_cells processing_system7_0] # 配置UART1用于调试输出 set_property CONFIG.PCW_UART1_PERIPHERAL_ENABLE {1} [get_bd_cells processing_system7_0] # 设置PL时钟为100MHz set_property CONFIG.PCW_FPGA0_PERIPHERAL_FREQMHZ {100} [get_bd_cells processing_system7_0]

2.2 添加并配置BRAM子系统

  1. 添加AXI BRAM Controller IP(默认AXI4-Lite模式)
  2. 添加Block Memory Generator IP,配置为:
    • 内存类型:True Dual Port RAM
    • 数据宽度:32位
    • 深度:1024(4KB容量)
    • 使能ECC:关闭

连接后的系统框图应包含以下关键信号路径:

PS.M_AXI_GP0 → AXI Interconnect → AXI BRAM Controller → BRAM ↗ PL用户逻辑通过另一个端口直接访问BRAM

3. PL侧数据访问实现

3.1 自定义IP核设计

我们创建一个简单的读控制模块,通过AXI4-Lite接口接收PS发送的配置参数:

module bram_reader ( input wire S_AXI_ACLK, input wire S_AXI_ARESETN, // AXI4-Lite从接口 input wire [31:0] S_AXI_AWADDR, // ...其他AXI信号... // BRAM端口B接口 output wire BRAM_CLK, output wire BRAM_EN, output wire [31:0] BRAM_ADDR, input wire [31:0] BRAM_RD_DATA ); // 寄存器配置空间 reg [31:0] start_addr; reg [31:0] data_length; reg start_trigger; // AXI寄存器写入逻辑 always @(posedge S_AXI_ACLK) begin if (!S_AXI_ARESETN) begin start_addr <= 32'h0; data_length <= 32'h0; start_trigger <= 1'b0; end else if (axi_write_en) begin case (S_AXI_AWADDR[7:0]) 8'h00: start_addr <= S_AXI_WDATA; 8'h04: data_length <= S_AXI_WDATA; 8'h08: start_trigger <= S_AXI_WDATA[0]; endcase end end // BRAM读取状态机 reg [1:0] state; reg [31:0] current_addr; always @(posedge S_AXI_ACLK) begin if (!S_AXI_ARESETN) begin state <= 2'd0; BRAM_EN <= 1'b0; end else begin case (state) 2'd0: if (start_trigger) begin BRAM_EN <= 1'b1; current_addr <= start_addr; state <= 2'd1; end 2'd1: if (current_addr >= start_addr + data_length - 4) begin BRAM_EN <= 1'b0; state <= 2'd2; end else begin current_addr <= current_addr + 4; end 2'd2: state <= 2'd0; endcase end end assign BRAM_ADDR = current_addr; assign BRAM_CLK = S_AXI_ACLK; endmodule

3.2 ILA调试配置

为验证数据传输正确性,添加Integrated Logic Analyzer(ILA)核监控关键信号:

# 在Vivado Tcl控制台执行 create_debug_core u_ila_0 ila set_property C_DATA_DEPTH 1024 [get_debug_cores u_ila_0] set_property C_INPUT_PIPE_STAGES 2 [get_debug_cores u_ila_0] # 添加监控信号 debug_core -add_probe BRAM_RD_DATA[31:0] -width 32 debug_core -add_probe BRAM_ADDR[31:0] -width 32 debug_core -add_probe BRAM_EN -width 1

4. PS侧软件实现

4.1 Vitis工程设置

  1. 导出硬件平台(包括bitstream)到Vitis
  2. 创建新的Application Project
  3. 配置Board Support Package(BSP),确保包含以下驱动:
    • xilffs(用于文件系统操作)
    • xilmailbox(可选,用于核间通信)
    • xilsecure(可选)

4.2 数据交互代码实现

#include "xil_io.h" #include "xparameters.h" #include "xil_printf.h" #define BRAM_BASE XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR #define USER_IP_BASE XPAR_BRAM_READER_0_S00_AXI_BASEADDR // 用户IP寄存器偏移量 #define REG_START_ADDR 0 #define REG_DATA_LEN 4 #define REG_TRIGGER 8 void write_to_bram(uint32_t addr, uint32_t data) { Xil_Out32(BRAM_BASE + addr, data); } uint32_t read_from_bram(uint32_t addr) { return Xil_In32(BRAM_BASE + addr); } void configure_pl_reader(uint32_t start, uint32_t len) { // 设置起始地址 Xil_Out32(USER_IP_BASE + REG_START_ADDR, start); // 设置数据长度 Xil_Out32(USER_IP_BASE + REG_DATA_LEN, len); // 触发读取(脉冲信号) Xil_Out32(USER_IP_BASE + REG_TRIGGER, 1); Xil_Out32(USER_IP_BASE + REG_TRIGGER, 0); } int main() { char input_str[256]; uint32_t str_len; xil_printf("BRAM数据交互演示\n\r"); while(1) { xil_printf("请输入字符串(最大256字符):\n\r"); scanf("%255s", input_str); str_len = strlen(input_str); // 写入BRAM for(int i=0; i<str_len; i++) { write_to_bram(i*4, input_str[i]); } // 配置PL读取器 configure_pl_reader(0, str_len*4); // 验证读取 xil_printf("BRAM内容验证:\n\r"); for(int i=0; i<str_len; i++) { char c = (char)read_from_bram(i*4); xil_printf("地址%04x: %c (0x%02x)\n\r", i*4, c, c); } } return 0; }

5. 性能优化与进阶技巧

5.1 双端口BRAM的高效使用

利用BRAM的双端口特性,可以同时实现PS和PL的并行访问:

  • 端口A:配置为32位宽,供PS通过AXI BRAM控制器访问
  • 端口B:配置为自定义宽度(如128位),供PL高效读取
// 在Block Memory Generator中配置不对称端口 set_property -dict [list \ CONFIG.Memory_Type {True_Dual_Port_RAM} \ CONFIG.Enable_A {Use_ENA_Pin} \ CONFIG.Enable_B {Use_ENB_Pin} \ CONFIG.Register_PortA_Output_of_Memory_Primitives {false} \ CONFIG.Register_PortB_Output_of_Memory_Primitives {false} \ CONFIG.Port_A_Write_Width {32} \ CONFIG.Port_A_Read_Width {32} \ CONFIG.Port_B_Write_Width {128} \ CONFIG.Port_B_Read_Width {128} \ ] [get_bd_cells bram_0]

5.2 数据一致性保障

当PS和PL同时访问BRAM时,需要考虑数据一致性问题:

  1. 软件同步:通过共享状态寄存器实现握手协议

    // PS侧写入数据后设置标志位 #define SYNC_REG_OFFSET 0x100 void signal_data_ready() { Xil_Out32(USER_IP_BASE + SYNC_REG_OFFSET, 0x1); // 等待PL确认 while(Xil_In32(USER_IP_BASE + SYNC_REG_OFFSET) != 0x2); }
  2. 硬件互斥:使用BRAM的字节使能信号实现原子操作

    // PL侧实现简单的互斥锁 reg bram_lock; always @(posedge clk) begin if (lock_request && !bram_lock) begin bram_lock <= 1'b1; // 访问BRAM... bram_lock <= 1'b0; end end

5.3 性能基准测试

在PYNQ-Z2上实测不同方案的延迟表现:

操作类型平均延迟(时钟周期)
PS写BRAM5-7
PL读BRAM1(组合逻辑)
PS通过DMA传输64字节50-60
PS通过BRAM传输64字节10-15

测试条件:PL时钟100MHz,AXI总线时钟50MHz,DMA使用默认配置

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询