lw r, a R<-a
lh r, a 无符号
lb r, a 无符号
lhu 有符号
lhbu 有符号
li r,c 加载立即数
Store
将寄存器中的数据存储到内存地址
Sw r, a R->a
Sh r, a Store low halfword
Sb r, a Store low byte
Move:
寄存器之间数据的直接交换
move r , s R<-s
逻辑运算
and r, s, t R <-s . t
or r, s, t R <-s + t
not r, s R <-s取反
xor r, s, t R <-s异或t
nor r, s, t R <-(s+t)再取反
算数运算
add r, s, t R->s + t
sub r, s, t R->s – t
mul r, s, t R->s*t
div r, s, t R->s/t
hi和lo乘法器相关的寄存器规模结果接口,不能用于乘和除之外的操作。对于以上二者,不存在直接寻址;必须要通过mfhi(“move from hi”)以及mflo(“move from lo”)两条指令分别来进行访问对应的内容。
乘法——将两个整数的相乘结果分成两部分存储的指定的寄存器里。
除法——lo寄存器存储结果(商),hi寄存器存储余数。
移位操作
sll r, s, c r ← shift s left c bits
srl r, s, c r ← shift s right c bits
分支操作
通过条件判断,使程序跳转到tag
b tag 跳转->tag
beq r, s, tag R=s ->tag
bne r, s, tag R != s->tag
bgt r, s, tag R > s->tag
bge r, s, tag R >=s->tag
blt r, s, tag R < s->tag
ble r, s, tag R<=s->tag
比较指令
slt r, s, t S < t
sle r, s, t S <= t
sgt r, s, t S >t
sge r, s, t S >= t
seq r, s, t S = t
sne r, s, t S != t
FALSE r <- 0
TRUE r <- 1
寻址方式
[ Last edited by zzz19760225 on 2017-6-17 at 13:11 ]作者: zzz19760225 时间: 2017-6-14 01:57 龙芯问答(各种龙芯空间太分散了,失去互动焦点。有相关问题链接蛮好) http://ask.loongnix.org/?/explore/
2003年,美国国防部发表备忘录,提出在美国军方规划实施的“全球信息网格”(Global Information Grid,GIG)中全面部署因特网的“下一代技术”IPV6。2012年,白宫发布部署IPV6的决定,要求所有美国政府机构的服务器和类似于电子邮件与网站的服务升级到IPV6,并要求美国政府机构在2014财政年度之前升级使用服务器的内部应用程序,并且让企业网络兼容IPV6。
1994年,美国因特网工程任务组提出IPV6演进解决IPV4地址域名不足的设想,并在1995年放弃对IPV9有组织的研究后重点转入IPV6。2006年,隶属美国因特网工程任务组的下一代过渡工作组(the Next Generation Transitions, the ngtrans)与6bone项目合作进行的IPV6测试结束。2007年开始,美国因特网工程任务组多次在国际会议上推荐IPV6为替代IPV4的下一代因特网协议。
_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("\r\nPANIC! 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 ra
la a0, v400_msg
bal stringserial
nop
/* when the exception occurs, do this code to present the CP0 register's content */
exc_common:
PRINTSTR("\r\nCAUSE=")
mfc0 a0, COP_0_CAUSE_REG
bal hexserial
nop
PRINTSTR("\r\nSTATUS=")
mfc0 a0, COP_0_STATUS_REG
bal hexserial
nop
PRINTSTR("\r\nERRORPC=")
mfc0 a0, COP_0_ERROR_PC
bal hexserial
nop
PRINTSTR("\r\nEPC=")
mfc0 a0, COP_0_EXC_PC
bal hexserial
nop
PRINTSTR("\r\nBADVADDR=")
mfc0 a0, COP_0_BAD_VADDR
bal hexserial
nop
PRINTSTR("\r\nRA=")
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
让我们看看 locate 标号之后的代码是些什么
/*
* We get here from executing a bal to get the PC value of the current execute
* location into ra. Check to see if we run from ROM or if this is ramloaded.
*/
locate:
la s0, start
subu s0, ra, s0
and s0, 0xffff0000
li t0, SR_BOOT_EXC_VEC
mtc0 t0, COP_0_STATUS_REG
mtc0 zero, COP_0_CAUSE_REG
.set noreorder
/* the varible bonito is register s4, BONITO_REG_BASE is 0x1fe00000 */
li bonito, PHYS_TO_UNCACHED(BONITO_REG_BASE)
bal 1f
nop /* now the value of ra is 0xbfc00xxx */
/* bonito endianess */
BONITO_BIC(BONITO_BONPONCFG, BONITO_BONPONCFG_CPUBIGEND)
BONITO_BIC(BONITO_BONGENCFG, BONITO_BONGENCFG_BYTESWAP|BONITO_BONGENCFG_MSTRBYTESWAP)
BONITO_BIS(BONITO_BONPONCFG, BONITO_BONPONCFG_IS_ARBITER)
/*
* In certain situations it is possible for the Bonito ASIC
* to come up with the PCI registers uninitialised, so do them here
*/
BONITO_INIT(BONITO_PCICLASS,(PCI_CLASS_BRIDGE << PCI_CLASS_SHIFT) | (PCI_SUBCLASS_BRIDGE_HOST << PCI_SUBCLASS_SHIFT))
BONITO_INIT(BONITO_PCICMD, BONITO_PCICMD_PERR_CLR | BONITO_PCICMD_SERR_CLR | BONITO_PCICMD_MABORT_CLR | BONITO_PCICMD_MTABORT_CLR | BONITO_PCICMD_TABORT_CLR | BONITO_PCICMD_MPERR_CLR )
//BONITO_INIT(BONITO_PCILTIMER, 0)
BONITO_INIT(BONITO_PCILTIMER, 255)
BONITO_INIT(BONITO_PCIBASE0, 0)
BONITO_INIT(BONITO_PCIBASE1, 0)
BONITO_INIT(BONITO_PCIBASE2, 0)
BONITO_INIT(BONITO_PCIEXPRBASE, 0)
BONITO_INIT(BONITO_PCIINT, 0)
BONITO_BIS(BONITO_PCICMD, BONITO_PCICMD_PERRRESPEN)
BONITO_BIS(BONITO_PCICMD, PCI_COMMAND_IO_ENABLE|PCI_COMMAND_MEM_ENABLE|PCI_COMMAND_MASTER_ENABLE)
BONITO_BIC(BONITO_BONGENCFG, 0x80) #???1iobc
#BONITO_BIS(BONITO_BONGENCFG, BONITO_BONGENCFG_BUSERREN)
/* Set debug mode */
BONITO_BIS(BONITO_BONGENCFG, BONITO_BONGENCFG_DEBUGMODE)
/******** added to init southbridge*/
#ifdef VGA_NOTEBOOK_V2
ISA_BRMW_INIT(0,0x74,0xeb,0x0)
ISA_BRMW_INIT(0,0x75,0xff,0x20)
ISABWWR_INIT(4,0x48,0xb000)
ISABBWR_INIT(4,0x41,0x80)
RMW_INIT(MOD_W,(PCI_IO_SPACE+0xb04c),0xffffffdf,0x0)
#endif
// SouthBridge settings
/* Set the SMB base address */
ISABWWR_INIT(4, SMBUS_IO_BASE_ADDR, SMBUS_IO_BASE_VALUE | 0x1)
/* enable the host controller */
ISABHWR_INIT(4, SMBUS_HOST_CONFIG_ADDR, SMBUS_HOST_CONFIG_ENABLE_BIT)
/* enable the SMB IO ports */
ISABBWR_INIT(4, PCI_COMMAND_STATUS_REG, PCI_COMMAND_IO_ENABLE)
ISARD_INIT(CTC_PORT+PT_CONTROL)
/* program i8254 ISA refresh counter */
ISAWR_INIT(CTC_PORT+PT_CONTROL,PTCW_SC(PT_REFRESH)|PTCW_16B|PTCW_MODE(MODE_RG))
ISAWR_INIT(CTC_PORT+PT_REFRESH, ISAREFRESH & 0xff)
ISAWR_INIT(CTC_PORT+PT_REFRESH, ISAREFRESH >> 8)
EXIT_INIT(0)
1:
move a0, ra /* now the value of ra is 0xbfc00xxx */
reginit:
lw t3, Init_Op(a0)
lw t0, Init_A0(a0) /* Init_A0 is 4 */
and t4, t3, OP_MASK /* OP_MASK is 0x000000fc, to keep 4 bytes aligned */
/*
* EXIT(STATUS)
*/
bne t4, OP_EXIT, 8f /* OP_EXIT is 0x00000000 */
nop
move v0, t0 /* now v0 is the content of 4 bytes offset from 0xbfc000xx */
b .done
nop
/*
* DELAY(CYCLES)
*/
8: bne t4, OP_DELAY, 8f /* OP_DELAY is 0x00000008 */
nop
1: bnez t0,1b /* t0 不等于 0就在这死循环 */
subu t0,1
b .next
nop
/*
* READ(ADDR)
*/
8: bne t4, OP_RD, 8f /* OP_RD is 0x00000010 */
nop
and t4, t3, MOD_MASK /* MOD_MASK is 0x00000003 */
bne t4, MOD_B, 1f /* MOD_B is 0x00000000 ??? why not 0x01 or 0x03 */
nop
lbu t5, 0(t0)
b .next
nop
1: bne t4, MOD_H, 1f /* MOD_H is 0x00000001 ??? why not 0x02 */
nop
lhu t5, 0(t0)
b .next
nop
1: bne t4, MOD_W, 1f /* MOD_H is 0x00000002 ??? why not 0x00 */
nop
#if __mips64
lwu t5, 0(t0)
#else
lw t5, 0(t0)
#endif
b .next
nop
1:
#if __mips64
lw t5,0(t0)
b .next
nop
#else
b .fatal
nop
#endif
/*
* WRITE(ADDR,VAL)
*/
8: bne t4, OP_WR, 8f /* OP_WR is 0x00000014 */
nop
lw t1, Init_A1(a0) /* Init_A1 is 8 */
and t4, t3, MOD_MASK /* MOD_MASK is 0x00000003 */
bne t4, MOD_B, 1f
nop
sb t1, 0(t0)
b .next
nop
1: bne t4,MOD_H,1f
nop
sh t1,0(t0)
b .next
nop
1: bne t4,MOD_W,1f
nop
sw t1,0(t0)
b .next
nop
1:
#if __mips64
sd t1,0(t0)
b .next
nop
#else
b .fatal
nop
#endif
/*
* RMW(ADDR,AND,OR)
*/
8: bne t4,OP_RMW,8f
nop
lw t1,Init_A1(a0)
lw t2,Init_A2(a0)
and t4,t3,MOD_MASK
bne t4,MOD_B,1f
nop
lbu t4,0(t0)
and t4,t1
or t4,t2
sb t4,0(t0)
b .next
nop
1: bne t4,MOD_H,1f
nop
lhu t4,0(t0)
and t4,t1
or t4,t2
sh t4,0(t0)
b .next
nop
1: bne t4,MOD_W,1f
nop
lw t4,0(t0)
and t4,t1
or t4,t2
sw t4,0(t0)
b .next
nop
1:
#if __mips64
ld t4,0(t0)
and t4,t1
or t4,t2
sd t4,0(t0)
b .next
nop
#else
b .fatal
nop
#endif
/*
* WAIT(ADDR,MASK,VAL)
*/
8: bne t4,OP_WAIT,8f
nop
lw t1,Init_A1(a0)
lw t2,Init_A2(a0)
and t4,t3,MOD_MASK
bne t4,MOD_B,1f
nop
3: lbu t4,0(t0)
and t4,t1
bne t4,t2,3b
nop
b .next
nop
1: bne t4,MOD_H,1f
nop
3: lhu t4,0(t0)
and t4,t1
bne t4,t2,3b
nop
b .next
nop
1: bne t4,MOD_W,1f
nop
3: lw t4,0(t0)
and t4,t1
bne t4,t2,3b
nop
b .next
nop
1:
#if __mips64
3: ld t4,0(t0)
and t4,t1
bne t4,t2,3b
nop
b .next
nop
#else
b .fatal
nop
#endif
.next:
addu a0, Init_Size /* Init_Size is 16 */
b reginit /* a big repeatation */
nop
8:
.fatal:
b .done
nop
bal stuck /* these two sentences seem been ignored */
nop
=====================
解释:
locate:
la s0, start
subu s0, ra, s0
and s0, 0xffff0000
此时,ra 中的地址值是前面 uncached 标号的地址,第二句作用是计算前面跳转时已运行过的代码的长度,最后一句把零头截掉。
这段代码是为了访问数据,因为这段汇编在Rom执行,而编译出来的数据段在 0x8002xxxx,
为了能够访问数据段的数据,需要进行一个地址的修正,s0 正是起到这种修正的目的。
li t0, SR_BOOT_EXC_VEC
mtc0 t0, COP_0_STATUS_REG
mtc0 zero, COP_0_CAUSE_REG
为保险起见,再清理一遍配置寄存器
.set noreorder
/* the varible bonito is register s4, BONITO_REG_BASE is 0x1fe00000 */
li bonito, PHYS_TO_UNCACHED(BONITO_REG_BASE)
bal 1f
nop /* now the value of ra is 0xbfc00xxx */
将 BONITO_REG_BASE 的物理地址值保存到 s4 寄存器
(通过映射到未经缓存的地址空间里,龙芯 CPU 访问外部空间,只能用映射后的地址),
然后跳转到后面1标号处执行。
1: move a0, ra /* now the value of ra is 0xbfc00xxx */
reginit: /* local name */
lw t3, Init_Op(a0)
lw t0, Init_A0(a0) // Init_A0 is 4
and t4, t3, OP_MASK // OP_MASK is 0x000000fc, to keep 4 bytes aligned
在1标号的地方,取跳转时压入的RA寄存器的值,然后通过寄存器相对寻址的方式,取得跳转指令后面保存的参数,并保存到t3, t0寄存器。
上句说的就是这些参数
/* bonito endianess */
BONITO_BIC(BONITO_BONPONCFG, BONITO_BONPONCFG_CPUBIGEND)
BONITO_BIC(BONITO_BONGENCFG, BONITO_BONGENCFG_BYTESWAP|BONITO_BONGENCFG_MSTRBYTESWAP)
BONITO_BIS(BONITO_BONPONCFG, BONITO_BONPONCFG_IS_ARBITER)
/*
* In certain situations it is possible for the Bonito ASIC
* to come up with the PCI registers uninitialised, so do them here
*/
BONITO_INIT(BONITO_PCICLASS,(PCI_CLASS_BRIDGE << PCI_CLASS_SHIFT) | (PCI_SUBCLASS_BRIDGE_HOST << PCI_SUBCLASS_SHIFT))
BONITO_INIT(BONITO_PCICMD, BONITO_PCICMD_PERR_CLR | BONITO_PCICMD_SERR_CLR | BONITO_PCICMD_MABORT_CLR | BONITO_PCICMD_MTABORT_CLR | BONITO_PCICMD_TABORT_CLR | BONITO_PCICMD_MPERR_CLR )
//BONITO_INIT(BONITO_PCILTIMER, 0)
BONITO_INIT(BONITO_PCILTIMER, 255)
BONITO_INIT(BONITO_PCIBASE0, 0)
BONITO_INIT(BONITO_PCIBASE1, 0)
BONITO_INIT(BONITO_PCIBASE2, 0)
BONITO_INIT(BONITO_PCIEXPRBASE, 0)
BONITO_INIT(BONITO_PCIINT, 0)
BONITO_BIS(BONITO_PCICMD, BONITO_PCICMD_PERRRESPEN)
BONITO_BIS(BONITO_PCICMD, PCI_COMMAND_IO_ENABLE|PCI_COMMAND_MEM_ENABLE|PCI_COMMAND_MASTER_ENABLE)
BONITO_BIC(BONITO_BONGENCFG, 0x80) #???1iobc
#BONITO_BIS(BONITO_BONGENCFG, BONITO_BONGENCFG_BUSERREN)
/* Set debug mode */
BONITO_BIS(BONITO_BONGENCFG, BONITO_BONGENCFG_DEBUGMODE)
/******** added to init southbridge*/
#ifdef VGA_NOTEBOOK_V2
ISA_BRMW_INIT(0,0x74,0xeb,0x0)
ISA_BRMW_INIT(0,0x75,0xff,0x20)
ISABWWR_INIT(4,0x48,0xb000)
ISABBWR_INIT(4,0x41,0x80)
RMW_INIT(MOD_W,(PCI_IO_SPACE+0xb04c),0xffffffdf,0x0)
#endif
// SouthBridge settings
/* Set the SMB base address */
ISABWWR_INIT(4, SMBUS_IO_BASE_ADDR, SMBUS_IO_BASE_VALUE | 0x1)
/* enable the host controller */
ISABHWR_INIT(4, SMBUS_HOST_CONFIG_ADDR, SMBUS_HOST_CONFIG_ENABLE_BIT)
/* enable the SMB IO ports */
ISABBWR_INIT(4, PCI_COMMAND_STATUS_REG, PCI_COMMAND_IO_ENABLE)
ISARD_INIT(CTC_PORT+PT_CONTROL)
/* program i8254 ISA refresh counter */
ISAWR_INIT(CTC_PORT+PT_CONTROL,PTCW_SC(PT_REFRESH)|PTCW_16B|PTCW_MODE(MODE_RG))
ISAWR_INIT(CTC_PORT+PT_REFRESH, ISAREFRESH & 0xff)
ISAWR_INIT(CTC_PORT+PT_REFRESH, ISAREFRESH >> 8)
EXIT_INIT(0)
这些宏实际上不是语句,看定义后就知道它们只是定义了一些数据参数,在ROM中占据了一定的长度。
/*
* EXIT(STATUS)
*/
bne t4, OP_EXIT, 8f // OP_EXIT is 0x00000000
nop
move v0, t0 // now v0 is the content of 4 bytes offset from 0xbfc000xx
b .done
nop
接着就运行
bne t4, OP_EXIT, 8f
这句了,在这里做是否初始化寄存器完成的判断,如果没有完成,就会跳到后面8标号处运行,然后经历一系列的设置(后面接着的那片代码)
DELAY(CYCLES)
READ(ADDR)
WRITE(ADDR,VAL) RMW(ADDR,AND,OR)
WAIT(ADDR,MASK,VAL)
后,直到 OP_EXIT 标志出现,才退出这个设置循环。看到前面有一行 EXIT_INIT(0),表示那个参数数据段结束了,它的宏定义如下:
#define EXIT_INIT(status) .word OP_EXIT, (status); .word 0,0
所以在最后一项的数据记录被读取后,总是能退出这个初始化循环的,接着就会跳到.done这个标号里运行。
不过,这段代码到底是要设置什么?由DELAY,READ,WRITE,RMW,WAIT 这些符号所标示的代码段实现其相应的功能没有?我还不清楚。
8
la s0, start
subu s0, ra, s0
and s0, 0xffff0000
这段代码是为了访问数据,因为这段汇编在Rom执行,而编译出来的数据段在0x8002xxxx,为了能够访问数据段的数据,需要进行一个地址的修正,s0这是起到这种修正的目的。
la v0, initmips
jalr v0
nop
从此代码便到内存中间去了,从这开始因为可以读写内存,所以有了栈,故可以用C的代码了,所以以后的程序便是C代码了.
4
接着看下面一段代码
.done:
bal superio_init /* initialize the southbridge config register */
nop
bal initserial /* initialize the output of serial port, after this step */
nop /* pmon can output some infomations from COM port*/
PRINTSTR("\r\nPMON2000 MIPS Initializing. Standby...\r\n")
/* begin to check some config registers on CP0 */
PRINTSTR("ERRORPC=")
mfc0 a0, COP_0_ERROR_PC
bal hexserial
nop
PRINTSTR(" CONFIG=")
mfc0 a0, COP_0_CONFIG
bal hexserial
nop
PRINTSTR("\r\n")
PRINTSTR(" PRID=")
mfc0 a0, COP_0_PRID
bal hexserial
nop
PRINTSTR("\r\n")
=====================
解释:
在这段程序里,主要做了两件大事情,一是初始化南桥芯片VIA686B,一是初始化串口输出。
初始化VIA686B是调用子函数superio_init 实现的。初始化串口是调用子函数initserial实现的。
为了尽快地从串口输出调试信息,所以要先初始化VIA686B芯片,才能输出信息出来。
由于 VIA686B芯片包括所有外面接口的功能,比如串口, PS2,USB,并口,还有软盘等等。
只要能从串口输出字符,就已经是成功的第一步了。
在嵌入式的软件开发中,调试软件是最难的,只能根据芯片的管脚电平,或者串口发些调信息出来。
使用管脚调试,最简单的办法,就是加一个指示灯,这也叫“点灯大法”。
只要串口能输出字符串后,使用串口调试就成为基本的方法了。
后面,输出三个CP0寄存器的值,第一个寄存器是出错信息,第二个寄存器是CP0配置信息,第三个寄存器是CP0处理器的ID信息。
=====================
5
下面一段代码从内存条上的SPD(eeprom)中读取内存参数,并且初始化内存窗口。这段代码放到另一篇文章中专门讲解吧。这里就不多说了。
/*
* Now determine DRAM configuration and size by
* reading the I2C EEROM (SPD) on the DIMMS (DDR)
*/
PRINTSTR("DIMM read\r\n")
/* only one memory slot, slave address is 10100001b */
li a1, 0x0
1:
li a0, 0xa1 /* a0: slave address, a1: reg index to read */
bal i2cread
nop
/* save a1 */
move t1, a1
/* print */
move a0, v0
bal hexserial
nop
PRINTSTR("\r\n")
/* restore a1 */
move a1,t1
addiu a1,a1,1
li v0, 0x20
bleu a1, v0, 1b /* repeat for 32 times */
nop
li msize, 0 /* msize is register s2 */
/* set some parameters for DDR333
rank number and DDR type field will be filled later
to check: fix TCAS?
*/
li sdCfg, 0x341043df /* sdCfg is register s6 */
/* read DIMM memory type (must be DDRAM) */
#if 0
li a0,0xa1
li a1,2
bal i2cread
nop
bne v0,7,.nodimm
nop
PRINTSTR("read memory type\r\n")
#endif
/* read DIMM number of rows */
li a0, 0xa1
li a1, 3
bal i2cread
nop
move a0, v0 // v0 is the return value register
subu v0, 12
move s1, v0 // save for later use
bgtu v0, 2, .nodimm // if v0 > 2 then jump to .nodimm
nop
PRINTSTR("read number of rows\r\n")
2: /* read DIMM number of cols */
li a0, 0xa1
li a1, 4
bal i2cread
nop
subu v0, 8 // v0 saved the return value
bgtu v0, 4, .nodimm
nop
// read and check ddr type, the combination of t1 and v0 represents a ddr type
move t1, s1
bne t1, 0, 10f
nop
bne v0, 2, 20f
nop
li v0, 0
b .ddrtype
nop
20: bne v0, 1, 21f
nop
li v0, 1
b .ddrtype
nop
21: bne v0, 0, 22f
nop
li v0, 2
b .ddrtype
nop
22: bne v0, 3, 33f
nop
li v0, 3
b .ddrtype
nop
10: bne t1, 1, 11f
nop
bne v0, 3, 20f
nop
li v0, 4
b .ddrtype
nop
20: bne v0, 2, 21f
nop
li v0, 5
b .ddrtype
nop
21: bne v0, 1, 22f
nop
li v0, 6
b .ddrtype
nop
22: bne v0, 4, 33f
nop
li v0, 7
b .ddrtype
nop
11: bne t1, 2, 33f
nop
bne v0, 4, 20f
nop
li v0, 8
b .ddrtype
nop
20: bne v0, 3, 21f
nop
li v0, 9
b .ddrtype
nop
21: bne v0, 2, 33f
nop
li v0, 10
b .ddrtype
nop
33: PRINTSTR("DDR type not supported!\r\n");
34: b 34b
nop
.ddrtype:
#bit 25:22 is DDR type field
sll v0, 22
and v0, 0x03c00000
or sdCfg, v0
/* read DIMM memory size per side */
li a0, 0xa1
li a1, 31
bal i2cread
nop
beqz v0,.nodimm
nop
sll tmpsize,v0,22 # multiply by 4M
PRINTSTR("read memory size per side\r\n")
2: /* read DIMM number of blocks-per-ddrram */
li a1,17
bal i2cread
nop
beq v0,2,2f
nop
bne v0,4,.nodimm
nop
PRINTSTR("read blocks per ddrram\r\n")
2: /* read DIMM number of sides (banks) */
li a1,5
bal i2cread
nop
beq v0,1,2f
nop
bne v0,2,.nodimm
nop
sll tmpsize,1 # msize *= 2
or sdCfg, 0x1<<27
PRINTSTR("read number of sides\r\n")
2: /* read DIMM width */
li a1,6
bal i2cread
nop
bleu v0,36,2f
nop
bgtu v0,72,.nodimm
nop
PRINTSTR("read width\r\n")
2: addu msize,tmpsize
b 2f
nop
.nodimm:
move dbg,a0 // dbg is s5
PRINTSTR ("\r\nNo DIMM in slot ")
move a0,dbg
bal hexserial
nop
PRINTSTR("\r\n")
move a0,dbg
#li msize,0x10000000
#li sdCfg,0x3d9043df #~133MHz
li msize,0x20000000
li sdCfg,0x3d5043df #~133MHz
2:
PRINTSTR("DIMM SIZE=")
move a0,msize
bal hexserial
nop
PRINTSTR("\r\n")
li t0, 0xbff00008
sd sdCfg, 0(t0)
nop
nop
/* (uint32_t *)0xbfe00040 = 0x80000000
* means only address below 1G will be sent to CPU
*/
lui t0, 0xbfe0
li t1, 0x80000000
sw t1, 0x40(t0)
nop
#### gx 2006-03-17: mode ####
#li t1,0x20
li t1,0x28
li t0, 0xbff00000
sd t1,0(t0)
nop
li t1,0x0
li t0, 0xbff00000
sd t1,0x30(t0)
nop
##fixed base address reg##
sd zero, 0x10(t0)
nop
lui t1,0x2000
sd t1,0x20(t0)
nop
li t1, 0x10000000
blt msize, t1, 1f
nop
####bigger than 256MB####
sd t1, 0x18(t0)
nop
move a0, msize
subu a0, t1
nop
nop
nop
sd a0, 0x28(t0)
nop
b 2f
1:
nop
nop
sd msize, 0x18(t0)
nop
nop
nop
sd zero, 0x28(t0)
nop
nop
nop
2:
PRINTSTR("sdcfg=");
move a0,sdCfg
bal hexserial
nop
PRINTSTR("\r\n");
PRINTSTR("msize=");
move a0,msize
bal hexserial
nop
PRINTSTR("\r\n")
skipdimm:
li t1,0 # accumulate pcimembasecfg settings
/* set bar0 mask and translation to point to SDRAM */
sub t0,msize,1
not t0
srl t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE0_MASK_SHIFT
and t0,BONITO_PCIMEMBASECFG_MEMBASE0_MASK
or t1,t0
li t0,0x00000000
srl t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE0_TRANS_SHIFT
and t0,BONITO_PCIMEMBASECFG_MEMBASE0_TRANS
or t1,t0
or t1,BONITO_PCIMEMBASECFG_MEMBASE0_CACHED
/* set bar1 to minimum size to conserve PCI space */
li t0, ~0
srl t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE1_MASK_SHIFT
and t0,BONITO_PCIMEMBASECFG_MEMBASE1_MASK
or t1,t0
li t0,0x00000000
srl t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE1_TRANS_SHIFT
and t0,BONITO_PCIMEMBASECFG_MEMBASE1_TRANS
or t1,t0
or t1,BONITO_PCIMEMBASECFG_MEMBASE1_CACHED
sw t1,BONITO_PCIMEMBASECFG(bonito)
/* enable configuration cycles now */
lw t0,BONITO_BONPONCFG(bonito)
and t0,~BONITO_BONPONCFG_CONFIG_DIS
sw t0,BONITO_BONPONCFG(bonito)
PRINTSTR("Init SDRAM Done!\r\n");
6
下面这段是缓存配置的代码
/*
* Reset and initialize caches to a known state.
*/
#define IndexStoreTagI 0x08
#define IndexStoreTagD 0x09
#define IndexStoreTagS 0x0b
#define IndexStoreTagT 0x0a
#define FillI 0x14
/*
* caches config register bits.
*/
#define CF_7_SE (1 << 3) /* Secondary cache enable */
#define CF_7_SC (1 << 31) /* Secondary cache not present */
#define CF_7_TE (1 << 12) /* Tertiary cache enable */
#define CF_7_TC (1 << 17) /* Tertiary cache not present */
#define CF_7_TS (3 << 20) /* Tertiary cache size */
#define CF_7_TS_AL 20 /* Shift to align */
#define NOP8 nop;nop;nop;nop;nop;nop;nop;nop
do_caches:
TTYDBG("Sizing caches...\r\n");
mfc0 t3, COP_0_CONFIG /* t3 = original config */
and t3, 0xffffeff0 /* Make sure coherency is OK */
and t3, ~(CF_7_TE|CF_7_SE|CF_7_TC|CF_7_SC) /* disable L2/L3 cache */
mtc0 t3, COP_0_CONFIG
li t2, 4096
srl t1, t3, 9
and t1, 3
sllv s3, t2, t1 /* s3 = I cache size */
#ifdef CONFIG_CACHE_64K_4WAY
sll s3,2
#endif
and t1, t3, 0x20
srl t1, t1, 1
addu s4, t1, 16 /* s4 = I cache line size */
srl t1, t3, 6
and t1, 3
sllv s5, t2, t1 /* s5 = D cache size */
#ifdef CONFIG_CACHE_64K_4WAY
sll s5,2
#endif
and t1, t3, 0x10
addu s6, t1, 16 /* s6 = D cache line size */
TTYDBG("Init caches...\r\n")
li s7, 0 /* no L2 cache */
li s8, 0 /* no L3 cache */
#if 0
mfc0 a0, COP_0_PRID
li a1, 0x6301
bne a0,a1,1f
nop
#endif
TTYDBG("godson2 caches found\r\n")
bal godson2_cache_init
nop
#####xuhua########open cp1
#if 1
mfc0 t0,COP_0_STATUS_REG
and t0,0xdbffffff
or t0,t0,0x24000000
mtc0 t0,COP_0_STATUS_REG
#endif
#################
/* close L2 cache */
li a0, 0xbfe00164
sw zero, 0(a0);
mfc0 a0,COP_0_CONFIG
and a0,a0,~((1<<12) | 3)
or a0,a0,2
mtc0 a0,COP_0_CONFIG
#ifdef DEBUG_LOCORE
TTYDBG("Init caches done, cfg = ")
mfc0 a0, COP_0_CONFIG
bal hexserial
nop
TTYDBG("\r\n\r\n")
#endif
7
下面这段代码是把PMON自身从ROM中拷贝到RAM中去
// copy self code segment
TTYDBG("Copy PMON to execute location...\r\n")
#ifdef DEBUG_LOCORE
TTYDBG(" start = 0x")
la a0, start
bal hexserial
nop
TTYDBG("\r\n s0 = 0x")
move a0, s0
bal hexserial
nop
TTYDBG("\r\n")
#endif
la a0, start
li a1, 0xbfc00000
la a2, _edata
or a0, 0xa0000000
or a2, 0xa0000000
subu t1, a2, a0
srl t1, t1, 2
move t0, a0
move t1, a1
move t2, a2
/* copy text section */
1: and t3,t0,0x0000ffff
bnez t3,2f
nop
move a0,t0
bal hexserial
nop
li a0,'\r'
bal tgt_putchar
nop
2: lw t3, 0(t1)
nop
sw t3, 0(t0)
addu t0, 4
addu t1, 4
bne t2, t0, 1b
nop
PRINTSTR("\ncopy text section done.\r\n")
/* Clear BSS */
la a0, _edata
la a2, _end
2: sw zero, 0(a0)
bne a2, a0, 2b
addu a0, 4
TTYDBG("Copy PMON to execute location done.\r\n")
@@@
下面这段代码从汇编世界跳到C世界中去了。
TTYDBG("sp=");
move a0, sp
bal hexserial
nop
#if 1
mfc0 a0,COP_0_CONFIG
and a0,a0,0xfffffff8
or a0,a0,0x3
mtc0 a0,COP_0_CONFIG
#endif
li a0, 4096*1024
sw a0, CpuTertiaryCacheSize /* Set L3 cache size */
move a0,msize
srl a0,20
/* pass pointer to kseg1 tgt_putchar */
la a1, tgt_putchar
addu a1,a1,s0 // la s0,start
// subu s0,ra,s0 ??? ra is the returning address
// and s0,0xffff0000 ??? now what does s0 mean?
la a2, stringserial
addu a2,a2,s0
la v0, initmips // further ENTRY of PMON
jalr v0
nop
+++++++++++++++++++++++++++++++++++++++++++++++
start.S函数详解
+++++++++++++++++++++++++++++++++++++++++++++++
本文讲解那些在 "start.S 文件详解" 一文中没有讲到的各函数的内容,放在一边讲是为了方便读者与"start.S 文件详解" 一文比对着看,那篇文章实在太长了。
CPU_TLBClear
/*
* Clear the TLB. Normally called from start.S.
*/
#if __mips64
#define MTC0 dmtc0
#else
#define MTC0 mtc0
#endif
LEAF(CPU_TLBClear)
li a3, 0 # First TLB index.
li a2, PG_SIZE_4K // here it is 0x00000000
MTC0 a2, COP_0_TLB_PG_MASK // Pagemask's bits 24~13 are 0, then it represents 4K page
1:
MTC0 zero, COP_0_TLB_HI # Clear entry high.
MTC0 zero, COP_0_TLB_LO0 # Clear entry low0.
MTC0 zero, COP_0_TLB_LO1 # Clear entry low1.
mtc0 a3, COP_0_TLB_INDEX # Set the index.
addiu a3, 1
li a2, 64
nop
nop
tlbwi # Write the TLB at index
bne a3, a2, 1b // do a repeat body, clear all TLB entries, that means it will execute 64 times
nop
jr ra
nop
END(CPU_TLBClear)
CPU_TLBInit
/*
* Set up the TLB. Normally called from start.S.
* All operation is due to the structure of EntryHi, EntryLo0, EntryLo1, so understand them first
*/
LEAF(CPU_TLBInit)
li a3, 0 # First TLB index.
li a2, PG_SIZE_16M // here it is 0x01ffe000, its bits of 24~13 are all 1
MTC0 a2, COP_0_TLB_PG_MASK # All pages are 16Mb.
1:
and a2, a0, PG_SVPN // PG_SVPN is 0xfffff000, a0's initial value is 0xc0000000
MTC0 a2, COP_0_TLB_HI // Set up entry high
move a2, a0
srl a2, a0, PG_SHIFT // PG_SHIFT is 6. shift 6 bits right on a0
and a2, a2, PG_FRAME // PG_FRAME is 0x3fffffc0, the most two bits are 0, because we only need 1G memory mapping
ori a2, PG_IOPAGE // PG_IOPAGE is 0x00000017
MTC0 a2, COP_0_TLB_LO0 # Set up entry low0.
addu a2, (0x01000000 >> PG_SHIFT) // 0x00020000, 128K interval???
MTC0 a2, COP_0_TLB_LO1 # Set up entry low1.
mtc0 a3, COP_0_TLB_INDEX # Set the index.
addiu a3, 1
li a2, 0x02000000 // 32M
subu a1, a2 // a1's initial value is 0x40000000, that is 1G memory
nop
tlbwi # Write the TLB
bgtz a1, 1b // if a1>0 then goto 1b, totally it will execute 32 times
addu a0, a2 // Step address 32Mb.
// the first entry is EntryHi: 0xc0000000, EntryLo0: 0x03000017, EntryLo1: 0x03020017, maybe bug?
jr ra
nop
END(CPU_TLBInit)
stringserial
LEAF(stringserial)
move a2, ra
addu a1, a0, s0 // a0 is the address of a string
lbu a0, 0(a1) // load one byte by one byte
1:
beqz a0, 2f // meet the char '\0', then go back
nop
bal tgt_putchar
addiu a1, 1 // a1 is the pointer to one char in the string
b 1b
lbu a0, 0(a1)
2:
j a2
nop
END(stringserial)
outstring
LEAF(outstring)
move a2, ra
move a1, a0
lbu a0, 0(a1)
1:
beqz a0, 2f
nop
bal tgt_putchar
addiu a1, 1
b 1b
lbu a0, 0(a1)
2:
j a2
nop
END(outstring)
hexserial
LEAF(hexserial)
move a2, ra
move a1, a0
li a3, 7
1:
rol a0, a1, 4
move a1, a0
and a0, 0xf
la v0, hexchar
addu v0, s0
addu v0, a0
bal tgt_putchar
lbu a0, 0(v0)
bnez a3, 1b
addu a3, -1
j a2
nop
END(hexserial)
tgt_putchar
LEAF(tgt_putchar)
la v0, COM1_BASE_ADDR // here it is 0xbfd003f8
1:
lbu v1, NSREG(NS16550_LSR)(v0)
and v1, LSR_TXRDY
beqz v1, 1b
nop
sb a0, NSREG(NS16550_DATA)(v0) // store byte, store the char wanted to print to the virtual address byte, then the serial device can process it correctly
#ifdef HAVE_NB_SERIAL
move v1, v0
la v0, COM3_BASE_ADDR
bne v0, v1, 1b
nop
#endif
j ra
nop
END(tgt_putchar)
initserial
LEAF(initserial)
#ifdef HAVE_NB_SERIAL
la v0, COM3_BASE_ADDR
1:
li v1, FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_4
sb v1, NSREG(NS16550_FIFO)(v0)
li v1, CFCR_DLAB
sb v1, NSREG(NS16550_CFCR)(v0)
li v1, NS16550HZ/(16*CONS_BAUD)
sb v1, NSREG(NS16550_DATA)(v0)
srl v1, 8
sb v1, NSREG(NS16550_IER)(v0)
li v1, CFCR_8BITS
sb v1, NSREG(NS16550_CFCR)(v0)
li v1, MCR_DTR|MCR_RTS
sb v1, NSREG(NS16550_MCR)(v0)
li v1, 0x0
sb v1, NSREG(NS16550_IER)(v0)
#endif
la v0, COM1_BASE_ADDR
1:
li v1, FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_4
sb v1, NSREG(NS16550_FIFO)(v0)
li v1, CFCR_DLAB
sb v1, NSREG(NS16550_CFCR)(v0)
li v1, NS16550HZ/2/(16*CONS_BAUD)
sb v1, NSREG(NS16550_DATA)(v0)
srl v1, 8
sb v1, NSREG(NS16550_IER)(v0)
li v1, CFCR_8BITS
sb v1, NSREG(NS16550_CFCR)(v0)
li v1, MCR_DTR|MCR_RTS
sb v1, NSREG(NS16550_MCR)(v0)
li v1, 0x0
sb v1, NSREG(NS16550_IER)(v0)
nop
j ra
nop
END(initserial)
godson2_cache_init
LEAF(godson2_cache_init)
####part 2####
cache_detect_2way:
mfc0 t4, CP0_CONFIG
andi t5, t4, 0x0e00
srl t5, t5, 9
andi t6, t4, 0x01c0
srl t6, t6, 6
addiu t6, t6, 11
addiu t5, t5, 11
addiu t4, $0, 1
sllv t6, t4, t6
srl t6,1
sllv t5, t4, t5
srl t5,1
addiu t7, $0, 2
####part 3####
lui a0, 0x8000
addu a1, $0, t5
addu a2, $0, t6
cache_init_d2way:
#a0=0x80000000, a1=icache_size, a2=dcache_size
#a3, v0 and v1 used as local registers
mtc0 $0, CP0_TAGHI
addu v0, $0, a0
addu v1, a0, a2
1: slt a3, v0, v1
beq a3, $0, 1f
nop
mtc0 $0, CP0_TAGLO
cache Index_Store_Tag_D, 0x0(v0)
mtc0 $0, CP0_TAGLO
cache Index_Store_Tag_D, 0x1(v0)
mtc0 $0, CP0_TAGLO
cache Index_Store_Tag_D, 0x2(v0)
mtc0 $0, CP0_TAGLO
cache Index_Store_Tag_D, 0x3(v0)
beq $0, $0, 1b
addiu v0, v0, 0x20
#if 1
1:
cache_init_l24way:
mtc0 $0, CP0_TAGHI
addu v0, $0, a0
addu v1, a0, 128*1024
1: slt a3, v0, v1
beq a3, $0, 1f
nop
mtc0 $0, CP0_TAGLO
cache Index_Store_Tag_S, 0x0(v0)
mtc0 $0, CP0_TAGLO
cache Index_Store_Tag_S, 0x1(v0)
mtc0 $0, CP0_TAGLO
cache Index_Store_Tag_S, 0x2(v0)
mtc0 $0, CP0_TAGLO
cache Index_Store_Tag_S, 0x3(v0)
beq $0, $0, 1b
addiu v0, v0, 0x20
1:
cache_flush_4way:
addu v0, $0, a0
addu v1, a0, 128*1024
1: slt a3, v0, v1
beq a3, $0, 1f
nop
cache Index_Writeback_Inv_S, 0x0(v0)
cache Index_Writeback_Inv_S, 0x1(v0)
cache Index_Writeback_Inv_S, 0x2(v0)
cache Index_Writeback_Inv_S, 0x3(v0)
beq $0, $0, 1b
addiu v0, v0, 0x20
# endif
1:
cache_flush_i2way:
addu v0, $0, a0
addu v1, a0, a1
1: slt a3, v0, v1
beq a3, $0, 1f
nop
cache Index_Invalidate_I, 0x0(v0)
# cache Index_Invalidate_I, 0x1(v0)
# cache Index_Invalidate_I, 0x2(v0)
# cache Index_Invalidate_I, 0x3(v0)
beq $0, $0, 1b
addiu v0, v0, 0x20
1:
cache_flush_d2way:
addu v0, $0, a0
addu v1, a0, a2
1: slt a3, v0, v1
beq a3, $0, 1f
nop
cache Index_Writeback_Inv_D, 0x0(v0)
cache Index_Writeback_Inv_D, 0x1(v0)
cache Index_Writeback_Inv_D, 0x2(v0)
cache Index_Writeback_Inv_D, 0x3(v0)
beq $0, $0, 1b
addiu v0, v0, 0x20
1:
cache_init_finish:
nop
jr ra
nop
cache_init_panic:
TTYDBG("cache init panic\r\n");
1: b 1b
nop
.end godson2_cache_init
superio_init
LEAF(superio_init)
PCICONF_WRITEW(PCI_IDSEL_VIA686B,0,4,7);
/*positive decode*/
PCICONF_ORB(PCI_IDSEL_VIA686B,0,0x81,0x80);
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x83,0x80|0x1| 0x8);
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x85,3);
/* enable RTC/PS2/KBC */
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x5A,7);
SUPERIO_WR(0xe2,E2_S2|E2_S1|E2_EPP|E2_FLOPPY) /*enable serial and floppy */
SUPERIO_WR(0xe3,0x3f0>>2) /*floppy base address*/
SUPERIO_WR(0xe6,0x378>>2) /*parallel port*/
SUPERIO_WR(0xe7,0x3f8>>2) /*set serial port1 base addr 0x3f8*/
SUPERIO_WR(0xe8,0x2f8>>2) /*set serial port2 base addr 0x2f8*/
SUPERIO_WR(0xee,0xc0) /* both ports on high speed*/
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x85,1)
jr ra
nop
END(superio_init)
+++++++++++++++++++++++++++++++++++++++++
PMON 中读取 DDR 内存 SPD 信息的代码
+++++++++++++++++++++++++++++++++++++++++
本文讲述用I2C协议从内存条上的SPD(eeprom)中读取内存参数。
SPD 信息简述
SPD(eeprom)中,定义了很多参数,这里给出一个简单的说明。
第0字节 表示厂商使用的字节数。
第1字节 表示EERPOM存储容量。
第2字节 表示内存类型。
第3字节 表示行地址位数。
第4字节 表示列地址位数。
第5字节 表示排数。
第6字节 表示数据宽度(低字节)。
第7字节 表示数据宽度(高字节)。
第8字节 表示信号电平。
第9字节 表示SDRAM最高时钟频率。
第10字节 表示SDRAM访问时间。
第11字节 表示配置类型。
第12字节 表示刷行率/类型。
第13字节 表示最小SDRAM颗粒数据宽度。
第16字节 表示支持突发传输长度。
第17字节 表示逻辑BANK数。
第18字节 表示CAS延时。
第23字节 表示SDRM时钟。是2的最大指数倍。
第24字节 表示SDRAM访问时间。
第34字节 表示输入数据建立时间。
第35字节 表示输入数据保持时间。
第62字节 表示SPD版本号。
其它的字节,就要参考SPD文档了。后面一大段程序就是实现了读取这些参数,然后根据这些参数来设置龙芯内存的SDRAM寄存器。
PMON 中对内存SPD读取及处理的方法
读取spd
● (offset 3)读取行地址数目
● (offset 4)读取列地址数目
● 根据行列地址数目判断ddrtype
● (offset 31)读取每个片选的容量
– 用该值初始化内存大小tmpsize
● (offset 17)读取芯片bank,一般为4
● (offset 5)读取片选数(相当于module数)
– 按照片选数,计算内存大小tmpsize
– 按照读出的片选数设置sdcfg寄存器
● (offset 6)读取位宽
● 其中sdcfg的初始值是按照ddr333设置的
(注释表明,没有详细验证)
初始化内存控制器
● 经过spd的读取过程我们获得sdcfg配置和
内存大小memsize
● 设置CPU内部sdcfg寄存器(0x1ff00008)
● 按照memsize设置内存窗口
● 1ff0 0000偏移开始的四个寄存器
– 0x10/0x20(mem window base)
– 0x18/0x28(mem window size)
– e.g. 128MB内存
● 0x10=0,0x18=128MB,0x20=512MB,0x28=0
– e.g. 512MB内存
● 0x10=0,0x18=256MB,0x20=512MB,0x28=256MB
– 有很多的nop,调试试验的结果
代码讲解
/*
* Now determine DRAM configuration and size by
* reading the I2C EEROM (SPD) on the DIMMS (DDR)
*/
PRINTSTR("DIMM read\r\n")
/* only one memory slot, slave address is 10100001b */
li a1, 0x0
1:
li a0, 0xa1 /* a0: slave address, a1: reg index to read */
bal i2cread
nop
/* save a1 */
move t1, a1
/* print */
move a0, v0
bal hexserial
nop
PRINTSTR("\r\n")
/* restore a1 */
move a1,t1
addiu a1,a1,1
li v0, 0x20
bleu a1, v0, 1b /* repeat for 32 times */
nop
li msize, 0 /* msize is register s2 */
/* set some parameters for DDR333
rank number and DDR type field will be filled later
to check: fix TCAS?
*/
li sdCfg, 0x341043df /* sdCfg is register s6 */
/* read DIMM memory type (must be DDRAM) */
#if 0
li a0,0xa1
li a1,2
bal i2cread
nop
bne v0,7,.nodimm
nop
PRINTSTR("read memory type\r\n")
#endif
/* read DIMM number of rows */
li a0, 0xa1
li a1, 3
bal i2cread
nop
move a0, v0 // v0 is the return value register
subu v0, 12
move s1, v0 // save for later use
bgtu v0, 2, .nodimm // if v0 > 2 then jump to .nodimm
nop
PRINTSTR("read number of rows\r\n")
2: /* read DIMM number of cols */
li a0, 0xa1
li a1, 4
bal i2cread
nop
subu v0, 8 // v0 saved the return value
bgtu v0, 4, .nodimm
nop
// read and check ddr type, the combination of t1 and v0 represents a ddr type
move t1, s1
bne t1, 0, 10f
nop
bne v0, 2, 20f
nop
li v0, 0
b .ddrtype
nop
20: bne v0, 1, 21f
nop
li v0, 1
b .ddrtype
nop
21: bne v0, 0, 22f
nop
li v0, 2
b .ddrtype
nop
22: bne v0, 3, 33f
nop
li v0, 3
b .ddrtype
nop
10: bne t1, 1, 11f
nop
bne v0, 3, 20f
nop
li v0, 4
b .ddrtype
nop
20: bne v0, 2, 21f
nop
li v0, 5
b .ddrtype
nop
21: bne v0, 1, 22f
nop
li v0, 6
b .ddrtype
nop
22: bne v0, 4, 33f
nop
li v0, 7
b .ddrtype
nop
11: bne t1, 2, 33f
nop
bne v0, 4, 20f
nop
li v0, 8
b .ddrtype
nop
20: bne v0, 3, 21f
nop
li v0, 9
b .ddrtype
nop
21: bne v0, 2, 33f
nop
li v0, 10
b .ddrtype
nop
33: PRINTSTR("DDR type not supported!\r\n");
34: b 34b
nop
.ddrtype:
#bit 25:22 is DDR type field
sll v0, 22
and v0, 0x03c00000
or sdCfg, v0
/* read DIMM memory size per side */
li a0, 0xa1
li a1, 31
bal i2cread
nop
beqz v0,.nodimm
nop
sll tmpsize,v0,22 # multiply by 4M
PRINTSTR("read memory size per side\r\n")
2: /* read DIMM number of blocks-per-ddrram */
li a1,17
bal i2cread
nop
beq v0,2,2f
nop
bne v0,4,.nodimm
nop
PRINTSTR("read blocks per ddrram\r\n")
2: /* read DIMM number of sides (banks) */
li a1,5
bal i2cread
nop
beq v0,1,2f
nop
bne v0,2,.nodimm
nop
sll tmpsize,1 # msize *= 2
or sdCfg, 0x1<<27
PRINTSTR("read number of sides\r\n")
2: /* read DIMM width */
li a1,6
bal i2cread
nop
bleu v0,36,2f
nop
bgtu v0,72,.nodimm
nop
PRINTSTR("read width\r\n")
2: addu msize,tmpsize
b 2f
nop
.nodimm:
move dbg,a0 // dbg is s5
PRINTSTR ("\r\nNo DIMM in slot ")
move a0,dbg
bal hexserial
nop
PRINTSTR("\r\n")
move a0,dbg
#li msize,0x10000000
#li sdCfg,0x3d9043df #~133MHz
li msize,0x20000000
li sdCfg,0x3d5043df #~133MHz
2:
PRINTSTR("DIMM SIZE=")
move a0,msize
bal hexserial
nop
PRINTSTR("\r\n")
li t0, 0xbff00008
sd sdCfg, 0(t0)
nop
nop
/* (uint32_t *)0xbfe00040 = 0x80000000
* means only address below 1G will be sent to CPU
*/
lui t0, 0xbfe0
li t1, 0x80000000
sw t1, 0x40(t0)
nop
#### gx 2006-03-17: mode ####
#li t1,0x20
li t1,0x28
li t0, 0xbff00000
sd t1,0(t0)
nop
li t1,0x0
li t0, 0xbff00000
sd t1,0x30(t0)
nop
##fixed base address reg##
sd zero, 0x10(t0)
nop
lui t1,0x2000
sd t1,0x20(t0)
nop
li t1, 0x10000000
blt msize, t1, 1f
nop
####bigger than 256MB####
sd t1, 0x18(t0)
nop
move a0, msize
subu a0, t1
nop
nop
nop
sd a0, 0x28(t0)
nop
b 2f
1:
nop
nop
sd msize, 0x18(t0)
nop
nop
nop
sd zero, 0x28(t0)
nop
nop
nop
2:
PRINTSTR("sdcfg=");
move a0,sdCfg
bal hexserial
nop
PRINTSTR("\r\n");
PRINTSTR("msize=");
move a0,msize
bal hexserial
nop
PRINTSTR("\r\n")
skipdimm:
li t1,0 # accumulate pcimembasecfg settings
/* set bar0 mask and translation to point to SDRAM */
sub t0,msize,1
not t0
srl t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE0_MASK_SHIFT
and t0,BONITO_PCIMEMBASECFG_MEMBASE0_MASK
or t1,t0
li t0,0x00000000
srl t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE0_TRANS_SHIFT
and t0,BONITO_PCIMEMBASECFG_MEMBASE0_TRANS
or t1,t0
or t1,BONITO_PCIMEMBASECFG_MEMBASE0_CACHED
/* set bar1 to minimum size to conserve PCI space */
li t0, ~0
srl t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE1_MASK_SHIFT
and t0,BONITO_PCIMEMBASECFG_MEMBASE1_MASK
or t1,t0
li t0,0x00000000
srl t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE1_TRANS_SHIFT
and t0,BONITO_PCIMEMBASECFG_MEMBASE1_TRANS
or t1,t0
or t1,BONITO_PCIMEMBASECFG_MEMBASE1_CACHED
sw t1,BONITO_PCIMEMBASECFG(bonito)
/* enable configuration cycles now */
lw t0,BONITO_BONPONCFG(bonito)
and t0,~BONITO_BONPONCFG_CONFIG_DIS
sw t0,BONITO_BONPONCFG(bonito)
PRINTSTR("Init SDRAM Done!\r\n");
=====================
解释:
/* only one memory slot, slave address is 10100001b */
li a1, 0x0
1:
li a0, 0xa1 /* a0: slave address, a1: reg index to read */
bal i2cread
nop
上面这段代码,把 0 设置给 a1,然后把 0xa1 设置给 a0,然后就调用 I2C 的子函数来读取数据。a0 和 a1 寄存器是 i2cread 这个函数的两个参数。
i2cread 函数的实现
下面来看 i2cread 函数的内容
/* a0: slave address
a1: reg off
*/
LEAF(i2cread)
/* set device address */
li v0, 0xbfd00000 + SMBUS_HOST_ADDRESS
li a0, 0xa1
sb a0, 0(v0);
/* store register offset */
li v0, 0xbfd00000 + SMBUS_HOST_COMMAND
sb a1, 0(v0);
/* read byte data protocol */
li v0, 0x08
li v1, 0xbfd00000 + SMBUS_HOST_CONTROL
sb v0, 0(v1);
/* make sure SMB host ready to start, important!--zfx */
li v1, 0xbfd00000 + SMBUS_HOST_STATUS
lbu v0, 0(v1)
andi v0,v0, 0x1f
beqz v0,1f
nop
sb v0, 0(v1)
lbu v0, 0(v1) #flush the write
1:
/* start */
li v1, 0xbfd00000 + SMBUS_HOST_CONTROL
lbu v0, 0(v1)
ori v0, v0, 0x40
sb v0, 0(v1);
/* wait */
li v1, 0xbfd00000 + SMBUS_HOST_STATUS
li a1, 0x1000
1:
#if 1
/* delay */
li a0, 0x1000
2:
bnez a0,2b
addiu a0, -1
#endif
addiu a1, -1
beqz a1, 1f
nop
lbu v0, 0(v1)
andi v0, SMBUS_HOST_STATUS_BUSY
bnez v0, 1b #IDEL ?
nop
1:
li v1, 0xbfd00000 + SMBUS_HOST_STATUS
lbu v0, 0(v1)
andi v0,v0, 0x1f
beqz v0,1f
nop
sb v0, 0(v1) #reset
lbu v0, 0(v1) #flush the write
1:
li v1, 0xbfd00000 + SMBUS_HOST_DATA0
lbu v0, 0(v1)
jr ra
nop
END(i2cread)
=====================
解释:
/* set device address */
li v0, 0xbfd00000 + SMBUS_HOST_ADDRESS
li a0, 0xa1
sb a0, 0(v0);
上面代码是输出从设备的地址。
/* store register offset */
li v0, 0xbfd00000 + SMBUS_HOST_COMMAND
sb a1, 0(v0);
上面代码是输出从设备的寄存器偏移量。
/* read byte data protocol */
li v0, 0x08
li v1, 0xbfd00000 + SMBUS_HOST_CONTROL
sb v0, 0(v1);
/* make sure SMB host ready to start, important!--zfx */
li v1, 0xbfd00000 + SMBUS_HOST_STATUS
lbu v0, 0(v1)
andi v0,v0, 0x1f
beqz v0,1f
nop
sb v0, 0(v1)
lbu v0, 0(v1) #flush the write
上面代码是查看数据总线是否准备好数据。
1:
/* start */
li v1, 0xbfd00000 + SMBUS_HOST_CONTROL
lbu v0, 0(v1)
ori v0, v0, 0x40
sb v0, 0(v1);
/* wait */
li v1, 0xbfd00000 + SMBUS_HOST_STATUS
li a1, 0x1000
1:
#if 1
/* delay */
li a0, 0x1000
2:
bnez a0,2b
addiu a0, -1
#endif
addiu a1, -1
beqz a1, 1f
nop
lbu v0, 0(v1)
andi v0, SMBUS_HOST_STATUS_BUSY
bnez v0, 1b #IDEL ?
nop
上面代码是查看总线是否在忙状态。
1:
li v1, 0xbfd00000 + SMBUS_HOST_STATUS
lbu v0, 0(v1)
andi v0,v0, 0x1f
beqz v0,1f
nop
sb v0, 0(v1) #reset
lbu v0, 0(v1) #flush the write
1:
li v1, 0xbfd00000 + SMBUS_HOST_DATA0
lbu v0, 0(v1)
jr ra
nop
上面代码是已经把命令成功发送出去,然后成功地读取回来数据,保存在v0寄存里。
通过上面的子函数,就可以通过I2C总线去读取内存条上的EEPROM参数,以便后面进行内存初始化。
在这里第一次读取是第一个字节。
+++++++++++++++++++++++++++++++++++++++++
end
+++++++++++++++++++++++++++++++++++++++++
~/bnn:75> /bin/mips64-elf-objdump -d Hello.o
Hello.o: file format elf32-bigmips
Disassembly of section .text:
/* main Function */
0000000000000000 :
/*create a stack frame by moving the stack pointer 8
*bytes down and meantime update the sp value
*/
0: 27bdfff8 addiu $sp,$sp,-8
/* Save the return address to the current sp position.*/
4: afbf0000 sw $ra,0($sp)
8: 0c000000 jal 0
/* nop is for the delay slot */
c: 00000000 nop
/* Fill the argument a0 with the value 1 */
10: 24040001 li $a0,1
/* Jump the addFunc */
14: 0c00000a jal 28
/* NOTE HERE: Why we fill the second argument
*behind the addFunc function call?
* This is all about the "-O1" compilation optimizaiton.
* With mips architecture, the instruciton after jump
* will also be fetched into the pipline and get
* exectuted. Therefore, we can promise that the
* second argument will be filled with the value of
* integer 2.
*/
18: 24050002 li $a1,2
/*Load the return address from the stack pointer
* Note here that the result v0 contains the result of
* addFunc function call
*/
1c: 8fbf0000 lw $ra,0($sp)
/* Return */
20: 03e00008 jr $ra
/* Restore the stack frame */
24: 27bd0008 addiu $sp,$sp,8
/* addFunc Function */
0000000000000028 :
/* Create a stack frame by allocating 16 bytes or 4
* words size
*/
28: 27bdfff0 addiu $sp,$sp,-16
/* Save the return address into the stack with 8 bytes
* offset. Please note that compiler does not save the
* ra to 0($sp).
*Think of why, in contrast of the previous PowerPC
* EABI convention
*/
2c: afbf0008 sw $ra,8($sp)
/* We save the s1 reg. value into the stack
* because we will use s1 in this function
* Note that the 4,5,6,7($sp) positions will then
* be occupied by this 32 bits size register
*/
30: afb10004 sw $s1,4($sp)
/* Withe same reason, save s0 reg. */
34: afb00000 sw $s0,0($sp)
/* Retrieve the argument 0 into s0 reg. */
38: 0080802d move $s0,$a0
/* Retrieve the argument 1 into s1 reg. */
3c: 00a0882d move $s1,$a1
/* Call the subFunc with a0 with 5 */
40: 0c000019 jal 64
/* In the delay slot, we load the 5 into argument a0 reg
*for subFunc call.
*/
44: 24040005 li $a0,5
/* s0 = s0+s1; note that s0 and s1 holds the values of
* x,y, respectively
*/
48: 02118021 addu $s0,$s0,$s1
/* v0 = s0+v0; v0 holds the return results of subFunc
*call; And we let v0 hold the final results
*/
4c: 02021021 addu $v0,$s0,$v0
/*Retrieve the ra value from stack */
50: 8fbf0008 lw $ra,8($sp)
/*!!!!restore the s1 reg. value */
54: 8fb10004 lw $s1,4($sp)
/*!!!! restore the s0 reg. value */
58: 8fb00000 lw $s0,0($sp)
/* Return back to main func */
5c: 03e00008 jr $ra
/* Update/restore the stack pointer/frame */
60: 27bd0010 addiu $sp,$sp,16
/* subFunc Function */
0000000000000064 :
/* return back to addFunc function */
64: 03e00008 jr $ra
/* Taking advantage of the mips delay slot, filling the
* result reg v0 by simply assigning the v0 as the value
*of a0. This is a bug from my c source
* codes--"value--". I should write my codes
* like "--value", instead.
68: 0080102d move $v0,$a0
[ Last edited by zzz19760225 on 2017-12-25 at 13:51 ]作者: zzz19760225 时间: 2017-10-2 12:48 1作者: zzz19760225 时间: 2017-10-2 12:49 zhengruohuang:
toddler C 语言操作系统 https://github.com/zhengruohuang/toddler