# 1 "arch/s3c2410/head.S" # 1 "" # 1 "" # 1 "arch/s3c2410/head.S" # 35 "arch/s3c2410/head.S" # 1 "include/config.h" 1 # 14 "include/config.h" # 1 "include/autoconf.h" 1 # 15 "include/config.h" 2 # 36 "arch/s3c2410/head.S" 2 # 1 "include/linkage.h" 1 # 37 "arch/s3c2410/head.S" 2 # 1 "include/machine.h" 1 # 19 "include/machine.h" # 1 "include/platform/smdk2410.h" 1
# 1 "include/s3c2410.h" 1 # 22 "include/s3c2410.h" # 1 "include/hardware.h" 1 # 23 "include/s3c2410.h" 2 # 1 "include/bitfield.h" 1 # 24 "include/s3c2410.h" 2 # 3 "include/platform/smdk2410.h" 2
# 1 "include/sizes.h" 1 # 8 "include/platform/smdk2410.h" 2 # 74 "include/platform/smdk2410.h" # 1 "include/architecture.h" 1 # 75 "include/platform/smdk2410.h" 2 # 20 "include/machine.h" 2 # 38 "arch/s3c2410/head.S" 2
@ Start of executable code
宏定义展开 .globl _start; .align 0; _start: .globl ResetEntryPoint; .align 0; ResetEntryPoint:
下面是装载中断向量表,ARM规定,在起始必须有8条跳转指令,你可以用b 标号也可以用ldr pc 标号。这样的8条规则的标志被arm定义为bootloader的识别标志,检测到这样的标志后,就可以从该位置启动。这样的做法是因为开始的时候不一定有bootloader,必须有一种识别机制,如果识别到bootloader,那么就从bootloader启动。 @ @ Exception vector table (physical address = 0x00000000) @
@ 0x00: Reset b Reset
@ 0x04: Undefined instruction exception UndefEntryPoint: b HandleUndef
@ 0x08: Software interrupt exception SWIEntryPoint: b HandleSWI
@ 0x0c: Prefetch Abort (Instruction Fetch Memory Abort) PrefetchAbortEnteryPoint: b HandlePrefetchAbort
@ 0x10: Data Access Memory Abort DataAbortEntryPoint: b HandleDataAbort
@ 0x14: Not used NotUsedEntryPoint: b HandleNotUsed
@ 0x18: IRQ(Interrupt Request) exception IRQEntryPoint: b HandleIRQ
@ 0x1c: FIQ(Fast Interrupt Request) exception FIQEntryPoint: b HandleFIQ
下面是固定位置存放环境变量 @ @ VIVI magics @
@ 0x20: magic number so we can verify that we only put .long 0 @ 0x24: .long 0 @ 0x28: where this vivi was linked, so we can put it in memory in the right place .long _start //_start用来指定链接后的起始装载地址装载到内存中的地址 @ 0x2C: this contains the platform, cpu and machine id .long ((1 << 24) | (6 << 16) | 193) @ 0x30: vivi capabilities .long 0
@ 0x34: b SleepRamProc
@ @ Start VIVI head @ Reset: //上电后cpu会从0x0地址读取指令执行,也就是b Reset
@ disable watch dog timer mov r1, #0x53000000 mov r2, #0x0 str r2, [r1] # 121 "arch/s3c2410/head.S" @ disable all interrupts mov r1, #0x4A000000 mov r2, #0xffffffff str r2, [r1, #0x08] //0x4A000008为中断屏蔽寄存器,将里面赋全1,表示屏蔽这32个中断源 ldr r2, =0x7ff str r2, [r1, #0x1C] //0x4A00001C为中断子屏蔽寄存器,里面低11位也用来表示屏蔽掉这11个中断源
@ initialise system clocks mov r1, #0x4C000000 mvn r2, #0xff000000 //MVN指令可完成从另一个寄存器、被移位的寄存器、或将一个立即数加载到目的寄存器。与MOV指令不同之处是在传送之前按位被取反了,即把一个被取反的值传送到目的寄存器中。也就是r2=0x00ffffff str r2, [r1, #0x00] //我们可以在程序开头启动MPLL,在设置MPLL的几个寄存器后,需要等待一段 时间(Lock Time), MPLL的输出才稳定。在这段时间(Lock Time)内,FCLK停振,CPU停止工作。Lock Time的长短由寄存器LOCKTIME设定,Lock Time之后,MPLL输出正常,CPU工作在新的FCLK下,前面说过,MPLL启动后需要等待一段时间(Lock Time),使得其输出稳定。 位[23:12]用于UPLL,位[11:0]用于MPLL。本实验使用确省值0x00ffffff。
@ldr r2, mpll_50mhz @str r2, [r1, #0x04]
@ 1:2:4 mov r1, #0x4C000000 mov r2, #0x3 str r2, [r1, #0x14] //0x4C000014为分频寄存器,用于设置FCLK、HCLK、PCLK三者的比例 bit[2] — — HDIVN1,若为1,则 bit[1:0] 必 须 设 为 0b00 , 此 时FCLK:HCLK:PCLK=1:1/4:1/4;若为0,三者比例由bit[1:0]确定 bit[1]——HDIVN,0:HCLK=FCLK;1:HCLK=FCLK/2 bit[0]——PDIVN,0:PCLK=HCLK;1:PCLK=HCLK/2 本实验设为0x03,则FCLK:HCLK:PCLK=1:1/2:1/4
mrc p15, 0, r1, c1, c0, 0 @ read ctrl register orr r1, r1, #0xc0000000 @ Asynchronous mcr p15, 0, r1, c1, c0, 0 @ write ctrl register 上面这三句代码的意思是切换模式:If HDIVN = 1, the CPU bus mode has to be changed from the fast bus mode to the asynchronous bus mode using following instructions: MMU_SetAsyncBusMode mrc p15, 0, r0, c1, c0, 0 orr r0, r0, #R1_nF:OR:R1_iA mcr p15,0, r0, c1, c0, 0 其中的“R1_nF:OR:R1_iA”等于0xc0000000。意思就是说,当HDIVN = 1时,CPU bus mode需要从原来的“fast bus mode”改为“asynchronous bus mode”。
@ now, CPU clock is 200 Mhz mov r1, #0x4C000000 ldr r2, mpll_200mhz str r2, [r1, #0x04] //0x4C000004为MPLLCON寄存器, 对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为SDIV。有如下计算公式: MPLL(FCLK) = (m * Fin)/(p * 2^s) 其中: m = MDIV + 8, p = PDIV + 2 对于本开发板,Fin = 12MHz,MPLLCON设为0x5c0040,可以计算出FCLK=200MHz,再由CLKDIVN的设置可知:HCLK=100MHz,PCLK=50MHz。 # 164 "arch/s3c2410/head.S" bl memsetup
@ Check if this is a wake-up from sleep ldr r1, PMST_ADDR //0x560000B4为GSTATUS2寄存器,里面[0:2]三位有效 ldr r0, [r1] //将该寄存器中的值取出来保存到r0中 tst r0, #((1 << 1)) //测试r0的第一位。这位是Power_OFF reset. The reset after the wakeup from Power_OFF mode.The setting is cleared by writing "1" to this bit.TST指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的与运算,并根据运算结果更新CPSR中条件标志位的值。 bne WakeupStart
@ All LED on 这里就需要对GPIO口进行控制 mov r1, #0x56000000 add r1, r1, #0x50 //0x56000050是GPFCON用来配置port F端口 ldr r2,=0x55aa str r2, [r1, #0x0] //设置为0101010110101010因为每两位用来控制一个引脚,也就是将GPF4-GPF7设置为输出,将GPF0-GPF3设置为中断 mov r2, #0xff str r2, [r1, #0x8] //0x56000058为GPFUP为port F的上拉寄存器,全设置为1表示禁止上拉功能 mov r2, #0x00 str r2, [r1, #0x4] //0x56000054是GPFDAT,总共8位有效,每位控制一个引脚,主要是将GPF4-GPF7数据位全设置为0而这四个引脚是用来控制板子上4个LED,置0则表示亮。 # 230 "arch/s3c2410/head.S" @ set GPIO for UART mov r1, #0x56000000 add r1, r1, #0x70 //0x56000070为GPHCON 用来配置port H 而port H主要就是来控制UART的各个引脚如:UART中接收和发送端口RXDn和TXDn,当然还有自动流控制模式的nRTS0和nCTS0端口。 ldr r2, gpio_con_uart //我们可以看到 gpio_con_uart: .long vGPHCON gpio_up_uart: .long vGPHUP 而在platform中的smdk2410.h中定义了这两个值#define vGPHCON 0x0016faaa 表示GPHCON控制11个引脚,如GPH2若设置为10则表示TXD0.类似,具体的查看数据手册 #define vGPHUP 0x000007ff //同样将这11位的引脚上拉禁止 str r2, [r1, #0x0] ldr r2, gpio_up_uart str r2, [r1, #0x8] //上面也是来配置串口所用到的GPIO口,因为串口的输入输出口都是利用到GPIO bl InitUART # 259 "arch/s3c2410/head.S" bl copy_myself
@ jump to ram ldr r1, =on_the_ram add pc, r1, #0 nop nop 1: b 1b @ infinite loop
on_the_ram: # 279 "arch/s3c2410/head.S" @ get read to call C functions开始调用C函数之前就需要将一些参数准备好,如堆栈要准备好函数调用时需要进出栈 ldr sp, DW_STACK_START @ setup stack pointer mov fp, #0 @ no previous frame, so fp=0 mov a2, #0 @ set argv to NULL
bl main @ call main
mov pc, #0x00000000 @ otherwise, reboot
@ @ End VIVI head @
下面是子例程 @ @ Wake-up codes @
WakeupStart: @ Clear sleep reset bit ldr r0, PMST_ADDR mov r1, #(1 << 1) str r1, [r0]
@ Release the SDRAM signal protections ldr r0, PMCTL1_ADDR ldr r1, [r0] bic r1, r1, #((1 << 19) | (1 << 18) | (1 << 17)) str r1, [r0]
@ Go... ldr r0, PMSR0_ADDR @ read a return address ldr r1, [r0] mov pc, r1 nop nop 1: b 1b @ infinite loop
SleepRamProc: @ SDRAM is in the self-refresh mode */ ldr r0, REFR_ADDR ldr r1, [r0] orr r1, r1, #(1 << 22) str r1, [r0]
@ wait until SDRAM into self-refresh mov r1, #16 1: subs r1, r1, #1 bne 1b
@ Set the SDRAM singal protections ldr r0, PMCTL1_ADDR ldr r1, [r0] orr r1, r1, #((1 << 19) | (1 << 18) | (1 << 17)) str r1, [r0]
ldr r0, PMCTL0_ADDR ldr r1, [r0] orr r1, r1, #(1 << 3) str r1, [r0] 1: b 1b # 379 "arch/s3c2410/head.S" .globl memsetup; .align 0; memsetup: //对ENTRY(memsetup)宏的展开 @ initialise the static memory //同样在这里是对内存控制中用到的13个寄存器进行初始化 @ set memory control registers mov r1, #0x48000000 adrl r2, mem_cfg_val add r3, r1, #52 1: ldr r4, [r2], #4 str r4, [r1], #4 cmp r1, r3 bne 1b
mov pc, lr
@ @ copy_myself: copy vivi to ram @下面的将vivi从flash中搬移到sdram中来执行,很重要 copy_myself: mov r10, lr //将返回地址保存到r10,为了执行完后返回到主程序
@ reset NAND mov r1, #0x4E000000 ldr r2, =0xf830 @ initial value 初始化NFCONF寄存器,至于为什么设置为0xf830前面在NAND里面讲过 str r2, [r1, #0x00] ldr r2, [r1, #0x00] bic r2, r2, #0x800 @ enable chip 也就是相当于NFCONF &= ~0x800 将第15为置为1使能NAND FLASH str r2, [r1, #0x00] mov r2, #0xff @ RESET command strb r2, [r1, #0x04] //向NFCMD寄存器中置0xff也就是reset命令 mov r3, #0 @ wait //下面几句是一个延时程序用来等待几秒,等到NAND 准备好 1: add r3, r3, #0x1 cmp r3, #0xa blt 1b 2: ldr r2, [r1, #0x10] @ wait ready tst r2, #0x1 //检查NFSTAT寄存器的第0位是否为1也就是是否准备好 beq 2b //没有准备好则继续等待 ldr r2, [r1, #0x00] orr r2, r2, #0x800 @ disable chip 相当于NFCONF |= 0x800 禁止掉NAND FLASH等到要使用FLASH时再使能 str r2, [r1, #0x00]
@ get read to call C functions (for nand_read()) ldr sp, DW_STACK_START @ setup stack pointer //每次需要从汇编调到C函数时 都需要设置好堆栈 mov fp, #0 @ no previous frame, so fp=0
@ copy vivi to RAM 之前都是一些初始化,下面才开始利用C函数来真正开始拷贝 ldr r0, =(0x30000000 + 0x04000000 - 0x00100000)//DRAM_BASE + DRAM_SIZE - VIVI_RAM_SIZE,为什么这里不是将vivi拷贝到sdram的起始地址而是拷贝到64MB的sdram的最后1M的区域,可能是这里的sdram采用高端模式,将映象从高地址向低地址存放 mov r1, #0x0 //r1则是vivi的起始地址,也就是flash上的0x0地址 mov r2, #0x20000 //上面三句都是用来为调用nand_read_ll函数准备好参数,r2表示拷贝大小 bl nand_read_ll //这个c函数在arch/s3c2410/nand_read.c中 nand_read_ll就不具体分析了在nand里面有讲过
tst r0, #0x0 //为什么要比较r0与上0,等于0的话 则去执行ok_nand_read,在这里r0是nand_read_ll函数的返回值,拷贝成功则返回0,所以这就是为什么将r0和0比较 beq ok_nand_read //ok_nand_read子程序用来比较拷贝到sdram最后1MB的vivi和原始的存放在flash上的vivi是否相同,检查拷贝是否成功,vivi在sdram中的位置也就是离0x34000000地址前1MB的位置也就是0x33f00000 # 441 "arch/s3c2410/head.S" ok_nand_read:
@ verify mov r0, #0 //r0这里为flash上vivi的起始地址 ldr r1, =0x33f00000 //r1这里为拷贝到sdram上vivi的起始地址 mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes //要比较多少个字节数 go_next: ldr r3, [r0], #4 //将r0对应地址的值取出来保存到r3中,然后r0自加4个字节 ldr r4, [r1], #4 teq r3, r4 //测试r3和r4是否相等 bne notmatch //若不相等,则跳到notmarch处 subs r2, r2, #4 //将比较总字节数减去4个字节,已经比较了4个字节 beq done_nand_read //若r2为0,则表示已经比较完毕,跳转到done_nand_read处 bne go_next //r2若还不等于0则继续取值比较 notmatch: # 469 "arch/s3c2410/head.S" 1: b 1b done_nand_read:
mov pc, r10 //比较完了 就退出子程序,返回主程序执行
@ clear memory @ r0: start address @ r1: length mem_clear: mov r2, #0 mov r3, r2 mov r4, r2 mov r5, r2 mov r6, r2 mov r7, r2 mov r8, r2 mov r9, r2
clear_loop: stmia r0!,{r2-r9} //将r2-r9也就是0赋值给从r0为内存起始地址的连续的8*4个字节中 subs r1, r1, #(8 * 4) //每次清除32个字节 bne clear_loop
mov pc, lr # 613 "arch/s3c2410/head.S" @ Initialize UART @ @ r0 = number of UART port InitUART: //这里也不细讲了,在UART章节中已经详细的讲解了每个寄存器的设置 ldr r1, SerBase mov r2, #0x0 str r2, [r1, #0x08] str r2, [r1, #0x0C] mov r2, #0x3 str r2, [r1, #0x00] ldr r2, =0x245 str r2, [r1, #0x04]
mov r2, #((50000000 / (115200 * 16)) - 1) str r2, [r1, #0x28]
mov r3, #100 mov r2, #0x0 1: sub r3, r3, #0x1 tst r2, r3 bne 1b # 653 "arch/s3c2410/head.S" mov pc, lr
@ @ Exception handling functions @ HandleUndef: 1: b 1b @ infinite loop
HandleSWI: 1: b 1b @ infinite loop
HandlePrefetchAbort: 1: b 1b @ infinite loop
HandleDataAbort: 1: b 1b @ infinite loop
HandleIRQ: 1: b 1b @ infinite loop
HandleFIQ: 1: b 1b @ infinite loop
HandleNotUsed: 1: b 1b @ infinite loop
@ @ Low Level Debug @ # 838 "arch/s3c2410/head.S" @ @ Data Area @ @ Memory configuration values .align 4 mem_cfg_val: //这些变量都是内存控制寄存器的初始值,因为寄存器比较多,所以将初始值制作成表的形式,然后分别读表来初始化各个寄存器 .long 0x22111110 .long 0x00000700 .long 0x00000700 .long 0x00000700 .long 0x00000700 .long 0x00000700 .long 0x00000700 .long 0x00018005 .long 0x00018005 .long 0x008e0459 .long 0xb2 .long 0x30 .long 0x30
@ Processor clock values .align 4 clock_locktime: .long 0x00ffffff mpll_50mhz: .long ((0x5c << 12) | (0x4 << 4) | (0x2))
mpll_200mhz: .long ((0x5c << 12) | (0x4 << 4) | (0x0)) clock_clkcon: .long 0x0000fff8 clock_clkdivn: .long 0x3 @ initial values for serial uart_ulcon: .long 0x3 uart_ucon: .long 0x245 uart_ufcon: .long 0x0 uart_umcon: .long 0x0 @ inital values for GPIO gpio_con_uart: .long 0x0016faaa gpio_up_uart: .long 0x000007ff
.align 2 DW_STACK_START: .word (((((0x30000000 + 0x04000000 - 0x00100000) - 0x00100000) - 0x00004000) -(0x00004000 + 0x00004000 +
0x00004000)) - 0x00008000)+0x00008000 -4 # 922 "arch/s3c2410/head.S" .align 4 SerBase:
.long 0x50000000 # 935 "arch/s3c2410/head.S" .align 4 PMCTL0_ADDR: .long 0x4c00000c PMCTL1_ADDR: .long 0x56000080 PMST_ADDR: .long 0x560000B4 PMSR0_ADDR: .long 0x560000B8 REFR_ADDR: .long 0x48000024 [root@lqm vivi_myboard]#
|