ZYNQ PS与PL通过AXI-LITE连接,在Linux下直接读写PL的物理地址,实现PS与PL的交互

一、前言

ZYNQ开发,如果PL与PS的交互方式仅为AXI-Lite总线的话,在Linux下可以通过直接访问PL的寄存器物理地址来实现PS-PL的数据交互。
测试代码的PC开发平台为Ubuntu18.04,QT5。
ZYNQ为7020,并移植了Linux系统和Ubuntu16.04的最小系统。

二、PL的设计

将PL的程序封装成IP核,通过AXI-LITE与PS连接,对外是18个寄存器,每个寄存器为32位。
寄存器定义是:寄存器0-7用来接收数据,寄存器8的最高位代表数据到来,寄存器9-16用来发送数据,寄存器17的最高位代表发送使能。程序逻辑比较简单,检测到接收信号后,将数据回传回去。

xPAA
#(
	.PL_GOLBAL_FREQ		(PL_GOLBAL_FREQ)
)u_xPAA
(
	.sys_clk			(S_AXI_ACLK),
	.sys_rst			(S_AXI_ARESETN),
	
	.pl_rx_data1		(slv_reg0),
	.pl_rx_data2		(slv_reg1),
	.pl_rx_data3		(slv_reg2),
	.pl_rx_data4		(slv_reg3),
	.pl_rx_data5		(slv_reg4),
	.pl_rx_data6		(slv_reg5),
	.pl_rx_data7		(slv_reg6),
	.pl_rx_data8		(slv_reg7),
	.pl_rx_en			(slv_reg8[31]),
	.ssr_none			(slv_reg8[30:0]),
	
	.pl_tx_data1		(slv_reg9),
	.pl_tx_data2		(slv_reg10),
	.pl_tx_data3		(slv_reg11),
	.pl_tx_data4		(slv_reg12),
	.pl_tx_data5		(slv_reg13),
	.pl_tx_data6		(slv_reg14),
	.pl_tx_data7		(slv_reg15),
	.pl_tx_data8		(slv_reg16),
	.pl_tx_en			(slv_reg17[31]),
	.sst_none			(slv_reg17[30:0]),
	
	.pl_led				(pl_led)
    );

module xPAA
#(
	parameter		  PL_GOLBAL_FREQ = 120_000000
)
(

	input			  sys_clk,
	input			  sys_rst,
	
	input [31:0] 	  pl_rx_data1,
	input [31:0] 	  pl_rx_data2,
	input [31:0] 	  pl_rx_data3,
	input [31:0]   	  pl_rx_data4,
	input [31:0] 	  pl_rx_data5,
	input [31:0]	  pl_rx_data6,
	input [31:0]	  pl_rx_data7,
	input [31:0] 	  pl_rx_data8,
	input			  pl_rx_en,
	input [30:0]	  ssr_none,
	
	output reg [31:0] pl_tx_data1,
	output reg [31:0] pl_tx_data2,
	output reg [31:0] pl_tx_data3,
	output reg [31:0] pl_tx_data4,
	output reg [31:0] pl_tx_data5,
	output reg [31:0] pl_tx_data6,
	output reg [31:0] pl_tx_data7,
	output reg [31:0] pl_tx_data8,
	output reg 		  pl_tx_en,
	output reg [30:0] sst_none,
	output reg        pl_led
    );
	
//全局主频1MS计次
parameter FREQ_MS_CNT = PL_GOLBAL_FREQ/1000; 

//全局主频100ms计次
parameter	FREQ_100MS_CNT= FREQ_MS_CNT*100;
	
reg pl_rx_en_d0,pl_rx_en_d1;
wire pl_rx_en_edge;
assign pl_rx_en_edge = (!pl_rx_en_d1) & pl_rx_en_d0;

//捕获pl_rx_en的上升沿
always @(posedge sys_clk or negedge sys_rst)begin
 if (~sys_rst)begin
	pl_rx_en_d0 <= 1'b0;
	pl_rx_en_d1 <= 1'b0;end
 else begin
	pl_rx_en_d0 <= pl_rx_en;
	pl_rx_en_d1 <= pl_rx_en_d0;end
end

always @(posedge sys_clk or negedge sys_rst)begin
	if (~sys_rst)
		pl_tx_en <= 1'b0;
	else if(pl_rx_en_edge)
		pl_tx_en <= 1'b1;
	else
		pl_tx_en <= 1'b0;
end
	
