1、 背景介紹
在zynq中,由于有PL部分的存在,操作系統(tǒng)需要對(duì)PL部分的物理地址進(jìn)行操作,也就是對(duì)操作相關(guān)IP核的寄存器。除了在驅(qū)動(dòng)中進(jìn)行映射外(參看前一篇文章點(diǎn)擊打開(kāi)鏈接),可以直接在用戶態(tài)進(jìn)行地址映射訪問(wèn)。
2、 IO接口頭文件
如果做過(guò)裸奔的應(yīng)用程序,可以看到用戶app最終調(diào)用的接口無(wú)非是下面以下這一類(lèi)函數(shù):
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);
在訪問(wèn)物理地址方面,沒(méi)有比這一類(lèi)函數(shù)更底層的了。當(dāng)在Linux下對(duì)這一類(lèi)函數(shù)加以實(shí)現(xiàn),用戶app便可直接訪問(wèn)PL部分物理地址。實(shí)現(xiàn)這一類(lèi)函數(shù)需要進(jìn)行物理映射,不過(guò)由于不是驅(qū)動(dòng),這種映射可以直接放在應(yīng)用層實(shí)現(xiàn)。
下面是xil_in32()和xil_out32()的具體實(shí)現(xiàn),映射時(shí)只需要對(duì)/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、 應(yīng)用層實(shí)現(xiàn)
應(yīng)用層中只需要指定起始物理地址,然后調(diào)用xil_in32()和xil_out32()進(jìn)行操作,為了調(diào)用方便,可以封裝一層。
#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 ",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 ",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 ",hopcount,offset,mrdataAdr);
return 0;
}
int main(int argc, char *argv[])
{
int mtRdata=0;
printf("get 1848 device id through rio ");
hlMaintRead(0xFF,0, 0, mtRdata);
printf("ok ");
return 0;
}
運(yùn)行結(jié)果如下,可以看到和在驅(qū)動(dòng)中實(shí)現(xiàn)一樣
4、 總結(jié)
出于安全考慮,在用戶態(tài)中直接訪問(wèn)物理地址,這種做法在linux中不常見(jiàn),不過(guò)對(duì)于zynq來(lái)說(shuō)這種方法要比實(shí)現(xiàn)驅(qū)動(dòng)后再通過(guò)app調(diào)用驅(qū)動(dòng)接口間接明了的多。也可以這樣說(shuō),是把驅(qū)動(dòng)移植到了用戶態(tài)中,在用戶態(tài)下實(shí)現(xiàn)地址映射。考慮到這一點(diǎn),上面給出的例子可以作為用戶態(tài)中提供給更上一層app的接口,這樣就能避免真正的用戶直接接觸物理地址。
評(píng)論
查看更多