天生万物,万物始于天。同样,龙芯2E要运行起来,那么就少不了程序。那么龙芯2E的盘古开天是从那里开始呢?追源寻根,才能了解事物的来龙去脉,才能把握住未来的方向。现在就去寻找龙芯电脑运行的第一行程序,去看看龙芯的初始化阶段都做了些什么事情,有没有做坏事情呢?哈哈,这个很难说的。如果有BUG的出现,就会干出各种各样的坏事情。
龙芯2E的内存布局已经说明它要从那里加载第一行程序了,很明确的地址就是
0xBFC0 0000,相应的物理地址是0x1FC0 0000。因此,只把PMON程序放到那个ROM Flash里,就会运行第一行程序。
打开PMON目录下的文件start.S,就会看到下面的汇编程序:
.set noreorder
.globl _start
.globl start
.globl __main
_start:
start:
.globl stack
stack = start - 0x4000 /* Place PMON stack below PMON start in RAM */
/* NOTE!! Not more that 16 instructions here!!! Right now it's FULL! */
mtc0 zero, COP_0_STATUS_REG
mtc0 zero, COP_0_CAUSE_REG
li t0, SR_BOOT_EXC_VEC /* Exception to Boostrap Location */
mtc0 t0, COP_0_STATUS_REG
la sp, stack
la gp, _gp
bal uncached /* Switch to uncached address space */
nop
bal locate /* Get current execute address */
nop
uncached:
or ra, UNCACHED_MEMORY_ADDR
j ra
nop
/* 蔡军生 2006-12-28 */
OK,从上面看到,第一行程序运行的就是mtc0 zero, COP_0_STATUS_REG。找到了第一行运行的程序,但这段程序是什么意思呢?为什么不是.set noreorder
最先运行的呢?有疑问就是好方向,下面就慢慢地给你解说。在龙芯所使用的编译器是GCC,那么它的汇编也有特定的格式。在这里看到的就是汇编器的编译程序所需要格式和编译条件。
.set noreorder是告诉汇编器不要对后面的代码进行优化处理,比如重新排列执行代码。
.globl _start
.globl start
.globl __main
这里,就定义了三个全局名称。可以任何地方引用它。
_start:
start:
.globl stack
stack = start - 0x4000 /* Place PMON stack below PMON start in RAM */
在这里定义了子程序的名称_start和start。并定义了堆栈的大小变量stack。
/* NOTE!! Not more that 16 instructions here!!! Right now it's FULL! */
mtc0 zero, COP_0_STATUS_REG
mtc0 zero, COP_0_CAUSE_REG
li t0, SR_BOOT_EXC_VEC /* Exception to Boostrap Location */
mtc0 t0, COP_0_STATUS_REG
la sp, stack
la gp, _gp
由于龙芯的地址空间决定,这里的代码不能超过32条指令,因为后面紧跟着是其它中断向量的地址。接着,就把CP0的状态寄存器COP_0_STATUS_REG和COP_0_CAUSE_REG寄存器全部清空为0。li t0, SR_BOOT_EXC_VEC接着设置状态寄存器的BEV位,这样就是让CP0运行在没有TLB的模式,并且运行在引导模式。后面两句主要设置引导程序的堆栈空间,la sp, stack是把栈首地址给sp寄存器,la gp, _gp是把编译器中的_gp全局地址给gp寄存器,这样做法是让全局变量可以作相对寄存器寻址。其中_gp是在连接脚本文件里定义的。
bal uncached /* Switch to uncached address space */
nop
bal locate /* Get current execute address */
nop
uncached:
or ra, UNCACHED_MEMORY_ADDR
j ra
nop
这段程序先进行一个无条件跳转连接指令,这样做的目的很明确就是想清空预取指令和流水线的指令。这样就跳到uncached这里运行,先来看看bal指令会做些什么事情,通常bal指令会算出相对于PC寄存器的偏移量,然后把PC+8指令地址放到RA寄存器里,也就是把bal locate指令地址放到RA寄存器,以便可以返回。由于龙芯2E的加电时启动地址是0xBFC0 0000,那么放在RA里的值就是0xBFCO 0028。后面or ra, UNCACHED_MEMORY_ADDR,这里进行是与0xA000 00000的或运算,也就是说从ROM加载时,不会改变返回地址RA的值。主要是从其它地址空间运行这段程序时才会引起跳转的地址空间的转换。所以接着就跳回来到bal locate位置并执行bal locate指令,这样就跳到locate的位置执行程序了。