linux下运行pl,Zynq—Linux移植学习笔记(十五):用户APP直接访问PL物理地址

074e13e8c1a5eeed99c7efb61295cd18.png

1、 背景介绍

在zynq中,由于有PL部分的存在,操作系统需要对PL部分的物理地址进行操作,也就是对操作相关IP核的寄存器。除了在驱动中进行映射外(参看前一篇文章点击打开链接),可以直接在用户态进行地址映射访问。

2、 IO接口头文件

如果做过裸奔的应用程序,可以看到用户app最终调用的接口无非是下面以下这一类函数:

u8 Xil_In8(INTPTR Addr);

u16 Xil_In16(INTPTR Addr);

u32 Xil_In32(INTPTR Addr);

void Xil_Out8(INTPTR Addr, u8 Value);

void Xil_Out16(INTPTR Addr, u16 Value);

void Xil_Out32(INTPTR Addr, u32 Value);

u16 Xil_In16BE(INTPTR Addr);

u32 Xil_In32BE(INTPTR Addr);

void Xil_Out16BE(INTPTR Addr, u16 Value);

void Xil_Out32BE(INTPTR Addr, u32 Value);

在访问物理地址方面,没有比这一类函数更底层的了。当在Linux下对这一类函数加以实现,用户app便可直接访问PL部分物理地址。实现这一类函数需要进行物理映射,不过由于不是驱动,这种映射可以直接放在应用层实现。

下面是xil_in32()和xil_out32()的具体实现,映射时只需要对/dev/mem映射即可。

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define PAGE_SIZE ((size_t)getpagesize())

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

void Xil_Out32(uint64_t phyaddr, uint32_t val)

{

int fd;

volatile uint8_t *map_base;

uint64_t base = phyaddr & PAGE_MASK;

uint64_t pgoffset = phyaddr & (~PAGE_MASK);

if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)

{

perror("open /dev/mem:");

}

map_base = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,

fd, base);

if(map_base == MAP_FAILED)

{

perror("mmap:");

}

*(volatile uint32_t *)(map_base + pgoffset) = val;

close(fd);

munmap((void *)map_base, PAGE_SIZE);

}

int Xil_In32(uint64_t phyaddr)

{

int fd;

uint32_t val;

volatile uint8_t *map_base;

uint64_t base = phyaddr & PAGE_MASK;

uint64_t pgoffset = phyaddr & (~PAGE_MASK);

//open /dev/mem

if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)

{

perror("open /dev/mem:");

}

//mmap

map_base = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,

fd, base);

if(map_base == MAP_FAILED)

{

perror("mmap:");

}

val = *(volatile uint32_t *)(map_base + pgoffset);

close(fd);

munmap((void *)map_base, PAGE_SIZE);

return val;

}

3、 应用层实现

应用层中只需要指定起始物理地址,然后调用xil_in32()和xil_out32()进行操作,为了调用方便,可以封装一层。

#ifndef SMARTCARMOVE_H

#define SMARTCARMOVE_H

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "move.h"

static int fd;

#define MODE (O_WRONLY | O_TRUNC)

/*

static char *gpio_addr[] = {

"/sys/class/gpio/export",

"/sys/class/gpio/gpio61/direction/","/sys/class/gpio/gpio61/value/",

"/sys/class/gpio/gpio62/direction/","/sys/class/gpio/gpio62/value/",

"/sys/class/gpio/gpio63/direction/","/sys/class/gpio/gpio63/value/",

"/sys/class/gpio/gpio64/direction/","/sys/class/gpio/gpio64/value/"

};

*/

extern void rio_setreg32(unsigned int addrBase,unsigned int addrOffset,unsigned int value);

extern int rio_getreg32(unsigned int addrBase,unsigned int addrOffset);

extern int hlMaintWrite(unsigned int dstId,unsigned short hopcount, unsigned int offset, unsigned int writedata);

extern int hlMaintRead(unsigned int dstId,unsigned short hopcount, unsigned int offset, int mrdataAdr);

#endif

#include "xil_io.h"

#include "move.h"

#define AXI_RIO_BASEADDR 0x40000000

#define AXI_RIO_NODE_BASEADDR 0x10100

#define AXI_RIO_MAX_HOPCOUNT 13

/**

read and write phy mem

* */

void rio_setreg32(unsigned int addrBase,unsigned int addrOffset,unsigned int value)

{

Xil_Out32(addrBase+addrOffset, value);

}

int rio_getreg32(unsigned int addrBase,unsigned int addrOffset)

{

int ans=0;

ans=Xil_In32(addrBase+addrOffset);

return ans;

}

int hlMaintWrite(unsigned int dstId,unsigned short hopcount, unsigned int offset, unsigned int writedata)

{

unsigned int reg_addr;

if( hopcount > AXI_RIO_MAX_HOPCOUNT )

{

printf("!!!error, hopcount = %d, > %d\n",hopcount,AXI_RIO_MAX_HOPCOUNT);

return -1;

}

rio_setreg32(AXI_RIO_BASEADDR,AXI_RIO_NODE_BASEADDR,dstId);

reg_addr = (((hopcount+1)<<24)|offset);

rio_setreg32(AXI_RIO_BASEADDR,reg_addr,writedata);

return 0;

}

int hlMaintRead(unsigned int dstId,unsigned short hopcount, unsigned int offset, int mrdataAdr)

{

unsigned int reg_addr;

if( hopcount > AXI_RIO_MAX_HOPCOUNT )

{

printf("!!!error, hopcount = %d, > %d\n",hopcount,AXI_RIO_MAX_HOPCOUNT);

return -1;

}

rio_setreg32(AXI_RIO_BASEADDR,AXI_RIO_NODE_BASEADDR,dstId);

reg_addr = (((hopcount+1)<<24)|offset);

mrdataAdr = rio_getreg32(AXI_RIO_BASEADDR,reg_addr);

printf("M_SRIO_MAINT_REG_READ: hopcount = %d, offset = 0x%x, value = 0x%x\n",hopcount,offset,mrdataAdr);

return 0;

}

int main(int argc, char *argv[])

{

int mtRdata=0;

printf("get 1848 device id through rio\n");

hlMaintRead(0xFF,0, 0, mtRdata);

printf("ok\n");

return 0;

}

运行结果如下,可以看到和在驱动中实现一样

ad9cb2e6394db0788bf83780b21b24ca.png

4、 总结

出于安全考虑,在用户态中直接访问物理地址,这种做法在linux中不常见,不过对于zynq来说这种方法要比实现驱动后再通过app调用驱动接口间接明了的多。也可以这样说,是把驱动移植到了用户态中,在用户态下实现地址映射。考虑到这一点,上面给出的例子可以作为用户态中提供给更上一层app的接口,这样就能避免真正的用户直接接触物理地址。

文章来源:Felven的博客

注:本文为授权转载文章,如需转载请联系作者授权

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值