Skip to main content

Linux预留内存管理

预留内存的目的是根据不同场景为特定功能分配独立内存区域,便于调试、减少内存碎片、提高稳定性。各预留内存的用途如下:

rmem:用于lcd、camera、jpeg编解码、2d等需要大块连续内存的场景。

rtos_size:双核双系统中预留给小核(rtos)的专用内存。

share_mem:双核双系统中,小核(rtos)先初始化外设并将配置结构体写入共享内存 share_mem(配置区固定32KB,工作区用于帧缓冲等大块内存),linux 启动后直接读取接管(lcd配置和帧内存,pwm背光配置等),避免重复初始化。

reserved_memory/vpu_mem:两种互斥的内存预留方式,用于为特定驱动(如H264解码、其他需要大块连续内存的模块)提前划分独立内存区域,避免内存碎片。本文档以H264解码为例进行说明。

1. 预留内存方式

1.1 rmem

说明:rmem的内存通过调用module_driver/libhardware2的rmem接口申请,主要提供给需要大块连续内存的驱动或场景使用,如lcd、camera、jpeg编解码、2d等。应用层可通过rmem接口申请带物理地址的内存,供硬件直接访问。

配置方法:在bootloader/uboot-x2000/boards.cfg中找到对应配置项,在options中添加RMEM_MB=XXX 或 RMEM_KB=XXX。rmem内存段将分配在系统内存的最末端

示例(x2600e_base_xImage_sfc_nand 配置):

x2600e_base_xImage_sfc_nand		mips        xburst2     x2600_base            ingenic        x2600         x2600_base:SPL_SFC_NAND,MTD_SFCNAND,SPL_OS_BOOT,SPL_PARAMS_FIXER,X2600E_DDR,RMEM_MB=16

当总内存 256 MB、RMEM_MB=16时,系统可用内存为0 到 240 MB(256-16),rmem内存范围为240 到 256 MB。

IConfig配置

使用rmem接口需要在IConfig中勾选以下选项:

查看方法:烧录固件后执行 cat /proc/cmdline,可看到 rmem 的地址和大小

# cat /proc/cmdline 
console=ttyS2,3000000n8 mem=240M@0x0 rmem=16M@0xf000000 init=/linuxrc flashtype=nand root=/dev/mtdblock_bbt_ro2 rootfstype=squashfs ro

配置大小说明

1.lcd

其中会在fb驱动初始化时申请描述符内存,结构体定义不发生变化时,描述符内存(layer/srdma模式):6 * 64 * 3 = 1152

若为mixer/rotator模式,额外增加 5 * 64 = 320

然后是帧内存,据屏幕分辨率、fb使能的图层数和配置的帧缓冲数而定,一帧的大小需要满足4096对齐(计算时需注意)

比如当前使用的lcd是720 * 1280,fb配置为layer模式并且只配置一层,然后帧缓冲数配置为2时,帧内存大小如下:

(720 * 1280 * 4) * 2 = 7372800(lcd 的内存是按照 ARGB8888 32位申请的,所以需要乘4)

所以,lcd总共使用的rmem内存大小为 1152 + 7372800 = 7373952(约7.04MB)

2.camera

当camera输出 640 * 480的8位灰度图,且配置camera的帧缓冲数为2时,实际申请的内存如下:

(640 * 480) * 3 = 921600(实际底层会多申请一个buffer,所以实际申请的内存为配置的帧缓冲数2 + 1个buffer)

还有就是camera的DMA描述符占用64字节。

所以,camera总共使用的rmem内存大小为 921600 + 64 = 921664(约0.9MB)

3.jpeg编解码

jpeg编码需要输入源和输出源,输出源和输入源申请的内存大小一致,而输入源的内存大小据像素格式确定,再向上对齐到页面大小 4096。

input_size = ALIGN_UP(width * height * bytes_per_pixel, 4096);

例如:输入源为1280 * 720 的 nv12 格式图片,则输入源需要申请的内存大小如下:

