通过 /dev/mem 写入 HW 寄存器需要额外写入
Writing to HW registers via /dev/mem needs extra write
通过/dev/mem写入硬件寄存器需要额外写入
我想在 Linux 上使用 /dev/mem 写入一些硬件寄存器。
目标板为ZYBO(Zynq,ARM Cortex-A9),HW为Xilinx Vivado自动生成的AXI4 Lite Slave,4个寄存器。
这是写入硬件寄存器的 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
经过调查,我意识到我需要对硬件进行额外的写入才能应用上次写入。
如何在不进行额外写入的情况下写入硬件寄存器?
作为序言,我不知何故错过了问题中的重要信息,所以这个答案的内容远没有它需要的那样可移植,但我相信它仍然可以解决问题。针对 Zynq 和 mmap 的 SO 有两个相关问题:
Flush cache to DRAM
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
应该就是你想要的功能吧。使用此功能,您可以指定外设的地址范围。在这种情况下,它很小,但是这个解决方案可以扩展到具有非常大的寄存器集的复杂外围设备。
在您对外围设备执行寄存器写入后,您应该能够调用您喜欢的上述函数中的任何一个来刷新全部或部分数据缓存。
可能还有一种方法可以在您的设计中禁用此地址范围的缓存,但我很难为此找到一个好的参考,而且您可能没有区域 location/size 中的粒度欲望。
通过/dev/mem写入硬件寄存器需要额外写入
我想在 Linux 上使用 /dev/mem 写入一些硬件寄存器。 目标板为ZYBO(Zynq,ARM Cortex-A9),HW为Xilinx Vivado自动生成的AXI4 Lite Slave,4个寄存器。
这是写入硬件寄存器的 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
经过调查,我意识到我需要对硬件进行额外的写入才能应用上次写入。
如何在不进行额外写入的情况下写入硬件寄存器?
作为序言,我不知何故错过了问题中的重要信息,所以这个答案的内容远没有它需要的那样可移植,但我相信它仍然可以解决问题。针对 Zynq 和 mmap 的 SO 有两个相关问题:
Flush cache to DRAM
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
应该就是你想要的功能吧。使用此功能,您可以指定外设的地址范围。在这种情况下,它很小,但是这个解决方案可以扩展到具有非常大的寄存器集的复杂外围设备。
在您对外围设备执行寄存器写入后,您应该能够调用您喜欢的上述函数中的任何一个来刷新全部或部分数据缓存。
可能还有一种方法可以在您的设计中禁用此地址范围的缓存,但我很难为此找到一个好的参考,而且您可能没有区域 location/size 中的粒度欲望。