通过/ dev / mem写入HW寄存器需要额外写入

时间:2016-09-19 10:28:51

标签: arm embedded linux-device-driver embedded-linux memory-barriers

通过/ 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寄存器?

1 个答案:

答案 0 :(得分:0)

为了序言,我在某种程度上错过了问题中的重要信息,因此这个答案的内容远不如它需要的那么便携,但我相信它仍然可以解决问题。关于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可能就是你想要的函数。使用此功能,您可以指定外围设备的地址范围。在这种情况下,它很小,但这种解决方案可以扩展到具有非常大寄存器组的复杂外设。

执行寄存器写入外设后,您应该能够调用上述任何一个函数来清除全部或部分数据缓存。

可能还有一种方法可以在您的设计中禁用此地址范围的缓存,但我很难为此找到一个好的参考,并且您可能没有所需的区域位置/大小的粒度。