• 在T3开发板上实现SylixOS最小系统(三)


    1. SylixOS 物理内存基本配置

    1.1 SylixOS 对物理内存的配置

      SylixOS 对物理内存的配置在 BSP_T3/config.h 中,其详细描述如下:
    在这里插入图片描述
      这部分内容需要根据实际情况作出修改。在后面会给出修改结果。

    1.2 物理内存介绍

      SylixOS将物理内存划分成4个区域进行使用:内核代码区,内核数据区,DMA内存区和APP内存区。系统启动开启MMU后会将内核代码区和内核数据区的物理地址和虚拟地址建立对等映射的关系,也就是物理地址和虚拟地址是一样的;DMA内存区会在需要的时候建立映射,也是对等映射的关系;APP内存区也是在需要的时候建立映射,但是映射关系不一定是对等映射,APP区的虚拟地址是可以在bspMap.h中进行配置的。
      使能MMU后,系统VMM组件接管的内存区是DMA和APP区,系统内核和内核数据区不归VMM组件管理:
    在这里插入图片描述
      通用内存区就是操作系统本身使用的内存空间,即内核空间(上图中的内核代码段和内核数据段),主要包括操作系统镜像、系统使用的内存堆和栈空间,他们物理地址和虚拟地址是完全相同的,因此可以看到,它们没有对应的虚拟页面。
      VMM(Virtual Memory Management),即虚拟内存管理单元,以页面的方式管理除通用内存区外的所有物理内存,VMM还负责以页面的方式管理一片虚拟内存空间,并在需要的时候将虚拟内存页面映射到物理内存页面。虚拟页面和物理页面的大小是相同的,值为PAGESIZE(通常为4KB)

      这几个区域的作用:
    内核代码区:这段区域存放有系统的中断向量表和内核代码,使能MMU后这段区域无法写入。
    内核数据区:这段区域是内核使用的数据区,包括内核栈、内核堆、全局数据等等。使能MMU后这段区域可读可写。
    DMA内存区:有些外设控制器需要申请物理连续的内存,DMA内存区就是为这些控制器准备的。
    APP内存区:SylixOS动态加载的应用程序、动态库和内核模块都是使用的这段区域。
    在上述的4个区域中,内核代码区、内核数据区和APP内存区都是带Cache属性的,DMA内存区根据需要可以申请带Cache或者不带Cache属性的物理连续内存。

    1.3 修改我们的BSP_T3工程的config.h文件

      我们手中的T3开发板使用的内存是1GB大小,根据上述信息,我们可以将物理内存空间在config.h 中进行如下配置:

    /*********************************************************************************************************
      ROM RAM 相关配置
    *********************************************************************************************************/
    #define BSP_CFG_ROM_BASE (0x00000000)   
    #define BSP_CFG_ROM_SIZE (4 * 1024 * 1024) /* 0x0040 0000*/
    
    #define BSP_CFG_RAM_BASE (0x40000000)   /* 内存基址       这个地址是我们T3开发板的DRAM基地址  */
    #define BSP_CFG_RAM_SIZE (1 * 1024 * 1024 * 1024) /* 内存大小   0x4000 0000 */
    
    #define BSP_CFG_TEXT_SIZE (10 * 1024 * 1024)  /* 内核代码段大小  0x00A0 0000*/
    #define BSP_CFG_DATA_SIZE (50 * 1024 * 1024) /* 内核数据段大小 */
    #define BSP_CFG_DMA_SIZE  (128 * 1024 * 1024) /* DMA内存段大小 */
    #define BSP_CFG_APP_SIZE  (BSP_CFG_RAM_SIZE  - BSP_CFG_TEXT_SIZE - \
                               BSP_CFG_DATA_SIZE - BSP_CFG_DMA_SIZE)       /* 动态加载内存段大小 */
    
    #define BSP_CFG_BOOT_STACK_SIZE (128 * 1024)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    分别介绍一下:

    • BSP_CFG_ROM_BASE:在本平台上不需要关心。使用默认的即可
    • BSP_CFG_ROM_SIZE:在本平台上不需要关心。使用默认的即可
    • BSP_CFG_RAM_BASE:SylixOS所使用的物理内存基址,这个地址也是内核镜像的加载地址。是手册地址的DRAM的首地址。
      在这里插入图片描述
    • BSP_CFG_RAM_SIZE:SylixOS使用的物理内存大小。
    • BSP_CFG_TEXT_SIZE:内核代码区大小。
    • BSP_CFG_DATA_SIZE:内核数据区大小。
    • BSP_CFG_DMA_SIZE:DMA内存区大小。
    • BSP_CFG_APP_SIZE:APP内存区大小。
    • BSP_CFG_BOOT_STACK_SIZE:BSP启动时使用的栈空间大小。
    • 内核数据段拷贝
        SylixOS内核代码段和内核数据段的链接地址(运行地址)是在config.ld链接脚本中定义的:
      在这里插入图片描述

      从上面的代码可以看出,内核数据段的运行地址是紧挨在内核代码段之后的,内核代码段大小一般我们都是根据经验来分配一个合理的大小,也许实际并没有使用全部的空间,这样为了减少生成的bin文件大小,默认生成的bin文件中内核数据和内核实际使用的代码空间是紧挨着存放的。所以在实际运行时,需要将内核数据区的数据从加载地址复制到运行地址:
    在这里插入图片描述
      这个拷贝的操作就是在startup.S中的初始化DATA段,就是将内核的数据段从加载地址搬运到运行地址处。

    2 初始化FPU、MMU和Cache组件

    这三个组件的初始化都是在bspInit.c中进行的。如下图所示,接下来一个一个函数进行实现
    在这里插入图片描述

    2.1 FPU初始化

    我们首先来看下FPU的初始化:

    /*********************************************************************************************************
    ** 函数名称: halFpuInit
    ** 功能描述: 目标系统 FPU 浮点运算单元初始化
    ** 输 入  : NONE
    ** 输 出  : NONE
    ** 全局变量:
    ** 调用模块:
    *********************************************************************************************************/
    #if LW_CFG_CPU_FPU_EN > 0
    
    static VOID  halFpuInit (VOID)
    {
        API_KernelFpuInit(ARM_MACHINE_A7,  ARM_FPU_VFPv4);
    }
    
    #endif                                     /*  LW_CFG_CACHE_EN > 0 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    可以看出来FPU的初始化很简单,只需要调用API_KernelFpuInit 即可。第一个参数表示当前CPU的架构,第二个参数表示当前SOC中FPU使用的类型,这两个参数根据芯片数据手册的信息然后使用内核提供好的宏填入就行了。
    在这里插入图片描述

    • 我们在BASE_T3/libsylixos/SylixOS/arch/arm/arm_support.h中可以查看其他宏。由于我们T3的架构是ARMv7的 Cortex-A7,所以第一个参数:ARM_MACHINE_A7

    在这里插入图片描述

    • 同样在arm_support.h文件查看第二个参数的宏。参考T3数据手册我们第二个参数: ARM_FPU_VFPv4 。

    在这里插入图片描述

    2.2 Cache初始化

    static VOID  halCacheInit (VOID)
    {
        API_CacheLibInit(CACHE_COPYBACK, CACHE_COPYBACK, ARM_MACHINE_A7);   /*  初始化 CACHE 系统           */
        API_CacheEnable(INSTRUCTION_CACHE);
        API_CacheEnable(DATA_CACHE);                                        /*  使能 CACHE                  */
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    首先使用API_CacheLibInit 接口初始化了内核Cache组件:
    这个函数定义在BASE_T3/libsylixos/SylixOS/kernel/cache/cache.h中
    在这里插入图片描述
    追到cache.c中可以看到这个函数实现了什么。
    在这里插入图片描述
    回到正题:

    • 第一个参数表示指令Cache的工作方式,一般有写回和写通两种模式,实际中一般使用写回模式。(这里如果大家不太理解Cache这两种模式可以去看一下博主的这个博客Cache与MMU管理)。即第一个参数:CACHE_COPYBACK
    • 第二个参数表示数据Cache的工作方式,一般有写回和写通两种模式,实际中一般使用写回模式。即第二个参数:CACHE_COPYBACK
    • 第三个表示当前CPU的架构类型。即三个参数:ARM_MACHINE_A7

      初始化完了内核Cache组件,接着使用了API_CacheEnable 接口使能了指令Cache和数据Cache,可以看出Cache的初始化还是比较简单的,因为内核已经为大部分arm架构封装好了Cache等部件的操作,我们只需要调用接口即可。

    2.3 MMU初始化

    MMU的初始化需要做三部分工作,1. 在BSP_T3/SylixOS/bsp/bspMap.h 中设置映射表,2. 在BSP_T3/SylixOS/bsp/bspInit.c 初始化内核VMM组件,3. 设置MMU页表大小。

    2.3.1 映射表设置

      映射表是定义在bspMap.h 中,_G_physicalDesc 描述物理地址空间的关系,_G_virtualDesc 描述虚拟地址空间关系,VMM通过这两个表中定义的关系来管理物理地址和虚拟地址。

    1.1 SylixOS的物理地址空间映射表

    1. VMM 子系统管理的物理内存是 DMA 内存区和 APP 内存区。 SylixOS 使用 LW_MMU_PHYSICAL_DESC 数据结构来描述物理内存段映射信息(即中断向量表、物理内存区、外设寄存器的物理地址和虚拟地址的映射关系),其中详细描述如下:
    /*********************************************************************************************************
      物理内存信息描述
      注意:
      TEXT, DATA, DMA 物理段 PHYD_ulPhyAddr 必须等于 PHYD_ulVirMap,
      TEXT, DATA, VECTOR, BOOTSFR, DMA 物理段 PHYD_ulVirMap 区间不能与虚拟内存空间冲突.
    *********************************************************************************************************/
    
    typedef struct __lw_vmm_physical_desc {
        addr_t                   PHYD_ulPhyAddr;                            /*  物理地址 (页对齐地址)       */
        addr_t                   PHYD_ulVirMap;                             /*  需要初始化的映射关系        */
        size_t                   PHYD_stSize;                               /*  物理内存区长度 (页对齐长度) */
        
    #define LW_PHYSICAL_MEM_TEXT        0                                   /*  内核代码段                  */
    #define LW_PHYSICAL_MEM_DATA        1                                   /*  内核数据段 (包括 HEAP)      */
    #define LW_PHYSICAL_MEM_VECTOR      2                                   /*  硬件向量表                  */
    #define LW_PHYSICAL_MEM_BOOTSFR     3                                   /*  启动时需要的特殊功能寄存器  */
    #define LW_PHYSICAL_MEM_BUSPOOL     4                                   /*  总线地址池, 不进行提前映射  */
    #define LW_PHYSICAL_MEM_DMA         5                                   /*  DMA 物理内存, 不进行提前映射*/
    #define LW_PHYSICAL_MEM_APP         6                                   /*  装载程序内存, 不进行提前映射*/
        UINT32                   PHYD_uiType;                               /*  物理内存区间类型            */
        UINT32                   PHYD_uiReserve[8];
    } LW_MMU_PHYSICAL_DESC;
    typedef LW_MMU_PHYSICAL_DESC *PLW_MMU_PHYSICAL_DESC;
    
    /*********************************************************************************************************
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • PHYD_ulPhyAddr:页对齐的物理地址;
    • PHYD_ulVirMap:需要映射的虚拟地址;
    • PHYD_stSize:需要映射的大小;
    • PHYD_uiType:物理内存段的类型,具体有 7 种,如下表所示
    物理内存段类型含义
    LW_PHYSICAL_MEM_TEXT内核代码段
    LW_PHYSICAL_MEM_DATA内核数据段
    LW_PHYSICAL_MEM_VECTOR硬件向量表
    LW_PHYSICAL_MEM_BOOTSFR特殊功能寄存器区域
    LW_PHYSICAL_MEM_BUSPOOL总线地址池
    LW_PHYSICAL_MEM_DMADMA 物理内存
    LW_PHYSICAL_MEM_APP装载程序内存
    • PHYD_uiReserve:预留区域,目前没用

    注意:这里的地址和大小值必须是当前平台页对齐的值,系统启动过程中会检测是否对齐,如果不对齐则会启动失败。

    1. VMM子系统通过一个全局数组来管理物内存段,其详细描述如下(其实这个就是IDE自动生成的bspMap.h的物理内存段分配的内容):
    
    /*********************************************************************************************************
       内存分配关系图
    
        +-----------------------+--------------------------------+
        |       通用内存区      |          VMM 管理区            |
        |         CACHE         |                                |
        +-----------------------+--------------------------------+
    
    *********************************************************************************************************/
    /*********************************************************************************************************
      physical memory
    *********************************************************************************************************/
    #ifdef  __BSPINIT_MAIN_FILE
    
    LW_MMU_PHYSICAL_DESC    _G_physicalDesc[] = {
        {                                                                   /*  中断向量表                  */
            BSP_CFG_RAM_BASE,
            0,
            LW_CFG_VMM_PAGE_SIZE,
            LW_PHYSICAL_MEM_VECTOR
        },
    
        {                                                                   /*  内核代码段                  */
            BSP_CFG_RAM_BASE,
            BSP_CFG_RAM_BASE,
            BSP_CFG_TEXT_SIZE,
            LW_PHYSICAL_MEM_TEXT
        },
    
        {                                                                   /*  内核数据段                  */
            BSP_CFG_RAM_BASE + BSP_CFG_TEXT_SIZE,
            BSP_CFG_RAM_BASE + BSP_CFG_TEXT_SIZE,
            BSP_CFG_DATA_SIZE,
            LW_PHYSICAL_MEM_DATA
        },
    
        {                                                                   /*  DMA 缓冲区                  */
            BSP_CFG_RAM_BASE + BSP_CFG_TEXT_SIZE + BSP_CFG_DATA_SIZE,
            BSP_CFG_RAM_BASE + BSP_CFG_TEXT_SIZE + BSP_CFG_DATA_SIZE,
            BSP_CFG_DMA_SIZE,
            LW_PHYSICAL_MEM_DMA
        },
    
        {                                                                   /*  APP 通用内存                */
            BSP_CFG_RAM_BASE + BSP_CFG_TEXT_SIZE + BSP_CFG_DATA_SIZE + BSP_CFG_DMA_SIZE,
            BSP_CFG_RAM_BASE + BSP_CFG_TEXT_SIZE + BSP_CFG_DATA_SIZE + BSP_CFG_DMA_SIZE,
            BSP_CFG_APP_SIZE,
            LW_PHYSICAL_MEM_APP
        },
    
        /*
         * TODO: 加入启动设备的寄存器映射, 参考代码如下:
         */
         #if 0
        {
            0x20000000,
            0x20000000,
            LW_CFG_VMM_PAGE_SIZE,
            LW_PHYSICAL_MEM_BOOTSFR
        },
        #endif
        {                                                                   /*  结束                        */
            0,
            0,
            0,
            0
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 前 5个内存段配置一般不需要修改;
    • 特殊寄存器区域需要根据芯片手册进行修改添加。

    1.2 我们T3开发板的物理地址空间映射表具体设置

    在bspMap.h中的物理地址空间映射表配置

    LW_MMU_PHYSICAL_DESC    _G_physicalDesc[] = {
        {                                                                   /*  中断向量表                  */
            BSP_CFG_RAM_BASE,
            0,
            LW_CFG_VMM_PAGE_SIZE,
            LW_PHYSICAL_MEM_VECTOR
        },
    
        {                                                                   /*  内核代码段                  */
            BSP_CFG_RAM_BASE,
            BSP_CFG_RAM_BASE,
            BSP_CFG_TEXT_SIZE,
            LW_PHYSICAL_MEM_TEXT
        },
    
        {                                                                   /*  内核数据段                  */
            BSP_CFG_RAM_BASE + BSP_CFG_TEXT_SIZE,
            BSP_CFG_RAM_BASE + BSP_CFG_TEXT_SIZE,
            BSP_CFG_DATA_SIZE,
            LW_PHYSICAL_MEM_DATA
        },
    
        {                                                                   /*  DMA 缓冲区                  */
            BSP_CFG_RAM_BASE + BSP_CFG_TEXT_SIZE + BSP_CFG_DATA_SIZE,
            BSP_CFG_RAM_BASE + BSP_CFG_TEXT_SIZE + BSP_CFG_DATA_SIZE,
            BSP_CFG_DMA_SIZE,
            LW_PHYSICAL_MEM_DMA
        },
    
        {                                                                   /*  APP 通用内存                */
            BSP_CFG_RAM_BASE + BSP_CFG_TEXT_SIZE + BSP_CFG_DATA_SIZE + BSP_CFG_DMA_SIZE,
            BSP_CFG_RAM_BASE + BSP_CFG_TEXT_SIZE + BSP_CFG_DATA_SIZE + BSP_CFG_DMA_SIZE,
            BSP_CFG_APP_SIZE,
            LW_PHYSICAL_MEM_APP
        },
    
        /*
         * TODO: 加入启动设备的寄存器映射, 参考代码如下:
         */
        {                                                                   /*  UART0 ~ 4                   */
            0x01C28000,                                                     /* UART0的起始地址——物理地址 */
            0x01C28000,                                                     /* UART0的虚拟地址 */ 
            2 * LW_CFG_VMM_PAGE_SIZE,                                       /* 需要映射的大小 */
            LW_PHYSICAL_MEM_BOOTSFR
        },
    
        {                                                                   /*  结束                        */
            0,
            0,
            0,
            0
        }
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

      由于我们已经配置了config.h中的地址。所以这里的断向量表、内核代码段、内核数据段、DMA缓冲区、APP通用内存我们不必进行修改。我们这里使能MMU之后,还需要串口进行输出信息,所以这里需要将串口寄存器地址进行静态映射。从数据手册查看uart的首地址。
    在这里插入图片描述
    由手册我们可得UART的物理地址是 0x01C28000

    2.1 SylixOS的虚拟地址空间表

    1. SylixOS中使用LW_MMU_VIRTUAL_DESC 数据结构来描述虚拟地址空间信息,在BASE_T3/libsylixos/SylixOS/kernel/vmm/vmm.h中详细描述如下:
    /*********************************************************************************************************
      虚拟地址空间描述
      注意:
      虚拟内存空间编址不能与物理内存空间 TEXT, DATA, VECTOR, BOOTSFR, DMA 区间冲突,
      DEV 属性内存分区最多一个.
      一般情况下 APP 虚拟内存区间要远远大于物理 APP 内存大小, 同时大于 DEV 虚拟内存区间大小.
    *********************************************************************************************************/
    
    typedef struct __lw_mmu_virtual_desc {
        addr_t                   VIRD_ulVirAddr;                            /*  虚拟空间地址 (页对齐地址)   */
        size_t                   VIRD_stSize;                               /*  虚拟空间长度 (页对齐长度)   */
        
    #define LW_VIRTUAL_MEM_APP      0                                       /*  装载程序虚拟内存区间        */
    #define LW_VIRTUAL_MEM_DEV      1                                       /*  设备映射虚拟内存空间        */
        UINT32                   VIRD_uiType;                               /*  虚拟内存区间类型            */
        UINT32                   VIRD_uiReserve[8];
    } LW_MMU_VIRTUAL_DESC;
    typedef LW_MMU_VIRTUAL_DESC *PLW_MMU_VIRTUAL_DESC;
    
    #endif                                                                  /*  __SYLIXOS_KERNEL            */
    /*********************************************************************************************************
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • VIRD_ulVirAddr:页对齐的虚拟地址,就是虚拟地址空间起始地址。
    • VIRD_stSize:虚拟地址空间大小。
    • VIRD_uiType:虚拟空间类型。
    虚拟内存段类型含义
    LW_VIRTUAL_MEM_APP动态装载区(系统动态加载的程序、动态库和内核模块所使用的虚拟地址空间)
    LW_VIRTUAL_MEM_DEVIO 设备映射区 (系统调用API_VmmIoRemap 这类接口动态映射寄存器地址时所使用的虚拟地址空间)
    • VIRD_uiReserve:预留区域,目前没用
    1. VMM 子系统通过一个全局数组来管理虚拟内存段,其详细描述如下(其实这个就是IDE自动生成的bspMap.h的虚拟内存段分配的内容):
    /*********************************************************************************************************
      virtual memory
    *********************************************************************************************************/
    LW_MMU_VIRTUAL_DESC    _G_virtualDesc[] = {
        /*
         * TODO: 加入虚拟地址空间的定义, 参考代码如下:
         */
    #if 0
        {                                                                   /*  应用程序虚拟空间            */
            0x60000000,
            ((size_t)2 * LW_CFG_GB_SIZE),
            LW_VIRTUAL_MEM_APP
        },
    
        {
            0xe0000000,                                                     /*  ioremap 空间                */
            (256 * LW_CFG_MB_SIZE),
            LW_VIRTUAL_MEM_DEV
        },
    #endif
    
        {                                                                   /*  结束                        */
            0,
            0,
            0
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    2.2 我们T3开发板的虚拟地址空间映射表具体设置

    在bspMap.h中的虚拟地址空间映射表配置:

    LW_MMU_VIRTUAL_DESC    _G_virtualDesc[] = {
        /*
         * TODO: 加入虚拟地址空间的定义, 参考代码如下:
         */
    #if 1
        {                                                                   /*  应用程序虚拟空间            */
            0x80000000,                                                    /* 这个地址只要不和我们之前配置的物理地址有重合就行,而且从起始地址加上大小不能超过4GB*/
            ((size_t)1 * LW_CFG_GB_SIZE),
            LW_VIRTUAL_MEM_APP
        },
    
        {
            0xC0000000,                                                     /*  ioremap 空间                */
            (64 * LW_CFG_MB_SIZE),
            LW_VIRTUAL_MEM_DEV
        },
    #endif
    
        {                                                                   /*  结束                        */
            0,
            0,
            0
        }
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 这里的虚拟地址和物理空间描述表中各个区域的虚拟地址不能有重复
      在这里插入图片描述
        通用内存內就是操作系统本身使用的内存空间,即内核空间,主要包括操作系统镜像、系统使用的内存堆和栈空间,它们的物理地址和虚拟地址是完全相同的,因此可以看到,它们没有对应的虚拟页面。
        VMM( Virtual Memory Management),即虚拟内存管理单元,以页面的方式管理除通用内存区外的所有物理内存,VMM还负责以页面的方式管理一片虚拟内存空间,并在需要的时候,将虚拟内存页面映射到物理内存页面。虚拟页面和物理页面的大小是相同的,值为PAGESIZE(通常为4 KB)。
        图10.1中,有一个特别的DMA页面区,专门用于DMA数据传输(因为DMA硬件只能访问物理地址)。SylixOS专门提供了分配DMA内存的API,仅供内核模块和驱动程序使用,应用程序不应该使用这些API,因此这里不作介绍。
        剩下的就是供应用程序和动态链接库使用的物理页面,它们均有对应的虚拟页面。通常我们所说的虚拟内存就是这一片地址连续的虚拟页面空间。
        操作系统会保证虚拟页面地址不会与通用内存和DMA内存地址有任何重叠。设想一下,若有任何重叠,则进程本身的数据(全局变量、栈空间、代码等)都可能会映射到系统内存或DMA内存,这将造成不可预知的错误。我们把这片不能重叠的空间叫作操作系统保留空间。
    • 注意:起始地址加上大小之后不能超过4GB
      因为我们现在用的是32位的地址空间。(1GB = 1024MB = 1024 * 1024KB = 1024 * 1024 * 1024B = 2^10 * 2 ^ 10 * 2 ^ 10B = 2 ^ 30B 所以32位的系统最大表示是 2 ^ 32 = 4 * 2 ^ 30 = 4 * 1GB 的空间)

    2.3.2 VMM组件初始化

    在配置好上述的两个映射表后,就可以回到我们的bspInit.c中使用API_VmmLibInit 这个接口来初始化VMM组件:
    在这里插入图片描述

    初始化内容如下:

    /*********************************************************************************************************
    ** 函数名称: halVmmInit
    ** 功能描述: 初始化目标系统虚拟内存管理组件
    ** 输 入  : NONE
    ** 输 出  : NONE
    ** 全局变量:
    ** 调用模块:
    *********************************************************************************************************/
    #if LW_CFG_VMM_EN > 0
    
    static VOID  halVmmInit (VOID)
    {
        API_VmmLibInit(_G_physicalDesc, _G_virtualDesc, ARM_MACHINE_A7);
        API_VmmMmuEnable();
    }
    
    #endif                                                                  /*  LW_CFG_VMM_EN > 0           */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    API_VmmLibInit的前两个参数我们在2.3.1已经配置好了,第三个参数是T3开发板的架构我们也很熟悉了,这里就不再赘述。

    2.3.3 MMU页表池设置

    1 SylixOS的MMU介绍

      内存管理单元(Memory Management Unit)简称 MMU,负责虚拟地址到物理地址的转换,并提供硬件机制的内存访问权限检查。在 MMU 开启之前,CPU 都是通过物理地址来访问内存;MMU 开启之后,CPU 都是通过虚拟地址来访问内存。如下图 所示:
    在这里插入图片描述
      虚拟地址转换为物理地址是通过 MMU 和页表(Page table)实现的。页表就是一段描述虚拟地址到物理地址转换关系及访问权限的内存区域,由一个个页表项组成。
      SylixOS 采用的是二级页表映射,二级页表映射过程如图所示:
    在这里插入图片描述

    • 一级页目录基址加一级页目录偏移得到存放二级页表基址的内存物理地址;
    • 二级页表基址加二级页表偏移得到存放物理页基址的内存物理地址;
    • 物理页基址加页内偏移得到最终需要访问的物理地址。

    2 我们T3开发板的MMU具体设置

      MMU正常工作需要使用页表,SylixOS内核使用的是二级页表机制。
      第一级页表叫全局页目录(Page Global Directory),也可以叫做一级页表,页目录中单个条目就叫做页目录项,一个页目录项可以映射1MB的物理页,32位地址空间有4GB(2 ^ 32 =4GB)大小,所以整个页目录共有4096(4GB/1MB=4096)个页目录项。
      第二级页表叫做二级页表,二级页表中单个条目就叫做页表项(Page Table Entry),一般arm32位都是用4KB页大小,也就是一个页表项可以映射4KB的物理页,一个二级页表可以映射1MB大小,所以一个二级页表中共有256(1MB/4KB=256)个页表项:
    在这里插入图片描述
    在SylixOS中,一级页表和二级页表所占的内存是在内核堆上的,也就是内核数据区空间,所以在配置config.h时,最好将内核数据区配置大点,否则有可能因为MMU页表申请不到内存空间而导致系统启动失败。
    在一个实际的嵌入式项目中,并不会完全使用4GB的空间,那么我们就可以将二级页表的个数减少点来节省下内存给其他的内核模块或者应用使用,这是通过bspLib.c中的bspMmuPgdMaxNum 和bspMmuPteMaxNum 这两个接口实现的。
    在这里插入图片描述

    1. bspMmuPgdMaxNum 接口主要用来获取一级页表的个数,一个一级页表已经能映射4GB的空间了,所以这个函数一般都是返回1:
      在这里插入图片描述
    2. bspMmuPteMaxNum 接口主要用来获取二级页表的个数,注意是二级页表的个数而不是页表项的个数。如果是4096则表示映射完整的4GB空间,这里实际返回的是2048,也就是表示只映射2GB的空间对于我们的T3开发板已经足够了:
      在这里插入图片描述

    至此,我们的内存相关配置就差不多了。接下来进行系统调试信息打印接口(bspLib.c文件中的bspDebugMsg )实现。再后面就是我们的串口驱动开发、时钟驱动开发以及中断驱动开发。

  • 相关阅读:
    Notion——构建个人知识库
    Java虚拟机对象创建的过程
    升级到.NET 6 后突然发现EFCore访问MySQL出现问题-没有 get_QueryProvider实现
    弹性布局flex或者grid元素平分,实际会被内容撑大,问题剖析
    sklearn中make_blobs方法:聚类数据生成器
    es带用户名密码验证并配置elasticsearch-head连接
    【FreeRTOS】信号量的使用
    床旁婴儿床上架亚马逊美国站 ASTM F2906测试的重要性
    三相逆变器下垂控制双机
    基于蝙蝠优化算法的电力系统经济调度研究(Matlab代码实现)
  • 原文地址:https://blog.csdn.net/graceyun/article/details/126296958