后,需要等待一段 时间(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 \
16
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 \指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的与运算,并根据运算结果更新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 \ @ 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 \ bl copy_myself
@ jump to ram ldr r1, =on_the_ram add pc, r1, #0
17
nop nop
1: b 1b @ infinite loop
on_the_ram:
# 279 \
@ 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:
18
@ 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 \
.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,为了执行完后返回到主程序
19
@ 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 \
20