1. 原理分析
S3C2440A有两个锁相环(PLLs):一个(MPLL)用于FCLK、HCLK和PCLK,另一个(UPLL)专用于USB。其中FCLK用于CPU,HCLK用于AHB总线,PCLK用于APB总线。
AHB(Advanced High performance Bus)高级高性能总线,用于高性能模块的连接。
APB(Advanced Peripheral Bus)外围总线,用于低速外设的连接。
将上图简化之后,我们可以粗略的得到下图。
由这个简化的图我们可以得知,晶振的频率经过MPLL生成的频率便是FCLK所用频率,经过UPLL生成的频率就是USB所用频率。如果将FCLK再经过HDIV和PDIV进行分频,那么将分别得到HCLK和PCLK。
那么从系统上电(复位)到按照设定的频率工作,整个的时序和工作流程是怎么样的呢?在芯片手册中有这样一个图,解释了这个流程。
系统上电后,晶振(crystal oscillator 简称OSC)开始工作。等到OSC稳定之后,nRESET才会拉至高电平,之后PLL会按照配置进行工作,锁定时间过后,可以立即将FCLK配置为锁相环输出。
在有了上述理论基础,假如我们想将FCLK:HCLK:PCLK=400M:100M:50M,晶振频率为12M,我们就可以将配置分为如下几步
-
设置LOCKTIME(0x4C000000)
-
设置HDIV和PDIV的分频值,FCLK:HCLK:PCLK=400M:100M:50M,所以可以得出HDIV=4、PDIV=8,这两个值可以通过CLKDIVN(0x4C000014)寄存器来配置。
根据上表,HDIVN我们应配置为0b10,PDIVN应配置为0b1,所以该寄存器应配置为0x5 -
设置CPU工作于异步模式
这段话的意思大致就是,如果HDIVN不是0(我们上面设置了,它的确不是0了),CPU的模式必须要从快速总线模式更改为异步总线模式。怎么改,芯片手册也提供了汇编代码,其中有疑问的就是#R1_nf:OR:R1_iA
这个是什么意思?
其实orr r0,r0,#R1_nf:OR:R1_iA
这句话的意思就是将30位(nf bit) 31位(ia bit) 置1,所以这句话也等同于orr r0,r0,#0xc0000000
-
设置MPLL中的P、M、S,使FCLK=400M
手册里面提供了公式,现在我们可以将手册给出的值带入公式验证一下。
M D I V = 92 MDIV = 92 MDIV=92 P D I V = 1 PDIV = 1 PDIV=1 S D I V = 1 SDIV = 1 SDIV=1
M p l l = 2 ∗ ( 92 + 8 ) ∗ 12 M ( 1 + 2 ) ∗ 2 1 = 400 M Mpll = \frac{2 \ast (92+8) \ast 12M}{(1+2) \ast 2 ^ 1} = 400M Mpll=(1+2)∗212∗(92+8)∗12M=400M可以通过MPLLCON(0x4C000004)来设置它们3个的值。
2. 程序编写
.text
.global _start
_start:
/* 关闭看门狗 */
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
ldr r0, =0x4C000000
ldr r1, =0xFFFFFFFF
str r1, [r0]
/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */
ldr r0, =0x4C000014
ldr r1, =0x5
str r1, [r0]
/* 设置CPU工作于异步模式 */
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)
* m = MDIV+8 = 92+8=100
* p = PDIV+2 = 1+2 = 3
* s = SDIV = 1
* FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
*/
ldr r0, =0x4C000004
ldr r1, =(92<<12)|(1<<4)|(1<<0)
str r1, [r0]
/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
* 然后CPU工作于新的频率FCLK
*/
/* 设置内存: sp 栈 */
/* 分辨是nor/nand启动
* 写0到0地址, 再读出来
* 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
* 否则就是nor启动
*/
mov r1, #0
ldr r0, [r1] /* 读出原来的值备份 */
str r1, [r1] /* 0->[0] */
ldr r2, [r1] /* r2=[0] */
cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */
ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
moveq sp, #4096 /* nand启动 */
streq r0, [r1] /* 恢复原来的值 */
bl main
halt:
b halt