当前位置: 首页 > news >正文

zynq pl访问ps ddr

1. 背景

在 xilinx mpsoc 平台上进行 Linux 软件开发,不可避免的会涉及到 PS 与 PL 之间的数据交互。这个系列介绍一种基于 DDR 的信息交互方式

这篇文章首先介绍下如何从系统中“偷”内存

2. 交互框图

交互流程:

  1. PS 写入数据到 DDR 中,使用中断通知 PL,PL 从协商好的 DDR 中读取数据;
  2. PL 写入数据到 DDR 中,使用中断通知 PS,PS 从协商好的 DDR 中读取数据;

3. reserved memory

如果 PS 与 PL 要基于 DDR 进行交互,那么,在 PS 端必须将内存空间从系统中“拿”出来,让系统无法知晓或无法使用这个空间。然后,应用程序要想办法操作 DDR 的物理地址进行数据读写。 如何做呢?需要借助预留内存。实现预留内存的简单方法是在设备树中增加 reserved-memory 设备节点,在该节点中定义预留内存的起始地址及大小。

3.1 reserve 1

比如,我要使用一块内存空间作为 PS 与 PL 信息交互的空间,该空间以 0x30000000 为起始地址,大小为 256MB。那么可以在设备树中定义如下节点信息。

project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi:

/include/ "system-conf.dtsi"
/ {
    reserved-memory {
        #address-cells = <2>;
        #size-cells = <2>;
        ranges;
​
        buffer@0x30000000 {
            no-map;
            reg = <0 0x30000000 0 0x10000000>;
        };
    };
}

按照上述方式修改设备树并编译内核,系统启动后通过命令 cat /proc/iomem 即可查看系统的内存分配:

cat /proc/iomem

从下图中可以看到系统使用的内存空间被分为了两部分,0~0x2fffffff 和 0x40000000~0x7fefffff。中间丢失的部分 0x30000000~0x3fffffff 即为我们的预留内存

3.2 reserve more

如果需要预留多块内存,可以添加多个节点。比如预留两块 256MB 的空间,起始地址分别是 0x30000000 和 0x50000000,如下:

/include/ "system-conf.dtsi"
/ {
    reserved-memory {
        #address-cells = <2>;
        #size-cells = <2>;
        ranges;
​
        res1:buffer@0x30000000 {
            no-map;
            reg = <0 0x30000000 0 0x10000000>;
        };
        res2:buffer@0x50000000 {
            no-map;
            reg = <0 0x50000000 0 0x10000000>;
        };
    };
};

从下图中可以看到,0x30000000~0x3fffffff 和 0x50000000~0x5fffffff 这两个地址空间系统无法使用。

4. 总结

基于 DDR 的 PS 与 PL 交互,PS 需要操作 DDR 的物理地址。此时就需要通过预留内存的手段让 Linux 无法使用进行交互的空间。那么,如何在用户空间中使用这块内存呢?答案是借助 UIO

 

 

自己的路径:versions/2020.2/project-spec/meta-user/recipes-bsp/device-tree/files

PS 4G 内存,去掉保留以后,可用内存为3506280 KB = 3.5GB

 

下图就能看出,0x30000000 -0x4fffffff 这段512G空间被默认保留了。

 

5. 参考

https://xilinx-wiki.atlassian.net

设备树中保留内存的定义方式

vexpress-v2p-ca9.dts中保留内存的定义方式为例,说明dts文件中如何定义保留内存。

  1. reserved-memory {

  2. #address-cells = <1>;

  3. #size-cells = <1>;

  4. ranges;

  5. /* Chipselect 3 is physically at 0x4c000000 */

  6. vram: vram@4c000000 {

  7. /* 8 MB of designated video RAM */

  8. compatible = "shared-dma-pool";

  9. reg = <0x4c000000 0x00800000>;

  10. no-map;

  11. };

  12. };

保留内存由根节点和1个或多个子结点组成。

