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

这篇具有很好参考价值的文章主要介绍了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,写数据之后重新读取,数据内容已经改变。
zynq7000中linux可以使用pl端的内存吗,FPGA 开发笔记,linux,arm,fpga文章来源地址https://www.toymoban.com/news/detail-846374.html

到了这里,关于ZYNQ PS与PL通过AXI-LITE连接,在Linux下直接读写PL的物理地址,实现PS与PL的交互的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【ARM AMBA AXI 入门 15 -- AXI-Lite 详细介绍】

    请阅读 【ARM AMBA AXI 总线 文章专栏导读】 AMBA AXI4 规范中包含三种不同的协议接口,分别是: AXI4-Full AXI4-Lite AXI4-Stream 上图中的 AXI FULL 和 AIX-Lite 我们都把它们叫做 Memory map, memory map的协议是可以寻址的,它是有地址的,它每次的访问都是针对内存中的一个

    2024年02月04日
    浏览(48)
  • FPGA----UltraScale+系列的PS侧与PL侧通过AXI-HP交互(全网唯一最详)附带AXI4协议校验IP使用方法

    1、之前写过一篇关于ZYNQ系列通用的PS侧与PL侧通过AXI-HP通道的文档,下面是链接。 FPGA----ZCU106基于axi-hp通道的pl与ps数据交互(全网唯一最详)_zcu106调试_发光的沙子的博客-CSDN博客 大家好,今天给大家带来的内容是,基于AXI4协议的采用AXI-HP通道完成PL侧数据发送至PS侧(PS侧数

    2024年02月13日
    浏览(50)
  • ZYNQ7000 PL与PS交互(一): PL到PS中断的使用与实现

    翻译以xilinx用户手册ug585,知道ZYNQ7000有几类中断即可。 PS基于ARM架构,使用了两个Cortex-A9处理器和GIC PL390中断控制器。中断结构与CPU密切相关,并接收来自IO外设和可编程单元PL的中断请求。 本章主要信息: 私有、共享和软件中断。 GIC功能介绍 中断优先级和处理 1.1.1 Zynq 中

    2024年04月15日
    浏览(53)
  • zynq pl访问ps ddr

    在 xilinx mpsoc 平台上进行 Linux 软件开发,不可避免的会涉及到 PS 与 PL 之间的数据交互。这个 系列 介绍一种 基于 DDR 的信息交互方式 。 这篇文章首先介绍下 如何从系统中“偷”内存 。 交互流程: PS 写入数据到 DDR 中,使用中断通知 PL,PL 从协商好的 DDR 中读取数据; PL 写

    2024年02月01日
    浏览(52)
  • zynq板上PS端给PL端复位

    最近接触zynq板-zcu104,记录一些实验笔记。 LED闪烁 在vivado中新建一个工程,命名为led-test。添加top.v文件。 其中,top模块接入zcu104开发板的clk_300_p/n信号,通过clock wizard转换为单端始终clock,接入到led闪烁模块led.v 其中,led模块通过一个24bit的计数器,经过相等间隔厚,让led灯

    2024年02月11日
    浏览(40)
  • zynq的PL向PS提供时钟和复位

    最近买了一块矿卡蚂蚁T9+,它的资源比EBAZ4205丰富。 需要矿卡资料包的朋友可以从这下载。里面包含蚂蚁T9+和EBAZ4205原理图和几个EBAZ4205例程,还有一些相关的pdf文档。 link 首先从fpga学起,可惜PL没有焊晶振,只好从PS端引,下面以点灯为例。 打开vivado,工具栏file -- project -

    2024年02月15日
    浏览(48)
  • ZYNQ学习笔记(四):PL与PS数据交互——基于BRAM IP 核的(PS端读写+PL端读)控制实验

    说起PS、PL数据交互,常见的有IO方式:MIO EMIO GPIO,还有利用BRAM或FIFO等,在上一篇文章ZYNQ学习笔记(三):PL与PS数据交互—— UART串口+AXI GPIO控制DDS IP核输出实验咱们学会了如何利用AXI GPIO IP核来实现PS(写)与PL(读)的数据交互,那么这篇文章来学习如何使用BRAM~ 本次实验

    2024年02月05日
    浏览(57)
  • ZYNQ PS使用axi uartlite进行串口收发

    由于使用的ZYNQ PS部分只有两个串口,其中一个还当成了控制台用,串口不够用,于是使用PL的逻辑部分并利用IP核:AXI UARTLITE 为PS增加串口数量,并添加了AXI TIMER。 Vivado和Vitis为2020,PS为裸机使用。 包含以下内容: 1、Vivado的配置 2、axi uartlite代码 3、axi timer代码 4、利用IP核:

    2024年04月12日
    浏览(42)
  • FPGA - ZYNQ 基于EMIO的PS和PL交互

    Xilinx ZYNQ 系列的芯片,GPIO分为 MIO 、 EMIO 、 AXI_GPIO 三种方式。 MIO    :固定管脚,属于PS端,也就是ARM端。 EMIO   :通过PL扩展,使用时需要分配 PL ( FPGA ) 管脚,消耗PL端资源。 AXI_GPIO :封装好的IP核,PS通过M_AXI总线可以控制PL的IO接口,使用时,消耗管脚资源和逻辑资源。

    2024年04月26日
    浏览(37)
  • AXI_UART调试说明-PS使用AXI_Uartlite拓展PL端串口资源

    ** ** 注:本例程是在xc7z010clg400_1实现,若导入至复旦微电子需更改为xc7z045iffg900-2L (目录中带*号的可略过) 背景:PS端UART资源有限,难以满足实际运用中多串口的需求。 具体方法:PS通过AXI总线调用PL的资源进行UART的拓展,本说明采用vivado自带的IP核AXI Uartlite完成,属于AX

    2024年02月05日
    浏览(111)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包