http://xenyinzen.wikidot.com/loongson-about
+++++++++++++++++++++++++++++++++++++++++
PMON-V1.1 目录结构
+++++++++++++++++++++++++++++++++++++++++
pmon的目录结构大致如下(由linux工具tree生成)
。
|-- Targets 目标结构相关代码,每个新结构在该目录下有一个子目录
| -- Bonito Bonito是我们的北桥代号,里面是一些关于北桥的代码 | |-- Bonito | |-- compile | |– zboot
| | |-- images
| | |-- pmon
| | -- utils | |-- conf | |-- dev | |-- include |– pci
|-- conf 源代码编译所依赖的配置文件所在目录
|-- doc 说明文档
|-- examples 一看就知道是样例,但我还不知道只面究竟写的是什么
|-- fb 在frambuffer上做文字和图形显示的代码
|-- include 系统头文件
|-- lib
| |-- libc C库
| -- libz zip压缩库 |-- pic 开机启动图片(压缩后的)存放目录 |-- pmon pmon主体代码 | |-- arch 平台相关代码 | |– mips 处理器相关的代码,比如Flush_Cache等
| |-- cmds pmon shell 命令代码
| |-- common 各模块共同依赖的代码
| |-- custom ??这个目录不知道干什么用的
| |-- dev 一些基本设备的驱动,比如Flash
| |-- fs 文件系统支持代码
| |-- loaders 二进制文件加载器代码
| -- netio 网络命令以及tftp的实现 |-- sys pmon的较低层的代码 | |-- arch 处理器相关代码一些定义 | |– mips
| | -- include | |-- dev 各种设备的驱动程序 | | |-- ata | | |-- fd | | |-- ic | | |-- microcode | | |– siop
| | |-- mii
| | |-- pci
| | -- u*** | |-- kern 主要是一些系统调用的实现,比如malloc,time, signal, socket | |-- linux | |-- net 实现网络协议 | |-- netinet 实现网络协议 | |-- scsi Scsi协议的实现 | |-- sys 这个大目录的头文件存放区 |– vm ??虚拟内存相关实现
|-- tools 一些工具
| |-- bmp_logo 把bmp图转换成C数据的工具
| |-- bootelf
| |-- mk
| |-- pmoncfg 源代码配置工具
| |-- scripts
| -- srecord |-- x86emu x86显卡模拟器,主要是运行显卡的BIOS,初始化显卡 |– int10
| -- x86emu | |-- include | |– x86emu
| -- src |– x86emu
| -- x86emu– zloader zip格式加载启动代码
Targets目录的组成
每个结构一个目录,我们拿Bonito来为例子,主要有下列文件:
start.S 位于Targets/Bonito/Bonito 目录下,是C环境建立之前的汇编代码,使整个BIOS运行的起点。
tgt_machdep.c位于Targets/Bonito/Bonito 目录下,一些板子相关的函数。
pci_machdep.c进行Targets/Bonito/pci 空间分配的一些函数
Targets/Bonito/dev 目录下一些板子特殊的设备的驱动。
Targets/Bonito/conf 目录下是一些编译环境建立需要的一些文件
参考说明
本文的撰写过程中,参考了诸多中科龙梦科技有限公司的内部资料(都是可以公开的),另外还有一些未署名的文档,无法一一列举,在此表示感谢。龙芯事业一定会在前仆后继的勇士所铺设的道路上日益壮大!
+++++++++++++++++++++++++++++++++++++++++
代码执行流程
+++++++++++++++++++++++++++++++++++++++++
当整个板子起电后,CPU将从 0xBFC00000 取指令开始执行,而ROM在系统中的地址就是从该地址开始的。所以,其中的第一条指令就是整个过程中 CPU 要执行的第一个指令。
初始化CPU内的寄存器,清TLB.
初始化一些北桥的基本配置,以确保uart能够正常工作。
初始化uart,主要是设置波特率。
初始化内存(主要通过I2C协议从内存的EEPROM读取内存参数来进行设置)。
初始化cache.
拷贝pmon的代码到内存,然后通过
la v0, initmips
jalr v0
nop
从此代码便到内存中间去了,从这开始因为可以读写内存,所以有了栈,故可以用C的代码了,所以以后的程序便是C代码了
+++++++++++++++++++++++++++++++++++++++++
start.S详解
+++++++++++++++++++++++++++++++++++++++++
1
start.S文件在 /Targets/Bonito/Bonito 目录当中,是整个PMON代码的起点。我们首先研究它。
文件一开头是版权声明部分,然后是包括一些头文件,然后是一些宏定义,然后才是代码。
与CPU相关的一些宏定义有
/*
Register usage:
s0 link versus load offset, used to relocate absolute adresses.
s1 free
s2 memory size.
s3 free.
s4 Bonito base address.
s5 dbg.
s6 sdCfg.
s7 rasave.
s8 L3 Cache size.
*/
#define tmpsize s1
#define msize s2
#define bonito s4
#define dbg s5
#define sdCfg s6
下面是程序的开头,不过并不生成实际的二进制数据,它告诉编译汇编器一些信息。
.set noreorder
.globl _start
.globl start
.globl __main
_start:
start:
.globl stack
stack = start - 0x4000 /* Place PMON stack below PMON start in RAM */
=====================
解释:
.set noreorder
是告诉汇编汇编器不要对后面的代码进行优化处理,比如重新排列执行代码。
.globl _start
.globl start
.globl __main
这里,定义了三个全局符号。可以PMON代码中的任何地方引用它。
_start:
start:
.globl stack
stack = start - 0x4000 /* Place PMON stack below PMON start in RAM */
在这里定义了子程序的名称 _start 和 start。并定义了堆栈的栈底 stack 值,在 start 以下 16K 处。
下面是程序执行的第一条语句
/* 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
/ SR’s BEV bit is set so the CPU uses the ROM(kseg1) space exception entry point when reboot exception occurs
la sp, stack
la gp, _gp
=====================
解释:
由于龙芯的地址空间决定,这里的代码不能超过 16 条指令,因为后面紧跟着的是中断向量的地址。(??)
接着,就把 CP0 的状态寄存器 COP_0_STATUS_REG 和 COP_0_CAUSE_REG 寄存器全部清空为0。
li t0, SR_BOOT_EXC_VEC
接着设置状态寄存器的BEV位,这样就是让 CP0 运行在没有 TLB 的模式,并且一旦发生异常,就进入ROM 的 bfc00000 位置重启。
后面两句主要设置引导程序的堆栈空间,
la sp, stack 是把栈底地址给 sp 寄存器,(现在有个疑问就是 pmon的栈是往上还是往下生长的??)
la gp, _gp 是把编译器中的 _gp 全局地址给 gp 寄存器,这样做法是让全局变量可以作相对寄存器寻址。
其中_gp是在连接脚本文件里定义的。
bal uncached
nop
bal locate
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(第8条指令)。
后面
or ra, UNCACHED_MEMORY_ADDR,这里进行是与0xA000 00000的或运算,
也就是说从ROM加载时,不会改变返回地址 ra 的值。
写这句的目的主要是保证要从ROM中运行后面的一段程序,而不是从其它地址(RAM中)运行。
所以接着就跳回来到 bal locate位置并执行 bal locate 指令,这样就跳到 locate 的位置执行程序了。
在MIPS中,异常处理入口有两套,通过 CP0 的 STATUS 寄存器位 BEV 来决定,当 BEV=1 时,异常的入口地址为 0xBFC00000 开始的地址。而 BEV=0,异常地址为 0x80000000 开始的地址,所以PMON程序段开始处是一些异常的调入口,需要跳过这段空间,程序就是通过这个 bal 指令跳到后面的
2
下面是那段被跳过去的异常代码
/*
Reboot vector usable from outside pmon.
/
/ started in aligned address by 2^8=256 Bytes, that is 0xbfc00000 + 0x100 = 0xbfc00100 */
.align 8
ext_map_and_reboot:
bal CPU_TLBClear
nop
li a0, 0xc0000000
li a1, 0x40000000
bal CPU_TLBInit
nop
la v0, tgt_reboot
la v1, start
subu v0, v1
lui v1, 0xffc0
addu v0, v1
jr v0
nop
/*
Exception vectors here for rom, before we are up and running. Catch
whatever comes up before we have a fully fledged exception handler.
/
/ TLB refill exception /
/ bfc00200, this code address is 0xbfc00200, 2^9 = 512 Bytes, it is a exception process function */
.align 9
move k0, ra /* save ra */
la a0, v200_msg
bal stringserial
nop
b exc_common
.align 7 /* bfc00280 */
move k0, ra #save ra
la a0, v280_msg
bal stringserial
nop
b exc_common // print the CP0 register’s infomation
/* Cache error handler /
.align 8 / bfc00300 */
PRINTSTR(“rnPANIC! Unexpected Cache Error exception! ”)
mfc0 a0, COP_0_CACHE_ERR
bal hexserial
nop
b exc_common
/* General exception handler /
.align 7 / bfc00380 */
move k0, ra #save ra
la a0, v380_msg
bal stringserial
nop
b exc_common
.align 8 /* bfc00400 */move k0, ra #save rala a0, v400_msgbal stringserialnop /* when the exception occurs, do this code to present the CP0 register’s content */
exc_common:
PRINTSTR(“rnCAUSrnSTATUrnERRORPrnEPrnBADVADDrnRA=”)
move a0, k0
bal hexserial
nop
// b ext_map_and_reboot
nop
/* control the distribution of the code, here we insert a bank 256 Bytes. */.align 8 nop/* handler name table */.align 8.word read.word write.word open.word close.word nullfunction.word printf.word vsprintf.word nullfunction.word nullfunction.word getenv.word nullfunction.word nullfunction.word nullfunction.word nullfunction 3
http://xenyinzen.wikidot.com/loongson-about
+++++++++++++++++++++++++++++++++++++++++
PMON-V1.1 目录结构
+++++++++++++++++++++++++++++++++++++++++
pmon的目录结构大致如下(由linux工具tree生成)
。
|-- Targets 目标结构相关代码,每个新结构在该目录下有一个子目录
| -- Bonito Bonito是我们的北桥代号,里面是一些关于北桥的代码 | |-- Bonito | |-- compile | |– zboot
| | |-- images
| | |-- pmon
| | -- utils | |-- conf | |-- dev | |-- include |– pci
|-- conf 源代码编译所依赖的配置文件所在目录
|-- doc 说明文档
|-- examples 一看就知道是样例,但我还不知道只面究竟写的是什么
|-- fb 在frambuffer上做文字和图形显示的代码
|-- include 系统头文件
|-- lib
| |-- libc C库
| -- libz zip压缩库 |-- pic 开机启动图片(压缩后的)存放目录 |-- pmon pmon主体代码 | |-- arch 平台相关代码 | |– mips 处理器相关的代码,比如Flush_Cache等
| |-- cmds pmon shell 命令代码
| |-- common 各模块共同依赖的代码
| |-- custom ??这个目录不知道干什么用的
| |-- dev 一些基本设备的驱动,比如Flash
| |-- fs 文件系统支持代码
| |-- loaders 二进制文件加载器代码
| -- netio 网络命令以及tftp的实现 |-- sys pmon的较低层的代码 | |-- arch 处理器相关代码一些定义 | |– mips
| | -- include | |-- dev 各种设备的驱动程序 | | |-- ata | | |-- fd | | |-- ic | | |-- microcode | | |– siop
| | |-- mii
| | |-- pci
| | -- u*** | |-- kern 主要是一些系统调用的实现,比如malloc,time, signal, socket | |-- linux | |-- net 实现网络协议 | |-- netinet 实现网络协议 | |-- scsi Scsi协议的实现 | |-- sys 这个大目录的头文件存放区 |– vm ??虚拟内存相关实现
|-- tools 一些工具
| |-- bmp_logo 把bmp图转换成C数据的工具
| |-- bootelf
| |-- mk
| |-- pmoncfg 源代码配置工具
| |-- scripts
| -- srecord |-- x86emu x86显卡模拟器,主要是运行显卡的BIOS,初始化显卡 |– int10
| -- x86emu | |-- include | |– x86emu
| -- src |– x86emu
| -- x86emu– zloader zip格式加载启动代码
Targets目录的组成
每个结构一个目录,我们拿Bonito来为例子,主要有下列文件:
start.S 位于Targets/Bonito/Bonito 目录下,是C环境建立之前的汇编代码,使整个BIOS运行的起点。
tgt_machdep.c位于Targets/Bonito/Bonito 目录下,一些板子相关的函数。
pci_machdep.c进行Targets/Bonito/pci 空间分配的一些函数
Targets/Bonito/dev 目录下一些板子特殊的设备的驱动。
Targets/Bonito/conf 目录下是一些编译环境建立需要的一些文件
参考说明
本文的撰写过程中,参考了诸多中科龙梦科技有限公司的内部资料(都是可以公开的),另外还有一些未署名的文档,无法一一列举,在此表示感谢。龙芯事业一定会在前仆后继的勇士所铺设的道路上日益壮大!
+++++++++++++++++++++++++++++++++++++++++
代码执行流程
+++++++++++++++++++++++++++++++++++++++++
当整个板子起电后,CPU将从 0xBFC00000 取指令开始执行,而ROM在系统中的地址就是从该地址开始的。所以,其中的第一条指令就是整个过程中 CPU 要执行的第一个指令。
初始化CPU内的寄存器,清TLB.
初始化一些北桥的基本配置,以确保uart能够正常工作。
初始化uart,主要是设置波特率。
初始化内存(主要通过I2C协议从内存的EEPROM读取内存参数来进行设置)。
初始化cache.
拷贝pmon的代码到内存,然后通过
la v0, initmips
jalr v0
nop
从此代码便到内存中间去了,从这开始因为可以读写内存,所以有了栈,故可以用C的代码了,所以以后的程序便是C代码了
+++++++++++++++++++++++++++++++++++++++++
start.S详解
+++++++++++++++++++++++++++++++++++++++++
1
start.S文件在 /Targets/Bonito/Bonito 目录当中,是整个PMON代码的起点。我们首先研究它。
文件一开头是版权声明部分,然后是包括一些头文件,然后是一些宏定义,然后才是代码。
与CPU相关的一些宏定义有
/*
Register usage:
s0 link versus load offset, used to relocate absolute adresses.
s1 free
s2 memory size.
s3 free.
s4 Bonito base address.
s5 dbg.
s6 sdCfg.
s7 rasave.
s8 L3 Cache size.
*/
#define tmpsize s1
#define msize s2
#define bonito s4
#define dbg s5
#define sdCfg s6
下面是程序的开头,不过并不生成实际的二进制数据,它告诉编译汇编器一些信息。
.set noreorder
.globl _start
.globl start
.globl __main
_start:
start:
.globl stack
stack = start - 0x4000 /* Place PMON stack below PMON start in RAM */
=====================
解释:
.set noreorder
是告诉汇编汇编器不要对后面的代码进行优化处理,比如重新排列执行代码。
.globl _start
.globl start
.globl __main
这里,定义了三个全局符号。可以PMON代码中的任何地方引用它。
_start:
start:
.globl stack
stack = start - 0x4000 /* Place PMON stack below PMON start in RAM */
在这里定义了子程序的名称 _start 和 start。并定义了堆栈的栈底 stack 值,在 start 以下 16K 处。
下面是程序执行的第一条语句
/* 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
/ SR’s BEV bit is set so the CPU uses the ROM(kseg1) space exception entry point when reboot exception occurs
la sp, stack
la gp, _gp
=====================
解释:
由于龙芯的地址空间决定,这里的代码不能超过 16 条指令,因为后面紧跟着的是中断向量的地址。(??)
接着,就把 CP0 的状态寄存器 COP_0_STATUS_REG 和 COP_0_CAUSE_REG 寄存器全部清空为0。
li t0, SR_BOOT_EXC_VEC
接着设置状态寄存器的BEV位,这样就是让 CP0 运行在没有 TLB 的模式,并且一旦发生异常,就进入ROM 的 bfc00000 位置重启。
后面两句主要设置引导程序的堆栈空间,
la sp, stack 是把栈底地址给 sp 寄存器,(现在有个疑问就是 pmon的栈是往上还是往下生长的??)
la gp, _gp 是把编译器中的 _gp 全局地址给 gp 寄存器,这样做法是让全局变量可以作相对寄存器寻址。
其中_gp是在连接脚本文件里定义的。
bal uncached
nop
bal locate
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(第8条指令)。
后面
or ra, UNCACHED_MEMORY_ADDR,这里进行是与0xA000 00000的或运算,
也就是说从ROM加载时,不会改变返回地址 ra 的值。
写这句的目的主要是保证要从ROM中运行后面的一段程序,而不是从其它地址(RAM中)运行。
所以接着就跳回来到 bal locate位置并执行 bal locate 指令,这样就跳到 locate 的位置执行程序了。
在MIPS中,异常处理入口有两套,通过 CP0 的 STATUS 寄存器位 BEV 来决定,当 BEV=1 时,异常的入口地址为 0xBFC00000 开始的地址。而 BEV=0,异常地址为 0x80000000 开始的地址,所以PMON程序段开始处是一些异常的调入口,需要跳过这段空间,程序就是通过这个 bal 指令跳到后面的
2
下面是那段被跳过去的异常代码
/*
Reboot vector usable from outside pmon.
/
/ started in aligned address by 2^8=256 Bytes, that is 0xbfc00000 + 0x100 = 0xbfc00100 */
.align 8
ext_map_and_reboot:
bal CPU_TLBClear
nop
li a0, 0xc0000000
li a1, 0x40000000
bal CPU_TLBInit
nop
la v0, tgt_reboot
la v1, start
subu v0, v1
lui v1, 0xffc0
addu v0, v1
jr v0
nop
/*
Exception vectors here for rom, before we are up and running. Catch
whatever comes up before we have a fully fledged exception handler.
/
/ TLB refill exception /
/ bfc00200, this code address is 0xbfc00200, 2^9 = 512 Bytes, it is a exception process function */
.align 9
move k0, ra /* save ra */
la a0, v200_msg
bal stringserial
nop
b exc_common
.align 7 /* bfc00280 */
move k0, ra #save ra
la a0, v280_msg
bal stringserial
nop
b exc_common // print the CP0 register’s infomation
/* Cache error handler /
.align 8 / bfc00300 */
PRINTSTR(“rnPANIC! Unexpected Cache Error exception! ”)
mfc0 a0, COP_0_CACHE_ERR
bal hexserial
nop
b exc_common
/* General exception handler /
.align 7 / bfc00380 */
move k0, ra #save ra
la a0, v380_msg
bal stringserial
nop
b exc_common
.align 8 /* bfc00400 */move k0, ra #save rala a0, v400_msgbal stringserialnop /* when the exception occurs, do this code to present the CP0 register’s content */
exc_common:
PRINTSTR(“rnCAUSrnSTATUrnERRORPrnEPrnBADVADDrnRA=”)
move a0, k0
bal hexserial
nop
// b ext_map_and_reboot
nop
/* control the distribution of the code, here we insert a bank 256 Bytes. */.align 8 nop/* handler name table */.align 8.word read.word write.word open.word close.word nullfunction.word printf.word vsprintf.word nullfunction.word nullfunction.word getenv.word nullfunction.word nullfunction.word nullfunction.word nullfunction 3
举报