always @(posedge sys_clk or negedge sys_rst)begin
	if (~sys_rst)begin
		pl_tx_data1 <= 32'd0;
		pl_tx_data2 <= 32'd0;
		pl_tx_data3 <= 32'd0;
		pl_tx_data4 <= 32'd0;
		pl_tx_data5 <= 32'd0;
		pl_tx_data6 <= 32'd0;
		pl_tx_data7 <= 32'd0;
		pl_tx_data8 <= 32'd0;
		sst_none	<= 31'd0;end
	else if(pl_rx_en_edge)begin
		pl_tx_data1 <= pl_rx_data1;
		pl_tx_data2 <= pl_rx_data2;
		pl_tx_data3 <= pl_rx_data3;
		pl_tx_data4 <= pl_rx_data4;
		pl_tx_data5 <= pl_rx_data5;
		pl_tx_data6 <= pl_rx_data6;
		pl_tx_data7 <= pl_rx_data7;
		pl_tx_data8 <= pl_rx_data8;
		sst_none	<= ssr_none;
	end
end
		
reg [31:0] sys_timer;

always @(posedge sys_clk or negedge sys_rst)begin
	if (~sys_rst)
		sys_timer <= 32'd0;
	else if(sys_timer < FREQ_100MS_CNT)
		sys_timer <= sys_timer + 32'd1;
	else
		sys_timer <= 32'd0;
end
	
always @(posedge sys_clk or negedge sys_rst)begin
	if (~sys_rst)
		pl_led <= 1'd0;
	else if(sys_timer == FREQ_100MS_CNT)
		pl_led <= pl_rx_data2[31];
end
	
endmodule

三、Linux下读写PL物理地址,完成与PL的数据交互

在Linux下直接读写PL的地址来进行数据交互,其实是把PL部分当作了PS的一段内存。
这里开发平台为QT,跨系统比较方便

3.1. 头文件

#include "fpga.h"
#include "sys/mman.h"
#include <unistd.h>
#include <fcntl.h>

h文件内容

这里主要定义了18个寄存器,把PL的基地址看作是0,每个寄存器长度是4

#ifndef FPGA_H
#define FPGA_H

#include <QObject>
#include <QDebug>

//Write Data Reg Define
#define xPAA_WR_DATA_REG0    0
#define xPAA_WR_DATA_REG1    4
#define xPAA_WR_DATA_REG2    8
#define xPAA_WR_DATA_REG3    12
#define xPAA_WR_DATA_REG4    16
#define xPAA_WR_DATA_REG5    20
#define xPAA_WR_DATA_REG6    24
#define xPAA_WR_DATA_REG7    28
//Write Data Control Reg
#define xPAA_WR_CTRL_REG     32

//Read Data Reg Define
#define xPAA_RD_DATA_REG0    36
#define xPAA_RD_DATA_REG1    40
#define xPAA_RD_DATA_REG2    44
#define xPAA_RD_DATA_REG3    48
#define xPAA_RD_DATA_REG4    52
#define xPAA_RD_DATA_REG5    56
#define xPAA_RD_DATA_REG6    60
#define xPAA_RD_DATA_REG7    64

//Read Data Control Reg
#define xPAA_RD_CTRL_REG     68

class fpga
{
public:
    fpga();

    int fpgaInit(uint32_t BaseAddr);
    void fpgaDeInit();
    void fpgaWrite32(uint32_t Reg,uint32_t Data);
    int fpgaRead32(uint32_t Reg);

private:
    uint8_t initFlg=0;
    uint32_t fpgaPgOffset=0;
    volatile uint8_t *fpgaMapBase;
};

#endif // FPGA_H

3.2. 物理地址与虚拟地址转换

封装成IP的FPGA程序,Vivado会分配一段物理地址,可以通过Address Editor页面查看,这个地址是物理地址,想要访问还需要转化为操作系统可以识别的虚拟地址,关于这一块感兴趣的可以查看MMU相关的资料。
以下是地址转换的代码,注意函数名为fpgaInit(),传入的参数是PL的物理地址

#define PAGE_SIZE  ((size_t)getpagesize())
#define PAGE_MASK ((uint64_t) (long)~(PAGE_SIZE - 1))