(1280 * 720) * 3 / 2 = 1382400 -> 对齐到4096就是1384448

总大小: 1384448 * 2 = 2768896(约2.65MB)

(多程序多线程调用时需要多一倍)

jpeg解码需要输入源和输出源,而输入源和输出源申请的内存大小据像素格式确定,再向上对齐到页面大小 4096。

输入源内存大小最大为 jpeg图片的 width * height,再向上对齐到页面大小 4096。

输出源的内存大小据像素格式确定,再向上对齐到页面大小 4096。

例如:输入源为1280 * 720 的 jpeg 图片,输出源为 nv12 格式的文件,申请的内存大小如下:

输入:1280 * 720 = 921600,对齐到4096 -> 921600

输出:(1280 * 720) * 3 / 2 = 1384448,对齐到4096 -> 1384448

总大小:921600 + 1384448 = 2306048(约2.2MB)

(多程序多线程调用时需要多一倍)

4.2d

2d的功能比较多,由各个功能的不同决定内存大小不同,此处以旋转为例子(2d的接口支持用已申请的物理地址进行计算,例如当你直接将一个源旋转到lcd的物理地址上时,则可以免去目标地址的内存申请)

例如:将一个 1280 * 720 的 bgra格式的图片旋转90度

输入:1280 * 720 * 4 = 3686400(旋转前的源大小)

输出:720 * 1280 * 4 = 3686400(旋转后的目标大小)

总大小:3686400 + 3686400 = 7372800(约7.04MB)

1.2 rtos_size

说明:双系统或双核运行时,为rtos分配专用内存,避免与linux内存交叉。

配置方法:在bootloader/uboot-x2000/boards.cfg的options中添加RTOS_SIZE_MB=XXX 或 RTOS_SIZE_KB=XXX

例如总内存256MB时,如果RTOS_SIZE_MB=32,则linux可用0 到 224MB,rtos可用224 到 256MB。

查看方法:烧录固件后可执行 cat /proc/cmdline 查看 rtos_size 的地址和大小

配置大小说明:没有固定的计算公式,根据rtos实际功能所需内存配置。。

1.3 share_mem

说明:在双核双系统中,设备通常由小核(rtos)先完成初始化,再跳转到linux。若linux重新探测和初始化这些硬件,不仅耗时,还可能导致状态不一致。因此需要一种机制,让小核(rtos)把已配置好的硬件参数可靠地传递给linux。share_mem就是为此设计的预留物理内存区域,小核(rtos)写入配置结构体,linux启动后直接读取并接管设备,无需重新初始化。目前share_mem已应用于lcd和pwm背光的配置继承,pwm背光继承不存在大块连续内存,lcd继承存在配置结构体继承和帧内存继承。

使用场景:小核播放开机动画、rtos提前点亮屏幕显示logo

配置方法:在bootloader/uboot-x2000/boards.cfg的options中添加SHARE_MEM_MB=XXX 或 SHARE_MEM_KB=XXX

配置大小说明:其中share_mem分为配置区和工作区,配置区存储所有外设的配置结构体(lcd,pwm背光等),固定大小为32KB;工作区则是供驱动申请大块连续内存,比如帧缓冲等。

SHARE_MEM_SIZE = LCD_WIDTH * LCD_HEIGHT * 4 * 帧数 + 32 * 1024

例如:lcd分辨率为 720 * 1280,使用 fb0 两帧,则 SHARE_MEM_MB = 720 * 1280 * 4 * 2 + 32768 = 7405568(约7.07MB),取整等于 8MB,所以 SHARE_MEM_MB=8

1.4 reserved_memory/vpu_mem

说明:用于H264编解码等需要大块连续物理内存的场景。若使用通用接口(如 dma_alloc_coherent),长期运行后易产生内存碎片,导致分配失败。通过预留独立内存区域,专供驱动使用,可避免此问题。

