lophyxp 发表于 2017-1-21 18:34:28

【PMON 研究】【08】start.S 文件详解

本帖最后由 lophyxp 于 2017-1-21 18:35 编辑

心映真的空间
苦心励志 技术强国
让我们面对现实 让我们忠于理想
欢迎来到唐刚的首页

start.S 文件详解
龙芯相关 » PMON 研究 » 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)#½ûÖ¹iobc

    #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)#½ûÖ¹iobc

    #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 */
    lia1, 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
    balhexserial
    nop

    PRINTSTR("\r\n")   

      /* restore a1 */
    movea1,t1
    addiu a1,a1,1
    li   v0, 0x20
    bleua1, 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   
    orsdCfg, 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
    #limsize,0x10000000
    #li    sdCfg,0x3d9043df    #~133MHz
    limsize,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\ns0 = 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 */
      laa1, 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?

      laa2, stringserial
    addu a2,a2,s0

    la    v0, initmips      // further ENTRY of PMON
    jalr    v0
    nop


页: [1]
查看完整版本: 【PMON 研究】【08】start.S 文件详解