int fpga::fpgaInit(uint32_t BaseAddr)
{
    initFlg = 0;
	//打开mem文件
    int fd = open("/dev/mem", O_RDWR | O_SYNC);

    if(fd == -1)
    {
        qDebug() << "open /dev/mem error!";
        return -1;
    }

    uint32_t base = BaseAddr & PAGE_MASK;

    fpgaPgOffset = BaseAddr & (~PAGE_MASK);
	//地址映射
    fpgaMapBase = (volatile uint8_t *)mmap(NULL,PAGE_SIZE,PROT_READ | PROT_WRITE,MAP_SHARED,fd,base);
    if(fpgaMapBase == MAP_FAILED)
    {
        qDebug() << "mmap error!";
        close(fd);
        return -2;
    }
	//关闭文件
    close(fd);

    initFlg = 1;

    return 0;
}

3.3. 数据读写

oid fpga::fpgaWrite32(uint32_t Reg, uint32_t Data)
{
    if(initFlg == 0)
    {
        qDebug() << "fpga is not init!";
        return;
    }

    *(volatile uint32_t *)(fpgaMapBase + fpgaPgOffset + Reg) = Data;
}

int fpga::fpgaRead32(uint32_t Reg)
{
    uint32_t Value;

    if(initFlg == 0)
    {
        qDebug() << "fpga is not init!";
        return -1;
    }

    Value = *(volatile uint32_t*)(fpgaMapBase + fpgaPgOffset + Reg);

    return Value;
}

3.4. 取消虚拟地址映射

建议直接放到析构函数里

void fpga::fpgaDeInit()
{
    initFlg = 0;
    munmap((void *)fpgaMapBase,PAGE_SIZE);
}

四、实际使用

QT中的main函数

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    //xMen xMem;
    fpga xFPGA;
    int Status;

	//传入物理地址
    Status = xFPGA.fpgaInit(0x43C00000);

    if(Status != 0)
    {
        qDebug() << "fpga init error!";
    }
	//先读出初始数据
    qDebug() << QString::asprintf("RD_DATA_REG0:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG0));
    qDebug() << QString::asprintf("RD_DATA_REG1:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG1));
    qDebug() << QString::asprintf("RD_DATA_REG2:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG2));
    qDebug() << QString::asprintf("RD_DATA_REG3:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG3));
    qDebug() << QString::asprintf("RD_DATA_REG4:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG4));
    qDebug() << QString::asprintf("RD_DATA_REG5:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG5));
    qDebug() << QString::asprintf("RD_DATA_REG6:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG6));
    qDebug() << QString::asprintf("RD_DATA_REG7:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG7));

    qDebug() << "FPGA WRITE!";

	//写数据
    xFPGA.fpgaWrite32(xPAA_WR_DATA_REG0,0x01010101);
    xFPGA.fpgaWrite32(xPAA_WR_DATA_REG1,0x02020202);
    xFPGA.fpgaWrite32(xPAA_WR_DATA_REG2,0x03030303);
    xFPGA.fpgaWrite32(xPAA_WR_DATA_REG3,0x04040404);
    xFPGA.fpgaWrite32(xPAA_WR_DATA_REG4,0x05050505);
    xFPGA.fpgaWrite32(xPAA_WR_DATA_REG5,0x06060606);
    xFPGA.fpgaWrite32(xPAA_WR_DATA_REG6,0x07070707);
    xFPGA.fpgaWrite32(xPAA_WR_DATA_REG7,0x08080808);

	//写信号
    xFPGA.fpgaWrite32(xPAA_WR_CTRL_REG,0x00000000);
    xFPGA.fpgaWrite32(xPAA_WR_CTRL_REG,0x80000000);

	//再次读出数据
    qDebug() << "FPGA Write Finish!";
    qDebug() << QString::asprintf("RD_DATA_REG0:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG0));
    qDebug() << QString::asprintf("RD_DATA_REG1:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG1));
    qDebug() << QString::asprintf("RD_DATA_REG2:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG2));
    qDebug() << QString::asprintf("RD_DATA_REG3:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG3));
    qDebug() << QString::asprintf("RD_DATA_REG4:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG4));
    qDebug() << QString::asprintf("RD_DATA_REG5:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG5));
    qDebug() << QString::asprintf("RD_DATA_REG6:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG6));
    qDebug() << QString::asprintf("RD_DATA_REG7:%08X",xFPGA.fpgaRead32(xPAA_RD_DATA_REG7));

	//测试完成
    xFPGA.fpgaDeInit();

    return a.exec();
}

生成ARM端的可执行文件.elf,拷贝到开发板中运行,可以看到初始的数据为全0,写数据之后重新读取,数据内容已经改变。

  • 12
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值