Zynq-Linux移植学习笔记之45-linux下访问物理地址时间优化
程序员文章站
2022-06-03 22:46:10
...
1、背景介绍
在zynq上运行linux程序时,有时候会需要arm去axi总线上挂载的IP核的某个寄存器去取值。最简单的方法就是使用devmem进行虚实地址转换,然后直接访问IP核该寄存器的物理地址。一般情况下如果只是读一两次寄存器这种操作耗时可以接受,但在面对fifo这种需要长时间读取的情况,这种每次都需要转换的耗时就很明显了。在之前的博客中记录过类似的优化方法(虚实地址转换建议),这里再做点补充。
2、优化前代码
优化之前代码很简单,比如下面GetGpioReg/SetGpioReg,里面都是每次打开devmem,mmap,再munmap,最后关闭设备,代码如下:
#define PAGE_SIZE ((size_t)getpagesize())
#define PAGE_MASK ((uint64_t) (long)~(PAGE_SIZE - 1))
//gpio reset operations
void SetGpioReg(unsigned int addrBase,unsigned int addrOffset,unsigned int value)
{
int fd;
volatile uint8_t *map_base;
uint64_t phyaddr=addrBase+addrOffset;
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) = value;
munmap((void *)map_base, PAGE_SIZE);
close(fd);
}
int GetGpioReg(unsigned int addrBase,unsigned int addrOffset)
{
int fd;
uint32_t val;
volatile uint8_t *map_base;
uint64_t phyaddr=addrBase+addrOffset;
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);
munmap((void *)map_base, PAGE_SIZE);
close(fd);
return val;
}
然后像下面这样直接调用即可
SetGpioReg(fifo_rlr_add0,0,0x10);
tmp_size = GetGpioReg(fifo_rdfo_add0,0);
3、优化后代码
其实对转换后的地址进行操作,可以牺牲空间换取时间,也就是mmap后先不进行munmap,把转换后的地址用全局变量记下来,以后直接对该全局变量操作即可。做法如下
先定义一个全局变量保存地址
u32 BaseAddrData=0;
u32 get_map_addr(u32 phyaddr1,u32 phyaddr2)
{
int fd;
u32 BaseAddr;
volatile uint8_t *map_base;
uint64_t phyaddr=phyaddr1+phyaddr2;
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, (void *)base);
if(map_base == MAP_FAILED)
{
perror("mmap:");
}
close(fd);
BaseAddr = map_base + pgoffset;
return BaseAddr;
}
BaseAddrData=get_map_addr(0x43c10020,0);
这样BaseAddrData就是已经映射好的地址,后面直接操作地址即可。这里是从PL的FIFO中取值
pkg_data=*(volatile uint32_t *)(BaseAddrData);
4、耗时对比
同样是从fifo中取值
struct timeval stru_StartT={0};
struct timeval stru_EndT={0};
double dExecTime=0;
gettimeofday(&stru_StartT,NULL);
pkg_data= GetGpioReg(fifo_rdfd_add0,0); //优化前的方式
//pkg_data=*(volatile uint32_t *)(BaseAddrData); //优化后的方式
gettimeofday(&stru_EndT,NULL);
dExecTime=1000000*(stru_EndT.tv_sec-stru_StartT.tv_sec)+(stru_EndT.tv_usec-stru_StartT.tv_usec);
printf("time to read 32bits is %.0lf us\n",dExecTime);
优化前读32位数的耗时
优化后的耗时
可以看到相差有20-40倍之多。
上一篇: 文件IO
下一篇: gem5中正确使用缓存的方法