Bootstrap

使用/dev/mem设备和mmap函数读写bram内存分享

在vivado中给pl添加bram,bram的物理地址是0x40000000,将导出的比特流文件挂载到开发板上,对bram内存的读写其实和对系统物理内存的读写应该是一样的。

1、/dev/mem设备的简介

        在计算机系统中,/dev/mem 是一个特殊的设备文件,它代表了整个物理内存。这个文件提供了对物理内存的直接访问,通常用于低级内存操作,比如驱动程序开发或系统级调试。

        访问 /dev/mem 设备文件通常需要 root 权限,因为它允许用户执行对物理内存的读写操作。以下是一些基本的步骤和概念:

        打开设备文件:使用标准的文件操作函数,比如 open(),打开 /dev/mem 设备文件。

        定位物理地址:确定要访问的物理内存地址。在Linux中,物理地址通常是从0开始的。

        内存映射:使用 mmap() 函数将物理地址映射到进程的地址空间。这允许进程像访问普通内存一样访问物理内存。

        读写操作:一旦内存被映射,就可以使用标准的内存访问函数,如 read()write(),来读取或写入物理内存。

        同步和一致性:由于直接访问物理内存可能会绕过CPU的缓存,因此可能需要使用 msync() 函数或其他机制来确保内存的一致性。

        关闭和解除映射:操作完成后,使用 close() 关闭设备文件,并使用 munmap() 来解除内存映射。

2、mmap函数的介绍

      mmap函数的原型:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

      参数说明:

  1. void *addr:这是映射区域的起始地址。如果传入 NULL,则系统会选择一个合适的地址来放置映射区域。如果传入非 NULL 值,系统将尝试在指定的地址创建映射区域,但这不是强制性的,系统可能根据内存管理的需要选择另一个地址。

  2. size_t length:映射区域的长度,以字节为单位。这个值必须与文件大小或者页的大小(通常是 4KB)对齐。

  3. int prot:保护选项,指定映射区域的访问权限。可以是以下选项的组合:

    PROT_EXEC:允许执行映射区域中的代码。
    PROT_READ:允许读取映射区域中的数据。
    PROT_WRITE:允许写入映射区域中的数据。
    PROT_NONE:没有访问权限。
  4. int flags:控制映射区域特性的标志位。常用的标志位包括:

    MAP_SHARED:对映射区域的写操作将反映到文件上,多个进程可以共享映射区域。MAP_PRIVATE:创建一个私有的复制-on-write映射,写操作不会影响文件,只影响当前进程的映射区域。
    MAP_ANONYMOUS:创建一个匿名映射,不与任何文件关联。
    MAP_FIXED:强制在 addr 指定的地址创建映射区域。
  5. int fd:文件描述符。如果使用匿名映射,这个值应该是 -1。否则,它应该是指向要映射的文件的文件描述符。

  6. off_t offset:文件映射的起始位置,通常应该是页大小的整数倍。对于匿名映射,这个值通常设置为 0。

3、源码

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define BRAM_CTRL_0 0x40000000
#define DATA_LEN    10

int main(int argc, char **argv)
{
    unsigned int *map_base0;

    int fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (fd < 0) {
        printf("can not open /dev/mem \n");
        return (-1);
    }
    printf("/dev/mem is open \n");

    map_base0 = mmap(NULL, DATA_LEN * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, BRAM_CTRL_0);
    if (map_base0 == 0) {
        printf("NULL pointer\n");
    }
    else {
        printf("mmap successful\n");
    }

    unsigned long addr;
    unsigned int content;

    printf("\nwrite data to bram\n");
    addr = (unsigned long)(map_base0);
    content = 0x88888888;
    map_base0[0] = content;
    printf("address: 0x%lx data_write: 0x%x\t\t\n",addr, content);

    close(fd);
    munmap(map_base0, DATA_LEN);

    return 0;
}

        以上代码的流程大概可以概括为:使用open() 打开/dev/mem设备文件,使用mmap函数将物理地址0x40000000映射成为虚拟地址map_base0,然后将数据赋值给变量content,再写入到内存中。

4、运行结果及测试

将源码保存为bram_test.c并编译运行,运行结果如图所示:

使用devmem2的指令查看内存0x4000000中的数据(devmem2的使用请看上篇):

devmem读出的数据和源码里写入的数据是一样的,都是0x88888888

参考:ZYNQ linux下AXI_BRAM的使用方法,PS与PL端数据交互_zynqmp linux axi little-CSDN博客

zynq操作系统: Linux驱动开发Bram篇_zynq bram linux-CSDN博客

;