嵌入式知識-ARM裸機-學習筆記(5):S5PV210時鐘系統詳解

嵌入式知識-ARM裸機-學習筆記(5):S5PV210時鐘系統詳解

一、SoC時鐘系統

1. 什麼是時鐘?

時鐘是同步工作系統的同步節拍,各部分通過這個節拍來找到協調一致的步伐,從而實現協調配合。
SoC內部有很多器件,例如CPU、串口、DRAM控制器、GPIO等內部外設,這些東西要彼此協同工作,需要一個同步的時鐘系統來指揮。這個就是我們SoC的時鐘系統

2. SoC系統時鐘

SoC的時鐘獲取方法

  • 外部直接輸入時鐘信號,SoC有個引腳用來輸入外部時鐘信號,用的很少,一般用在多個CPU需要協同工作時。
  • 外部晶振+內部時鐘發生器產生時鐘,大部分低頻單片機都是這麼工作的。
  • 外部晶振+內部時鐘發生器+內部PLL(倍頻用)產生高頻時鐘+內部分頻器分頻得到各種頻率的時鐘,大部分高頻嵌入式芯片都採用這種方法。

S5PV210系統時鐘的設計
(1)爲什麼不用外部高頻晶振產生高頻信號直接給CPU?
主要是因爲芯片外部電路不適宜使用高頻率,因爲傳導輻射比較難控制;高頻率的晶振太貴了。
(2)爲什麼要內部先高頻然後再分頻?
主要因爲SoC內部有很多部件都需要時鐘,而且各自需要的時鐘頻率不同,沒法統一供應。因此設計思路是PLL後先得到一個最高的頻率(1GHz、1.2GHz),然後各外設都有自己的分頻器再來分頻得到自己想要的頻率。(因爲倍頻比較麻煩,而分頻簡單,所有通過統一倍頻後再根據需要進行分頻處理)

時鐘和系統性能的關係、超頻、穩定性
(1)一般SoC時鐘頻率都是可以人爲編程控制的,頻率的高低對系統性能有很大影響。
(2)S5PV210建議工作頻率800MHz~1.2GHz,一般爲了保證穩定都將系統時鐘設置到1GHz主頻。如果你設置到1.2GHz就叫超頻。超頻的時候系統性能會提升,但是發熱也會增大,因此會影響系統穩定性

時鐘和外設編程的關聯
每個外設工作都需要一定頻率的時鐘,這些時鐘都是由時鐘系統提供的。時鐘系統可以編程控制工作模式,因此我們程序員可以爲每個外設指定時鐘來源、時鐘分頻係數,從而指定這個外設的工作時鐘。

時鐘和功耗控制的關係
(1)SoC中各種設備工作時,時鐘頻率越高其功耗越大,發熱越大,越容易不穩定,需要外部的散熱條件越苛刻。
(2)SoC內部有很多外設,這些外設不用的時候最好關掉(不關掉會一定程度浪費電),開關外設不是通過開關,而是通過時鐘。也就是說我們給某個外設斷掉時鐘,這個外設就不工作了。

二、S5PV210系統時鐘詳解

1. S5PV210時鐘系統簡介

在這裏插入圖片描述
時鐘域:MSYS、DSYS、PSYS
因爲S5PV210的時鐘體系比較複雜,內部外設模塊太多,因此把整個內部的時鐘劃分爲3大塊,叫做3個域。要清楚知道你要設置的這個模塊屬於什麼域纔行。
劃分時鐘域的原因是:210內部的這些模塊彼此工作時鐘速率差異太大了,所以有必要把高速的放一起,相對低速的放一起。

(1)MSYS域: 頻率較高的部分(CPU、DRAM、IRAM&IROM)。
ARMCLK: 給cpu內核工作的時鐘,也就是所謂的主頻。
HCLK_MSYS: MSYS域的高頻時鐘,給DMC0和DMC1使用
PCLK_MSYS: MSYS域的低頻時鐘
HCLK_IMEM:給iROM和iRAM(合稱iMEM)使用

