Zynq-Linux移植学习笔记之15-用户APP直接访问PL物理地址
来源:互联网 发布:淘宝店面装修教程 编辑:程序博客网 时间:2024/06/15 09:03
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 <stdio.h>#include <stdlib.h>#include <stdint.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/mman.h>#include <unistd.h>#include <fcntl.h>#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/memif((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1){perror("open /dev/mem:");}//mmapmap_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 <stdio.h>#include <fcntl.h>#include <unistd.h>#include <string.h>#include <sys/time.h>#include <sys/stat.h>#include <sys/types.h>#include <syslog.h>#include <memory.h>#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_BASEADDR0x10100#define AXI_RIO_MAX_HOPCOUNT13/**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;}
运行结果如下,可以看到和在驱动中实现一样
4、 总结
出于安全考虑,在用户态中直接访问物理地址,这种做法在linux中不常见,不过对于zynq来说这种方法要比实现驱动后再通过app调用驱动接口间接明了的多。也可以这样说,是把驱动移植到了用户态中,在用户态下实现地址映射。考虑到这一点,上面给出的例子可以作为用户态中提供给更上一层app的接口,这样就能避免真正的用户直接接触物理地址。
阅读全文
0 0
- Zynq-Linux移植学习笔记之15-用户APP直接访问PL物理地址
- Zynq-Linux移植学习笔记之15-用户APP直接访问PL物理地址
- Zynq-Linux移植学习笔记之八-linux网络驱动
- Zynq-Linux移植学习笔记之二-知识点
- Zynq-Linux移植学习笔记之三-ramdisk
- Zynq-Linux移植学习笔记之四-fsbl
- Zynq-Linux移植学习笔记之五-rootfs配置
- Zynq-Linux移植学习笔记之六-netperf网络测试
- Zynq-Linux移植学习笔记之七-网络驱动
- Zynq-Linux移植学习笔记之九-petalinux
- Zynq-Linux移植学习笔记之11-qspi驱动配置
- Zynq-Linux移植学习笔记之12-gpio驱动配置
- Zynq-Linux移植学习笔记之13-i2c驱动配置
- Zynq-Linux移植学习笔记之14-RapidIO驱动开发
- Zynq-Linux移植学习笔记之七-网络驱动
- Zynq-Linux移植学习笔记之14-RapidIO驱动开发
- Zynq-Linux移植学习笔记之16-Zynq下linux XADC驱动
- Zynq-Linux移植学习笔记之20-Zynq linux can驱动开发
- 博客第一天
- JS写一个现在的时间练习
- PMP模拟试题与解析(八)
- 省时省力
- 信号捕捉函数中的exit函数
- Zynq-Linux移植学习笔记之15-用户APP直接访问PL物理地址
- TCP新手误区--粘包的处理
- UML类图符号 各种关系说明以及举例
- Hadoop分布式 之 Hbase集群搭建
- bzoj1816: [Cqoi2010]扑克牌
- 【Netty基础】Netty的“零拷贝”
- ROS节点
- 应用Token的流程和作用
- 哲理问题