裸機——時鐘系統
1. 首先需要知道時鐘有什麼用
時鐘頻率是用來知道硬體工作快慢的,
頻率高,硬體工作快,但發熱高,
頻率低,硬體工作慢,發熱低
無頻率,硬體即使有點也不工作。
2. 要知道時鐘是怎麼產生和管理的。
可以直接在SoC外給高頻時鐘。
可以SoC使用晶振+時鐘發生器,也就是使用低頻時鐘。
可以SoC使用晶振+時鐘發生器產生低頻時鐘,再用PLL生成高頻時鐘,再用DIV分頻。
綜上,可以知道硬體要工作就要時鐘,設定時鐘頻率在合理的值,硬體才能合理工作,
也知道了時鐘可選的產生方式。
接下來開始看自己板子的時鐘。
目標是,先了解硬體上對時鐘的管理做了什麼。
要了解板子就要先看原理圖。
原理圖找到晶振。
可見板子可以接3個晶振,但實際上從廠家瞭解只接了一個USB晶振。
然後看SoC資料手冊,對於時鐘,是在SoC內部進行管理的,所以前面的原理圖並不能給出很多資訊,而大部分都在SoC資料手冊中。
從頭開看,先是這張圖
發現,SoC以硬體的工作頻率的不同,將硬體分成了3個域,目的是為了方便管理硬體的時鐘。
其中MSYS與CPU,RAM有關
DSYS與視訊顯示有關
PSYS與外設有關
接下來是這張圖
這張圖說明了這款 SoC 可以外界的晶振+時鐘發生器的4種方法,
同時也說明了這款SoC的時鐘大致的管理邏輯:先使用晶振+時鐘發生器產生低頻時鐘,然後分配給不同模組,然後使用PLL進行倍頻,產生SCLK。
然後看到這張圖
這張圖說明了SCLK是如何選源時鐘,倍頻,分配。
這裡有幾個符好需要注意:
MSYS:
ARMCLK: 這是CPU的
HCLK_MSYS: MSYS的高頻時鐘
PCLK_MSYS: MSYS的低頻時鐘
HCLK_IMEM: IMEM指 iROM 和 IRAM用的
DSYS:
HCLK_DSYS: DSYS的高頻
PCLK_DSYS: DSYS的低頻
PSYS:
HCLK_PSYS: PSYS的高頻
PCLK_PSYS: PSYS的低頻
對於上面的圖還需要理解3個東西:
(1)多路選擇MUX
其中灰色的表示軟體可以設定,白色的是硬體設定了的。
(2)PLL
鎖相環,對變成而言需要知道,這是用於倍頻的,由於剛剛開始倍頻的結果不穩定,所以需要一定延遲後才能使用PLL出來的時鐘,所以倍頻前需要設定鎖定時間,即開啟PLL後,不會立即使用PLL的時間,而是等一段時間後再使用。
(3)DIV
用來分頻,如果設定為8,輸入80MZ,輸出10MZ。
自此,已經瞭解了該板子的時鐘設定。
即SoC使用晶振+時鐘發生器產生低頻時鐘,再用PLL生成高頻時鐘,再用DIV分頻。
而程式設計師通過設定暫存器控制源時鐘,MUX,PLL,DIV配置時鐘。
而配置的值應該遵照SoC資料手冊的推薦值。
接下來根據參考程式碼,進行理解:
// 時鐘控制器基地址 #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