(2)DSYS域: 和視頻顯示、編解碼相關的部分。
HCLK_DSYS:DSYS域的高頻時鐘
PCLK_DSYS:DSYS域的低頻時鐘

(3)PSYS域: 頻率相對較低的部分。
HCLK_PSYS:PSYS域的高頻時鐘
PCLK_PSYS:PSYS域的低頻時鐘
SCLK_ONENAND:

總結:210內部的各個外設都是接在(內部AMBA總線)總線上面的,AMBA總線有1條高頻分支叫AHB,有一條低頻分支叫APB。上面的各個域都有各自對應的HCLK_XXX和PCLK_XXX,其中HCLK_XXX就是XXX這個域中AHB總線的工作頻率;PCLK_XXX就是XXX這個域中APB總線的工作頻率。

時鐘的來源是通過:晶振+時鐘發生器+PLL+分頻電路

在這裏插入圖片描述
從上圖可以看出,S5PV210外部有4個晶振接口,設計板子硬件時可以根據需要來決定在哪裏接晶振。接了晶振之後上電相應的模塊就能產生振盪,產生原始時鐘。原始時鐘再經過一系列的篩選開關進入相應的PLL電路生成倍頻後的高頻時鐘。高頻時鐘再經過分頻到達芯片內部各模塊上。(有些模塊,譬如串口內部還有進一步的分頻器進行再次分頻使用)

2. 初始化S5PV210時鐘前的準備

各時鐘的典型值
當210剛上電時,默認是外部晶振+內部時鐘發生器產生的24MHz頻率的時鐘直接給ARMCLK的,這時系統的主頻就是24MHz,運行非常慢。因此三星公司推薦了一個工作性能和穩定性最佳的頻率,我們只需按這個頻率去配置並初始化時鐘,即可提高系統性能。
在這裏插入圖片描述
時鐘系統框圖
下圖爲S5PV210的時鐘系統框圖,圖從左到右依次完成了原始時鐘生成->PLL倍頻得到高頻時鐘->初次分頻得到各總線時鐘,本圖是理解整個時鐘體系的關鍵。
在這裏插入圖片描述
該圖中有兩個很重要的符號:MUX開關和DIV分頻器。
(1)MUX開關就是個或門,實際對應某個寄存器的某幾個bit位的設置,設置值決定了哪條通道通的,分析這個可以知道右邊的時鐘是從左邊哪條路過來的,從而知道右邊時鐘是多少。
(2)DIV分頻器,是一個硬件設備,可以對左邊的頻率進行n分頻,分頻後的低頻時鐘輸出到右邊。分頻器在編程時實際對應某個寄存器中的某幾個bit位,我們可以通過設置這個寄存器的這些對應bit位來設置分頻器的分頻係數(譬如左邊進來的時鐘是80MHz,分頻系統設置爲8,則分頻器右邊輸出的時鐘頻率爲10MHz)。

時鐘設置的關鍵性寄存器
(1)xPLL_LOCK :主要控制PLL鎖定週期的。多長時間能夠將低頻變爲高頻,就是能夠鎖定住。
(2)xPLL_CON/xPLL_CON0/xPLL_CON1 :主要用來打開/關閉PLL電路,設置PLL的倍頻參數,查看PLL鎖定狀態等。
(3)CLK_SRCn(n:0~6):用來設置時鐘來源的,對應時鐘框圖中的MUX開關。
(4)CLK_SRC_MASKn:決定MUX開關n選1後是否能繼續通過。默認的時鐘都是打開的,好處是不會因爲某個模塊的時鐘關閉而導致莫名其妙的問題,壞處是功耗控制不精細、功耗高。(在之後發現寄存器如果不工作時,要考慮此模塊的時鐘是否打開)。
(5)CLK_DIVn:各模塊的分頻器參數配置。
(6)CLK_GATE_x: 類似於CLK_SRC_MASK,對時鐘進行開關控制。
(7)CLK_DIV_STATn/CLK_MUX_STATn: 這兩類狀態位寄存器,用來查看DIV和MUX的狀態是否已經完成還是在進行中
總結:其中最重要的寄存器有3類:CON、SRC、DIV。其中CON決定PLL倍頻到多少,SRC決定走哪一路,DIV決定分頻多少。

