前言
本篇文章主要讲解在Xilinx ZedBoard上开发PS与PL读写BRAM进行数据通信,从BRAM硬件平台设计、VxWorks6.9开发PS与PL的BRAM读写功能,力求讲述清楚开发流程,并配套完整的演示软件和相关代码进行验证。下面将从以下几个方面进行讲解。
- BRAM设计概述
- Xilinx Vivado BRAM设计与实现
- VxWorks6.9开发PS与PL的BRAM读写
开发使用工具说明:
- Xilinx Vivado2018.3
- Xilinx SDK2018.3
- WorkBench3.3
1、BRAM设计概述
本文介绍一种基于 PL 端 BRAM 的方式,进行 PS 和 PL之间的数据交互。适用于在 PS 和 PL 之间传输少量,地址不连续且长度不规则的数据。
在本例中,在PL端设计了1个4KB(位宽 32,深度1024)的BRAM。首先, PS通过M_AXI_GP口向BRAM中1024个地址依次存入1024 个32位的数据。PS 每向BRAM完成写入 1 个 32 位数据后通过AXI GPIO输出 1 个上升沿信号, PL捕获上升沿后立即将 PS 写入的 32 位数据读出,然后加 2,再存入原地址中。PS主动从BRAM中读出数据,判断对应地址的数据是否被加了 2,若不一致,则报错。
1.1 BRAM Vivado工程
BRAM的FPGA工程框图如图1所示,需要说明的是VIVADO 2017.4 开始可以使用 axi_smc IP 这个 IP 和 AXI_interconnect IP功能类似,但是没有 AXI_interconnect IP强大,在一些高带宽的要求下,还是要用AXI_interconnect IP, 本文使用的VIVADO2018.3自动连线会优先使用axi_smc的IP。
图1 BRAM FPGA框图
(1)PS配置使能 M_AXI_GP0口,将FCLK_CLK0 设为100MHz。
(2)AXI BRAM Controller该IP核连接PS的M_AXI_GP0口和BRAM,完成 AXI 接口至 BRAM接口的转换。设置如下图所示。
添加 BRAM,将 BRAM设置为双口RAM,将PORTA与AXI BRAM Controller连接, PS 通过 PORTA读写BRAM,将 PORTB引出至外部模块, PL 通过 PORTB 读写 BRAM。如下图所示。
由于要与AXI BRAM Controller进行连接,BRAM接口的位宽固定为 32 位。BRAM的深度无法在该 IP 中进行设置,需要在所有模块连接完成后,在Address Editor里对AXI BRAM Controller 的地址范围进行设置,设置为 4KB。该地址范围即对应BRAM的深度,如下图所示。
(3)添加AXI GPIO,位宽设为 1,设置如下图所示。其中 GPIO0作为 PS 向 PL 输出的 PS BRAM 写入完成信号,对于PL而言,上升沿有效。
1.2 BRAM逻辑设计
PL部分逻辑设计主要包括以下几个过程:检测AXI GPIO输出GPIO0的上升沿;若检测到 GPIO0 的上升沿,则从 BRAM 的某 1 个地址中读出 1 个PS写入32位数据,然后加 2,存入原地址中;不断循环上述过程,依次遍历BRAM中0~4092的地址范围。
逻辑代码如下:
module system_wrapper_BRAM( inout [14:0]DDR_addr, inout [2:0]DDR_ba, inout DDR_cas_n, inout DDR_ck_n, inout DDR_ck_p, inout DDR_cke, inout DDR_cs_n, inout [3:0]DDR_dm, inout [31:0]DDR_dq, inout [3:0]DDR_dqs_n, inout [3:0]DDR_dqs_p, inout DDR_odt, inout DDR_ras_n, inout DDR_reset_n, inout DDR_we_n, inout FIXED_IO_ddr_vrn, inout FIXED_IO_ddr_vrp, inout [53:0]FIXED_IO_mio, inout FIXED_IO_ps_clk, inout FIXED_IO_ps_porb, inout FIXED_IO_ps_srstb );
wire [31:0]BRAM_PORTB_addr; wire BRAM_PORTB_clk; wire [31:0]BRAM_PORTB_din; wire [31:0]BRAM_PORTB_dout; wire BRAM_PORTB_en; wire BRAM_PORTB_rst; wire [3:0]BRAM_PORTB_we; wire [0:0]GPIO_tri_i_0; wire [0:0]GPIO_tri_o_0; wire [0:0]aresetn;
reg gpio_tri_o_0_reg; reg ps_bram_wr_done; reg bram_en; reg [3:0] bram_we; reg [31:0] bram_addr; reg [31:0] bram_rd_data; reg [31:0] bram_wr_data; reg [2:0] state;
localparam BRAM_ADDRESS_HIGH = 32'd4096 - 32'd4;
always@(posedge FCLK_CLK0) begin if(!aresetn) gpio_tri_o_0_reg <= 1'b0; else gpio_tri_o_0_reg <= GPIO_tri_o_0; end
always@(posedge FCLK_CLK0) begin if(!aresetn) ps_bram_wr_done <= 1'b0; else if({gpio_tri_o_0_reg, GPIO_tri_o_0} == 2'b01) //gpio0 rising edge ps_bram_wr_done <= 1'b1; else ps_bram_wr_done <= 1'b0; end
always@(posedge FCLK_CLK0) begin if(!aresetn) begin bram_we <= 4'd0; bram_en <= 1'b0; bram_addr <= 32'd0; bram_rd_data <= 32'd0; bram_wr_data <= 32'd0; state <= 3'd0; end else begin case(state) 0: begin if(ps_bram_wr_done) begin bram_en <= 1'b1; bram_we <= 4'd0; state <= 1; end else begin state <= 0; bram_en <= 1'b0; bram_we <= 4'd0; bram_addr <= bram_addr; end end 1: begin bram_en <= 1'b0; state <= 2; end 2: begin bram_rd_data <= BRAM_PORTB_dout; state <= 3; end 3: begin bram_en <= 1'b1; bram_we <= 4'hf; bram_wr_data <= bram_rd_data + 2; state <= 4; end 4: begin state <= 0; bram_en <= 1'b0; if(bram_addr == BRAM_ADDRESS_HIGH) bram_addr <= 32'd0; else bram_addr <= bram_addr + 32'd4; end default: state <= 0; endcase end end
assign BRAM_PORTB_en = bram_en; assign BRAM_PORTB_we = bram_we; assign BRAM_PORTB_rst = ~aresetn; assign BRAM_PORTB_clk = FCLK_CLK0; assign BRAM_PORTB_addr = bram_addr; assign BRAM_PORTB_din = bram_wr_data;
system system_i (.BRAM_PORTB_addr(BRAM_PORTB_addr), .BRAM_PORTB_clk(BRAM_PORTB_clk), .BRAM_PORTB_din(BRAM_PORTB_din), .BRAM_PORTB_dout(BRAM_PORTB_dout), .BRAM_PORTB_en(BRAM_PORTB_en), .BRAM_PORTB_rst(BRAM_PORTB_rst), .BRAM_PORTB_we(BRAM_PORTB_we), .DDR_addr(DDR_addr), .DDR_ba(DDR_ba), .DDR_cas_n(DDR_cas_n), .DDR_ck_n(DDR_ck_n), .DDR_ck_p(DDR_ck_p), .DDR_cke(DDR_cke), .DDR_cs_n(DDR_cs_n), .DDR_dm(DDR_dm), .DDR_dq(DDR_dq), .DDR_dqs_n(DDR_dqs_n), .DDR_dqs_p(DDR_dqs_p), .DDR_odt(DDR_odt), .DDR_ras_n(DDR_ras_n), .DDR_reset_n(DDR_reset_n), .DDR_we_n(DDR_we_n), .FCLK_CLK0(FCLK_CLK0), .FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn), .FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp), .FIXED_IO_mio(FIXED_IO_mio), .FIXED_IO_ps_clk(FIXED_IO_ps_clk), .FIXED_IO_ps_porb(FIXED_IO_ps_porb), .FIXED_IO_ps_srstb(FIXED_IO_ps_srstb), .GPIO_tri_i({GPIO_tri_i_0}), .GPIO_tri_o({GPIO_tri_o_0}), .GPIO_tri_t(), .aresetn(aresetn));
endmodule |
2、VxWorks6.9开发BRAM读写功能
VxWorks6.9的BRAM读写功能测试函数的完成的功能如下:PS配置 AXI GPIO控制GPIO0产生上升沿依次向 BRAM 所对应的地址写入1024 个32 位整型数据,PS依次从 BRAM 所对应的地址读出 1024 个 32 位整型数据,并判断是否被加了2,若比对不一致则报错。
- 首先在sysLib.c中配置BRAM和AXI GPIO映射的内存配置。
#define ZYNQ7K_BRAM_BASE (0x40000000) #define ZYNQ7K_BRAM_SIZE (SZ_4K)
#define ZYNQ7K_XGPIO_BASE (0x41200000) #define ZYNQ7K_XGPIO_SIZE (SZ_64K)
{ ZYNQ7K_BRAM_BASE, /* bram axi address */ ZYNQ7K_BRAM_BASE, SZ_4K, MMU_ATTR_VALID_MSK | MMU_ATTR_PROT_MSK | MMU_ATTR_DEVICE_SHARED_MSK, MMU_ATTR_VALID | MMU_ATTR_SUP_RWX | MMU_ATTR_DEVICE_SHARED },
{ ZYNQ7K_XGPIO_BASE, /* xgpio axi address */ ZYNQ7K_XGPIO_BASE, SZ_64K, MMU_ATTR_VALID_MSK | MMU_ATTR_PROT_MSK | MMU_ATTR_DEVICE_SHARED_MSK, MMU_ATTR_VALID | MMU_ATTR_SUP_RWX | MMU_ATTR_DEVICE_SHARED }, |
- BSP包里面新建bram文件夹,新建AXI GPIO和BRAM操作的代码文件。如下图所示。
bram.c关键代码如下:
#define PS_BRAM_MASK 0x00000001
int test_bram(void) {
unsigned int write_data = 0; unsigned int read_data; int i = 0;
while(1) { /*write 1024 32bit data to bram*/ for(i = 0; 4*i < (XPAR_BRAM_0_HIGHADDR - XPAR_BRAM_0_BASEADDR); i++) { write_data = i;
/*pull gpio0 high*/ vxWritel(write_data, XPAR_BRAM_0_BASEADDR + 4*i); XGpio_DiscreteWrite(1, PS_BRAM_MASK); usleep(1); //延时1US,等待PL修改数据完毕 /*pull gpio0 low*/ XGpio_DiscreteWrite(1, ~PS_BRAM_MASK); }
/*read 1024 32bit data from bram*/ for(i = 0; 4*i < (XPAR_BRAM_0_HIGHADDR - XPAR_BRAM_0_BASEADDR); i++) { read_data = vxReadl(XPAR_BRAM_0_BASEADDR + 4*i); printf("data at address %d is 0x%08x\r\n", 4*i, read_data); /*compare data read form bram if they are add by 2*/ if(read_data != (i + 2)) printf("error: data at address %d should be %d, but is 0x%08x\r\n", 4*i, (i + 2), read_data); }
}
return 0; } |
下载到ZedBoard测试结果部分如下所示,验证了PS与PL通过BRAM通信的正确性。
data at address 0 is 0x00000002 data at address 4 is 0x00000003 data at address 8 is 0x00000004 data at address 12 is 0x00000005 data at address 16 is 0x00000006 data at address 20 is 0x00000007 data at address 24 is 0x00000008 data at address 28 is 0x00000009 data at address 32 is 0x0000000a data at address 36 is 0x0000000b ……. |