欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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位数的耗时

Zynq-Linux移植学习笔记之45-linux下访问物理地址时间优化

优化后的耗时

Zynq-Linux移植学习笔记之45-linux下访问物理地址时间优化

可以看到相差有20-40倍之多。