注意:此处的预留内存不同于前文所述的 rmem,该段内存主要用于给H264编解码内存使用。H264编解码是一个相对独立且内存消耗较大的驱动。

以下以 x2600 平台,总内存 256 MB (0x10000000) 为例,且uboot已配置 RMEM_MB=16(占高地址16MB),因此 Linux 可用内存为240 MB(0xf000000)。

配置位置:kernel/kernel/module_drivers/dts/对应平台的 dts 文件(可参考x2600_vast_module_base_video.dts)。

方式一:dts预留(reserved_memory)

在设备树中显式声明预留内存节点,内核保留该区域不纳入常规内存管理。。此方式最常用于 H264 编解码驱动,因其内存需求大且要求物理连续。

示例:预留32MB给H264解码使用

	reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges =<>;

reserved_memory: reserved_mem@0xd000000{
compatible = "shared-dma-pool";
reg = <0x0d000000 0x2000000>;
};
};

地址计算

Linux 可用内存起始为 0,结束为 0xf000000。预留 32 MB (0x2000000) 时,起始地址 = 0xf000000 - 0x2000000 = 0xd000000。 若需改变大小,需同步修改节点名中的地址和 reg 属性的起始地址及大小。

驱动引用:在需要使用该内存的设备节点中添加 memory-region 属性。

&felix {
status = "okay";
memory-region=<&reserved_memory>;
};
方式二:cmdline预留(vpu_mem)

通过 uboot 配置变量指定预留内存大小,uboot 自动从 Linux 可用内存末尾向前分配,并将信息拼入内核命令行。内核中的 cmdline-mem 驱动会解析该参数,为指定设备注册 DMA coherent 内存池。

配置方法:在uboot的boards.cfg配置表中找到对应的编译配置添加 VPU_MEM_MB=32

x2600e_base_xImage_sfc_nand		mips        xburst2     x2600_base            ingenic        x2600         x2600_base:SPL_SFC_NAND,MTD_SFCNAND,SPL_OS_BOOT,SPL_PARAMS_FIXER,X2600E_DDR,RMEM_MB=16,VPU_MEM_MB=32

uboot 自动计算:起始地址 = 0xf000000 - 0x2000000 = 0xd000000,并生成内核命令行参数:

vpu_mem=32M@0xd000000

驱动引用:设备节点中使用 memory-region 属性,格式为 "cmdline,<名称>";

&felix {
status = "okay";
memory-region="cmdline,vpu_mem";
};

扩展说明:此机制不仅限于 vpu_mem。任何在 uboot 中定义了 XXX_MBXXX_KB 变量的配置,uboot 都会生成对应的 cmdline 参数 xxx=size@addr,驱动只需通过 memory-region = "cmdline,xxx" 引用即可。

注意事项

  • 两种方式不可同时为同一设备预留重叠区域,否则内存冲突。
  • 驱动优先从设备树获取预留内存,失败则回退到 cmdline 方式。
  • 建议使用方式二(vpu_mem),无需手动计算地址,减少内存重叠风险。

2. 总体空间划分

1.使用VPU_MEM_MB预留H264解码内存

以256MB总内存为例,采用share_mem预留16MB共享内存、rtos_size预留32MB的小核专用内存、rmem预留8MB地址连续大块内存、vpu_mem预留H264解码内存。boards.cfg配置如下:

SHARE_MEM_MB=16,RTOS_SIZE_MB=32,RMEM_MB=8,VPU_MEM_MB=16

说明

  • 图中的mem、vpu_mem、rmem、rtos_size、share_mem的地址顺序固定,未设置的选项不预留对应内存段。
  • vpu_mem、rmem、rtos_size和share_mem的大小可直接修改,设为0表示不使用该段。
2.使用 reserved_memory预留H264解码内存

以256MB总内存为例,采用share_mem预留16MB共享内存、rtos_size预留32MB小核专用内存、rmem预留8MB连续大块内存,H264解码内存通过dts的 reserved_memory预留。boards.cfg配置如下(不包含 VPU_MEM_MB):

