rt-smart移植分析:从树莓派3b入手

2020-12-22 15:05:44 浏览数 (1)

rt-smart移植分析:从树莓派3b入手

  • 1.说明
  • 2.rt-smart移植整体思路
    • 2.1 树莓派的启动问题
    • 2.2 SDK编译问题
    • 2.3 串口和中断
    • 2.4 程序执行
  • 3.树莓3b rt-smart编译体验
    • 3.1 下载rt-smart sdk
    • 3.2 存放树莓派3b的boot文件
  • 4.实现细节分析
    • 4.1 启动地址细节
    • 4.2 外设地址空间管理
    • 4.3 树莓派3b的中断
    • 4.4 RT_USING_USERSPACE约束
  • 5.总结

1.说明

移植rt-smart到最新的板子上具体需要注意哪些细节,哪些才是移植rt-smart的关键点?本文从树莓派3b上移植rt-smart的角度,从头分析rt-smart移植的关键细节。为了简化系统,这里只做了rt-smart的最小系统的移植,启用了rt-smart最基本的特性。

下面来描述一下移植的基本过程了思路,这种思路也可以借鉴移植到其他的类似的带有mmu的系统平台上,可以在不同的开发板上体验rt-smart的开发过程。

2.rt-smart移植整体思路

由于rt-smart是具有用户态和内核态的区别的,所以从内核系统层面来看,所有的操作系统的使用的内存地址都是在高地址位,也就是0xc0000000处。而应用程序是单独编译的,在低地址处执行。

为了移植和调试方便,我依次按照下面的步骤进行

(1)编译uboot,可以从tftp服务器上loader固件(rt-thread非rt-smart)到内存执行。

(2)将程序链接地址改为0x100000,并让uboot加载到该处,并正常执行。

(3)分离出bsp代码,放到rt-samrt SDK包中,可以用musl库gcc编译通过。

(4)串口可正常输出信息。

(5)中断可正常产生。

(6)调度可正常运转,rt-smart可正常执行起来。

2.1 树莓派的启动问题

其中前(1)、(2)都是为了准备调试和运行环境。这里就需要细致的说一下树莓派的启动问题。

树莓派启动首先是需要加载SD卡中的start.elf文件,该程序会读取同样位于sd卡中的config.txt文件,config.txt中记录了一些配置信息,比如是否设置hdmi,启动地址,启动文件名称等等。如果没有设置启动地址和启动文件,则默认会加载kernel8.img文件,该文件是aarch64编译的程序,启动地址0x80000,如果没有找到kernel8,img,则会去找kernel7.img文件,该程序是32位程序,启动地址为0x8000

例如:

代码语言:javascript复制
enable_uart=1
#rt-smart
#kernel=kernel7.img
#kernel_address=0x100000
#uboot
kernel=u-boot.bin

下面的示例就很好的描述了相关的启动设置。

为什么要将rt-smart的地址设置为0x100000?这是因为我想通过uboot的tftp启动rt-smart,方便调试,省去每次插拔sd卡的烦恼,当然,如果不怕麻烦,那直接在config.txt文件中设置kernel_address=0x100000效果也一样。

因为uboot的启动地址为0x8000,我预留了一些内存空间,所以将rt-samrt的启动地址设置为0x100000

2.2 SDK编译问题

在第(3)步的过程中,需要重构rt-thread版本的架构,主要原因是需要适配rt-smart的编译方式,两者最大的区别在于换了musl库的gcc,同时集成了一些系统调用的接口。其他差别不大,具体rt-smart的编译方式可以参考下面的文章描述。

2.3 串口和中断

第(4)、第(5)步这里涉及到rt-smart一些很关键的部分,就是地址空间映射到内核态地址空间的问题,这里需要进行转换,将实际的物理地址转换成虚拟地址可以供内核程序使用,这里是在内核态写驱动程序需要注意的地方。

2.4 程序执行

这里已经开启了lwp,也就是可以加载单独编译应用程序,具体可以参考rt-smart的其他文档,编译成app,通过romfs加载到程序中编译,直接运行即可。

3.树莓3b rt-smart编译体验

在描述实现细节之前,首先描述一下如何编译树莓派3b。

