通过/ dev / mem写入HW寄存器需要额外写入
我想在Linux上使用/ dev / mem写入一些HW寄存器。 目标板是ZYBO(Zynq,ARM Cortex-A9),硬件是AXI4 Lite Slave,有4个寄存器,由Xilinx Vivado自动生成。
这是写入HW寄存器的C代码。
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#define MAP_BASE (0x43c30000)
#define MAP_RANGE (0x10000)
int main(int argc, char *argv[])
{
volatile unsigned int *p;
void *iomap_ptr;
int fd, i, v;
fd = open("/dev/mem", O_RDWR | O_SYNC);
iomap_ptr = mmap(0, MAP_RANGE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, MAP_BASE);
p = (volatile unsigned int *)iomap_ptr;
for (i = 0; i < 4; i++)
p[i] = i;
for (i = 0; i < 4; i++)
printf("%04X: %08X\n", i, p[i]);
munmap(iomap_ptr, MAP_RANGE);
close(fd);
return 0;
}
当我第一次运行C代码时,结果是:
0000: 00000000
0001: 00000001
0002: 00000002
0003: 00000000
似乎没有应用最终写入。
然后,当我再次运行C代码时,结果是:
0000: 00000000
0001: 00000001
0002: 00000002
0003: 00000003
经过调查,我意识到我需要额外写入硬件才能应用最后一次写入。
如何在没有额外写入的情况下写入HW寄存器?
答案 0 :(得分:0)
为了序言,我在某种程度上错过了问题中的重要信息,因此这个答案的内容远不如它需要的那么便携,但我相信它仍然可以解决问题。关于Zynq和mmap特有的SO有两个相关的问题:
How to get writes via an mmap mapped memory pointer to flush immediately?
此处列出的Xilinx缓存功能似乎最终使用与第一个链接问题中的最佳答案相同的方法。
这可能是一个缓存问题。在这种特定情况下,您可以使用Xilinx库来刷新寄存器所在的地址范围。头文件xil_cache.h
有两个可用于此的函数:
void Xil_DCacheFlush(void);
void Xil_DCacheFlushRange(unsigned int adr, unsigned len);
根据版本的不同,您可能有第三种选择(原型略有不同):
void Xil_DCacheFlush(void);
void Xil_DCacheFlushRange(INTPTR adr, u32 len);
void Xil_DCacheFlushLine(INTPTR adr);
第一个函数Xil_DCacheFlush
将刷新整个缓存。这不是一个特别理想的行为,因为可能存在大量与刷新和无效的问题无关的缓存数据,因此您可能会看到性能影响。
第三个函数Xil_DCacheFlushLine
(如果可用)在这种情况下可能会起作用,因为您只有四个寄存器。它将强制写入(如果行是脏的)然后使高速缓存无效,因此下一次读取将是未命中并且必须从寄存器中取出。然而,这不是最佳解决方案,因为它依赖于对外围设备的一些假设。
第二个函数Xil_DCacheFlushRange
可能就是你想要的函数。使用此功能,您可以指定外围设备的地址范围。在这种情况下,它很小,但这种解决方案可以扩展到具有非常大寄存器组的复杂外设。
执行寄存器写入外设后,您应该能够调用上述任何一个函数来清除全部或部分数据缓存。
可能还有一种方法可以在您的设计中禁用此地址范围的缓存,但我很难为此找到一个好的参考,并且您可能没有所需的区域位置/大小的粒度。