SHARE_MEM_MB=16,RTOS_SIZE_MB=32,RMEM_MB=8

说明

  • reserved_memory从系统可用内存mem中划分,可放置在该段内的任意位置,建议放在mem的末尾。
  • 需在dts中配置起始地址和大小。若后续增大rmem、rtos_size或share_mem,则mem的可用空间缩小,reserved_memory的起始地址需相应前移,否则可能与其他内存段重叠,导致异常。

3.小核播放开机动画-大小核配置

整体流程:

小核:启动后播放开机动画,动画播放结束后,释放相关资源(若使用了H264解码驱动,则需释放解码资源),并通知大核动画结束。

大核:在此期间执行系统初始化,并等待小核的"动画结束"通知;收到通知后,大核根据自身需要初始化相关驱动(如H264解码驱动),进入正常运行状态。

配置原则

  • 若小核播放开机动画过程中使用了H264解码驱动(即播放H264格式动画),则需配置本章节的3.2节及3.4节中与vpu相关的内存与驱动选项。
  • 若小核通过其他方式(如显示logo)播放动画,则无需上述配置。

大核以x2600e_nand_5.10_defconfig为例,小核以x2600_test_defconfig为例。需要同步下载linux代码。

3.1 uboot配置

在工程的uboot路径下,编辑boards.cfg,找到对应的编译配置项,添加以下选项:

SPL_MCU_RTOS_BOOT,SHARE_MEM_MB=16,RTOS_SIZE_MB=32,RMEM_MB=8
# SPL_MCU_RTOS_BOOT 在spl阶段启动小核
# SHARE_MEM_MB=16 分配16MB的内存给lcd和pwm背光使用(具体大小参考1.3 share_mem章节说明)
# RTOS_SIZE_MB=32 根据实际情况为小核分配内存大小
# RMEM_MB=8 分配8MB的大块连续内存

3.2 kernel配置

适用条件:小核使用H264解码驱动播放开机动画。

配置目的:将H264解码驱动的初始化方式改为由应用层触发(而非驱动加载时自动初始化),避免与小核阶段冲突。

配置步骤:

  1. 进入Linux工程目录下的 kernel/kernel 路径。

  2. 执行 make menuconfig 打开内核配置菜单。

  3. 依次进入以下路径,勾选 felix init triggered by user

Ingenic device-drivers Configurations  --->
[VPU] Drivers --->
[*] felix init triggered by user
  1. 保存配置并退出。

注:若小核未使用H264解码驱动,可忽略本节配置。

确定当前kernel的配置:

linux工程/build$ grep -nr  "kernel" configs/x2600e_nand_5.10_defconfig
6:APP_kernel_dir=../kernel/kernel
7:APP_kernel_config=x2600_evb_module_base_linux_sfc_nand_defconfig

所以当前的kernel配置为:x2600_evb_module_base_linux_sfc_nand_defconfig

上面保存以后,执行如下操作保存kernel配置

kernel/kernel$ cp .config arch/mips/configs/x2600_evb_module_base_linux_sfc_nand_defconfig

3.3 IConfigTool-大核配置

  • LCD外设:配置复位引脚、背光控制引脚、电源控制引脚

  • FrameBuffer驱动:lcd配置方式为 使用rtos相同配置,驱动初始化由应用层触发

  • fb_layer_mixer驱动:初始化由应用层触发

  • mcu(片内riscv mcu)驱动

  • 开机等待riscv小核播放开机动画

  • mcu shell命令

  • RMEM shell命令(若小核memory使用RTOS_SIZE_MB配置,且在uboot的boards.cfg配置了RMEM)

编译大核:

/Linux工程目录/build$ make x2600e_nand_5.10_defconfig
/Linux工程目录/build$ make

3.4 IConfigTool-小核配置

小核内存配置说明:

