本文分享自华为云社区《2021 LiteOS树莓派移植指南(一)》,作者: Lionlace 。
树莓派是英国的慈善组织“Raspberry Pi 基金会”开发的一款基于arm的微型电脑主板。本文介绍基于LiteOS的树莓派移植过程。

  硬件信息

开发板:Raspberry Pi 2 Model B(树莓派2B)
CPU:Broadcom BCM2836
主频:900MHz
内存:1GB
GPU:VideoCore IV GPU

  移植准备
  硬件环境
本实验使用了Raspberry Pi 2 Model B开发板、USB转TTL模块、SDcard和读卡器。
  软件环境

  • 本实验需要先按照码云上的LiteOS教程搭建好linux环境(make、arm-none-eabi编译工具链)。环境搭建教程:https://gitee.com/LiteOS/LiteOS/blob/master/doc/LiteOS_Build_and_IDE.md
  • 本实验需要下载官方的镜像制作工具(Raspberry Pi Imager),下载地址:https://www.raspberrypi.org/software/
  
移植步骤
  创建目录结构
在targets目录下新增Raspberry_Pi2B目录,参考与cortex-A7架构差异较小的realview-pbx-a9的启动流程进行移植。

  • 将realview-pbx-a9目录下的reset_vector.S和main.c拷贝到Raspberry_Pi2B目录下并将reset_vector.S重命名为los_startup_gcc.S。
  • 将realview-pbx-a9目录下的board.ld和liteos.ld中内容合并到Raspberry_Pi2B目录下liteos.ld文件中。
  • 拷贝realview-pbx-a9目录下include、os_adapt文件夹到Raspberry_Pi2B目录下,并删除不需要的dma相关头文件include/asm/dma.h。
  
关闭SMP和MMU
在los_startup_gcc.S文件中增加关闭SMP和MMU的代码。

  • 关闭SMP功能
  
