![]() |
中国DOS联盟-- 联合DOS 推动DOS 发展DOS --联盟域名:www.cn-dos.net 论坛域名:www.cn-dos.net/forum |
| 游客 | 登录 | 注册 | 会员 | 搜索 | 中国DOS联盟 |
|
中国DOS联盟论坛 现在时间是 2026-08-25 07:43 |
共 47,812 主题排行 / 349,910 发帖 / 今日 1 篇 / 48,262 会员排行 |
| 网络日志(Blog) » 3龙芯系列电脑硬件和相关信息搜集 <丙申羊年20160206> |
| 可打印版本 47,550 / 120 |
| 第76楼 zzz19760225 | 发表于 2017-06-14 02:02 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
PMON PMON详解 :专注嵌入式dustLiYang
http://blog.csdn.net/sdustliyang/article/details/7295803 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 | | `-- usb | |-- 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("\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 +++++++++++++++++++++++++++++++++++++++++ [ Last edited by zzz19760225 on 2017-11-2 at 00:21 ] |
|
| 第77楼 zzz19760225 | 发表于 2017-10-02 12:44 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 第78楼 zzz19760225 | 发表于 2017-10-02 12:44 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 第79楼 zzz19760225 | 发表于 2017-10-02 12:45 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 第80楼 zzz19760225 | 发表于 2017-10-02 12:46 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 第81楼 zzz19760225 | 发表于 2017-10-02 12:46 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 第82楼 zzz19760225 | 发表于 2017-10-02 12:46 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 第83楼 zzz19760225 | 发表于 2017-10-02 12:46 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 第84楼 zzz19760225 | 发表于 2017-10-02 12:47 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 第85楼 zzz19760225 | 发表于 2017-10-02 12:47 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 第86楼 zzz19760225 | 发表于 2017-10-02 12:47 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 第87楼 zzz19760225 | 发表于 2017-10-02 12:47 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 第88楼 zzz19760225 | 发表于 2017-10-02 12:47 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 第89楼 zzz19760225 | 发表于 2017-10-02 12:47 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 第90楼 zzz19760225 | 发表于 2017-10-02 12:47 |
| 超级版主 发帖 2,020 积分 3,673 | |
|
1
|
|
| 上一页 1 … 3 4 5 6 7 8 9 下一页 |
|
[ 联系联盟系统管理团队 -
中国DOS联盟 -
标准版 ] Sponsored by ifanr Inc | © 2001–2023 |