1. 程式人生 > >裸機——時鐘系統

裸機——時鐘系統

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