赞
踩
关于zynq PS PL 数据交互的方式,本文搭建了一个基于Bram的数据交互方式
以下是本篇文章正文内容,下面案例可供参考
AXI DMA
AXI DMA 主要进行大批量的数据交换,有两种模式Direct Register Mode和Scatter/Gather Mode,前者为传统的DMA方式,目标地址,源地址,长度,就可以完成一次连续的读/写,后者可以完成复杂一点读/写操作,按照设定的规则跳着读/写一段地址。
AXI BRAM
AXI BRAM主要进行少量的数据交换,与PL端例化双口RAM大小有关,本生借助AXI control ip 与PS端实现读/写,PL端读/写主要通过双口RAM的另一个口进行读/写,注意点在于,因为要满足AXI control ip的要求所以双口ram要设置成32bit的模式,考虑到PS PL读写不能冲突,所以直接双口ram设置成真双口ram。
网上及主流教程Bram的教程都是直接用两个AXI总线完成的,基本上都是PS控制的为主,本文主要不同之处在于搭建pl端读写的自制ip与ps端进行数据交互。
截图如下(示例):
.bd文件右
.bd文件左
自己封装的ip,pl写入了512个数据进去,等会ps端读出来。
真双口ram配置
ram接口位宽配置
代码如下:
module Bram_rw_control( input clk, input rst_n, output [31:0] addra , output clka , output [31:0] dina , input [31:0] douta , output ena , output rsta , output[3:0] wea ); //wire [31:0] addra; //wire clka ; //wire [31:0] dina ; //wire [31:0] douta; reg rsta_reg; reg ena_reg; assign ena = ena_reg ; assign rsta = rsta_reg ; reg [3:0] wea_reg= 4'd0000; //wire [3:0] wea ; reg [31:0] addra_reg = 'd0; reg [31:0] dina_reg = 'd0; reg [31:0] douta_reg = 'd0; assign addra = addra_reg; assign dina = dina_reg; assign wea = wea_reg; assign clka = clk; reg data_val0 = 'd0; reg data_val1 = 'd0; // 状态空间定义 parameter idle = 2'b00; parameter read = 2'b01; parameter write = 2'b11; parameter stop = 2'b10; reg [1:0] state_current ; reg [1:0] state_next ; reg [9:0] count ; //count 计数 0-1023循环 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin count <= 'd0; end else if(count == 'd512)begin count <= 'd512; end else begin count <= count + 'd1; end end // 状态的转移 always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin state_current <= idle; end else begin state_current<=state_next; end end // 状态转移条件 always @(*)begin case(state_current) idle: begin state_next <= write ; end read:begin if(count >= 'd512) state_next <= read ; else state_next <= write ; end write:begin if(count <= 'd511) state_next <= write ; else state_next <= stop ; end stop:begin if(count == 'd512) state_next <= stop ; else state_next <= read ; end default: state_next <= idle ; endcase end //第三段,产生输出 always @(posedge clk) begin case(state_current) idle: begin wea_reg <= 4'd0000; ena_reg <= 1'd1; rsta_reg <= 1'd0; end read: begin wea_reg <= 4'b0000;//0011 data_val0 <= 'd1; data_val1 <= data_val0; if(data_val1)douta_reg <= douta; // dina_reg <= 'b0; addra_reg <= addra_reg - 'd4; end write: begin wea_reg <= 4'b1111; data_val0 <= 'd0; data_val1 <= 'd0; douta_reg <= 'd0; dina_reg <= dina_reg + 'd2; addra_reg <= addra_reg + 'd4; end stop: begin wea_reg <= 4'b1111; data_val0 <= 'd0; data_val1 <= 'd0; douta_reg <= 'd0; dina_reg <= 'd0; addra_reg <= 'd0; ena_reg <= 1'd0; end // default:; endcase end //blk_mem_gen_0 my_blk_mem_gen_0( // .addra (addra ), // .clka (clk ), // .dina (dina ), // .douta (douta ), // .ena (ena ), // .rsta (rsta ), // .wea (wea ) //); endmodule
这是自己封装的一个测试ip用于pl端读写Bram
导入硬件工程,旧版的.hdf,新版的.xsa文件,编译一下,新建例程hello world,然后改一下helloworld.c。
代码如下
/* * mai.c * * Created on: 2016年6月26日 * Author: Administrator */ #include <stdio.h> #include "xil_io.h" //这个头文件下面包含很重要的IO读写函数 #include "xparameters.h" //这个头文件里把硬件的地址映射等参数都写成了宏定义方便使用 //void print(char *str); int main() { int num; int rev; for( num=0; num<500; num++ ) { ; } xil_printf("------The test is start...------\n\r"); //XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR是axi_bram_ctrl_0的地址,Xil_Out32通过控制axi_bram_ctrl_0,向blk_mem_gen_0写数据 // for( num=0; num<15; num++ ) // // { // Xil_Out32(XPAR_BRAM_0_BASEADDR + num*4, 0x10000000+num); // // // } //XPAR_AXI_BRAM_CTRL_1_S_AXI_BASEADDR是axi_bram_ctrl_1的地址,Xil_In32 通过控制axi_bram_ctrl_0,把blk_mem_gen_0里的数据读出来 //PS和PL可以在blk_mem_gen_0里共享数据 for( num=0; num<20; num++ ) { rev = Xil_In32(XPAR_BRAM_0_BASEADDR + num*4); xil_printf( "The PL write data at %x is %x \n\r",XPAR_BRAM_0_BASEADDR + num*4,rev); } xil_printf("------PS read PL BrAM ok!------\n\r"); // XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR是axi_bram_ctrl_0的地址,Xil_Out32通过控制axi_bram_ctrl_0,向blk_mem_gen_0写数据 for( num=0; num<10; num++ ) { Xil_Out32(XPAR_BRAM_0_BASEADDR + num*4, 0x10000000+num); } xil_printf("------PS write BrAM ok!------\n\r"); for( num=0; num<20; num++ ) { rev = Xil_In32(XPAR_BRAM_0_BASEADDR + num*4); xil_printf( "The PS write data at %x is %x \n\r",XPAR_BRAM_0_BASEADDR + num*4,rev); } xil_printf("------The test is end!------\n\r"); return 0; }
输出效果如图:
PL端对RAM连续写入以2自增的数写入,每一个数32bit 一个字节8bit,所以基址偏移增量为32/8为4,所以左边的地址以4自增,最终程序读了20个pl端写入的值,写了10个值,又读20个值对比pl端写入ps端写入,现在的输出截图是最初版截图,现在没有实物板子不方便在跑一遍输出截个图。
工程版本是2019.2的,zynq7010,我放在我的资源里,0积分,可以下载一波。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。