||
第一章 初始化时钟
注:本章的内容来自韦东山老师的《嵌入式Linux系统开发完全手册_基于4412_上册》
在芯片手册里,“时钟管理单元(Clock Management Unit)”的简称为CMU。CMU_XXX表示“XXX模块内的CMU”,比如CMU_CPU等。
第一节 Exynos 4412的时钟体系对于PC来说,CPU、内存、主板、声卡、显卡等等,这些功能部件由不同的芯片组成,在实体上是相互独立的。在嵌入式系统里,一块芯片往往集成了多种功能,比如Exynos 4412上面既有CPU,还有音频/视频接口、LCD接口、GPS等模块。这类芯片被称为SoC,即System on Chip,译为芯片级系统或片上系统。
不同的模块往往工作在不同的频率下,在一个芯片上采用单时钟设计基本上是不可能实现的,在SoC设计中采取多时钟域设计。4412的时钟域有5个,如图6.1所示。
图6.1 Exynos 4412时钟域
这5个时钟域名如下(下文中的BLK表示block,模块):
① CPU_BLK:
内含Cortex-A9 MPCore处理器、L2 cache控制器、CoreSight(调试用)。CMU_CPU用于给这些部件产生时钟。
② DMC_BLK:
内含DRAM内存控制器(DMC)、安装子系统(Security sub system)、通用中断控制器(Generic Interrupt Controller,GIC)。CMU_DMC用于给这些部件产生时钟。
③ LEFTBUS_BLK和RIGHTBUS_BLK:
它们是全局的数据总线,用于在DRAM和其他子模块之间传输数据。
④其他BLK:在图6.1中,用画笔圈起来的模块。
CMU_TOP用于给这些模块产生时钟。
从原理图上可知,4412开发板外接24MHz的晶振;但是4412的CPU频率可达1.4GHz。可以想象,一定有硬件部件,把24MHZ的频率提升为1.4GHZ,这个部件被称为PLL。4412内部其他部件也要工作于一定频率,比UART、DDR里等,也应该有PLL把24MHZ的频率提高后供给它使用。
4412有3个初始时钟源:
① XRTCXTI引脚:接32KHz的晶振,用于实时时钟(RTC)。
② XXTI引脚:接12M~50MHz的晶振,用于向系统提供时钟。也可以不接。
③ XUSBXTI引脚:接24MHz的晶振,用于向系统提供时钟。
在UT4412BV03的开发板中,XRTCXTI上接有32KHz晶振,系统时钟来源是XUSBXTI引脚上接的24MHz晶振。
4412有4个PLL:APLL、MPLL、EPLL和VPLL;2个PHY:USB PHY和HDMI PHY(PHY:物理层,一般指与外部信号接口的芯片):
① APLL:用于CPU_BLK;作为MPLL的补充,它也可以给DMC_BLK、LEFTBUS_BLK、RIGHTBUS_BLK和CMU_TOP提供时钟。
② MPLL:用于DMC_BLK、LEFTBUS_BLK、RIGHTBUS_BLK和CMU_TOP
③ EPLL:主要给音频模块提供时钟
④ VPLL:主要给视频系统提供54MHz时钟,给G3D(3D图形加速器)提供时钟。
⑤ USB PHY:给USB子系统提供30MHz和48MHz时钟。
⑥ HDMI PHY:产生54MHz时钟。
第二节 Exynos 4412中设置PLL的方法
图6.2 APLL时钟流程图
以图6.2为例,里面涉及3个概念:
① MUX:多路复用,即从多个输入源中选择一个
② PLL:把低频率的输入时钟提高后输出
③ DIV:分频器,把高频率的输入时钟降频后输出
以APLL为例,它的时钟来源可以是XXTI引脚上接的晶振,也可以是XUSBXTI引脚上接的晶振,通过图6.2中左边的MUX来选择,这个MUX的输出被称为FINPLL。
通过设置APLL的寄存器(根据公式选择参数值),可以把FINPLL提高为某个频率输出,假设为1.4GHz,在图上它被命名为FOUTAPLL。
继续往右看图,里面有多个DIV,可以设置对应的寄存器把频率降下来。CPU可以工作于1.4GHz,但是其他模块不能工作于这么高的频率,所以要把频率降下来。
设置PLL的流程如下:
①设置PLL的P、M、S值,这是根据期望得到的频率用公式计算出来的
②设置PLL的其他控制参数
③使能PLL
④ PLL会等待一段时间使得时钟稳定
⑤设置MUX,选择PLL所输出的时钟
简单地说,就是:先设置,再启动,后使用。
第5点意味着:如果当前正使用该PLL,那么先设置MUX改用其他时钟源或禁止使用此PLL,设置完PLL后再设置MUX改回原来所使用的PLL时钟。
4个PLL的寄存器功能是相似的,这些寄存器可以分为6类,见表6.1。
表6.1 PLL的寄存器分类
寄存器偏移地址 | 功能 |
0x000-0x1FF | PLL的锁定时间和控制 |
0x200-0x4FF | MUX的选择、输出使能、状态 |
0x500-0x6FF | 分频器设置、状态 |
0x700-0x8FF | 保留 |
0x900-0x9FF | 整个IP模块或某个功能的时钟使能 |
0xA00-0xAFF | CLKOUT相关设置,供测试时钟用 |
以APLL为例:
(1) APLL_LOCK(地址: 0x10044000)
表6.2 APLL_LOCK的寄存器格式
功能 | 位 | 说明 |
PLL锁定时间 | [15:0] | 用于指定PLL的锁定时间 |
设置APLL的参数并使能它后,APLL并不能立刻输出稳定的时钟,它需要经历一个锁定的时间(lock time)。APL的最大锁定时间是:(270 x PDIV)个周期。所以APLL_LOCK设置为(270 x PDIV)就可以了。
PDIV在后面的APLL_CON1寄存器中介绍。
(2) APLL_CON0(地址: 0x10044100)
表6.3 APLL_CON0的寄存器格式
功能 | 位 | 说明 |
使能 | [31] | 用于使能PLL 0- 禁止 1- 使能 |
锁定状态 | [29] | 只读位,用于显示PLL的锁定状态 0-未锁定 1-已锁定(表示PLL输出已稳定) |
FSEL | [27] | 用于选择输出哪种频率供测试用 |
MDIV | [15:16] | M值 |
PDIV | [13:8] | P值 |
SDIV | [2:0] | S值 |
根据M、P、S的值,可以算出APLL的输出时钟:
FOUT = MDIV x FIN / (PDIV x 2 ^ SDIV)
M、P、S的值不能乱取,需要满足一些限制条件(请参考芯片手册),芯片手册里给出了推荐的取值。
(3) APLL_CON1(地址: 0x10044104)
表6.4 APLL_CON1的寄存器格式
功能 | 位 | 说明 |
BYPASS | [22] | 1-使用BYPASS模式,APLL直接输出FIN 0-APLL输出提升频率后的时钟 |
该寄存器用于设置BYPASS模式,即APLL是直接输出FIN时钟,还是提升频率后再输出时钟;也用于设置AFC(自动频率控制)功能,暂时无需理会。
该寄存器取默认值即可。表6.4中省略了其他位的描述。
(4) CLK_SRC_CPU(地址: 0x10044200)
表6.5 CLK_SRC_CPU的寄存器格式
功能 | 位 | 说明 |
MUX_MPLL_USER_SEL_C | [24] | 控制MUXMPLL 0- FINPLL 1- FOUTMPLL |
MUX_HPM_SEL | [20] | 控制MUXHPM 0-MOUTAPLL 1-SCLKMPLL |
MUX_CORE_SEL | [16] | 控制MUXCORE 0-MOUTAPLL 1-SCLKMPLL |
MUX_APLL_SEL | [0] | 控制MUXAPLL 0-FINPLL 1-MOUTAPLLFOUT |
参考“图6.2 APLL时钟流程图”:
① BIT[0]控制第1个MUX(即MUXAPLL),用于选择是使用FIN还是APLL的输出时钟,这个输出被称为MOUTAPLL。
② BIT[16]控制第2个MUX(即MUXCORE),用于选择MOUTAPLL还是SCLKMPLL。其中SCLKMPLL由下面的MUXMPLL控制。
③ BIT[24]控制第3个MUX(即MUXMPLL),用于选择FINPLL还是FOUTMPLL,这个输出被称为SCLKMPLL。其中,FOUTMPLL来自MPLL的输出。
④ BIT[20]控制第4个MUX(即MUXHPM),用于选择MOUTAPLL还是SCLKMPLL。
(5) CLK_MUX_STAT_CPU(地址: 0x10044400):用于读取CLK_SRC_CPU寄存器里所设置的MUX状态。
(6) CLK_DIV_CPU0(地址: 0x10044500),CLK_DIV_CPU1(地址: 0x10044504)
表6.6 CLK_DIV_CPU0的寄存器格式
功能 | 位 | 说明 |
CORE2_RATIO | [30:28] | ARMCLK = DOUTCORE/(CORE2_RATIO + 1) |
MUX_HPM_SEL | [20] | 控制MUXHPM 0-MOUTAPLL 1-SCLKMPLL |
APLL_RATIO | [26:24] | SCLKAPLL = MOUTAPLL/(APLL_RATIO + 1) |
PCLK_DBG_RATIO | [22:20] | PCLK_DBG = ATCLK/(PCLK_DBG_RATIO + 1) |
ATB_RATIO | [18:16] | ATCLK = MOUTCORE/(ATB_RATIO + 1) |
PERIPH_RATIO | [14:12] | PERIPHCLK = DOUTCORE/(PERIPH_RATIO + 1) |
COREM1_RATIO | [10:8] | ACLK_COREM1 = ARMCLK/(COREM1_RATIO +1) |
COREM0_RATIO | [6:4] | ACLK_COREM0 = ARMCLK/(COREM0_RATIO +1) |
CORE_RATIO | [2:0] | DIVCORE_OUT = MOUTCORE/(CORE_RATIO + 1) |
表6.7 CLK_DIV_CPU1的寄存器格式
功能 | 位 | 说明 |
CORES_RATIO | [10:8] | ACLK_CORES = ARMCLK/(CORES_RATIO +1)
|
HPM_RATIO | [6:4] | SCLK_HPM = DOUTCOPY/(HPM_RATIO + 1)
|
COPY_RATIO | [2:0] | DOUTCOPY = MOUTHPM/(COPY_RATIO + 1)
|
参考“图6.2 APLL时钟流程图”,以CPU的工作频率ARMCLK为例,根据表6.6、表6.7计算ARMCLK的频率:
ARMCLK = MUXCORE的输出 / DIVCORE / DIVCORE2
= MOUTCORE / (CORE_RATIO + 1) / DIVCORE2
= MOUTCORE / (CORE_RATIO + 1) / (CORE2_RATIO + 1)
MOUTCORE表示MUXCORE的输出,在MUXAPLL和MUXCORE都设置为0时,它等于“MDIV x FIN / (PDIV x 2 ^ SDIV)”
(7) CLK_DIV_STAT_CPU0(地址: 0x10044600),CLK_DIV_STAT_CPU1(地址: 0x10044604)
用于判断设置分频参数后,分频器输出是否已经稳定。
(8) CLK_GATE_IP_CPU(地址: 0x10044900)
用于控制是否给某个模块提供时钟,暂时不用理会。
第三节 程序相关讲解完整代码见目录4.system_clock_pll
1.start.S
.text
.globl _start
_start:
//关看门狗
ldr r1, = 0x10060000
mov r0, #0x0
str r0, [r1]
//使能Icache
orr r0, r0, #0x00001000
mcr p15, 0, r0, c1, c0, 0
//设置栈
ldr sp, =0x02050000
bl system_clock_init
bl main
halt:
b halt
这部分代码相信大家应该能看的懂。system_clock_init就是时钟初始化函数,具体代码大家可以看看system_clock.c文件。
第四节 编译代码和烧写运行将sd卡插入PC,在Ubuntu终端执行如下命令:
#cd 4.system_clock_pll
#make
#sudo ./sd_fusing.sh /dev/sdb bl2.bin
第五节 实验现象将sd卡插入UT4412BV03中,选择sd卡启动,然后上电,可以看到以下现象: LED正常闪烁加快。