小核运行需要一块内存区域,根据大核是否使用H264解码,存在两种不同的内存使用策略:

  • 方式一:大核使用H264 解码(有vpu_mem或reserved_memory预留)

    当大核需要H264解码功能时,系统会为 VPU 预留一块连续内存(通过VPU_MEM_MB或 dts的reserved-memory指定)。

在小核播放开机动画期间,尚未使用H264解码,因此这块预留内存可以临时让小核作为运行内存使用。小核在此内存中运行,播放动画;动画结束后,小核释放该内存并退出,大核随后即可正常使用这块内存进行H264解码。

  • 方式二:大核不会使用H264解码(无vpu_mem或reserved_memory预留)

此时系统会通过RTOS_SIZE_MB单独为小核分配一块专用内存。(注:需要预留出播放H264开机动画的内存)

小核在此专用内存中运行,动画播放结束后,小核退出运行,系统通过rmem应用层接口将这块内存回收至rmem管理池,供大核后续使用。

配置选择:

若大核使用H264解码 -> 采用方式一,在uboot中配置VPU_MEM_MB或在dts中配置reserved_memory,小核内存地址/大小填写对应的预留区域的地址和大小。

若大核不使用H264解码 -> 采用方式二,在uboot中配置RTOS_SIZE_MB,小核内存地址/大小填写RTOS专用区域的地址和大小。

具体配置步骤
  • 根据情况设置小核的memory选项
  1. 有H264解码的情况
  • dts预留(reserved_memory)方式

  • cmdline预留(vpu_mem)方式

在uboot的boards.cfg配置表中配置VPU_MEM_MB作为h264解码预留内存

x2600e_base_xImage_sfc_nand		mips        xburst2     x2600_base            ingenic        x2600         x2600_base:SPL_SFC_NAND,MTD_SFCNAND,SPL_OS_BOOT,SPL_PARAMS_FIXER,X2600E_DDR,SPL_MCU_RTOS_BOOT,SHARE_MEM_MB=16,VPU_MEM_MB=32

内存布局示例(总内存256MB,SHARE_MEM_MB=16,VPU_MEM_MB=32):

  • 0MB-208MB:系统可用内存
  • 208MB-240MB:vpu预留内存(vpu_mem)
  • 240MB-256MB:共享内存(share_mem)
  • 计算:208 = 256 - 16 - 32,240 = 256 - 16。

  1. 无H264解码的情况

此时只需为小核分配专用内存,通过RTOS_SIZE_MB指定大小,例如:

x2600e_base_xImage_sfc_nand		mips        xburst2     x2600_base            ingenic        x2600         x2600_base:SPL_SFC_NAND,MTD_SFCNAND,SPL_OS_BOOT,SPL_PARAMS_FIXER,X2600E_DDR,SPL_MCU_RTOS_BOOT,SHARE_MEM_MB=16,RTOS_SIZE_MB=32,RMEM_MB=8

  • H264硬件解码驱动(有解码时需要)

  • 同大核LCD型号相同的LCD驱动 小核和大核的屏幕相关配置必须保持一致(lcd设备、背光设备、fb驱动)

  • 根据大核需求配置小核的FrameBuffer驱动

  • 勾选图像格式转换convert_yuv_to_rgb

修改完成配置后保存,在Linux工程目录/libmcu/vendor/vendor.c中,修改代码如下:

小核播放开机动画之后通知大核例子,源码位于/Linux工程目录/libmcu/example/fb_h264_boot_animation_example.c

开机动画文件位于/Linux工程目录/libmcu/example/resource/test.h264,可替换,格式仅限h264

#define VIDEO_WIDTH 1280
#define VIDEO_HEIGHT 720

/**
* 提取视频文件中的h264 流可在pc端用ffmpeg 命令实现:
* ffmpeg -i input.mp4 -c:v libx264 -profile:v baseline -an -f h264 output.h264
**/
INCBIN(h264, "example/resource/test.h264");