3.1 下载rt-smart sdk

编译rt-smart最方便的是下载rt-smart sdk

由于在Windows环境下编译,所以下载下面两个文件。

代码语言:javascript复制
xhttps://realthread.cowtransfer.com/s/1c2b64ba968748

下载sdk集成环境和交叉编译工具链。

下载完成后,将gcc解压到rt-smarttoolsgnu_gcc目录下。

然后进入rt-smartkernelbspraspberry-pi

代码语言:javascript复制
git clone https://gitee.com/bigmagic/raspi3_32.git

rt-smart目录打开env,输入.smart-env.bat

3.2 存放树莓派3b的boot文件

这里直接从下面的网址上下载固件存放在sd卡中

代码语言:javascript复制
https://gitee.com/bigmagic/raspi_sd_fw/tree/master/raspi3/raspi3-32/uboot_boot_rtt

插上网线,设置tftp服务器,可以直接从服务器上获取代码然后执行,如果不具有网络条件,可以自行修改文件,让其直接从sd卡中boot。

4.实现细节分析

4.1 启动地址细节

看程序启动地址首先看链接地址

这里就有点疑惑了,启动的地址是0x100000,为什么链接地址是0xc0000000,那这样程序可以运行么。这是可行的,rt-smart前面有一段位置无关代码,会将地址进行重定位,让其变成符合内核态运行的地址。

这里配置物理地址和虚拟地址偏移量,0xc00000000 0x40100000在32位模式,得到实际的启动地址为0x100000,这里就是实际的启动地址。

如果更加细致的细节,可详见libcpu/start_gcc.S的启动代码的实现细节。

4.2 外设地址空间管理

系统启动起来了,接下来就要分析设备地址该如何处理了,根据rt-thread的启动流程,板级最底层的驱动初始化工作一般在具体的bsp的board.c文件下。从rt_hw_board_init开始分析。

前面的page初始化以及设备的初始化代码基本都一样,对于arm 32位平台基本不用修改。而具体的转换函数其实就是rt_hw_kernel_phys_to_virt,该程序会将实际的物理地址转换成内核态可以访问的虚拟地址空间。

只需要注意这一点,串口驱动就很好处理了,因为串口操作本质上也是访问一系列的寄存器。

4.3 树莓派3b的中断

树莓派3b的中断是属于bcm特定的中断控制器,一般目前arm上使用比较多的是gic,而树莓派4b也是gic,只需要将gic地址转换成内核态可以访问的地址空间即可。对于树莓派3b,中断直接出来的,所以需要排除gic处理。

这里不属于rt-smart的特性,移植其他的平台不需要过多的考虑,只是树莓派3b需要注意一下。具体的文件实现细节可以参考libcpuarmcortex-ainterrupt.c

中断的分发和处理,除了gic的处理比较通用,将gic_dist_basegic_cpu_base转换一下即可。树莓派3b上转换的是irq_base

4.4 RT_USING_USERSPACE约束

查看rt-smart的代码,往往都会看到这个宏定义,其原因是预留适配切换开关,也就是如果不开启lwp情况下,预先可以用rt-thread去编译代码的。这样可以切换选择,对于用户空间和内核空间的区别其实本质上就是使用好mmu的一些特性,进行隔离和转换的过程。或许有一天,rt-smart与rt-thread可以无缝的结合到一起,只需进行menuconfig就可以选择了,我猜想大概就是目前rt-smart的代码在rt-thread的分支的原因吧。

5.总结

rt-smart从我角度上来看不见得那么复杂与神秘,就是使用好mmu的一些特性,掌握好内核空间与用户空间隔离的概念即可。程序的权限理清楚,各司其职,各行其事。操作系统本质上就是合理的管理硬件资源,同时给用户提供好的使用体验,其上层软件资源与生态的重要性不言而喻。

看树莓派3b上最基本的rt-smart的移植过程与注意细节点,其实和rt-thread的差别并不大,其实还有一些我没有提及,比如syscall、比如crt、比如musl库等等,这些通用的东西我没有单独的提出来,一是移植不需要关注这些,另外就是这些都是和linux的机制大同小异,懂得人自然懂,不懂这篇文章也说不清楚。

0 人点赞