引言
ITRON(Industrial The Realtime Operating system Nucleus,工业实时操作系统核)是在日本被广泛应用的一个嵌入式实时操作系统标准。ITRON提出了实时操作系统的一系列规范,遵循这个规范的操作系统上层软件可以相互移植,有点类似于Unix世界里面的POSIX。ITRON是一种在国内的知名度非常低的嵌入式实时操作系统,但是却占据了全球微处理器操作系统市场大约60%的份额。ITRON具有标准的实时内核,适用于任何小规模的嵌入式系统,日本国内现有很多基于该内核的产品,其中消费电器较多,目前已成为事实上的工业标准。[1]
除此之外,基于ITRON操作系统的开发还是我国嵌入式开发的重要组成部分,但是由于相关资料和开发环境的欠缺,使得很多嵌入式技术人员难以对其进行学习及研究。目前基于ITRON最新标准μITRON 4.0的开源内核主要有TEngine(http://www.tengine.org/)的TKernel内核和TOPPERS(http://www.toppers.jp/)的TOPPERS/ASP内核。其中TKernel内核源代码只提供给会员下载,而TOPPERS/ASP内核则非常开放,无需注册即可在其网站下载到完整的源代码及相关文档。然而由于中日嵌入式开发的市场背景不尽相同,TOPPERS/ASP内核官方支持的开发板和CPU在国内市场一般较难买到,使得很多嵌入式技术人员与这个学习μITRON的良好平台失之交臂。正是基于这个原因,本文特将TOPPERS/ASP内核主流版本1.6.0移植到了国内常见的ARM9 CPU S3C2440[2]上,同时揭示了TOPPERS/ASP内核移植到ARM架构CPU上的一般方法,以供大家共同学习和研究,并且一定程度上解决了国内一直以来存在的学习ITRON内核没有合适的硬件平台的问题。
1 移植环境
(1) 硬件环境
TQ2440开发板,CPU为S3C2440AL,板载2 MB Nor Flash,64 MB SDRAM。
(2) 软件环境
TOPPERS/ASP Release 1.6.0内核(http://www.toppers.jp/aspddownload.html)
Arch Linux x86_64(或其他主流Linux发行版如Ubuntu和Fedora等)
Armelfgcc 4.5.2编译环境(或其他ARM交叉编译环境)
编者注:移植完成的完整源代码可以在本刊网站www.mesnet.com.cn下载。
2 编写板级相关宏定义
为了将板级相关定义和常量与具体实现分离,将所有板级相关的宏定义都有序地放在了target/tq2440/s3c2440.h头文件下,该头文件共分为以下6个部分:
① 板级常量定义。定义了如FIN初始时钟频率、SDRAM基地址、SDRAM大小等常量。
② 特殊寄存器初始化设定。定义了一些特殊寄存器初始化时使用到的值,如CLKDIVN寄存器初始化时需要的HDIVN部分和PDIVN部分的值就在这里设定。
③ 中断号。定义了相应中断请求的编号。
④ 寄存器相关常量。定义了寄存器相关的一些常量,如TIMER1_UPD_BIT就定义了TIMER1寄存器UPD位的位置。
⑤ 寄存器地址。定义了相应寄存器的访问地址。
⑥ 派生设定。定义了由以上常量所派生出来的值,如TIMER1的输入时钟频率TIMER_1_CLK在这里定义为由(PCLK/(TCFG0_PRESCALER_0+1)/(2<<TCFG1_MUX_1))这个公式所派生。
3 初始化流程分析及实现
ARM架构通用的启动程序位于内核代码树的arch/arm_gcc/common/start.S文件内,其主要代码是:
start:
msr cpsr, #(CPSR_SVC|CPSR_INTLOCK)
ldr r0, =_kernel_istkpt /*初始化栈寄存器*/
ldr sp, [r0]
mov fp, #0
mov r7, #0
ldr r0, =hardware_init_hook/*调用硬件初始化函数*/
cmp r0, #0x00
movne lr, pc
movne pc, r0
start_5:
ldr r0, =software_init_hook/*调用软件初始化函数*/
cmp r0, #0x00
movne lr, pc
movne pc, r0
start_6:
bl sta_ker/*启动内核*/
从以上汇编程序可以看出,在启动内核前,会经历“初始化栈寄存器→初始化硬件→初始化软件”的过程。其中硬件初始化函数hardware_init_hook需要根据实际情况编写相应实现,而软件初始化函数在移植的过程中可以不用实现,视具体任务的需要而定。
在硬件初始化阶段,主要的任务有:禁用watchdog,防止系统自动重启;初始化SDRAM控制器,使SDRAM可用;初始化系统时钟,设置FCLK,HCLK和PCLK频率。其主要的实现代码如下:
hardware_init_hook:
mov r1, #0x53000000 /*禁用watchdog*/
mov r2, #0x0
str r2, [r1]
ldr r0, =CLKDIVN_INI_VAL /*初始化系统时钟*/
ldr r1, =CLKDIVN
str r0, [r1]
.if CLKDIVN_HDIVN != 0 /*HDIVN非零时,设置CPU为异步总线模式*/
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #0xc0000000
mcr p15, 0, r0, c1, c0, 0
.endif
ldr r0, =MPLLCON_INI_VAL
ldr r1, =MPLLCON
str r0, [r1]
.equ MEM_CTL_BASE, 0x48000000 /*SDRAM控制寄存器基址*/
mov r1, #MEM_CTL_BASE /*初始化SDRAM控制器*/
adrl r2, mem_cfg_val /*mem_cfg_val为寄存器值数组*/
add r3, r1, #52 /*13个寄存器×4字节大小=52字节*/
1:/*通过循环向13个寄存器赋值*/
ldr r4, [r2], #4
str r4, [r1], #4
cmp r1, r3
bne 1b
mov pc, lr
整个系统经过以上初始化过程后,即可调用sta_ker进入内核的启动过程。
4 必要驱动程序的实现
前文的初始化过程仅仅保证了内核程序能够启动起来,要使得内核能够正确地运作,还要实现必要的硬件资源的驱动程序。
4.1 UART串口驱动程序实现
通用异步收发器(Universal Asynchronous Receiver/Transmitter, UART)具有传输线少、可靠性高、波特率可调等优点,广泛应用于数据通信和控制系统[3]。S3C2440也采用了UART总线进行串口通信,一共支持UART0、UART1和UART2三组UART接口。而本文选取的目标开发板TQ2440使用了UART0接口作为板载串口进行通信。所以,要查看内核输出日志,且与内核任务进行通信,就要实现TOPPERS/ASP内核的UART串口驱动。UART串口通信相关的驱动实现较为简单,且都位于target/tq2440_gcc/target_serial.c源文件中。下面仅对其中一些关键函数进行介绍。
void uart_initialize(void):UART底层初始化函数,会在sta_ker的初始化被调用,主要工作是使能对应GPIO口,设置UART数据位、终止位及波特率等和打开接收字符次中断掩码(INTSUBMSK)等。
bool_t uart_getready (SIOPCB *p_siopcb)和bool_t uart_putready(SIOPCB *p_siopcb):用来返回一个串口的接收或发送缓冲区是否准备好。
uint8_t uart_getchar(SIOPCB *p_siopcb)和uint8_t uart_putchar(SIOPCB *p_siopcb):使用串口接收或发送一个字符。
void sio_initialize(intptr_t exinf):UART上层初始化函数,将UART封装为TOPPERS通用的SIOPCB数据结构。
SIOPCB * sio_opn_por(ID siopid, intptr_t exinf)和void sio_cls_por(SIOPCB *p_siopcb):用来打开和关闭一个串口。
void sio_handler(void):UART中断处理函数,在这里主要用来处理从PC接收字符的中断。
通过查阅S3C2440手册将这些函数实现后,UART接口就可以正常工作了,通过Minicom等串口终端工具即可看到内核的日志输出以及向内核发送字符进行控制。
4.2 定时器计数器驱动程序的实现
在内核启动后,需要CPU周期性提供时间节拍(time tick)才能正确运作。能够精确提供时间节拍的方法是通过CPU内置的定时器计数器来实现。S3C2440有5个16位定时器,其中定时器0、1、2和3具有脉宽调制(PWM)功能。定时器4仅仅是一个内部定时器,没有输出引脚。定时器0具有死区发生器,通常应用于大电流设备。本文选用定时器1作为CPU时间节拍的定时器。
μITRON 4.0规范[4]规定了时间节拍的周期由TIC_NUME和TIC_DENO两个常量来定义,时间节拍周期=TIC_NUME/TIC_DENO ms。在target/tq2440_gcc/target_kernel.h中定义了TIC_NUME=TIC_DENO=1,即为TOPPERS/ASP内核所推荐的1 ms时间节拍周期。因此,定时器1产生中断的周期也要设置为1 ms。
在板级相关宏定义文件s3c2440.h中的派生设定部分,定义了定时器1的输入时钟频率为TIMER_1_CLK,所以TIMER_CLOCK=TIMER_1_CLK×TIC_NUME/TIC_DENO/1000即为定时器应该设置的计数值。因此,只要设置好定时器1的计数值,将定时器1设置为自动重装(auto reload)模式并使能定时器1即可完成定时器1的驱动,向内核提供周期性的时间节拍。具体的实现代码位于target/tq2440_gcc/target_timer.c文件。
5 中断处理过程的实现
前文实现了UART串口和定时器的驱动程序,在UART接收到字符以及定时器计数归零时都会产生相应的中断,为了能够正确地处理中断,还要实现相应的中断处理过程irq_handler函数。irq_handler函数的实现位于target/tq2440_gcc/target_support.S文件内,流程如图1所示,将对流程图中的一些关键步骤进行重点解释。

图1 中断处理流程
① 将SVC模式上下文压栈:ARM处理器共有7种工作模式,分别为用户模式(USER)、快速中断模式(FIQ)、外部中断模式(IRQ)、特权模式(SVC)、数据访问终止模式(ABT)、未定义指令终止模式(UND)以及系统模式(SYS)。TOPPERS ASP内核及其任务通常是运行在SVC模式,而产生中断后会跳转到IRQ模式执行irq_handler处理函数。由于中断处理完成后,程序应回到被中断处以SVC模式继续执行,所以要在进入irq_handler处理函数时就将SVC模式上下文压栈,以免在中断处理过程中改变了寄存器的内容而导致无法恢复中断前状态。
② 临时更新ipm:TOPPERS ASP内核允许多重中断的处理。但是,中断拥有各自的优先级,即较高优先级的中断处理过程无法被较低优先级的中断所抢占。为了实现这个算法,引入了中断优先级掩码(Interrupt Priority Mask,IPM),并将其存放在一个一维数组ipm_mask_tbl中,ipm_mask_tbl[PRI]的值表示在PRI优先级的中断处理中,应当设置的中断屏蔽掩码。同样,这里也采用堆栈的方式对ipm进行更新和还原。
6 编译与测试
除了上述主要工作外,移植到S3C2440还有一些细节的工作由于篇幅问题无法在文中一一指出,完整的移植后的源代码可以在本刊网站下载。下面对移植完成后的完整源代码进行编译和测试。
① 下载完整的移植后的源代码并解压。
② 进入asp目录。
③ 建立编译用文件夹并进入:
mkdir tq2440
cd tq2440
④ 配置并编译:
../configure T tq2440_gcc
make
⑤ 编译完成后会得到asp和asp.srec两个文件,通过JTAG将asp.srec烧入目标板NOR Flash。
⑥ 通过Minicom等串口通信工具可以看到类似输出,并且可以输入字符与内核交互,如输入r可以实现优先级轮转。
TOPPERS ASP移植结果如图2所示。
结语
本文实现了将TOPPERS/ASP内核移植到S3C2440 CPU上。填补了TOPPERS/ASP内核对ARM9架构支持的空白。使得国内嵌入式开发人员能够方便的在自己

图2 TOPPERS ASP移植结果
熟悉的平台上进行相关的开发与研究。实验结果表明,TOPPERS/ASP移植到目标板TQ2440的工作已经完成,并且能稳定的运行在目标板上,为后续的嵌入式系统开发与研究提供了良好的环境。