根节点包括如下信息:

  • #address-cells、#size-cells
    必须项,需要同dts根节点中相关属性保持一致。
  1. /dts-v1/;

  2. #include "vexpress-v2m.dtsi"

  3. / {

    1. model = "V2P-CA9";

    2. arm,hbi = <0x191>;

    3. arm,vexpress,site = <0xf>;

    4. compatible = "arm,vexpress,v2p-ca9", "arm,vexpress";

    5. interrupt-parent = <&gic>;

    6. #address-cells = <1>;

    7. #size-cells = <1>;

    8. ...

  • ranges
    必须项,且定义为空

子结点包括如下信息:

  • 空间大小
    可以通过regsize来指定保留内存空间大小,若二者同时存在,以reg属性为准。通过size的方式如下:
  1. reserved-memory {

    1. #address-cells = <1>;

    2. #size-cells = <1>;

    3. ranges;

    4. mfc_left: region_mfc_left {

    5. compatible = "shared-dma-pool";

    6. no-map;

    7. size = <0x2400000>;

  2. ...

  • alignment
    可选项
  • alloc-ranges
    可选项,通常可以和size同时使用。
  1. reserved-memory {

    1. #address-cells = <1>;

    2. #size-cells = <1>;

    3. ranges;

    4. default-pool {

    5. compatible = "shared-dma-pool";

    6. size = <0x6000000>;

    7. alloc-ranges = <0x40000000 0x10000000>;...

  • compatible
    可能包括shared-dma-pool或者shared-dma-pool
    主要关注shared-dma-pool,当驱动程序需要申请DMA空间时,可以从这里进行申请内存空间。
  • no-map
    该属性意为不会为这段内存创建地址映射,在使用之前,需要调用者通过ioremap创建页表映射关系才可以正常访问。这个属性与reusable是互斥的。
  • no-map-fixup
    保持内存映射。
  • reusable
    当驱动程序不使用这些内存的时候,OS可以使用这些内存。
  • linux,cma-default
    定义该段保留内存空间是默认的CMA内存池。
  1. reserved-memory {

    1. #address-cells = <1>;

    2. #size-cells = <1>;

    3. ranges;

    4. default-pool {

    5. compatible = "shared-dma-pool";

    6. size = <0x6000000>;

    7. alloc-ranges = <0x40000000 0x10000000>;

    8. reusable;

    9. linux,cma-default;

  2. };

  3. };

4. 保留内存的使用

4.1 设备树编码

定义保留内存:

  1. memory@60000000 {

    1. device_type = "memory";

    2. reg = <0x60000000 0x40000000>;

  2. };

  3. reserved-memory {

    1. #address-cells = <1>;

    2. #size-cells = <1>;

    3. ranges;

  4. /* test reserve memory */

  5. test_reserve: test_reserve@90000000 {

    1. /* 1 MB reserve memory */

    2. compatible = "shared-dma-pool";

    3. reg = <0x90000000 0x00100000>;

    4. no-map;

  6. };

  7. };

定义memory-region,将保留内存指定给特定设备,如下:

  1. driver-test@8000 {

    1. /* compatible = "test_driver_0", "simple_bus"; */

    2. compatible = "test_driver_0";

    3. reg = <0x80008000 0x1000>;

    4. interrupt-parent = <&gic>;

    5. interrupts= <0 89 4>, <0 90 4444>;

    6. interrupt-names = "first_irq", "second_irq";

    7. clocks = <&oscclk2>;

    8. clock-names = "apb_pclk";

    9. memory-region = <&test_reserve>;

      1. status = "okay";

    10. simple_bus_test{

    11. compatile = "simple_bus_test";

    12. };

  2. };

加载kernel后,保留内存相关的打印信息如下:

 
  1. Reserved memory: created DMA memory pool at 0x90000000, size 1 MiB

  2. OF: reserved mem: initialized node test_reserve@90000000, compatible id shared-dma-pool

参考链接:Linux设备树学习(一)基本知识点 - solonj - 博客园

基于Xilinx Zynq SoC / MPSoC的系统的常见要求之一是为特殊用途预留内存。预留的内存区域需要从linux内核的使用区域中分离出来,仅给特定的驱动程序使用。
reserved-memory 架构包含了预留内存的功能。预留内存的功能又与内核中的 DMA-API 和 CMA 框架密切相关。

本文旨在展示和解释一些可用的用例,并且已经使用Petalinux构建工具进行了测试。由于本文中的修改仅涉及DTS文件定制和设备驱动程序中分配内存位置的改动,还是可以将其导出到Yocto或OSL工作流程中的。

预留内存给设备驱动

为了从系统地址空间预留内存,设备树须配置预留内存的节点。每个节点定义一个特定的内存空间,并且可以根据内核文档中关于可用于预留内存节点的说明配置不同的参数。然后就可以通过memory-region参数将预留的内存空间分配给特定的设备驱动程序使用。

用于64位Cortex-A53 MPSoC的system-top.dts文件中的设备树节点:

 
  1. reserved-memory {

  2. #address-cells = <2>;

  3. #size-cells = <2>;

  4. ranges;

  5. reserved: buffer@0 {

  6. no-map;

  7. reg = <0x0 0x70000000 0x0 0x10000000>;

  8. };

  9. };

  10. reserved-driver@0 {

  11. compatible = "xlnx,reserved-memory";

  12. memory-region = <&reserved>;

  13. };

或32位Cortex-A9 Zynq上,最新的基于 Yocto的Petalinux 的自定义的类似的设备树节点:

 
  1. /include/ "system-conf.dtsi"

  2. / {

  3. reserved-memory {

  4. #address-cells = <1>;

  5. #size-cells = <1>;

  6. ranges;

  7. reserved: buffer@0x38000000 {

  8. no-map;

  9. reg = <0x38000000 0x08000000>;

  10. };

  11. };

  12. reserved-driver@0 {

  13. compatible = "xlnx,reserved-memory";

  14. memory-region = <&reserved>;

  15. };

  16. };

在设备驱动程序中,可以通过解析设备树节点来处理内存区域的属性,并且一旦知道了物理地址和大小,就可以使用 memremap / ioremap 调用来映射内存区域。

下面的代码使用了预留的内存分配:

 
  1. /* Get reserved memory region from Device-tree */

  2. np = of_parse_phandle(dev->of_node, "memory-region", 0);

  3. if (!np) {

  4. dev_err(dev, "No %s specified\n", "memory-region");

  5. goto error1;

  6. }

  7. rc = of_address_to_resource(np, 0, &r);

  8. if (rc) {

  9. dev_err(dev, "No memory address assigned to the region\n");

  10. goto error1;

  11. }

  12. lp->paddr = r.start;

  13. lp->vaddr = memremap(r.start, resource_size(&r), MEMREMAP_WB);

  14. dev_info(dev, "Allocated reserved memory, vaddr: 0x%0llX, paddr: 0x%0llX\n", (u64)lp->vaddr, lp->paddr);

由于保留的内存区域已被内核排除,并标记为no-map,因此 iomem 信息(/ proc / iomem)显示系统RAM小于主板中的内存量。

  1. root@plnx_aarch64:~# cat /proc/iomem

  2. 00000000-6fffffff : System RAM

  3. 00080000-00b37fff : Kernel code

  4. 011c9000-012b8fff : Kernel data

加载设备后,可以确认内存分配:

  1. [ 126.191774] reserved-memory reserved-driver@0: Device Tree Probing

  2. [ 126.198595] reserved-memory reserved-driver@0: Allocated reserved memory, vaddr: 0xFFFFFF8020000000, paddr: 0x7000

  3.  Linux Reserved Memory 预留内存【转】 - sky-heaven - 博客园

  4. Linux设备树学习(一)基本知识点 - solonj - 博客园

  5.  

相关文章:

  • JavaEE初阶:HTML
  • IDEA中JDBC连接MYSQL数据库步骤超详细总结
  • docker 开启 nginx 容器
  • 109 使用Ajax传递请求本地数据库
  • 《算法系列》之设计
  • xerces-c++内存管理策略为何耗费大量内存
  • STM32学习笔记:驱动SPI外设读写FLASH
  • 操作系统安全 基本概念
  • 猿创征文——C++|string类2
  • 【51单片机】认识单片机
  • Windows中执行C语言编译的程序乱码的解决方法
  • 商城项目10_JSR303常用注解、在项目中如何使用、统一处理异常、分组校验功能、自定义校验注解
  • 一天时间迅速准备前端面试|JS基础—原型和原型链【三座大山之一,必考】
  • Spring Security详细讲解(JWT+SpringSecurity登入案例)
  • 【Network】网络基础@应用层 —— 协议 | http | https
  • [译] 怎样写一个基础的编译器
  • 【译】理解JavaScript:new 关键字
  • Bootstrap JS插件Alert源码分析
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • css属性的继承、初识值、计算值、当前值、应用值
  • java8-模拟hadoop
  • JS 面试题总结
  • Mac转Windows的拯救指南
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • Spring Cloud Feign的两种使用姿势
  • spring-boot List转Page
  • Vue--数据传输
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 简单基于spring的redis配置(单机和集群模式)
  • 力扣(LeetCode)357
  • 判断客户端类型,Android,iOS,PC
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 微信小程序开发问题汇总
  • 我是如何设计 Upload 上传组件的
  • 详解NodeJs流之一
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • Nginx惊现漏洞 百万网站面临“拖库”风险
  • 带你开发类似Pokemon Go的AR游戏
  • ​ 轻量应用服务器:亚马逊云科技打造全球领先的云计算解决方案
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • ​ArcGIS Pro 如何批量删除字段
  • ​决定德拉瓦州地区版图的关键历史事件
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • (11)MSP430F5529 定时器B
  • (4)logging(日志模块)
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (二)WCF的Binding模型
  • (附源码)小程序 交通违法举报系统 毕业设计 242045
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (转)VC++中ondraw在什么时候调用的
  • .bat批处理(二):%0 %1——给批处理脚本传递参数