一、一些想法
一直以来,linux被认为是个32位的“现代化”操作系统,一般也没有人在8位、16位单片机上面去移植linux。但是,从原理上来讲,从任意位数的单片机,都是可以运行linux的——本质上就是个操作系统么,既然ucos可以,为什么linux不行?
于是,我的毕业设计打算做这个选题。当然,虽然从原理上来讲是可行的,但是在实际中遇到了一个非常大的困难,那就是底层的代码量改动非常大。并且linux体系中涉及到很多32位的东西,比如MMU。
后来就萌发了另外一个思路:作为uclinux而言,本身是为了不支持MMU的单片机而设计的,可以考虑移植uclinux到16位单片机上面。同时,linux的早期版本,对ram和flash的要求都比较小,因此考虑移植早期版本的uclinux。
二、新的思路及其价值
但是今天又在google上面搜索,突然见到了如下的文章:
http://dmitry.co/index.php?p=./04.Thoughts/07.%20Linux%20on%208bit
这篇文章是用8位单片机来启动ubuntu,并且发表时间也比较新(代码最后修改是在3.22号,最近的网站更新是在4.3号)。其参考价值在于,作者并没有采用传统的“移植”的方法,来将linux的内核直接搬到8位的单片机上面,而是通过加入了一个中间层的方法——首先,作者在8位单片机上面实现了一个ARM模拟器,模拟出来一个ARM环境;其次,将linux内核在模拟的ARM环境中进行了运行。其巧妙之处在于,避开了复杂的移植过程(以及16位和32位的兼容性问题),将工作主要集中在如何在8位单片机上模拟32位的单片机(并且模拟了诸如MMU、DMA等系统模块,以及UART等外设)。那么这样的工作还有另外一个重要的价值:在要求不高的情况下,我们可以采用更廉价、更低端的单片机(16位单片机、廉价的32位单片机)来模拟高端的单片机(ARM9系列等等)。
三、新思路的具体实现方案
实际上从原理上来讲,这种思路的实现也很简单。不考虑外设(当然部分外设也可以模拟,比如模拟IIC,模拟SPI)的情况下,实际上单片机内部就是①指令集②寄存器。
首先来说寄存器。实际上32位的单片机,其寄存器都是32位的。同时,每个单片机的核心寄存器就那么几个(比如8086,就是AX BX CX DX,还有DS SS SP等等)。既然是寄存器,那么对于8位单片机来说,32位的寄存器不就是4个寄存器连接起来么?不就相当于一个结构体(或者是数组)么?因此,我们的模拟器可以考虑用一系列的结构体来模拟32位单片机的内部寄存器。
再来说指令集。不同单片机之间的指令集是不同的,这个道理显而易见(同时,这个也是阻碍linux或者其他操作系统移植的最大的因素,因为移植linux的话最难的部分就在于底层汇编的编写)。但是,汇编语言的本质是什么?实际上就是机器语言的一种表现形式。那么机器语言是什么?不就是0和1么。话又说回来,所有的C语言的程序,最终都需要转化成机器语言才能够执行——也就是说,C语言和机器语言是对应的。那么,如果我手头有一个ARM编译出来的程序,那么肯定全部是机器语言,由0和1组成。如果我知道ARM系列每个机器语言的含义,并且对这个编译出来的程序进行解码,那么就能得到对应的每一句的汇编语言(这也就是所谓的反编译)。那么,如果我们在8位单片机的程序中,首先读出ARM编译出来的程序中每一句机器指令,然后加以解析,并且将这个机器指令对应的操作进行实现——
那么我们不就通过程序,来完成了ARM单片机中CPU的功能么?那么,这样我们实际上就通过8位单片机上面的程序,完成了32位命令的解析,也就完成了一个ARM模拟器的工作的大部分。
当然,除了上述两项,还有就是linux运行毕竟还是需要一定量的RAM和外设,因此我们还要再加入一些外部RAM和FLASH,这些方案没有什么难度,就只是工作量的问题啦。
四、一点展望
刚才那篇文档的作者,已经实现了ARMv5TE指令集的模拟,也就是ARM9单片机的内核,但是他是启动的ubuntu系统,比较复杂,而且内核启动也比较麻烦(模块较多)。事实上,我们完全可以通过这样一个系统来启动我们嵌入式的内核,大小在几M级别而已,如果精简一下的话,启动会更迅速,也会更具有实用价值。