一、时钟树分析

从上图的左上角我们可以得知,S3C2440A支持两种外部时钟源输入,一种是通过接在XTIpll与XTOpll上的外部振荡电路(一般由晶振为核心组成),还有一种是通过将现成的时钟频率通过EXTCLK输入。
时钟输入芯片后,送到MPLL(主锁相环)与UPLL(USB锁相环)进行倍频。MPLL出来的时钟信号称之为FCLK,一般直接用于给CPU核心提供时钟信号;UPLL出来的时钟信号称为UCLK,一般用于给USB提供时钟信号。FCLK经过HDIVN与PDIVN分频后会产生HCLK与PCLK,前者一般用于给AHB高速总线与高速外设提供时钟信号,比如USB模块、NAND FLASH控制器等;后者一般用于APB外设总线,给低速外设提供信号,比如各种串行通讯模块、外部GPIO等。
以下摘录一段S3C2440A数据手册中对各个时钟信号的说明
USB host interface and USB device interface needs 48Mhz clock. In the S3C2440A, the USB dedicated PLL (UPLL) generates 48Mhz for USB. UCLK does not fed until the PLL (UPLL) is configured.
FCLK is used by ARM920T. HCLK is used for AHB bus, which is used by the ARM920T, the memory controller, the interrupt controller, the LCD controller, the DMA and USB host block. PCLK is used for APB bus, which is used by the peripherals such as WDT, IIS, I
《S3C2440A 32-BIT CMOS MICROCONTROLLER USER’S MANUAL》 Revision 1.3
二、时钟树的初始化
在进行芯片上电前,需要通过修改OM3和OM2引脚去配置所使用的时钟源,引脚和对应的设置关系如下表所示:
然后就开始对寄存器进行设置了!总体的设置流程是:1、设置锁相环生效延时时间;2、设置PCLK与HCLK的分频系数;3、配置CPU进入异步模式以使得CPU可以使用FCLK作为时钟源; 4、设置UCLK与FCLK;
首先需要设置的是LOCK TIME COUNT REGISTER (LOCKTIME),此寄存器用来作为锁相环的生效延时计数,因为锁相环设置后到产生稳定的时钟信号中间会需要一定时间,通过设置这个寄存器来告诉芯片锁相环稳定所需要花费的时间,一般我们保持此寄存器的默认值即可。

然后是通过CLOCK DIVIDER CONTROL (CLKDIVN) REGISTER设置HCLK与PCLK的分频,一定要先设置这两者的分频寄存器然后再开始设置MPLL和UPLL,这样可以防止AHB与APB总线上的时钟频率过快。寄存器说明如下:

其中HDIVN设置值对应的实际分频系数还取决于CAMDVN寄存器的第8位和第9位,上电时,这两位默认为0。
在接着设置UCLK与FCLK之前,我们要先设置CPU总线进入异步工作模式,这样才可以在HDIVN不为0时使用FCLK作为CPU的工作时钟源,官方手册提供了CPU进入异步模式的汇编代码,代码如下:
/* 设置CPU工作于异步模式 */
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
最后是设置UPLL与MPLL来设置UCLK与MCLK。根据手册上的指示,在设置FCLK前要先设置UCLK,所以接下来先配置UPLLCON寄存器,然后配置MPLLCON寄存器,两个寄存器说明如下:

通过寄存器位端MDIV、PDIV、SDIV的组合可以计算得到想要的频率,不过官方为了方便使用提供了一组常用频率对应的值,想自己手动计算的也可以通过手册提供的公式自行计算,官方参考值如下:

三、时钟树配置参考汇编代码
.text.global _start
_start:
/* 关闭看门狗 */
ldr r0, =0x53000000
ldr r1, =0str r1, [r0]
/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
ldr r0, =0x4C000000
ldr r1, =0xFFFFFFFFstr r1, [r0]
/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */
ldr r0, =0x4C000014
ldr r1, =0x5str 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