文章目录
前言
关于zynq PS PL 数据交互的方式,本文搭建了一个基于Bram的数据交互方式
以下是本篇文章正文内容,下面案例可供参考
一、ZYNQ数据交互方式
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端进行数据交互。
二、Create Block Design
1.创建硬件工程
截图如下(示例):
.bd文件右
.bd文件左
自己封装的ip,pl写入了512个数据进去,等会ps端读出来。
真双口ram配置
ram接口位宽配置
2.封装的ip代码
代码如下:
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
3.使用vitis写ps端程序
导入硬件工程,旧版的.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积分,可以下载一波。