您的位置:首页 > 运维架构 > Linux

Zynq-Linux移植学习笔记之15-用户APP直接访问PL物理地址

2017-08-04 14:46 791 查看
 


Zynq-Linux移植学习笔记之15-用户APP直接访问PL物理地址



参考网址:http://blog.csdn.net/zhaoxinfan/article/details/73351247

1、  背景介绍

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

 

2、  IO接口头文件

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

[cpp] view
plain copy

 print?

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映射即可。

[cpp] view
plain copy

 print?

#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/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()进行操作,为了调用方便,可以封装一层。

 

[cpp] view
plain copy

 print?

#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  

 

[cpp] view
plain copy

 print?

#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;  

}  

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



4、  总结

出于安全考虑,在用户态中直接访问物理地址,这种做法在linux中不常见,不过对于zynq来说这种方法要比实现驱动后再通过app调用驱动接口间接明了的多。也可以这样说,是把驱动移植到了用户态中,在用户态下实现地址映射。考虑到这一点,上面给出的例子可以作为用户态中提供给更上一层app的接口,这样就能避免真正的用户直接接触物理地址。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: