时钟系统——S5PV210的时钟系统的理论与代码实践

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

参考内容

s5pv210——初始化时钟 - biaohc - 博客园

s5pv210的用户手册

一、SoC时钟系统的简介

1、时钟的含义与作用

时钟是同步工作系统的同步节拍。SoC内部有很多器件,比如CPU、串口、DRAM控制器、GPIO等内部外设,这些器件要彼此协同工作,需要一个同步的时钟系统来指挥,即SoC的时钟系统。

2、获取时钟信号的途径

一般有以下三种途径获得时钟信号:

(1)外部直接输入时钟信号。SoC有个引脚用来输入外部时钟信号。这种方式用得很少。

(2)外部晶振+内部时钟发生器。大部分低频单片机使用这种方式产生时钟信号。

(3)外部晶振 + 内部时钟发生器 + 内部PLL产生高频时钟 + 内部分频器分频。

3、时钟和系统性能的关系

一般情况下SoC的时钟频率可以人为编程控制,而SoC时钟频率的高低对系统性能有很大影响。S5PV210建议工作频率800MHz~1.2GHz,通常设置为1GHz。如果设置大于1.2GHz则叫超频,这时候系统性能会提升,但是发热也会增大,影响系统稳定性。

4、时钟和外设编程的关联

每个外设工作都需要一定频率的时钟信号,这些时钟信号都是由时钟系统提供的。可以通过编程来控制时钟系统的工作模式,程序员可以为每个外设指定时钟来源、时钟分频系统。

5、时钟和功耗控制的关系

SoC中各种设备工作时,时钟频率越高,其功耗越大、发热越大、越不稳定,需要外部的散热条件越苛刻。SoC内部有很多外设,为了降低功耗,这些外设不用的时候最好关掉。外设的开与关是通过时钟来设置的,当我们给某个外设断掉时钟,则这个外设就不会工作。

二、S5PV210的时钟系统简介

此部分内容在用户手册的section 02_system:3 CLOCK CONTROLLER 。

1、三个时钟域

S5PV210有很多内部外设,它们的工作时钟速率差异很大,因此有必要按照时钟频率的高低,将整个内部的时钟划分为以下三个域(把高速的放一起,相对低速的放一起):

(1)MSYS(main system)

这个域主要为CPU、DRAM控制器(DMC0与DMC1)、IRAM与IROM等模块提供时钟。

(2)DSYS(display system)

这个域主要为音视频编解码相关的模块提供时钟。

(3)PSYS(peripheral system)

这个域主要为内部外设提供时钟,比如GPIO、I2C、UART、WDT、SD接口、USB等模块。

2、时钟来源

在上面第一节中我们讲过,时钟来源有三种方式,而S5PV210的时钟来源方式是:外部晶振 + 内部时钟发生器 + 内部PLL产生高频时钟 + 内部分频器分频。

有人可能会问,为什么不直接采用高频晶振产生高频信号给CPU?主要是因为芯片外部电路不适宜使用高频率,因为传导辐射比较难控制;另外高频晶振价格昂贵。

为什么内部先高频然后再分频?主要是因为SoC内部有很多模块都需要时钟,各模块所需要的时钟频率不同,无法统一供应。因此设计思路是先 PLL得到一个最高的频率(1GHz或1.2GHz),然后各外设根据自己想要的频率来设置自己的分频器。

接下来将详细分析S5PV210的时钟来源。

(1)S5PV210时钟体系框图

S5PV210时钟体系框图位于用户手册第361与362页,即下面的两张图。

第一张图从左到右依次完成了原始时钟生成、PLL倍频得到高频时钟、初次分频得到各总线时钟。第二张图是从各中间时钟(第一张图中某个步骤生成的时钟)得到各外设自己使用的时钟(实际就是个别外设自己再额外分频的设置)。可见第一张图是理解整个时钟体系的关键,第二种图是进一步分析各外设时钟来源的关键。

(2)四个晶振

S5PV210外部有4个晶振接口,设计板子时可以根据需要来决定选取哪个晶振。接了晶振之后上电,相应的模块就能产生振荡,产生原始时钟。

(3)四个锁相环

原始时钟经过一系列的筛选开关进入相应的PLL电路,生成倍频后的高频时钟。

锁相环描述
APLLAPLL generates ARM core and MSYS clocks
MPLLMPLL generates a system bus clock and special clocks
VPLLVPLL generates clocks for video interface
EPLLEPLL generates special clocks

(4)MUX开关

MUX 开关是一个多路选择开关,其实就是一个或门。我们可以通过设置某个寄存器的某几个 bit 位,来决定选取哪条通道。具体设置细节见下面第三节的第3点内容。另外我们通过分析这个 MUX 开关,可以知道右侧的时钟是从左侧哪条路过来的,从而知道右侧的时钟大小是多少。

(5)DIV分频器

DIV 分频器是一个硬件设备,可以对左边的频率进行n分频后输出到右边。我们可以通过设置某个寄存器的某些bit位,来设置分频器的分频系数。比如左边进来的时钟是80MHz,如果分频系统设置为8,则分频器右边输出的时钟频率为10MHz。具体设置细节见下面第三节的第4点内容。

3、默认的时钟典型值

当X210开发板刚上电时,“外部晶振+内部时钟发生器” 产生24MHz的时钟,供给ARMCLK,这时系统的主频就是24MHz,运行非常慢。IROM代码执行时,第6步会初始化时钟系统(见上图),从而给系统各模块提供默认的时钟频率,这些时钟频率是三星推荐的S5PV210各模块工作性能和稳定性最佳的频率,如下表所示:

时钟域时钟信号典型频率描述
MSYSARMCLK1000 MHzCPU工作的时钟,即所谓的主频
HCLK_MSYS200 MHzMSYS域的高频时钟
PCLK_MSYS100 MHzMSYS域的低频时钟
HCLK_IMEM100 MHz给iROM和iRAM(合称iMEM)使用的时钟
SCLK_DMC0
DSYSHCLK_DSYS166 MHzDSYS域的高频时钟
PCLK_DSYS83 MHzDSYS域的低频时钟
PSYSHCLK_PSYS133 MHzPSYS域的高频时钟
PCLK_PSYS66 MHzPSYS域的低频时钟
SCLK_ONENAND133 MHz, 166 MHz

这里的默认,是指已经将相应的设置写死在IROM中的代码里,IROM代码开始执行时,就会产生上面这些默认的时钟典型值。我们这里之所以还进行讨论与设置,只不过是为了学习如何设置时钟而已;另外在 uboot 中,对时钟部分也进行了初始化,其实也是多此一举。 

为了学习,我们后续将根据这些推荐的时钟频率,重新设置多路选择开关、锁相环、分频器的值。

三、时钟设置有关的寄存器

这部分内容在用户手册第367页,有很多寄存器,其中最重要的寄存器有3类:

  • xPLL_CON寄存器:决定PLL倍频到多少。
  • CLK_SRC寄存器:决定MUX开关选择哪一条路线。
  • CLK_DIV寄存器:决定分频多少。

1、xPLL_LOCK寄存器(控制锁定周期)

(1)寄存器的作用

锁相环PLL倍频需要一定时间才能达到相应的频率。

xPLL_LOCK寄存器,主要用来控制锁相环PLL锁定周期。

其设置一般为默认值,官方推荐值为0xFFF,因此我们设置为0xFFFF。

(2)寄存器的地址

(3)寄存器的设置

2、xPLL_CONn寄存器(PLL的 开关、设置倍频系数)

(1)寄存器的作用

这些寄存器用来打开或关闭PLL电路,设置倍频参数,查看PLL锁定状态等。

(2)寄存器的地址

(3)寄存器的设置

APLL_CON0寄存器为例,各bit的含义如下。

通过对APLL_CON0寄存器进行设置,可以设定锁相环APLL的倍频输出。

其计算公式为:FOUT = MDIV X FIN / (PDIV × 2^(SDIV)-1)。

比如锁相环输入24MHz,倍频后输出1000MHZ,(根据推荐)则1000=125*24 / (3*2^1-1)。也就是说,MDIV要设为125,PDIV要设为3,SDIV要设为1。

 对于锁相环ALL,S5PV210推荐的倍频输出情况如下:

(4)各锁相环推荐输出值

下面是各锁相环推荐的输入输出值,可见输入都是24MHz(这是“外部晶振+内部时钟发生器” 产生的24MHz的时钟),输出由MDIV、PDIV、SDIV这个内容决定。

另外各个锁相环还有其他的输入输出情况,这里不再列出。  

3、CLK_SRCn寄存器(选择MUX哪路)、CLK_SRC_MASKn寄存器(使能MUX开关)

(1)寄存器的作用

这里的n=0,1,2,3,4,5,6。

CLK_SRCn寄存器,主要用来设置MUX开关(多路选择开关),即选择哪一个作为输入。

CLK_SRC_MASKn寄存器,用来决定 MUX 开关是否使能。如果没有使能,信号不能从MUX开关输出。不过默认都是使能的。

(2)寄存器的设置

以CLK_SRC0寄存器为例,各bit的含义如下。

注意,我们在编写代码的时候,一般首先要关闭4个PLL后面的MUX开关,设置好相应寄存器以后再把这4个MUX开关打开。

4、CLK_DIVn寄存器(设置分频系数)

(1)寄存器的作用

CLK_DIVn寄存器,主要用来设置各个分频器的值。

(2)寄存器的设置

以CLK_DIV0寄存器为例,各bit的含义如下。

比如将CLK_DIV0寄存器的内容设置为0x14131440,分析得知这个值表示的意思如下。

首先0x14131440=0b0001_0100_0001_0011_0001_0100_0100_0000

  • PCLK_PSYS = HCLK_PSYS / (1+1)
  • HCLK_PSYS = MOUT_PSYS / (4+1)
  • PCLK_DSYS = HCLK_DSYS / (1+1)
  • HCLK_DSYS = MOUT_DSYS / (3+1)
  • PCLK_MSYS = HCLK_MSYS / (1+1)
  • HCLK_MSYS = ARMCLK / (4+1)
  • ……
  • ARMCLK = MOUT_MSYS / (0+1) 

5、CLK_GATE_x寄存器

类似于CLK_SRC_MASK,对时钟进行开关控制。

6、CLK_DIV_STATn、CLK_MUX_STATn寄存器

这两类状态位寄存器,用来查看DIV和MUX的状态:已经完成、在进行中。

四、代码实战

这里的代码实践,是指初始化时钟系统,即按照S5PV210推荐的各时钟典型值来进行时钟系统的初始化。初始化完成后,各时钟产生与典型值一样频率的时钟信号。

1、编程步骤

第1步:在MUL开关处选择不使用PLL,让外部24MHz原始时钟直接过去。

第2步:设置锁相环锁定时间。默认值为0x0FFF,保险起见我们设置为0xFFFF。

第3步:设置分频系统,决定(由PLL出来的)最高时钟如何分频得到各个分时钟。

第4步:设置PLL,主要是设置PLL的倍频系统,决定由输入端24MHz的原始频率可以得到多大的输出频率。我们按照默认设置值设置输出为ARMCLK为1GHz。

第5步:打开PLL。前面4步已经设置好了所有的开关和分频系数,本步骤打开PLL后PLL开始工作,锁定频率后输出,然后经过分频得到各个频率。 

2、代码实践

注意,下面的代码只设置了APLL和MPLL两个锁相环的倍频,其他两个锁相环没有管。

(1)主流程文件start.S文件

​
#define WTCON		0xE2700000
#define SVC_STACK	0xd0037d80

.global _start	// 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
	// 第1步:关看门狗(向WTCON的bit5写入0即可)
	ldr r0, =WTCON
	ldr r1, =0x0
	str r1, [r0]
	
	// 第2步:初始化时钟
	bl clock_init   
	
	// 第3步:设置SVC栈
	ldr sp, =SVC_STACK
	
	// 第4步:开/关icache
	mrc p15,0,r0,c1,c0,0;			// 读出cp15的c1到r0中
	//bic r0, r0, #(1<<12)			// bit12 置0  关icache
	orr r0, r0, #(1<<12)			// bit12 置1  开icache
	mcr p15,0,r0,c1,c0,0;

	
	// 从这里之后就可以开始调用C程序了
	bl led_blink					// led_blink是C语言实现的一个函数
	
	// 汇编最后的这个死循环不能丢
	b .

​

(2)clock_init.c文件(C语言实现时钟系统的初始化):

#define _REG_APLL_LOCK            *((unsigned int*)0xE0100000)
#define _REG_MPLL_LOCK            *((unsigned int*)0xE0100008)
#define _REG_EPLL_LOCK            *((unsigned int*)0xE0100010)
#define _REG_VPLL_LOCK            *((unsigned int*)0xE0100020)
#define _REG_APLL_CON0            *((unsigned int*)0xE0100100)
#define _REG_MPLL_CON            *((unsigned int*)0xE0100108)
#define _REG_CLK_SRC0            *((unsigned int*)0xE0100200)
#define _REG_CLK_DIV0            *((unsigned int*)0xE0100300)

#define APLL_SDIV                (1)
#define APLL_PDIV                (3)
#define APLL_MDIV                (125)
#define APLL_EN                    (1)

#define MPLL_SDIV                (1)
#define MPLL_PDIV                (12)
#define MPLL_MDIV                (667)
#define MPLL_EN                    (1)

void clock_init(void)
{
    //第一步先关闭PLL的MUX开关
    //即设置MUX开关选择路线0,不选择锁相环的那路,也就相当于关闭PLL
    _REG_CLK_SRC0 = 0x0;
    
    //第二步设置LOCK时间,设置为默认值0x0FFF
    _REG_APLL_LOCK = 0x0FFF;
    _REG_MPLL_LOCK = 0x0FFF;
    _REG_EPLL_LOCK = 0x0FFF;
    _REG_VPLL_LOCK = 0x0FFF;
    
    //第三步设置DIV分频器的值    
    _REG_CLK_DIV0 = 0x14131400;
    
    //第四步设置APLL、MPLL的倍频值。
    _REG_APLL_CON0 = (APLL_EN<<31) | (APLL_MDIV<<16) | (APLL_PDIV<<8) | (APLL_SDIV<<0);
    _REG_MPLL_CON  = (MPLL_EN<<31) | (MPLL_MDIV<<16) | (MPLL_PDIV<<8) | (MPLL_SDIV<<0);
    
    //第五步设置MUX开关,即设置MUX开关选择从PLL出来的那一路
    _REG_CLK_SRC0 = 0x1111;    
        
}

或者用汇编实现时钟系统的初始化:clock_init.S文件

// 时钟控制器基地址
#define ELFIN_CLOCK_POWER_BASE		0xE0100000	

// 时钟相关的寄存器相对时钟控制器基地址的偏移值
#define APLL_LOCK_OFFSET		0x00		
#define MPLL_LOCK_OFFSET		0x08

#define APLL_CON0_OFFSET		0x100
#define APLL_CON1_OFFSET		0x104
#define MPLL_CON_OFFSET			0x108

#define CLK_SRC0_OFFSET			0x200
#define CLK_SRC1_OFFSET			0x204
#define CLK_SRC2_OFFSET			0x208
#define CLK_SRC3_OFFSET			0x20c
#define CLK_SRC4_OFFSET			0x210
#define CLK_SRC5_OFFSET			0x214
#define CLK_SRC6_OFFSET			0x218
#define CLK_SRC_MASK0_OFFSET	0x280
#define CLK_SRC_MASK1_OFFSET	0x284

#define CLK_DIV0_OFFSET			0x300
#define CLK_DIV1_OFFSET			0x304
#define CLK_DIV2_OFFSET			0x308
#define CLK_DIV3_OFFSET			0x30c
#define CLK_DIV4_OFFSET			0x310
#define CLK_DIV5_OFFSET			0x314
#define CLK_DIV6_OFFSET			0x318
#define CLK_DIV7_OFFSET			0x31c

#define CLK_DIV0_MASK			0x7fffffff

// 这些M、P、S的配置值都是查数据手册中典型时钟配置值的推荐配置得来的。
// 这些配置值是三星推荐的,因此工作最稳定。如果是自己随便瞎拼凑出来的那就要
// 经过严格测试,才能保证一定对。
#define APLL_MDIV      	 		0x7d		// 125
#define APLL_PDIV       		0x3
#define APLL_SDIV       		0x1

#define MPLL_MDIV				0x29b		// 667
#define MPLL_PDIV				0xc
#define MPLL_SDIV				0x1

#define set_pll(mdiv, pdiv, sdiv)	(1<<31 | mdiv<<16 | pdiv<<8 | sdiv)
#define APLL_VAL			set_pll(APLL_MDIV,APLL_PDIV,APLL_SDIV)
#define MPLL_VAL			set_pll(MPLL_MDIV,MPLL_PDIV,MPLL_SDIV)


.global clock_init
clock_init:
	ldr	r0, =ELFIN_CLOCK_POWER_BASE
	
	// 1 设置各种时钟开关,暂时不使用PLL
	ldr	r1, =0x0
	// 芯片手册P378 寄存器CLK_SRC:Select clock source 0 (Main)
	str	r1, [r0, #CLK_SRC0_OFFSET]				

	// 2 设置锁定时间,使用默认值即可
	// 设置PLL后,时钟从Fin提升到目标频率时,需要一定的时间,即锁定时间
	ldr	r1,	=0x0000FFFF					
	str	r1,	[r0, #APLL_LOCK_OFFSET]				
	str r1, [r0, #MPLL_LOCK_OFFSET]	 				

	// 3 设置分频
	// 清bit[0~31]
	ldr r1, [r0, #CLK_DIV0_OFFSET]					
	ldr	r2, =CLK_DIV0_MASK					
	bic	r1, r1, r2
	ldr	r2, =0x14131440						
	orr	r1, r1, r2
	str	r1, [r0, #CLK_DIV0_OFFSET]

	// 4 设置PLL
	// FOUT = MDIV*FIN/(PDIV*2^(SDIV-1))=0x7d*24/(0x3*2^(1-1))=1000 MHz
	ldr	r1, =APLL_VAL						
	str	r1, [r0, #APLL_CON0_OFFSET]
	// FOUT = MDIV*FIN/(PDIV*2^SDIV)=0x29b*24/(0xc*2^1)= 667 MHz
	ldr	r1, =MPLL_VAL						
	str	r1, [r0, #MPLL_CON_OFFSET]

	// 5 设置各种时钟开关,使用PLL
	ldr	r1, [r0, #CLK_SRC0_OFFSET]
	ldr	r2, =0x10001111
	orr	r1, r1, r2
	str	r1, [r0, #CLK_SRC0_OFFSET]

	mov	pc, lr
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天糊土

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值