我有一个 Terasic-SoCKIT(fpga 和 arm cortex a9),并且我在 HPS 上运行 Linux。我正在尝试访问内存映射 I/O,编写了一个带有函数“request_mem_region”和“ioremap”的简单字符驱动程序。
内存映射 IO 是 AXI 总线,我可以使用它向 FPGA 传输数据。我发现每次写入大约需要 6us,而对于我的应用程序,我需要它少于 1us。另外,驱动程序在几次写入后停止写入映射的IO(没有看到fpga中的数据被更改;驱动程序中的缓冲区是否已满?)。
问题是我是否遗漏了某些内容,或者因为写入是从虚拟地址到物理地址发生的,所以速度不能再快了?如果从虚拟地址写入速度变慢,有没有办法加快速度?我知道 ARM 有 DMAC,但我还没有探索过。
谢谢你,卡西克
抱歉,我错过了告诉大家我正在测量用户空间代码中的时间。后来我检查了写入驱动程序所花费的时间,单位是纳秒。所以,我认为大部分时间都是从用户空间写入内核。
因此,我进一步阅读并了解 ioremap() 将物理地址映射到内核虚拟地址,而 remap_pfn_range() 将物理地址/IO 内存映射到用户虚拟空间(这就是我所需要的;从用户写入 IO 内存)空间)。我使用了简单的 mmap 示例 -http://web.cecs.pdx.edu/~jrb/ui/linux/examples.dir/simple/simple.c作为内核驱动程序。以下代码是我的用户空间代码:
using namespace std;
#include <iostream>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <ctime>
#define PAGE_SIZE 4096
#define HPS2FPGA_BRIDGE_BASE 0xc0000000
#define BLINK_OFFSET 0x0
volatile unsigned char *blink_mem;
void *bridge_map;
int main()
{
int fd, ret = EXIT_FAILURE;
unsigned int i;
unsigned char value;
int dummy;
off_t blink_base = HPS2FPGA_BRIDGE_BASE;
clock_t start, stop;
double duration;
/* open the memory device file */
fd = open("/dev/HPS2FPGA", O_RDWR|O_SYNC);
if (fd < 0) {
perror("open");
exit(EXIT_FAILURE);
}
/* map the LWHPS2FPGA bridge into process memory */
bridge_map = mmap(NULL, PAGE_SIZE, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_SHARED,
fd, blink_base);
if (bridge_map == MAP_FAILED) {
perror("mmap");
goto cleanup;
}
/* get the delay_ctrl peripheral's base address */
blink_mem = (unsigned char *) (bridge_map + BLINK_OFFSET);
start = clock();
/* write the value */
for(i = 0; i < 1000000; i++)
{
*blink_mem = i;
dummy = *blink_mem;
}
stop = clock();
duration = ( stop - start ) / (double) CLOCKS_PER_SEC;
printf("%f", duration);
if (munmap(bridge_map, PAGE_SIZE) < 0) {
perror("munmap");
goto cleanup;
}
ret = 0;
cleanup:
close(fd);
return ret;
}
我正在写入 mmap 返回的虚拟地址空间,并且我可以通过读取该地址的值来验证写入,但我没有看到 FPGA 中的值得到更新。
当我写入用户虚拟空间时,物理地址是如何写入的?有没有办法调试并查看物理地址空间是否确实被写入?
答案1
好吧,看来这个问题的主题是毛皮...内存映射 I/O(正确完成)将与处理器为正在访问的硬件执行此操作一样快,并且与内核模式相反,从用户模式执行此操作不会产生任何开销(即没有“从用户空间写入内核”)。
但是,您仍然必须考虑当您对地址进行读取或写入时会发生什么(这就是问题所在)。在大多数架构中,有两种映射 - 虚拟到物理,以及物理到设备。第一个是在虚拟内存硬件中设置的,第二个是在内存控制器中设置的。
除了映射之外,所有访问通常都通过缓存硬件,因此您必须决定是否要缓存访问。如果正在访问的底层设备是某种 RAM,那么您通常希望缓存访问。对于其他类型的设备,通常不需要。
可能还有很多其他事情需要考虑(例如,VM 映射是否驻留在 VM 硬件中、访问的宽度和时间、优先级、权限等),但缓存是第一位的。
在@Karthik的例子中,因为他没有关掉缓存在他的映射中,取决于缓存的类型,要么是整个缓存线当他写入地址时正在写入(直写),或者写入被延迟(回写)(如果您想了解有关缓存的一些细节,请尝试这)。
为了回答特定的(后续)问题,一旦虚拟地址映射完成并且缓存完成其工作,访问就会进入内存控制器 - 该硬件决定正在访问哪个总线和/或设备并执行“正确的操作”。该硬件的“东西”,通常涉及断言片选和/或一个写使能信号,可能复制部分或全部身体的地址到地址线,也许一些设置时序, ETC。
...调试这个东西的最好方法是有一个分析仪某种连接到您的设备或总线的设备,或者如果这太困难/昂贵,则内存控制器中可能有一些调试支持。
blink_mem
另一个次要但重要的一点......请注意上面代码中的声明-易挥发的类型限定符非常重要。它告诉编译器不要破坏对地址的访问。除此之外,您还应该了解与内存访问有关的任何特殊管道指令(查看埃伊奥powerpc 教学 - 有人有幽默感:-)
最后,只是重申一下评论中所说的内容,事实证明这是问题的真正答案,当打电话时remap_pfn_range()你关通过修改最后一个参数中指定的页面保护来缓存(蛋白质) 使用pgprot_noncached()宏。另请阅读这和这特别是这。干杯!