例子默认显示在fb1图层,可根据实际需求进行修改。

编译小核:

/Linux工程目录/libmcu$ make x2600_test_defconfig
/Linux工程目录/libmcu$ make

3.5 烧录

小核固件烧录方式

  1. 使用cmd_mcu烧录

  2. 使用烧录工具烧录

目前仅支持x2600的riscv可使用烧录工具直接烧录

打开烧录工具进行分区信息和固件地址的配置:

要烧录的固件配置好后,保存烧录配置,点击开始就可以进行烧录了。

烧录方法:点击开始,按住开发板BOOT键不放,开发板重新上电或按reset键复位开发板

4. rtos提前点亮屏幕显示logo配置

SPL(core0) 加载引导 RTOS(core0), RTOS(core0)同时进行加载kernel镜像和显示logo 任务, 二者均完成后返回SPL(core0), SPL(core0) 引导 kernel(core0)

大核以x2600e_nand_5.10_defconfig为例,rtos以x2600e_nand_defconfig为例。需要同步下载linux代码和freertos代码。

4.1 uboot配置

在工程的uboot路径下的boards.cfg配置表中,找到对应的编译配置,加上以下options

SHARE_MEM_MB=16,RTOS_SIZE_MB=32,RMEM_MB=8,QUICK_START
# SHARE_MEM_MB=16 分配16MB的内存给lcd和pwm背光使用(具体大小参考1.3 share_mem章节说明)
# RTOS_SIZE_MB=32 根据实际情况选择RTOS_SIZE_MB为rtos的memory
# RMEM_MB=8 分配8MB的大块连续内存
# QUICK_START 在spl阶段启rtos

4.2 IConfigTool-linux配置

  • LCD外设配置(lcd复位引脚、背光控制引脚、电源控制引脚)

  • FrameBuffer驱动 选择lcd配置方式为 使用rtos相同配置

  • RMEM shell命令(若rtos memory使用RTOS_SIZE_MB配置,且在uboot的boards.cfg配置了RMEM)

编译linux系统:

/Linux工程目录/build$ make x2600e_nand_5.10_defconfig
/Linux工程目录/build$ make

4.3 IConfigTool-rtos配置

rtos和linux的屏幕相关配置必须保持一致(lcd设备、背光设备、fb驱动)
  • 配置rtos的memory

  • 同大核LCD型号相同的LCD驱动

  • 根据大核需求配置rtos的FrameBuffer驱动

  • libjpeg库

  • CMYK_TO_RGB图像格式转换

修改完成配置后保存,在rtos工程目录/freertos/vendor/vendor.c中,修改代码如下:

#include <stdio.h>
#include "../example/driver/quick_boot_logo_and_load_kernel_example.c"

void vendor_init(void *arg)
{
printf("vendor init...\n");
quick_boot_logo_and_load_kernel(arg);
}

rtos加载完logo进入kernel例子,源码位于/rtos工程目录/freertos/example/driver/quick_boot_logo_and_load_kernel_example.c

logo图片位于/rtos工程目录/freertos/example/resource/test.jpeg,可替换,格式仅限jpeg

/******************************boot_logo******************************/
#include <cmyk_to_rgb.h>
#include "jpeglib.h"
#include "jerror.h"

#include <driver/fb.h>
#include <driver/backlight.h>
#include <driver/irq.h>

#include <include_bin.h>
INCBIN(jpeg_display, "example/resource/test.jpeg");
例子默认为pwm背光,而linux和rtos的背光设备配置的是gpio背光,则需要修改例子为gpio背光方式

编译rtos系统:

/rtos工程目录/freertos$ make x2600e_nand_defconfig
/rtos工程目录/freertos$ make

4.4 烧录

分区表配置如下:在烧录工具分区内分配一块rtos分区:

rtos分区烧录/rtos工程目录/freertos/zero.bin文件,烧录成功后板子上电显示logo后启动linux。