3. 初始化S5PV210時鐘

第一步:先選擇不使用PLL。讓外部24MHz原始時鐘直接過去,繞過APLL那條路。
在這裏插入圖片描述
在這裏插入圖片描述
如果想走紅色這條路,只需將MUX_APLL開關置爲0即可,因此通過設置寄存器CLK_SRC0即可控制MUX開關,設置第0位即控制MUX_APLL開關。

	// 1 設置各種時鐘開關,暫時不使用PLL
	ldr	r1, =0x0
	// 芯片手冊P378 寄存器CLK_SRC:Select clock source 0 (Main)
	str	r1, [r0, #CLK_SRC0_OFFSET]	

第二步:設置鎖定時間。默認值爲0x0FFF,保險起見我們設置爲0xFFFF。
PLL鎖存器將低頻變爲高頻時,需要一段鎖存時間,因此我們需要給PLL一段時間確保其完成了鎖存,直到高頻時鐘能夠穩定產生爲止。在這裏插入圖片描述

	// 2 設置鎖定時間,使用默認值即可
	// 設置PLL後,時鐘從Fin提升到目標頻率時,需要一定的時間,即鎖定時間
	ldr	r1,	=0x0000FFFF					
	str	r1,	[r0, #APLL_LOCK_OFFSET]				
	str r1, [r0, #MPLL_LOCK_OFFSET]	 

第三步:設置分頻係數,決定由PLL出來的最高時鐘如何分頻得到各個分時鐘。

	// 3 設置分頻
	// 清bit[0~31]
	ldr	r2, =0x14131440						
	orr	r1, r1, r2
	str	r1, [r0, #CLK_DIV0_OFFSET]

通過代碼可知,我們向寄存器CLK_DIV0中存入的數爲0x14131440,將其進行二進制解析如下:
在這裏插入圖片描述
下圖爲CLK_DIV0寄存器對應位控制圖:
在這裏插入圖片描述
分析可知:
PCLK_PSYS_RATIO[30:28]=0x001,PCLK_PSYS=HCLK_PSYS / 2
HCLK_PSYS_RATIO[27:24]=0x0100,HCLK_PSYS = MOUT_PSYS / 5
PCLK_DSYS_RATIO[22:20]=0x001,PCLK_DSYS = HCLK_DSYS / 2
HCLK_DSYS_RATIO[19:16]=0x0011,HCLK_DSYS = MOUT_DSYS / 4
PCLK_MSYS_RATIO[14:12]=0x001,PCLK_MSYS=HCLK_MSYS / 2
HCLK_MSYS_RATIO[10:8]=0x100,HCLK_MSYS = ARMCLK / 5
A2M_RATIO[6:4]=0x100,SCLKA2M=SCLKAPLL / 5
APLL_RATIO[2:0]=0x000,ARMCLK = MOUT_MSYS / 1

第四步:設置PLL,主要是設置PLL的倍頻係數,決定由輸入端24MHz的原始頻率可以得到多大的輸出頻率。我們按照默認設置值設置輸出爲ARMCLK爲1GHz。
這裏設置PLL倍頻係數,一般都是根據數據手冊的推薦,通過設置D、P、S的值來根據公式進行係數的確定,這裏還是使用了推薦值。在這裏插入圖片描述
在這裏插入圖片描述

	// 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]

第五步:打開PLL。前面4步已經設置好了所有的開關和分頻係數,本步驟打開PLL後PLL開始工作,鎖定頻率後輸出,然後經過分頻得到各個頻率。

	// 5 設置各種時鐘開關,使用PLL
	ldr	r1, [r0, #CLK_SRC0_OFFSET]
	ldr	r2, =0x10001111
	orr	r1, r1, r2
	str	r1, [r0, #CLK_SRC0_OFFSET]

通過代碼可知,我們向寄存器CLK_SRC0中存入的數爲0x10001111,將其進行二進制解析如下:
在這裏插入圖片描述
下圖爲CLK_SRC0寄存器對應位控制圖:
在這裏插入圖片描述
分析可知:
ONENAND_SEL=1,MUXFLASH=HCLK_DSYS
MUX_PSYS_SEL=0,MUX_PSYS=SCLKMPLL
MUX_DSYS_SEL=0,MUX_DSYS=SCLKMPLL
MUX_MSYS_SEL=0,MUX_MSYS=SCLKAPLL
VPLL_SEL=1,MUXVPLL=FOUTVPLL
EPLL_SEL=1,MUXEPLL=FOUTEPLL
MPLL_SEL=1,MUXMPLL=FOUTMPLL
APLL_SEL=1,MUXAPLL=FOUTAPLL

結合幾個步驟可以得到,系統時鐘框圖爲:
在這裏插入圖片描述
從圖中可得,通過該方法配置得到的各部分時鐘均與上述推薦的時鐘典型值相符,時鐘配置完成。

附C語言配置時鐘版本:

// 時鐘控制器基地址
#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)


#define REG_CLK_SRC0	(ELFIN_CLOCK_POWER_BASE + CLK_SRC0_OFFSET)
#define REG_APLL_LOCK	(ELFIN_CLOCK_POWER_BASE + APLL_LOCK_OFFSET)
#define REG_MPLL_LOCK	(ELFIN_CLOCK_POWER_BASE + MPLL_LOCK_OFFSET)
#define REG_CLK_DIV0	(ELFIN_CLOCK_POWER_BASE + CLK_DIV0_OFFSET)
#define REG_APLL_CON0	(ELFIN_CLOCK_POWER_BASE + APLL_CON0_OFFSET)
#define REG_MPLL_CON	(ELFIN_CLOCK_POWER_BASE + MPLL_CON_OFFSET)

#define rREG_CLK_SRC0	(*(volatile unsigned int *)REG_CLK_SRC0)
#define rREG_APLL_LOCK	(*(volatile unsigned int *)REG_APLL_LOCK)
#define rREG_MPLL_LOCK	(*(volatile unsigned int *)REG_MPLL_LOCK)
#define rREG_CLK_DIV0	(*(volatile unsigned int *)REG_CLK_DIV0)
#define rREG_APLL_CON0	(*(volatile unsigned int *)REG_APLL_CON0)
#define rREG_MPLL_CON	(*(volatile unsigned int *)REG_MPLL_CON)


void clock_init(void)
{
	// 1 設置各種時鐘開關,暫時不使用PLL
	rREG_CLK_SRC0 = 0x0;
	
	// 2 設置鎖定時間,使用默認值即可
	// 設置PLL後,時鐘從Fin提升到目標頻率時,需要一定的時間,即鎖定時間
	rREG_APLL_LOCK = 0x0000ffff;
	rREG_MPLL_LOCK = 0x0000ffff;
	
	// 3 設置分頻
	// 清bit[0~31]
	rREG_CLK_DIV0 = 0x14131440;
	
	// 4 設置PLL
	// FOUT = MDIV*FIN/(PDIV*2^(SDIV-1))=0x7d*24/(0x3*2^(1-1))=1000 MHz
	rREG_APLL_CON0 = APLL_VAL;
	// FOUT = MDIV*FIN/(PDIV*2^SDIV)=0x29b*24/(0xc*2^1)= 667 MHz
	rREG_MPLL_CON = MPLL_VAL;
	
	// 5 設置各種時鐘開關,使用PLL
	rREG_CLK_SRC0 = 0x10001111;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章