mrc p15, 0, r0, c1, c0, 1
  • bic r0, r0, #0x40
  • mcr p15, 0, r0, c1, c0, 1
  • 复制代码

    v2-b48ef33164cae14f2f73c879acb6d724_720w.jpg
    上表是ACTLR(Auxiliary Control Register)寄存器bit6功能描述信息,了解更多寄存器相关信息可以参考Cortex-A7 MPCore Technical Reference Manual。

    • 关闭MMU的功能
      
    mrc     p15, #0, r0, c1, c0, #0
  • bic     r0, r0, #1
  • mcr     p15, #0, r0, c1, c0, #0    @ clear mmu bit
  • 复制代码

    v2-bab9ad1454d883ff13a293227e207338_720w.jpg
    上表是SCTLR (System Control Register)寄存器bit0功能描述信息,了解更多寄存器相关信息可以参考Cortex-A7MPCore Technical Reference Manual。

    • 删除调用SMP相关函数
    删除los_startup_gcc.S中的enable_scu和secondary_cpu_start。

      使能FPU/ENON
      配置FPU/NEON:
      
    /* enable fpu+neon */
  • LDR     r0, =(0xF << 20)
  • MCR     p15, 0, r0, c1, c0, 2
  • MOV     r3, #0x40000000
  • VMSR    FPEXC, r3
  • 复制代码

    以上前两行代码用于设置CP10和CP11的访问权限,后两行用于设置寄存器FPEXC的EN位来使能FPU。
    :在arm的协处理器设计中,最多可以支持16个协处理器,通常被命名为cp0~cp15。
    v2-0f869ffb05aa1d5f20bf271887968418_720w.jpg
    上表为寄存器CPACR bit20-23功能描述信息,了解更多寄存器相关信息可以参考Cortex-A7 MPCore Technical Reference Manual。

      修改链接脚本
    树莓派启动时首先加载SD卡中的start.elf文件,该程序会读取SD卡中的config.txt文件内容,该文件记录了一些配置信息。如果没有设置启动地址和启动文件,则默认会加载kernel8.img文件,该文件是aarch64编译的程序,启动地址为0x80000。如果SD卡中无kernel8.img镜像文件,则会加载kernel7.img镜像文件,该文件是32位编译器编译的程序,启动地址为0x8000。树莓派2B的cpu是32位架构,因此设置liteos.ld文件中启动地址为0x8000。

      栈初始化
    树莓派2B启动文件los_startup_gcc.S中只设置了SVC模式的sp寄存器,新增cpuInit函数来初始化其他模式的sp指针。如下所示:
      
    VOID cpuInit(VOID)
  • {
  •     __asm__ (
  •     "msr    cpsr_c, %1\n\t"
  •     "mov    sp,     %0\n\t"
  •     "msr    cpsr_c, %3\n\t"
  •     "mov    sp,     %2\n\t"
  •     "msr    cpsr_c, %5\n\t"
  •     "mov    sp,     %4\n\t"
  •     "msr    cpsr_c, %7\n\t"
  •     "mov    sp,     %6\n\t"
  •     "msr    cpsr_c, %8\n\t"
  •         :
  •         : "r" (__irq_stack_top),
  •           "I" (PSR_F_BIT | PSR_I_BIT | CPSR_IRQ_MODE),
  •           "r" (__abt_stack_top),
  •           "I" (PSR_F_BIT | PSR_I_BIT | CPSR_ABT_MODE),
  •           "r" (__undef_stack_top),
  •           "I" (PSR_F_BIT | PSR_I_BIT | CPSR_UNDEF_MODE),
  •           "r" (__fiq_stack_top),
  •           "I" (PSR_F_BIT | PSR_I_BIT | CPSR_FIQ_MODE),
  •           "I" (PSR_F_BIT | PSR_I_BIT | CPSR_SVC_MODE)
  •         : "r14");
  • }
  • 复制代码

      
    配置动态内存地址
      
    #define OS_SYS_MEM_ADDR        ((void *)(&__bss_end))
  • #define LOS_HEAP_ADDR_END      (void*)(0x0 + 4 * 1024 * 1024)
  • #define OS_SYS_MEM_SIZE        (UINT32)(((UINT32)LOS_HEAP_ADDR_END - (UINT32)OS_SYS_MEM_ADDR + (64 - 1)) & ~(64 - 1))
  • 复制代码

    以上代码定义OS_SYS_MEM_ADDR为动态内存起始地址,LOS_HEAP_ADDR_END为动态内存结束地址,OS_SYS_MEM_SIZE为动态内存大小。

      串口实现
    树莓派2B原理图引出了mini_uart串口TXD0、RXD0,对应的引脚为GPIO14、GPIO15,如下图所示:
    v2-7bdf26bc96ff794e118b0ba10bbeb2e4_720w.jpg
    创建usart.c和usart.h文件,在usart.c中编写串口初始化函数UartInit,并实现uart_debug.c文件中uart_getc、uart_hwiCreate、uart_write接口,实现printf函数从串口输出。

      适配中断
    树莓派2B的中断属于bcm特定的中断控制器。在drivers/interrupt目录下新增arm_control.c文件,并在该文件中实现HwiControllerOps结构体内的回调函数。
      
    STATIC const HwiControllerOps g_armControlOps = {
  •     .enableIrq      = HalIrqUnmask,
  •     .disableIrq     = HalIrqMask,
  •     .getCurIrqNum   = HalCurIrqGet,
  •     .getIrqVersion  = HalIrqVersion,
  •     .getHandleForm  = HalIrqGetHandleForm,
  •     .handleIrq      = IrqEntryArmControl,
  •     .clearIrq       = HalIrqClear,
  •     .triggerIrq     = HalIrqPending,
  • };
  • 复制代码

    v2-0a7f5907a1017db4946f65a79959619e_720w.jpg
    以上表格是interrupt寄存器偏移地址,读者想了解详细寄存器相关信息请参考官方芯片手册。

      适配systick
    树莓派2B通过Timer(arm side)来触发systick中断。具体操作细节请参考文件:drivers\timer\rasp_systick.c。
      
    /* systime=250000000 */
  • timer->preDivider = (OS_SYS_CLOCK / OS_SYS_US_PER_SECOND - 1);
  • timer->reload   = 0;   
  • timer->load     = 0;   
  • timer->IRQClear = 0;   
  • timer->control  = 0;   
  • timer->reload   = LOSCFG_BASE_CORE_TICK_PER_SECOND;   
  • timer->load     = LOSCFG_BASE_CORE_TICK_PER_SECOND;   
  • /* 23-bit counter, enable interrupt, enable timer */    timer->control = (1 << 1) | (1 << 5) | (1 << 7);   
  • UINT32 ret = LOS_HwiEnable(ARM_TIMER_INI);
  • 复制代码

    以上代码配置定时器Timer为每1ms触发一次systick中断。
    v2-f378077a49ba78cb70f396a3ec0ddf8a_720w.jpg
    以上是Timer寄存器偏移地址,读者想了解详细寄存器相关信息请参考官方芯片手册。

      配置编译
      在targets目录下新增kconfig.raspberry文件:  
    ConfigLOSCFG_PLATFORM
  •     config LOSCFG_PLATFORM
  •     string
  •     default "Raspberry_Pi2B"      if LOSCFG_PLATFORM_Raspberry_Pi2B
  • choice
  •     prompt "Board"
  •     depends on LOSCFG_FAMILY_RASPBERRY
  •     default LOSCFG_PLATFORM_Raspberry_Pi2B
  •     help
  •       Raspberry_Pi2B
  • config LOSCFG_PLATFORM_Raspberry_Pi2B
  •     bool "Raspberry_Pi2B"
  •     select LOSCFG_ARCH_CORTEX_A7
  •     select LOSCFG_USING_BOARD_LD
  •     select LOSCFG_PLATFORM_ARM_CONTROL
  •     select LOSCFG_Raspberry_Pi2B_SYSTICK
  • endchoice
  • 复制代码

      修改Makefile文件
    分别修改以下路径Makefile(详情请参考gitee仓库对应文件):driver/timer/Makefiledriver/interrupt/Makefiletargets/Raspberry_Pi2B/Makefile
      添加.img生成指令
    在根目录下Makefile中添加指令<span class="MathJax" id="MathJax-Element-1-Frame" tabindex="0" style="position: relative;" data-mathml="(OBJCOPY)&#x2212;Obinary" role="presentation">(OBJCOPY)−Obinary
    (OUT)/@.elf
    (OUT)/kernel7.img,用来将生成的elf文件转换生成kernel7.img文件。
      
    制作启动SDcard

    • 使用Raspberry Pi Imager工具制作Raspberry Pi系统。
    v2-199da332fcaeddcd03d8e91124cd3965_720w.jpg
    Raspberry Pi Imager 下载链接:https://www.raspberrypi.org/software/

    • 将编译生成的kernel7.img文件替换掉SDcard中kernel7.img文件。
    • 将写入镜像文件的SDcard插入树莓派2B中并上电,树莓派2B即可运行LiteOS系统。运行结果如下:
      
    ********Hello Huawei LiteOS********
  • LiteOS Kernel Version : 5.1.0
  • build data : Jul 13 2021 16:40:42
  • **********************************
  • OsAppInit
  • cpu 0 entering scheduler
  • app init!
  • Hello, welcome to liteos demo!
  • Huawei LiteOS #
  • 复制代码

    至此,LiteOS系统成功启动和运行。该移植工程已经在Gitee LiteOS社区上线,相关代码链接地址为:https://gitee.com/LiteOS/LiteOS/tree/master/targets/Raspberry_Pi2B

      参考文献链接
    [1] Raspberry Pihardware - Raspberry Pi Documentation:https://www.raspberrypi.org/documentation/hardware/raspberrypi/README.md
    [2] 树莓派官方芯片手册:
    https://datasheets.raspberrypi.org/bcm2835/bcm2835-peripherals.pdf
    [3] Cortex-A7 MPCore Technical Reference Manual:
    https://developer.arm.com/documentation/ddi0464/f?lang=en