[學習資料] Tiny210(S5PV210) u-boot移植

Tiny210(S5PV210) u-boot移植
http://www.microoh.com/bbs/forum.php?mod=viewthread&tid=254&fromuid=6205
(出處: 麥可網論壇)php

請你們關注原做者南山一夢linux

 

一直想開一個帖子,針對課程的Stage4系統移植階段,把一些在視頻課程中沒有講透徹的地方,和你們一塊兒討論交流一下,今天開一個關於u-boot移植的帖子,分享一些我u-boot移植過程當中的筆記和學習心得,全當是拋磚引玉,也但願你們能指出其中的錯誤,對於視頻中沒有講全講透的地方,你們一塊兒交流,共同進步。廢話就很少說了,仍是先從啓動原理,一點一點往下說吧。

Tiny210 U-BOOT(一)--啓動過程

講解啓動過程,首先的源頭就是打開電源,這個相信沒人人不知道。CPU上電後,此時SP指針指向0x0000_0000,從這個地址取第一條指令。但此時:PLL沒有啓動,CPU工做頻率爲外部輸入晶振頻率,很是低(S5PV210中晶振在CPU旁邊,兩顆24MHz,一顆27MHz);CPU的工做模式、中斷設置等不肯定;存儲空間的各個BANK(包括內存)都沒有驅動,內存(tiny210用的DDR2)不能使用。在這種狀況下必須在第一條指令處作一些初始化工做,這段初始化程序與操做系統獨立分開,稱之爲Bootloader。下面以S5PV210用的U-BOOT爲例:

友善之臂Tiny210 SDK2用的是三星公司的S5PV210,在開始U-BOOT的移植以前,有必要弄清楚S5PV210的啓動過程:
<ignore_js_op>vim


由圖上所知,由OM(Operating Mode)pin導入到iROM中,OM是什麼?打開Tiny210的原理圖,搜索OM
<ignore_js_op>緩存


查找到一個表格,有上表可知,OM1/OM2/OM3決定的是從SD卡啓動仍是從Nand Flash啓動。
<ignore_js_op>數據結構


繼續往下看,由圖可知,OM1/OM2/OM3分別接在XOM1/XOM2/XOM3上面。
<ignore_js_op>架構


再來看旁邊XOM的一個信號反相器74LVC1G04的圖,你怎麼知道74LVC1G04是信號反相器呢?我也不知道,我打開了74LVC1G04的芯片手冊看了才知道。
<ignore_js_op>app


打開74LVC1G04的芯片手冊,找到pin腳圖和邏輯圖
<ignore_js_op>dom

<ignore_js_op>ide


<ignore_js_op>函數


由圖可知,這是一個反相器,2中輸入高電平,則4中輸出低電平;2中輸入低電平,則4中輸出高電平,OK。
當XOM撥到1時,XOM1爲NC狀態,XOM1 = 0,pin4的輸出爲1,XOM2=XOM3=1,則爲0-1-1,爲SDBOOT;
當XOM撥到2時,XOM1爲輸入狀態,XOM1 = 1,pin4的輸出爲0,XOM2 = XOM3 = 0,爲1-0-0,Nand啓動模式,至此,OM的輸入判斷分析結束。

繼續看芯片手冊,手冊上關於iROM/iRAM及Bootloader描述以下:

  • The iROM code is placed in internal 64KB ROM. It initializes basic system functions such as clock, stack, and heap.
  • The iROM loads the first boot loader image from a specific booting device to internal 96KB SRAM. The booting device is selected by Operating Mode (OM) pins. According to the secure boot key values, the iROM code may do an integrity check on the first boot loader image.
  • The first boot loader loads the second boot loader then may check the integrity of the second boot loader according the secure boot key values.
  • The second boot loader initializes system clock, UART, and DRAM controller. After initializing DRAM controller, it loads OS image from the booting device to DRAM. According to the secure boot key values, the second boot loader can do an integrity check on the OS image.
  • After the booting completes, the second boot loader jumps to the operating system.


按照芯片手冊上的啓動分析圖及上面的描述,總結啓動流程爲:
S5PV210上電將iROM(interal ROM)處執行固化的啓動代碼,它對系統時鐘進行初始化,對啓動設備進行判斷,並從啓動設備中複製BL1(最大16KB)到iRAM(0xd002_0000處,其中0xd002_0010以前的16個字節存儲的的有BL1的校驗信息和BL1尺寸)中,並對BL1進行校驗,檢驗OK轉入BL1進行執行;BL1執行完成後,開始執行BL2,BL2加載內核,把OS在SDRAM中運行起來。
BL0,BL1,BL2:
(1)BL0:是指S5PV210的iROM中固化的啓動代碼
做用:初始化系統時鐘,設置看門狗,初始化堆和棧,加載BL1
(2)BL1:是批在iRAM自動從外擴存儲器(nand/sd/usb)中拷貝的uboot.bin二進制文件的頭最大16K代碼
做用:初始化RAM,關閉Cache,設置棧,加載BL2
(3)BL2:是指在代碼重定向後在內存中執行的uboot的完整代碼
做用:初始化其它外設,加載OS內核
(4)三者之間的關係:(Interal ROM固化代碼)BL0將BL1(bootloader的前16KB--BL1)加載到iRAM;BL1而後在iRAM中運行將BL2(剩下的bootloader)加載到SDRAM;BL2加載內核,把OS在SDRAM中運行起來,最終OS是運行在SDRAM(內存)中的。

在這個過程當中,u-boot其實嚴格來講有三段,由於第一段是在iROM中,由三星公司給咱們寫好了,因此,一般,將片內的bootloader稱爲BL0,而片外Bootloader,也就是在NAND FLASH中的bootloader,咱們稱爲BL1,BL2。
咱們片外的u-boot是被分爲了兩個階段:
1.第一階段代碼
arch/arm/cpu/armv7/start.S
1).設置異常向量地址
2).設置SVC32模式(ARM七種工做模式)
3).cpu_init_crit
清TLB(頁面緩存)、關MMU及Cache等
4).轉入低級初始化lowlevel_init
主要是對時鐘、片外內存(DDR2)、串口、nand(這裏初始化nand主要是爲第二階段搬內核到內存而準備的)等進行初始化
5).判斷啓動開關進行自搬移
6).跳轉到C入口board_init_f( ) 

2.第二階段代碼
1).board_init_f( )
A.gd_t數據結構空間分配
B.回調一組初始化函數
C.對gd_t數據結構進行初始化
D.relocate_code(U-boot重定義代碼,即自搬移)
2).board_init_r( )
A.使能Cache
B.板子初始化
C.串口初始化
D.外存初始化
E.環境變量初始化
F.控制檯初始譁
G.中斷使能
H.以太網初始化
I. 進入main_loop(),等待命令或自動加載內核

先看硬件,CPU時鐘、NAND FLASH、DDR、串口、網卡,至少這些硬件,是咱們須要在u-boo的啓動過程當中完成初始化的,其他的細節問題,暫且不說,至少,前面的這些硬件,是u-boot在啓動過程,作自搬移,及最終引導內核前必需要用到的,OK,爲了理解u-boot源碼中那些硬件操做彙編代碼,咱們先看看這些硬件的工做原理是如何的。

Tiny210 U-BOOT(二)--配置時鐘頻率基本原理
OK,接着說,此次先講講CPU的系統時鐘。U-BOOT在啓動的過程當中,須要配置系統時鐘,沒有這東西,CPU就跑不起來。
配置系統時鐘,大體是如下幾個步驟:

(1)設置系統PLL鎖定時間
(2)配置PLL
(3)配置各模塊分頻係數
(4)切換到PLL時鐘

 

之後你們常常要翻的就是S5PV210的芯片手冊了,之後說Pxxx頁,指的就是S5PV210的芯片手冊,這裏我使用的是S5PV210_UM_REV1.1的版本,之後的頁數都以這個爲準。OK,翻吧,騷年

 

1.基本原理
首先輸出一個高電平,而後,經過三個晶振,輸出一個頻率,而後,經過倍頻器(鎖相環),將頻率升高,而後,再經過分頻,把分出來的不一樣的頻率,提供給不一樣的器件,好比ARM Cotrex內核、各類設備控制器等等。

 

Tiny210(S5PV210)上藍色的框標註的就是晶振的位置,最上面的是27MHz,中間和下面兩顆是24MHz,順便講一下另外幾片東西,最左邊的四片是內存DDR2-800,這表示數據傳輸頻率爲800MHz,外部時鐘頻率200MHz,內部時鐘頻率爲100MHz;下面的黃色框是NAND FLASH,外部時鐘頻率133MHz。我怎麼知道的?你們看了這一章之後,天然就知道怎麼查了,別急,耐心往下看

 

<ignore_js_op>

 

 

 

 

倍頻的原理:

 

下圖就是上電後的XXTI輸出的頻率變化圖(XXTI引腳見P361的系統時鐘流程圖最左邊的XXTI引腳,XXTI的詳細介紹見P354的Figure 3-2 S5PV210 Top-Level Clocks),頻率從小變到指定頻率須要一段時間(圖中標紅框的部分),當CPU頻率在變化的時候,好比由復位後的初始的400HZ,我要升到1000HZ, 這時,首先把CPU的頻率鎖定,因這此時CPU的頻率是變化的,頻率變化,CPU的狀態就沒法肯定,因此,此時用PLL--phase-locked loop鎖相環,將CPU頻率鎖定一段時間,直到個人頻率輸出穩定爲止。芯片手冊上顯示APLL默認的設置時間爲30us(30毫秒)。

 

<ignore_js_op>

 

 

 

 

鎖定頻率後,此時,應該設置一個倍頻因子,在ARM手冊中去查表10.2,P,M,S, 設置對應的位的值,而後,將頻率提高,好比從晶振輸出的24HZ,擡升到1000Hz(S5PV210的CPU旁邊有三個晶振,兩個24Hz, 一個27Hz)。

 

<ignore_js_op>

 

 

 

分頻的原理:
設置不一樣的位,好比,設置某一位爲0,那麼,分頻時,原來頻率好比爲1000HZ,那麼頻率就被分爲1000/1=1000Hz, 這樣就能夠分給ARMCLK使用。

2.開始分析

下圖取自芯片手冊section 02_system的第3章 CLOCK CONTROLLER,在開篇的P353頁的第一張圖就是這個。<ignore_js_op>


S5PV210的Clock分爲三個domain,意思是三個區域--MSYS,DSYS,PSYS,這三個區域分別都是AMBA總線,AMBA總線分爲AHB和APB兩種總線(這是不嚴格的分法,僅僅是便於理解),每種總線都有不一樣的時鐘頻率,AHB--HCLK/APB--PCLK
那麼MSYS,DSYS,PSYS最少有6個時鐘(實際上不止6個),分別爲HCLK_MSYS/PCLK_MSYS、HCLK_DSYS/PCLK_DSYS、HCLK_PSYS/PCLK_PSYS,再外加一個CPU要用的時鐘ARMCLK,總共7個時鐘頻率。MSYS,DSYS,PSYS分別管理不一樣的設備,爲不一樣的設備提供不一樣的頻率。
<ignore_js_op>


再往下翻,這些不一樣設備的頻率是如何產生的呢?由圖可知最後,總共有13個CLK提供出來。
<ignore_js_op>


由上圖可知,經過XOM[0]產生頻率,而後在APLL升頻,而後在分頻1時,有一個兩級分頻,而後,在分頻2又有一個8級分頻,最後,就能夠輸出一個頻率,提供給S5PV210的ARM芯片使用,P356頁查到經常使用的CLK值,這裏咱們的S5PV210的ARMCLK爲1000MHz。
那麼,在硬件上是如何實現的呢?
觀察:
開發板上晶振有三顆,兩個24Hz,一個27Hz。(晶振,全名晶體振盪器,成份石英(二氧化硅),晶振用於經過壓電效應給CPU提出振盪頻率,再經過別的電路,將例如正弦曲線波轉換成方波,至關於CPU的起搏器,有了晶振,CPU纔有頻率輸出)。
CPU輸出好比24Hz頻率後,此時,就須要把頻率放大了(不然CPU才24Hz的頻率能幹嘛呢?因此,CPU並非一上電就跑到幾GHz上面去了,跑車再好,跑的再快,從0加速到100,就是蘭博基尼的Aventador他不也得3秒鐘嗎,因此沒有一上電就幾G這回事兒
),在頻率放大的過程當中,首先須要考慮的一個問題就是----phase-locked loop鎖相環。簡單的說,好比復位後,CPU默認工做頻率在400Hz,如今須要升到1000Hz工做,那麼從400-->1000Hz須要一個過程,假設爲時間t1,在t1這段時間內,CPU的頻率是變化的,那麼CPU的狀態就是不穩定的,此時,就須要把頻率鎖定,設置鎖定時間,直到CPU穩定的輸出頻率。
CPU第一次啓動時,PLL有一個默認的初始值,芯片手冊找到P522頁,找到默認的初始頻率:
• APLL: M=200, P=6, S=1 FOUT = (MDIV X FIN )/ (PDIV X 2(SDIV-1))) = 800MHz 
• MPLL: M=667, P=12, S=1 FOUT = (MDIV X FIN) / (PDIV X 2SDIV) = 667MHz 
• EPLL: M=80, P=3, S=3, K=0 FOUT = ((MDIV+KDIV) X FIN) / (PDIV X 2SDIV) = 80MHz 
<ignore_js_op>


由上圖查出可知,ARMCLK的默認頻率爲400MHz
1)查看芯片手冊的P356頁,查出總共有如下幾種由CMU輸出的時鐘
有四種PLLs(APLL,MPLL,EPLL,HPLL),還包括USB_OTG PHY clock。
To generate internal clocks, the following components are used.
• APLL uses FINPLL (refer to Figure 3-1) as input to generate 30MHz ~ 1GHz.
• MPLL uses FINPLL as input to generate 50MHz ~ 2GHz.
• EPLL uses FINPLL as input to generate 10MHz ~ 600MHz.
• VPLL uses FINPLL or SCLK_HDMI27M as input to generate 10MHz ~ 600MHz. This PLL generates 54MHz video clock.
• USB OTG PHY uses XUSBXTI to generate 30MHz and 48MHz
• HDMI PHY uses XUSBXTI or XHDMIXTI to generate 54MHz
經常使用的APLL/MPLL/EPLL/VPLL是幹什麼的呢?P448頁
• APLL: used to generate ARM clock
• MPLL: used to generate system bus clock and several special clocks
• EPLL: used to generate several special clocks
• VPLL: used to generate Video clocks. Usually, generates 54 MHz.

2)先查看APLL PMS的倍頻表
P357 S5PV210_UM_REV1.1.pdf -- Table 3-1. APLL PMS Value 
<ignore_js_op>

查看芯片手冊,設置P/M/S的bit,能夠將頻率拉昇到咱們想要的頻率。
那麼咱們要設置升高頻,翻看手冊,P371頁,找到PLL CONTROL REGISTERS。
• (APLL_LOCK, R/W, Address = 0xE010_0000)
• (MPLL_LOCK, R/W, Address = 0xE010_0008)
• (EPLL_LOCK, R/W, Address = 0xE010_0010)
• (VPLL_LOCK, R/W, Address = 0xE010_0020) 


由芯片手冊上顯示APLL的lock time是30us,若是是晶振輸出的頻率是24MHZ,lock time是30毫秒,那麼PLL_LOCKTIME是720,也就是0x2D0。OK,下一篇開始結合u-boot的源碼來分析如何配置系統時鐘

 

Tiny210 U-BOOT(三)----配置時鐘頻率源碼分析
上篇講了配置時鐘的原理,今天就結合源碼具體分析一下。這裏要小吐槽一下三星的S5PV210的芯片手冊,以前的S5PC100的芯片手冊,是按各個模塊來列表的,Clock Control、Interrupt Controller、NFCON(Nandf Flash Controller),一個一個列出來,目錄很是清楚,結果到了S5PV210的芯片手冊上,出現了一個腦殘的section分法,將各具模塊所有按section劃分紅12大section,而後,你就本身去每一個section裏找去吧,如此劃分真是讓人搞不懂三星是什麼意思,可能三星本身也覺是加入的section有些腦殘,在後面的S5PV310的芯片手冊中,又去掉了,恢復了原來的樣子。OK,回到正題。
在u-boot的源碼中,系統時鐘的初始化是放在板文件的lowlevel_init.S文件中的system_clock_init函數中。咱們的tiny210是拷貝的smdkc100,因此,你們能夠先參看smdkc100的lowlevel_init.S文件中的system_clock_init函數。
對於芯片手冊中每個模塊的學習,在瞭解前面的基本原理後,關鍵的寄存器的操做,能夠先瀏覽一下這個模塊的全部寄存器的簡介,對各個寄存器的做用作到心中有數,這樣,能夠大致知道,須要配置哪些寄存器,不至於被下面一大堆的寄存器給弄懵了,系統時鐘的寄存器簡介在P367頁--3.7 REGISTER DESCRIPTION 。
1.設置APLL/MPLL/EPLL/EPLL鎖相環時間
翻看手冊,P371頁,找到PLL CONTROL REGISTERS。
• (APLL_LOCK, R/W, Address = 0xE010_0000)
• (MPLL_LOCK, R/W, Address = 0xE010_0008)
• (EPLL_LOCK, R/W, Address = 0xE010_0010)
• (VPLL_LOCK, R/W, Address = 0xE010_0020) 

<ignore_js_op>


這裏出現了一個問題,APLL/MPLL/EPLL/VPLL的鎖相環的時間是不同的,而像S5PC100,頻率爲667MHz,他的A/M/E/HPLL的鎖相環時間均是300us,而在Exynos 4xxx中,時間又變成了250,這個時間必定要查芯片手冊。那麼,因此,這裏咱們要用4個寄存器來存不一樣的LOCK_OFFSET的值。查詢ATPCS文檔(Procedure Call Standard for the ARM v2.08),在第P15頁找到,r4-r8是用來存儲變量的寄存器,OK,咱們選取4個寄存器來保存值。
APLL的鎖時間爲:24*30=720    --0x2D0
MPLL的鎖時間爲:24*200=4800  --0x12C0
EPLL的鎖時間爲:24*375=9000  --0x2328
VPLL的鎖時間爲:24*100=2400  --0x960
將上面四個值存入寄存器r4--r8,這裏爲了表示代碼的專業,是資深老菜鳥Codeing,因此使用寄存器的別名v1--v4,而後再將寄存值的分別存入APLL_LOCK/MPLL_LOCK/EPLL_LOCK/VPLL_LOCK對應的寄存器地址中

  1. ldr    r0, =0xE0100000                /*ELFIN_CLOCK_POWER_BASE*/
  2. ldr    v1, =0x2D0
  3. ldr    v2, =0x12C0
  4. ldr    v3, =0x2328
  5. ldr    v4, =0x960
  6. str    v1, [r0, #0x00]                /*APLL_LOCK_OFFSET*/
  7. str    v2, [r0, #0x08]                /*MPLL_LOCK_OFFSET*/
  8. str    v3, [r0, #0x10]                /*EPLL_LOCK_OFFSET*/
  9. str    v4, [r0, #0x20]                /*VPLL_LOCK_OFFSET*/
複製代碼

2.開始倍頻
2.1倍頻APLL
咱們只要把P=3 M=125,S=1便可讓輸出頻率爲1000MHz,即MSOUT_MSYS=1000MHz,那麼配置哪一個寄存器能夠倍頻呢?繼續向下看手冊,在P372頁找到APLL_CON0(0xE010_0100)。

<ignore_js_op>


上面這個表顯示出如何配置P/M/S位,那麼以下代碼,咱們能夠配置輸出爲1000MHz。

  1. ldr    r1, =(1<<31 |125<<16 |3<<8 |1<<0)  @APLL_VAL=(1<<31 | 445<<16 | 0x4<<8 | 1<<0)
  2. /*APLL_CON0_OFFSET 0x100*/
  3. str    r1, [r0, #APLL_CON_OFFSET]      @24  1000  APLL  3  125  1  2000.0  FOUTAPLL = 1000.0MHz
複製代碼

2.2倍頻MPLL
MPLL的P/M/S位配置爲P=12 M=667,S=1,按上面的配置爲MPLL_CON寄存器,Address=0xE010_0108


<ignore_js_op>


  1. ldr    r1, =(1<<31 |667<<16 |12<<8 |1<<0)  @MPLL_VAL=(1<<31 |667<<16 |12<<8 |1<<0)
  2. /*MPLL_CON0_OFFSET 0x108*/
  3. str    r1, [r0, #MPLL_CON_OFFSET]      @24  667  MPLL  12  667  1  667.0  FOUTAPLL = 667.0MHz
複製代碼

2.3倍頻EPLL
EPLL的P/M/S位配置爲P=3 M=48,S=2,按上面的配置配置EPLL_CON寄存器,Address=0xE010_0110
<ignore_js_op>

 

  1. ldr    r1, =(1<<31 |108<<16 |6<<8 |3<<0)  @VPLL_VAL=(1<<31 |108<<16 |6<<8 |3<<0)
  2. /*VPLL_CON0_OFFSET 0x120*/
  3. str    r1, [r0, #VPLL_CON_OFFSET]      @24  54  VPLL  6  108  3  54.0  FOUTAPLL = 54.0MHz
複製代碼

OK,倍頻完成,代碼整理一下:

  1. ldr    r1, =(1<<31 |125<<16 |3<<8 |1<<0)  @APLL_VAL=(1<<31 | 445<<16 | 0x4<<8 | 1<<0)
  2. /*APLL_CON0_OFFSET 0x100*/
  3. str    r1, [r0, #APLL_CON_OFFSET]      @24  1000  APLL  3  125  1  2000.0  FOUTAPLL = 1000.0MHz 
  4. ldr    r1, =(1<<31 |667<<16 |12<<8 |1<<0)  @MPLL_VAL=(1<<31 |667<<16 |12<<8 |1<<0)
  5. /*MPLL_CON0_OFFSET 0x108*/
  6. str    r1, [r0, #MPLL_CON_OFFSET]      @24  667  MPLL  12  667  1  667.0  FOUTAPLL = 667.0MHz 
  7. ldr    r1, =(1<<31 |48<<16 |3<<8 |2<<0)  @EPLL_VAL=(1<<31 |48<<16 |3<<8 |2<<0)
  8. /*EPLL_CON0_OFFSET 0x110*/
  9. str    r1, [r0, #EPLL_CON_OFFSET]      @24  96  EPLL  3  48  2  96.0  FOUTAPLL = 96.0MHz  
  10. ldr    r1, =(1<<31 |108<<16 |6<<8 |3<<0)  @VPLL_VAL=(1<<31 |108<<16 |6<<8 |3<<0)
  11. /*VPLL_CON0_OFFSET 0x120*/
  12. str    r1, [r0, #VPLL_CON_OFFSET]      @24  54  VPLL  6  108  3  54.0  FOUTAPLL = 54.0MHz
複製代碼

參考芯片手冊P361頁時鐘生成電路圖,系統的MSYS/DSYS/PSYS的CLK所有來自於APLL和MPLL,如今開始對APLL/MPLL進行分頻,分給不一樣的設備,如Cotrex A8內核或其它設備。在分頻以前,必需要先選擇時鐘源,不然怎麼分頻呢?

3.選擇時鐘源<ignore_js_op>


參考芯片手冊P361頁時鐘生成電路圖,以APLL以例,查找FOUTAPLL和FINPLL
<ignore_js_op>


由圖所知,FINPLL是沒有倍頻的頻率,咱們要選的是倍頻後的,因此應該選擇FOUTAPLL,依此類推,後面幾位選上FOUTMPLL/FOUTEPLL/FOUTVPLL。那第[28]位,ONENADN選擇什麼呢?往上查一下ONENAND用的是什麼CLK,在P363頁查找到用的是PSYS,因此這裏應該選擇0。在Tiny210中是沒有接ONENADN的吧,咱們用的是NAND FLASH,所以,這一位應該能夠不配置。
<ignore_js_op>

 

  1. //CLK_SRC0_OFFSET            0x200
  2. ldr    r1, [r0, #CLK_SRC0_OFFSET]
  3. ldr    r2, =(0<<28|1<<12 |1<<8 |1<<4 |1<<0)
  4. orr    r1, r1, r2
  5. str    r1, [r0, #CLK_SRC0_OFFSET]
複製代碼

OK,如今開始對APLL/MPLL進行分頻了。

 

 

4.分頻
繼續向下查找芯片手冊,找到P387頁,系統時鐘分頻操做的寄存器是CLK_DIV0,Address = 0xE010_0300
<ignore_js_op>



回過頭去,再去看時鐘頻率的產生圖,最終MSYS有五個CLK,ARMCLK/HCLK_MSYS/PCLK_MSYS/HCLK_IMEM/SCLK_DMC0。P356查看經常使用CLK
Values for the high-performance operation:
•  freq(ARMCLK)   = 1000 MHz
•  freq(HCLK_MSYS)   = 200 MHz
•  freq(HCLK_IMEM)   = 100 MHz
•  freq(PCLK_MSYS)   = 100 MHz
•  freq(HCLK_DSYS)   = 166 MHz
•  freq(PCLK_DSYS)   = 83 MHz
•  freq(HCLK_PSYS)   = 133 MHz
•  freq(PCLK_PSYS)   = 66 MHz
•  freq(SCLK_ONENAND)    = 133 MHz, 166
在寄存器CLK_DIV0的表格上還有這樣一段話:There are operating frequency limitations. The maximum operating frequency of SCLKAPLL, SCLKMPLL, SCLKA2M, HCLK_MSYS, and PCLK_MSYS are 1GHz, 667 MHz, 400 MHz, 200 MHz, and 100 MHz, respectively. These operating clock conditions must be met through CLK_DIVX configuration.
查找SCLKAPLL,找到SCLKAPLL的來源

<ignore_js_op>


OK,將上面列表中CLK也整理一下,列表出來:
•  freq(SCLKAPLL)   = 1000 MHz
•  freq(SCLKMPLL)   = 667 MHz
•  freq(SCLKA2M)   = 400 MHz 

接着分析圖,在[19:16]要用到的MSOUT_DSYS,他的頻率來自MPLL,因此MSOUT_DSYS =MPLL = 667

<ignore_js_op>


下面一位一位的開始分析:
[2:0]ARMCLK = 1000MHz,MOUT_MSYS = 2000MHz,ARMCLK = MOUT_MSYS/(0+1),APLL_RATIO爲0,0<<0
[6:4]SCLKA2M = 400MHz,SCLKAPLL = 1000MHz,因爲沒法整除,而SCLKA2M最大爲400,咱們只要保證不超過就行,能夠低一些,SCLKA2M = SCLKAPLL / (2 + 1)  = 333,A2M_RATIO爲2,2<<4
[10:8]HCLK_MSYS = 200MHz,ARMCLK = 1000MHz,HCLK_MSYS = ARMCLK /(4 + 1),HCLK_MSYS_RATIO爲4,4<<8
[14:12]PCLK_MSYS = 100MHz,HCLK_MSYS = 200MHz,PCLK_MSYS = HCLK_MSYS / (1 + 1) ,PCLK_MSYS_RATIO爲1,1<<12  
[19:16]HCLK_DSYS = 166MHz,MOUT_DSYS = 667MHz,HCLK_DSYS = MOUT_DSYS / (3+ 1) ,HCLK_DSYS_RATIO爲1,3<<16
[22:20]PCLK_DSYS = 83MHz,HCLK_DSYS = 166MHz,PCLK_DSYS = HCLK_DSYS / (1 + 1) ,PCLK_DSYS_RATIO爲1,1<<20 
[27:24]HCLK_PSYS = 133MHz,MOUT_PSYS = 667MHz,HCLK_PSYS = MOUT_PSYS / (4 + 1) ,HCLK_PSYS_RATIO爲1,4<<24 
[30:28]PCLK_PSYS = 66MHz,HCLK_PSYS = 133MHz,PCLK_PSYS = HCLK_PSYS / (1 + 1) ,PCLK_PSYS_RATIO爲1,1<<28 

OK,至此,分頻完成,整理代碼以下: 

  1. /*將r0的值偏移300,此時地址爲CLK_DIV0 = 0xE010_0300),保存到r1中*/
  2. ldr r1, [r0, #0x300]            /*CLK_DIV0_OFFSET*/
  3. /*將CLK_DIV0中有關分頻的位所有清零0~30*/
  4. ldr    r2, =0x7fff                /*CLK_DIV0_MASK*/
  5. bic    r1, r1, r2
  6. /*分頻給系統時鐘*/
  7. ldr    r2, =(0<<0)|(2<<4)|(4<<8)|(1<<12)|(3<<16)|(1<<20)|(4<<24)|(1<<28)       
  8. orr    r1, r1, r2
  9. str    r1, [r0, #0x300]  //CLK_DIV0_OFFSET
複製代碼

5.延時一段時間

  1.     /*delay*/
  2.     mov    r1, #0x10000
  3. 1:  subs   r1, r1, #1
  4.     bne    1b
複製代碼

6.返回指針

  1. mov    pc, lr
複製代碼

OK,到此,系統時鐘頻率的所有設置工做完成。最後將代碼從新歸整以下:

  1. system_clock_init:
  2.     ldr    r0, =ELFIN_CLOCK_POWER_BASE    @0xe0100000
  3.         ldr    v1, =0x2D0
  4.         ldr    v2, =0x12C0
  5.         ldr    v3, =0x2328
  6.         ldr    v4, =0x960
  7.         str    v1, [r0, #0x00]                /*APLL_LOCK_OFFSET*/
  8.         str    v2, [r0, #0x08]                /*MPLL_LOCK_OFFSET*/
  9.         str    v3, [r0, #0x10]                /*EPLL_LOCK_OFFSET*/
  10.         str    v4, [r0, #0x20]                /*VPLL_LOCK_OFFSET*/
  11. /**************************************************************************
  12.   APLL_CON0_OFFSET 0x100   @24  1000  APLL  3  125  1  2000.0  FOUTAPLL = 1000.0MHz
  13.   MPLL_CON0_OFFSET 0x108   @24  667  MPLL  12  667  1  667.0  FOUTAPLL = 667.0MHz
  14.   EPLL_CON0_OFFSET 0x110   @24   54  VPLL  6  108  3  54.0  FOUTAPLL = 54.0MHz 
  15. ***************************************************************************/
  16.     ldr    r1, =(1<<31 |125<<16 |3<<8 |1<<0)  @APLL_VAL=(1<<31 | 445<<16 | 0x4<<8 | 1<<0)
  17.     str    r1, [r0, #APLL_CON0_OFFSET]      
  18.     ldr    r1, =(1<<31 |667<<16 |12<<8 |1<<0)  @MPLL_VAL=(1<<31 |667<<16 |12<<8 |1<<0)
  19.     str    r1, [r0, #MPLL_CON_OFFSET]     
  20.     ldr    r1, =(1<<31 |48<<16 |3<<8 |2<<0)  @EPLL_VAL=(1<<31 |48<<16 |3<<8 |2<<0)
  21.     str    r1, [r0, #EPLL_CON_OFFSET]     
  22.     ldr    r1, =(1<<31 |108<<16 |6<<8 |3<<0)  @VPLL_VAL=(1<<31 |108<<16 |6<<8 |3<<0)
  23.     str    r1, [r0, #VPLL_CON_OFFSET]     
  24.     //CLK_SRC0_OFFSET            0x200
  25.     ldr    r1, [r0, #CLK_SRC0_OFFSET]
  26.     ldr    r2, =(0<<28|1<<12 |1<<8 |1<<4 |1<<0)
  27.     orr    r1, r1, r2
  28.     str    r1, [r0, #CLK_SRC0_OFFSET]
  29.     /*將r0的值偏移300,此時地址爲CLK_DIV0 = 0xE010_0300,保存到r1
  30.      * 將CLK_DIV0中有關分頻的位所有清零0~30
  31.      */
  32.     ldr r1, [r0, #0x300]            /*CLK_DIV0_OFFSET*/
  33.     ldr    r2, =0x7fff                /*CLK_DIV0_MASK*/
  34.     bic    r1, r1, r2
  35.     /*分頻給系統時鐘*/
  36.     ldr    r2, =(0<<0)|(2<<4)|(4<<8)|(1<<12)|(3<<16)|(1<<20)|(4<<24)|(1<<28)      
  37.     orr    r1, r1, r2
  38.     str    r1, [r0, #0x300]  //CLK_DIV0_OFFSET
  39.     mov    r1, #0x10000
  40. 1:    subs    r1, r1, #1
  41.     bne    1b
  42.     mov    pc, lr
複製代碼

OK,至此,u-boot中最簡單的系統時鐘配置完成,上面代碼中各個位的配置所有直接用數字,是爲了方便新學的朋友看代碼對比各個bit的值,實際中,應該儘可能用宏進行替換,後面,該是那幾個讓人頭疼的NAND FLASH、DDR這些東西了,頭疼,確實讓人頭疼

 

補充一點,在P363頁,有系統各個時鐘的分配圖,通常各個模塊進來的時鐘,你想要查詢是多少Hz,均可以在這裏查的到<ignore_js_op>


好比,像P825頁WDT的原理圖中,進來的PCLK是多少,就能夠在上面的表中查出來,PCLK = 66MHz。
<ignore_js_op>


上面的圖中,VIC表示中斷控制器,CFCON表示COMPACT FLASH CONTROLLER,CF卡控制器;NFCON表示NAND FLASH CONTROLLER,NAND FLASH控制器;TSADC表示TP的時鐘頻率,其他的PWM,WDT,RTC你們都知道,我就不用說了,這個表,在裸板開發,熟悉各個模塊的原理是頗有用的,至於在實際的Linux設備驅動開發調試中,好像用處不大,不過,知道原理,對本身總歸沒壞處。

 

 

 

Tiny210 U-BOOT(四)----Nand Flash原理
1.查看芯片型號
我在上次講系統時鐘的時候,提過一下,在S5PV210下面的那一片就是Nand Flash,旁邊的那4片是DDR,個人型號是K9F2G08UOB
<ignore_js_op>


查找三星芯片的命名手冊,這個網上有,PDF名稱叫----三星_Nand_Flash_芯片型號命名規則.pdf,打開,查找K9F2G08UOB所表明的具體參數以下:
芯片類型:Nand Flash
Small Classification:SLC Normal 
容量:2G/8bit
Technology:Normal
Organization:x8
Vcc:2.7V~3.6V
Mode:Normal
Generation:3rd Generation
整理一下信息:Nand Flash , 容量256M(2G/8bit) , 是用SLC存儲(Single Level Cell單層存儲) , 總線爲8bit , 工做電壓爲2.7V~3.6V , 第3批次。

2.存儲原理
打開K9F2G08UOB芯片手冊的P6頁
<ignore_js_op>


由芯片手冊上可知,這款NAND FLASH,頁大小2K,塊大小128K,意思就是讀寫操做,每次最小爲2K,擦除操做每次最小爲128K。什麼原理呢,繼續向下看。
<ignore_js_op>


由上圖可知:1page = 2K, 1 block = 128K, 1Block=64Page, 芯片有128K
如今開始計算:
1 Block = 64*2 = 128K
那麼咱們FLASH的容量就2K * 128K = 256M (圖中每一個頁還有Page都有保留區,因此實際上咱們用不到256M),這裏就有一個問題,爲啥要設計成這樣?
由於Nand Flash是用於設計成嵌入式系統的內存,程序進行內存的尋址時,爲了提搞速度,因此,將線性的內存在邏輯上進行分塊,分頁,造成邏輯上的立體結構,這樣,尋址就會方便,先找到是哪一塊,再找到是哪一頁,最後找到是這一頁的哪一個位置,這要比起從頭至尾遍歷,就要快不少了。

到這裏,存儲原理基本弄清楚了,Nand Flash的擦除操做是以block塊爲單位的,與此相對應的是其餘不少存儲設備,是以bit位爲最小讀取/寫入的單位,Flash是一次性地擦除整個塊:在發送一個擦除命令後,一次性地將一個block,常見的塊的大小是128KB/256KB,所有擦除爲1,也就是裏面的內容所有都是0xFF了。這裏就有了一個新問題,爲何擦除是寫1,而不是寫0呢?這和咱們正常的邏輯怎麼是個反的
所謂1和0的介定,在存儲器中,是經過向存儲單元中施加電壓來控制向裏面是充電荷仍是釋放電荷。因此,本質上來講,數據0和1的表示,就是存儲單元上的電壓否超過一個特定的電壓值,一般叫閾值,超過了這個閾值,就是1,沒超過,就是0。因此說,所有擦除爲1,也就意味着對整個block進行一次所有的充電。那這裏,咱們又有新問題了,那爲何不把擦除作成統一放電呢
關於這個,我也沒找到資料,書上也沒有查到相關的,若是有人知道,麻煩也一併指點下我。我我的的理解是放電是時時刻刻存在的,放電比充電容易。就像咱們的這些存儲芯片,你充了一次電之後,是否是就一直爲1了?不可能,能量是會衰減的,因此,你充一次電之後,隔了一段時間,無論你願意不肯意,電荷必然會慢慢釋放掉,那麼,若是你要保證你的數據的正解性,仍然爲1,不會被認爲是0,你還得再充一次電,這個時間大概是多少,目前公認的標準是,存儲體中電容的數據有效保存期上限是64ms(毫秒,1/1000 秒),也就是說每一行刷新的循環週期是 64ms。在書沒有查到,至因而不是,我也不清楚,但願高手能考證一下,因此,反正是要隔一段時間還得再充電一下。那麼,如今咱們再來理解一下,SRAM和DRAM的區別,百度百科裏查一下SRAM的介紹----SRAM不須要刷新電路即能保存它內部存儲的數據。而DRAM(Dynamic Random Access Memory)每隔一段時間,要刷新充電一次,不然內部的數據即會消失,所以SRAM具備較高的性能。可是SRAM也有它的缺點,即它的集成度較低,相同容量的DRAM內存能夠設計爲較小的體積,可是SRAM卻須要很大的體積,且功耗較大
到這裏,你們是否是有點明白了,爲何SRAM不須要刷新電路,你不須要刷新電路,那你怎麼解決電荷釋放的問題?這世上沒有天上掉餡餅的事兒,別人不給你,那就本身帶一個唄,因此SRAM是本身帶有刷新電路的,因此,它體積大,因此它容量作不上去。因此,SRAM和DRAM的區別,就很是好理解了,一個帶充電電路,一個不帶,而DRAM的充電是由內存控制器隔一段時間去充電的。
因此,到這裏,我理解的Nand Flash爲何擦除是擦爲1,就是由於放電比充電容易,你控制每一個存儲單元去放電,確定比控制每個存儲單元去充電要容易的多,硬件實現也要簡單的多吧。這裏再次聲明一下,這屬於本身的推斷,沒有通過論證,若是有高手,在哪本書上有介紹,但願能指點或論證一下。

3.原理圖和引腳分析 
<ignore_js_op>


芯片封裝圖,也就是實際的Pin腳位置圖,N.C表示未鏈接,48pin,TSOP1封裝,軟件工程師,能夠主要和原理圖對比一下,看實際各個pin腳的位置,不過,其實,不看也不要緊,看原理圖吧

<ignore_js_op>


看Nand Flash的芯片手冊對各個pin腳的描述(K9F2G08U0B.pdf--P5)
經過觀察CE2#和R/B2#處於NC狀態,也就是未鏈接狀態,因此,這兩個芯腳去掉,還剩下8個I/O和其他7條線
I/O0 ~ I/O7:  
<ignore_js_op>


file:///C:/Users/Xuer/AppData/Local/Temp/Wiz/dce2f929-663b-41db-8bdf-3f4615c43d4c_128_files/IO.jpg
I/O pin用於輸入command,addresss和data,以及在讀操做時輸出數據。當芯片沒有被選中或沒法正常輸出數據時,處於高阻態(high-z即高阻態,至關於隔斷狀態,當處於高阻抗狀態時,其輸出至關於斷開狀態,沒有任何邏輯控制功能)
CLE(COMMAND LATCH ENABLE): 指示當前數據爲控制命令
<ignore_js_op>


file:///C:/Users/Xuer/AppData/Local/Temp/Wiz/dce2f929-663b-41db-8bdf-3f4615c43d4c_128_files/CLE.jpg
CLE輸入控制路徑發送到命令寄存器。
ALE(ADDRESS LATCH ENABLE): 指示當前數據爲地址
<ignore_js_op>


file:///C:/Users/Xuer/AppData/Local/Temp/Wiz/dce2f929-663b-41db-8bdf-3f4615c43d4c_128_files/ALE.jpg
CE#(CHIP ENABLE):芯片使能,俗稱片選信號,表示flash chip是否被系統選中爲當前操做芯片,低電平表示選中

<ignore_js_op>


file:///C:/Users/Xuer/AppData/Local/Temp/Wiz/dce2f929-663b-41db-8bdf-3f4615c43d4c_128_files/CE.png
RE#(READ ENABLE): 讀使能,RE是串行數據輸入,低電平時讀取data到I/O總線,RE降低沿時讀取數據有效,同時internal column address counter(內部列地址計數器)加1
<ignore_js_op>


file:///C:/Users/Xuer/AppData/Local/Temp/Wiz/dce2f929-663b-41db-8bdf-3f4615c43d4c_128_files/RE.png
WE#(WRITE ENABLE): 寫使能,低電平時向I/O中寫入Command,address和data,在上升沿時操做完成。
<ignore_js_op>


file:///C:/Users/Xuer/AppData/Local/Temp/Wiz/dce2f929-663b-41db-8bdf-3f4615c43d4c_128_files/WE.png
WP#(WRITE PROTECT): 寫保護,在電源置換過程當中處於有效的低電平狀態,保護flash裏面的數據不受改寫。當WP低電平有效時,高電平產生復位
file:///C:/Users/Xuer/AppData/Local/Temp/Wiz/dce2f929-663b-41db-8bdf-3f4615c43d4c_128_files/WP.png
<ignore_js_op>


R/B#(READY/BUSY OUTPUT): 讀/忙狀態輸出,表示芯片是處於Read仍是Busy狀態,低電平表示Busy,返回高電平時表示處理完成。當芯片未被選中或沒法輸出數據時不會處於high-z狀態,仍可用於檢測芯片的閒忙狀態
<ignore_js_op>


file:///C:/Users/Xuer/AppData/Local/Temp/Wiz/dce2f929-663b-41db-8bdf-3f4615c43d4c_128_files/69b37b2a147e1c0e57a1a77cdf168333.png
Vcc/Vss/N.C(POWER/GROUND/NO CONNECTION):Vcc是供電電壓,Vss接地,N.C沒定義
file:///C:/Users/Xuer/AppData/Local/Temp/Wiz/dce2f929-663b-41db-8bdf-3f4615c43d4c_128_files/VCC.jpg
<ignore_js_op>






FAQ: 爲何要有CLE和ALE?
好比命令鎖存使能(Command Latch Enable,CLE)地址鎖存使能(Address Latch EnableALE),那是由於,Nand Flash8I/O,並且是複用的,也就是,能夠傳數據,也能夠傳地址,也能夠傳命令,爲了區分你當前傳入的究竟是啥,因此,先要用發一個CLE(或ALE)命令,告訴nand Flash的控制器一聲,我下面要傳的是命令(或地址),這樣,裏面才能根據傳入的內容,進行對應的動做。不然,nand flash內部,怎麼知道你傳入的是數據,仍是地址,仍是命令啊,也就沒法實現正確的操做了。那麼,爲何很少幾個I/O呢?

FAQ:Nand Flash爲何
只設計8個I/O引腳,有什麼好外?
1) 減小外圍引腳:相對於並口(Parellel)的Nor Flash的48或52個引腳來講,的確是大大減少了引腳數目,這樣封裝後的芯片體積,就小不少。如今芯片在向體積更小,功能更強,功耗更低發展,減少芯片體積,就是很大的優點。同時,減小芯片接口,也意味着使用此芯片的相關的外圍電路會更簡化,避免了繁瑣的硬件連線。

2) 提升系統的可擴展性,由於沒有像其餘設備同樣用物理大小對應的徹底數目的addr引腳,在芯片內部換了芯片的大小等的改動,對於用所有的地址addr的引腳,那麼就會引發這些引腳數目的增長,好比容量擴大一倍,地址空間/尋址空間擴大一倍,因此,地址線數目/addr引腳數目,就要多加一個,而對於統一用8I/O的引腳的Nand Flash,因爲對外提供的都是統一的8個引腳,內部的芯片大小的變化或者其餘的變化,對於外部使用者(好比編寫nand flash驅動的人)來講,不須要關心,只是保證新的芯片,仍是遵循一樣的接口,一樣的時序,一樣的命令,就能夠了。這樣就提升了系統的擴展性。

4.Nand原理分析
Nand Flash是一個典型的串口通訊的芯片,咱們知道,總線的三大元素地址、命令、數據。先看一個NOR FLASH的原理圖:
<ignore_js_op>


上圖中,地址線,數據線,命令線,三者清清楚楚,再來看咱們的NAND FLASH的原理圖,只有8根Data線,大部分線都是NC,未鏈接狀態。
<ignore_js_op>

1)Nand Flash原理圖上只有8根線Data0-Data7,是典型的串口通訊,由於只有8根線,因此地址線、數據線確定是複用的
2)Nand Flash K9F2G08UOB容量爲256M * 8bit,它的地址應該有28位,原理圖上只有Data0-Data7,因此須要發出屢次地址信號,下面是地址時序圖,A0-A28,一共要發送五次,每次發8bit。
<ignore_js_op>


3)Nand Flash不能像內存同樣直接讀寫,要先發命令,再發地址,再讀寫數據
<ignore_js_op>


很容易看出,咱們要讀取數據,要用到Read命令,該命令須要2個週期,第一個週期發0x00,第二個週期發0x30。在這個過程,你發了一個命令,對方可否馬上變接收到,並作出反應呢,確定不行,因此,這得須要一個維持時間,這樣才能保證你的數據的有效性。
<ignore_js_op>


4)CLE爲高電平時,Data0-Data7發的是命令;
   ALE爲高電平時,Data0-Data7發的是地址;
   CLE/ALE都爲低電平時,Data0-Data7發的是數據,nWE,表示寫,nRE,表示讀。

下面以一個實際的讀操做,來說一下Nand Flash的時序:
<ignore_js_op>


在開始解釋前,多羅嗦一下」使能」這個詞,使能(Enable),是指使其(某個信號)有效,使其生效的意思......好比,上面圖中的CLE線號,是高電平有效,若是此時將其設爲高電平,咱們就叫作,將CLE使能,也就是使其生效的意思。使能,這個中文翻譯有點怪怪的,有點像WDT(Watch Dog Timer),中文翻譯叫看門狗,這是英文直譯,另外一種解釋叫作監視定時器,也許後一種解釋會更好一點兒吧。

按照時序來劃分,總共下面七個信號共同協調工做,咱們先來豎着看,看同一時刻的操做:<ignore_js_op>


咱們來一塊兒看看,我在圖6中的特地標註的①邊上的黃色豎線。
黃色豎線所處的時刻,是在發送讀操做的第一個週期的命令0x00以前的那一刻。
讓咱們看看,在那一刻,其所穿過好幾行都對應什麼值,以及進一步理解,爲什麼要那個值。
(1)黃色豎線穿過的第一行,是CLE。還記得前面介紹命令所存使能(CLE)那個引腳吧?CLE,將CLE置1,就說明你將要經過I/O複用端口發送進入Nand Flash的,是命令,而不是地址或者其餘類型的數據。只有這樣將CLE置1,使其有效,才能去通知了內部硬件邏輯,你接下來將收到的是命令,內部硬件邏輯,纔會將受到的命令,放到命令寄存器中,才能實現後面正確的操做,不然,不去將CLE置1使其有效,硬件會無所適從,不知道你傳入的究竟是數據仍是命令了。
(2)而第二行,是nCE,那一刻的值是0。這個道理很簡單,你既然要向Nand Flash發命令,那麼先要選中它,因此,要保證nCE爲低電平,使其有效,也就是片選有效。
(3)第三行是nWE,意思是寫使能。由於接下來是往nand Flash裏面寫命令,因此,要使得nWE有效,因此設爲低電平。
(4)第四行,是ALE是低電平,而ALE是高電平有效,此時意思就是使其無效。而對應地,前面介紹的,使CLE有效,由於將要數據的是命令,而不是地址。若是在其餘某些場合,好比接下來的要輸入地址的時候,就要使其有效,而使CLE無效了。
(5)第五行,nRE,此時是高電平,無效。能夠看到,知道後面低6階段,才變成低電平,纔有效,由於那時候,要發生讀取命令,去讀取數據。
(6)第六行,就是咱們重點要介紹的,複用的輸入輸出I/O端口了,此刻,尚未輸入數據,接下來,在不一樣的階段,會輸入或輸出不一樣的數據/地址。
(7)第七行,R/nB,高電平,表示R(Ready)/就緒,由於到了後面的第5階段,硬件內部,在第四階段,接受了外界的讀取命令後,把該頁的數據一點點送到頁寄存器中,這段時間,屬於系統在忙着幹活,屬於忙的階段,因此,R/nB才變成低,表示Busy忙的狀態的。
讀操做,查P7頁的Command Set,1st Cycle發00,2nd Cycle發30。

<ignore_js_op>


<ignore_js_op>

 
 
<ignore_js_op>
 

上面介紹了時刻(1)的各個信號的值,以及爲什麼是這個值以後,相信,後面的各個時刻,對應的不一樣信號的各個值,下面咱們來分析一下其他時刻都幹了什麼。

上面圖中標出來的,1-6個階段,具體是什麼含義。
(1) 操做準備階段:此處是讀(Read)操做,因此,先發一個圖5中讀命令的第一個階段的0x00,表示,讓硬件先準備一下,接下來的操做是讀。
(2) 發送兩個週期的列地址。也就是頁內地址,表示,我要從一個頁的什麼位置開始讀取數據。
(3) 接下來再傳入三個行地址。對應的也就是頁號。
(4) 而後再發一個讀操做的第二個週期的命令0x30。接下來,就是硬件內部本身的事情了。
(5) Nand Flash內部硬件邏輯,負責去按照你的要求,根據傳入的地址,找到哪一個塊中的哪一個頁,而後把整個這一頁的數據,都一點點搬運到頁緩存中去。而在此期間,你所能作的事,也就只須要去讀取狀態寄存器,看看對應的位的值,也就是R/nB那一位,是1仍是0,0的話,就表示,系統是busy,仍在」忙「(着讀取數據),若是是1,就說系統活幹完了,忙清了,已經把整個頁的數據都搬運到頁緩存裏去了,你能夠接下來讀取你要的數據了。
對於這裏。估計有人會問了,這一個頁一共2048+64字節,若是我傳入的頁內地址,就像上面給的1028一類的值,只是想讀取1028到2011這部分數據,而不是頁開始的0地址整個頁的數據,那麼內部硬件卻讀取整個頁的數據出來,豈不是很浪費嗎?答案是,的確很浪費,效率看起來不高,可是實際就是這麼作的,並且自己讀取整個頁的數據,相對時間並不長,並且讀出來以後,內部數據指針會定位到你剛纔所制定的1208的那個位置。
(6) 接下來,就是你「竊取「系統忙了半天以後的勞動成果的時候了,呵呵。經過先去Nand Flash的控制器中的數據寄存器中寫入你要讀取多少個字節(byte)/字(word),而後就能夠去Nand Flash的控制器的FIFO中,一點點讀取你要的數據了。
至此,整個Nand Flash的讀操做就完成了。
對於其餘操做,能夠根據我上面的分析,一點點本身去看datasheet,根據裏面的時序圖去分析具體的操做過程,而後對照代碼,會更加清楚具體是如何實現的。

5.地址傳送
Nand Flash有一個"位反轉"的特性,就是在讀一頁的時候,有一位或某幾位,可能出現原來是1,讀完了之後就是0,Linux系統中,通常叫作OOB(Out Of Band),這個區域,基於Nand Flash的硬件特性:數據在讀寫時候相對容易錯誤,因此爲了保證數據的正確性,必需要有對應的檢測和糾錯機制,此機制被叫作EDC(Error Detection Code)/ECC(Error Code Correction,或者Error Checking and Correcting),因此設計了多餘的區域,用於放置數據的校驗值。這也就是每一頁2K Byte旁邊都有64 Byte的一個區域,這個64Byte就是OOB區域。

好比訪問地址8000
8000/2048 = 3.9,因此地址在第3頁
8000-3*2048 = 1856,因此在第3頁的第1856個地址
因此,1,2Cycle發的就是Ox740(1856),第1st Cycle發的就是0x40,2nd Cycle發的就是0x7
而3,4,5Cycle發的是3,第3rd Cycle發的是0x3,第4th Cycle發的是0x0,第5th Cycle發的是0x0。OK,至此,Nand Flash的原理分析完畢,將經常使用的存儲原理,還有引腳功能,時序功能,和地址傳送簡單的講了一些,這些對於後面分析u-boot源碼中的Nand Flash的初始化代碼和搬內核的代碼頗有用,理解了Nandf Flash的基本原理,你們再衝到u-boot內核中去對Nand Flash的代碼來庖丁解牛吧!

扯些和技術無關的東西,我的以爲,學習內核,無論是u-boot這種小型的,仍是Linux和Android這種超巨型的內核,有點相似於 趙子龍長板橋上七進七出 ,只不過,剛開始的時候,咱們每次衝進去找到一個點,而後,被打的灰頭土臉,而後,爬出來,看視頻,看書,查資料,補充能量,而後,補充的差很少了,再衝進去,而後,可能再被戰勝,那再回來補充,而後,再衝去,這時可能這個點,就被你打倒了。而後,可能再被遇到一個點,剛剛神氣不了一下子,又被打的灰頭土臉,咱們又得出去再補充能量,而後,再衝進來,當經歷的失敗和挫折多了,有一天,雖然咱們可能達不到衝入內核中像 趙子龍長板橋上七進七出,如入無人之境的地步,但,這時的你,應該比通常的人要深刻或走的遠的多了,信念這個東西,你要說虛,確實很虛,根本摸不着,多少錢一斤?不過,若是你願意,你能夠把它裝在本身的心裏裏在,天天均可以感覺的到,很是的真實。不因挫折和失敗而放棄,不懼怕改變和挑戰,在Linux底層的學習路上,與你們共勉之,一塊兒進步。

 

Tiny210 U-BOOT(五)----Nand Flash源碼分析
1.u-boot參考源碼 
Nand Flash的初始化代碼在board/samsung/tiny210/lowlevel_init.S 

2.初始化Nand Flash
在u-boot中,Nand的低級初始化在lowlevel_init.S中的nand_asm_init函數中。打開原理圖,配置各個功能引腳----狀態引腳R/nB, 讀使能引用腳nRE, 片選信號nCE, 命令使能引腳CLE ,地址使能引腳ALE, 寫使能引腳nWE。

<ignore_js_op>


<ignore_js_op>


搜索各個引腳的值,好比R/nB接在Xm0FRnB0上,搜索Xm0FRnB0,發現Xm0FRnB0和ONDXL_INT0/MP03_4共用一個引腳,上圖中將各個信號線的複用引腳所有在左邊用紅色標註出來。
2.1配置片選信號引腳
查找到nCE接在複用引腳Xm0CSn2/NFCSn0/MP01_2上,打開S5PV210芯片手冊,P185頁查找到MP0_1 Control Registe,MP0_1CON[2]的配置以下:

<ignore_js_op>


因此,只需將這一位,配置成011,才能識別成Nand Flash。

  1.         /*ELFIN_GPIO_BASE 0xE0200000*/
  2.     ldr    r0, =ELFIN_GPIO_BASE
  3.      
  4.     /*Nand Flash nCE pin*/
  5.     ldr    r1, [r0, #MP01CON_OFFSET]  @0x2E0
  6.     bic    r1, r1, #(0xf<<8)
  7.     orr    r1, r1, #(0x3<<8)
  8.     str    r1, [r0, #MP01CON_OFFSET] @0x2E0
複製代碼

將Port Group MP0_1 Control Register 的4-5位所有清爲0

<ignore_js_op>

  1.         /*MP01PUD_OFFSET 0x2E8*/
  2.     ldr    r1, [r0, #MP01PUD_OFFSET]
  3.     bic    r1, r1, #(0x3<<4)
  4.     str    r1, [r0, #MP01PUD_OFFSET]
複製代碼

咱們的nCE片選信號是低電平有效,在電路圖中,像咱們的三星的原理圖中,nXX表示低電平有效,而在TI的芯片手冊中,XXn表示低電平有效,好比,上面的芯片手冊中nCE表示低電平有效,而在TI的芯片手冊中,則是CEn,表示的也是低電平片選信號有效,反正看到帶n的引腳,即表示低電平有效。

2.2配置CLE/ALE/nWE/nRE/R/nB引腳
除開nCE引腳,其他的幾個引腳都是複用的MP03,OK,一塊兒配置,查找MP03,P189,Port Group MP0_3 Control Register,配置以下:
Port Group MP0_3 Control Register (MP0_3CON, R/W, Address = 0xE020_0320)

<ignore_js_op>


<ignore_js_op>


先將0-23位所有清零,而後所有置爲0010,也就是所有置爲2

  1.        /*MP03CON_OFFSET 0x320*/
  2.     ldr    r1, [r0, #MP03CON_OFFSET]
  3.     bic    r1, r1, #0xFFFFFF
  4.     ldr    r2, =0x22222222
  5.     orr    r1, r1, r2
  6.     str    r1, [r0, #MP03CON_OFFSET]
複製代碼

將Port Group MP0_3 Control Register的0-23位所有清爲0
Port Group MP0_3 Control Register (MP0_3PUD, R/W, Address = 0xE020_0328)
<ignore_js_op>

  1.      /*MP03PUD_OFFSET 0x328*/
  2.     ldr    r1, [r0, #MP03PUD_OFFSET]
  3.     ldr    r2, =0x3fff
  4.     bic    r1, r1, r2
  5.     str    r1, [r0, #MP03PUD_OFFSET]
複製代碼

2.3 配置Nandf Flash控制器的寄存器NFCONF
Nand Flash Configuration Register (NFCONF, R/W, Address = 0xB0E0_0000) 
先看前四位[3:0]
<ignore_js_op>


[0]位保留,置爲0。
[1]用來配置頁的一個地址週期,這款K9F2G08UOB的page是2K,那麼地址週期是多少,再去看K9F2G08UOB的芯片手冊,看到第P6頁

<ignore_js_op>


顯示地址週期爲5th,因此[1]配爲1
先看[3],這裏是配置是SLC仍是MCL,根據芯片名稱,這款K9F2G08UOB是SLC存儲,因此[3]配爲0
再來看[2],K9F2G08UOB是SLC,PageSize是2K,因此[2]配爲0
整理,前四位配置爲--0010。OK,繼續配置。

<ignore_js_op>


這裏多出了兩個不認識的東西TWRPH1和TWRPH0。查看一下芯片手冊上的NAND FLASH MEMORY TIMING, P693-P694

<ignore_js_op>


TACLS:當將CLE和ALE拉高之後,再過多少時間才能發出寫使能信號(nWE)
TWRPH0:nWE使能持續時間,信號被拉低的時間
TWRPH1:當將CLE和ALE拉高之後,WE使能,這時,後面開始發數據,這就是數據起做用的時間(Data hold time)

TACLS是發給NAND FLASH的,因此去查看K9F2G08UOB的芯片手冊,打開P17頁,並無顯示出來CLE拉高後,通過多少時間nWE,將WE的信號拉低,根據下面的時序圖,顯示:
<ignore_js_op>


打開K9F2G08UOB的芯片手冊,在P10頁查找到各個pin腳在各個狀態下的最小時間

<ignore_js_op>


OK,至此,三個最小時間查出來了,串口通信中最麻煩的就是時序,OK,整理數據
TACLS = tCLS-tWP = 12 -12 =0
TWRPH0 = tWP = 12
TWRPH1 = tDH = 5
OK,如今再返回去看NFCONF寄存器裏這幾個bit的一個計算公式:

這裏S5PV210的NandFlash的HCLK頻率爲133MHz(P363頁),NFCON屬於HCLKD0 = 133MHz,T = 1/133 = 7.5ns
TACLS:配置0,算出時間爲7.5*(0+1)= 7.5ns >0ns
TWRPH0:配置1,算出時間爲7.5*(1+1)= 15ns >12ns
TWRPH1:配置0,算出時間爲7.5*(0+1)= 7.5ns >5ns
經過實際的測試,若是配置成最小值,Nand Flash會出現沒法讀寫的狀況,這裏咱們仍是參考提供的Nand Flash的裸機的參考代碼,將這三位配成1--4--1
TACLS:配置0,算出時間爲7.5*(1+1)= 15ns >0ns
TWRPH0:配置1,算出時間爲7.5*(4+1)= 15ns >37.5ns
TWRPH1:配置0,算出時間爲7.5*(1+1)= 15ns >5ns

OK,符合咱們的最小時序的要求,因此[7:4]1,[11:8]4,[12:15]1。

  1.        /*ELFIN_NAND_BASE    0xB0E00000*/
  2.     ldr    r0, =ELFIN_NAND_BASE
  3.     ldr    r1, [r0, #NFCONF_OFFSET]
  4.     ldr    r2, =0x777F
  5.     bic    r1, r1, r2
  6.     /* NFCONF_VAL    (1<<12)|(4<<8)|(1<<4)|(0<<3)|(0<<2)|(1<<1)|(0<<0) */
  7.     ldr    r2, =NFCONF_VAL
  8.     orr    r1, r1, r2
  9.     str    r1, [r0, #NFCONF_OFFSET]
複製代碼

ECC咱們先不開,其他的所有配置爲0,OK,到此,所有位配置完成。

2.4配置CONTROL REGISTER 
(NFCONT, R/W, ADDRESS = 0XE720_0004)
<ignore_js_op>


第[0]位,讓NAND Flash控制器Enable,這裏設置爲1。使能意思就是生效的意思,爲何會翻譯成使能,這個估計是和WDT翻譯成看門狗的道理同樣,直譯吧。
剩下的幾位,我沒有設置,Nand Flash也沒有出現問題,不過,爲了謹慎期間,仍是用三星的參考代碼,所有設置上。OK,代碼整理一下

  1. ldr    r1, [r0, #NFCONT_OFFSET]
  2. ldr    r2, =0x707C7
  3. bic    r1, r1, r2
  4. /*NFCONT_VAL  (0x1<<23)|(0x1<<22)|(0<<18)|(0<<17)|(0<<16)|(0<<10)|(0<<9)|(0<<8)|(0<<7)|(0<<6)|(0x2<<1)|(1<<0)*/
  5. ldr    r2, =NFCONT_VAL
  6. orr    r1, r1, r2
  7. str    r1, [r0, #NFCONT_OFFSET]
複製代碼

OK,至此Nand Flash的初始化完成,代碼整理以下:

  1. /*
  2. * Nand Interface Init for SMDKC110
  3. */
  4. nand_asm_init:
  5. /* Setting GPIO for NAND */
  6. /* This setting is NAND initialze code at booting time in iROM. */
  7. ldr        r0, =ELFIN_GPIO_BASE
  8. ldr        r1, [r0, #MP01CON_OFFSET]
  9. bic        r1, r1, #(0xf<<8)
  10. orr        r1, r1, #(0x3<<8)
  11. str        r1, [r0, #MP01CON_OFFSET]
  12. /*MP01PUD_OFFSET 0x2E8*/
  13. ldr        r1, [r0, #MP01PUD_OFFSET]
  14. bic        r1, r1, #(0x3<<4)
  15. str        r1, [r0, #MP01PUD_OFFSET]
  16. /*MP03CON_OFFSET 0x320*/ 
  17. ldr        r1, [r0, #MP03CON_OFFSET]
  18. bic        r1, r1, #0xFFFFFF
  19. ldr        r2, =0x22222222
  20. orr        r1, r1, r2
  21. str        r1, [r0, #MP03CON_OFFSET]
  22. /*MP03PUD_OFFSET 0x328*/
  23. ldr        r1, [r0, #MP03PUD_OFFSET]
  24. ldr        r2, =0x3fff
  25. bic        r1, r1, r2
  26. str        r1, [r0, #MP03PUD_OFFSET]
  27. /*ELFIN_NAND_BASE        0xB0E00000*/
  28. ldr        r0, =ELFIN_NAND_BASE
  29. ldr        r1, [r0, #NFCONF_OFFSET]
  30. ldr        r2, =0x777F
  31. bic        r1, r1, r2
  32. /* NFCONF_VAL        (0<<12)|(1<<8)|(0<<4)|(0<<3)|(0<<2)|(1<<1)|(0<<0) */
  33. ldr        r2, =NFCONF_VAL
  34. orr        r1, r1, r2
  35. str        r1, [r0, #NFCONF_OFFSET]
  36. ldr        r1, [r0, #NFCONT_OFFSET]
  37. ldr        r2, =0x707C7
  38. bic        r1, r1, r2
  39. /*NFCONT_VAL (0x1<<23)|(0x1<<22)|(0<<18)|(0<<17)|(0<<16)|(0<<10)|(0<<9)|(0<<8)|(0<<7)|(0<<6)|(0x2<<1)|(1<<0)*/
  40. ldr        r2, =NFCONT_VAL
  41. orr        r1, r1, r2
  42. str        r1, [r0, #NFCONT_OFFSET]
  43. mov        pc, lr
複製代碼

OK,下一步,開始去分析Nand Flash的代碼搬移那一塊的代碼,看Nand Flash是如何工做的。

 

南山一夢 發表於 2013-7-29 12:52
4.分頻
繼續向下查找芯片手冊,找到P387頁,系統時鐘分頻操做的寄存器是CLK_DIV0,Address = 0xE010_0300


  寫的不錯,學習了,關於CMU(時鐘管理單元部分),(A/M/E/V)PLL_LOCK的地址有些筆誤。
              ldr    r0, =ELFIN_CLOCK_POWER_BASE    @0xe0100000
        ldr    v1, =0x2D0
        ldr    v2, =0x12C0
        ldr    v3, =0x2328
        ldr    v4, =0x960

        str    v1, [r0, #0x00]                /*APLL_LOCK_OFFSET*/
        str    v2, [r0, #0x04]                /*MPLL_LOCK_OFFSET*/中間有保留位,實際應該是:str    v2, [r0, #0x08]
        str    v3, [r0, #0x08]                /*EPLL_LOCK_OFFSET*/中間有保留位,實際應該是:str    v2, [r0, #0x10]
        str    v4, [r0, #0x0c]                /*VPLL_LOCK_OFFSET*/中間有保留位,實際應該是:str    v2, [r0, #0x20]

   還有,經過學習發現,ARMCLK、HCLK_MSYS、PCLK_MSYS、HCLK_IMEM分頻參數,實際上是有規律的:
    一、先得出MOUT_MSYS頻率,ARMCLK=MOUT_MSYS / n(1-8取值範圍),參考CLK_DIV0[2:0]計算公式,該值很容易算出
    二、計算出ARMCLK後,HCLK_MSYS=ARMCLK / n(1-8取值範圍),參考CLK_DIV0[10:8]計算公式,該值很容易算出
    三、計算出HCLK_MSYS後,PCLK_MSYS=HCLK_MSYS / n(1-8取值範圍),參考CLK_DIV0[14:12]計算公式,該值很容易算出
    四、計算出HCLK_MSYS後,HCLK_IMEM=HCLK_MSYS / 2,參考CLK_DIV0[6:4]計算公式,該值很容易算出

    有一點困或的地方,就是MOUT_MSYS頻率是怎麼設置出來的?
     個人理解是:MOUT_MSYS分頻==》ARMCLK分頻==》HCLK_MSYS分頻==》PCLK_MSYS、HCLK_IMEM

      不知道是否理解有誤,主席看看哈!

 本帖最後由 南山一夢 於 2013-8-12 01:12 編輯

北山 發表於 2013-8-11 10:41
寫的不錯,學習了,關於CMU(時鐘管理單元部分),(A/M/E/V)PLL_LOCK的地址有些筆誤。
              l ...


謝謝北山的指出,確實寫錯了,已更改,謝謝!
另外關於MOUT_MSYS頻率是怎麼設置出來的?看下面的圖
<ignore_js_op>


MOUT_MSYS的頻率是怎麼出來的,MUX的這個梯形的符號表示的是多路複用器,MOUT_MSYS表示你選的值就是1,能夠向下查找到MUX_DSYS多路複用器,向左找到,能夠發現,這個頻率來自於多路複用器MUX_MPLL的輸出,而MUX_MPLL,若是選0,選的是FIN_PLL的頻率輸入,選1,選的是FOUT_MPLL,MPLL倍頻後的頻率輸入。<ignore_js_op>


而另外ARMCLK、HCLK_MSYS、PCLK_MSYSHCLK_IMEM這幾個頻率,能夠看圖:
ARMCLK::MUX_MSYS---->DIV_APLL,MUX_MSYS的頻率通過DIV_APLL預分頻後就直接生成
HCLK_MSYS: :MUX_MSYS---->DIV_APLL---->DIV_HCLKM,通過兩次分頻
PCLK_MSYS:MUX_MSYS---->DIV_APLL---->DIV_HCLKM---->DIV_PCLKM,通過三次分頻
HCLK_IMEM:MUX_MSYS---->DIV_APLL---->DIV_HCLKM---->DIV_IMEM,通過三次分頻
ARMCLK、HCLK_MSYS、PCLK_MSYSHCLK_IMEM這四個頻率都是最終的輸出頻率,不存在再分頻的關係。

 

 

接着上面的講,上次講完了Nand Flash的低級初始化,而後Nand Flash的操做主要是在board_init_f_nand(),中,涉及到將代碼從Nand Flash中copy到DDR中,這個放到後面實際移植的過程當中再結合源碼流程來分析,正常來講,DDR應該是放在Nand Flash前面開始講,由於DDR相對於Nand Flash來講,更加複雜一些,因此,將DDR拖後來說,一來準備更多的資料研究,二來也是但願能把這個講明白,本身若是都沒搞懂,講給別人聽只能是貽笑大方了,OK,接着開始講DDR。

Tiny210 U-BOOT(六)----DDR內存配置
1.S5PV210內存芯片簡介
最左邊的四片就是內存芯片,是DDR2-800,這表示數據傳輸頻率爲800MHz,外部時鐘頻率200MHz,內部時鐘頻率爲100MHz;由於內部一次傳輸的數據就可供外部接口傳輸4次,雖然以DDR方式傳輸,但數據傳輸頻率的基準——外部時鐘頻率仍要是內部時鐘的兩倍才行。<ignore_js_op>


個人板子上顯示芯片型號爲K4T1G084QF,打開三星的DDR2芯片命名規則文檔(這個文檔上網去搜索):
1~3. 比較簡單,表示是DDR2內存;
4~5. Density,表示容量爲1Gbit = 1Gbit/8 = 128MByte
6~7. Bit Organization,表示數據有多少bit,這裏是08,則表示數據爲8bit,四片拼起來,數據線就是32位。 
<ignore_js_op>


8. # of Internal Banks,表示多少Bank,這裏是4,表示爲8個Banks
9. Interface, VDD, VDDQ,這裏解釋一下這兩個電壓的意思
VDD:北橋芯片供電電壓或者存儲芯片的輸入緩衝和核心邏輯的供電電壓。
VDDQ:存儲芯片的輸出緩衝供電電壓。
Q : SSTL_18, 1.8V, 1.8V,根據芯片手冊得出這兩個電壓爲1.8V
10. Generation
F : F-die,這是什麼意思???
芯片上型號K4T1G084QF,再往上看,會看到有四個BCF7的字母,接着看錶明什麼意思
12. Package
B : FBGA (Lead-Free & Halogen-Free, Flip Chip),表示這FBGA封裝,FBGA是Fine-Pitch Ball Grid Array(意譯爲「細間距球柵陣列」)的縮寫
13. Temp, Power
C : Commercial Temp.(0°C ~ 85°C), Normal Power,表示工做的溫度範圍和供電
14~15. Speed (Wafer/Chip Biz/BGD: 00)
F7 : DDR2-800 (400MHz@CL=6, tRCD=6, tRP=6),表示爲DDR2-800的內存
分析完成,開發板上共有4片這樣的內存芯片,總容量就是128M*4=512M

2.分析硬件原理圖
2.1 從芯片角度
地址線:
A0-A13 14根
BA0,BA1,BA2
3根BA線表示這個芯片被分紅了2^3=8個Bank,每一個Bank則爲128M/8=16M
<ignore_js_op>


這裏出現一個問題,爲何128M裏面還要劃分出8個Bank?
前面在Nand Flash的時候,我曾經分析過,因爲DDR是不自備充電電路的,因此,每隔一段時間,內存控制器就會刷新一次電路,也就是要充一次電,若是隻有一個Bank,那麼結果就是在某一時刻,要麼都充電,要麼都不充電。
[size=14.399999618530273px]<ignore_js_op>


像上面這樣分紅了8個Bank,當我對000充電的時候,我還能夠在010或是剩下的別的Bank中讀取數據,這樣就減少了等待的時間,不用說當電路刷新時,不能讀取數據了。
數據線:DQ0-DQ7*4 = 8bit * 4chips = 32bit

[size=14.399999618530273px]
控制線:nCS,nRAS,nCAS
nCS:片選信號,當這個芯片被片選後,這個芯片就使能了,若是這芯片地址線上有地址,那麼基本上數據線上就要出數據了。
地址線是A0-A13,是14根線,2^14=16K (128M = 2^27),因此地址線上地址要發兩次,一次發行地址,一次發列地址,這就是行列地址複用--nRAS,nCAS這兩根線的做用。
14 + 14 = 28根 + BA0/BA1/BA2 = 2^31=2G,最大支持2G的內存
128M = 2^27,27 - 3 = 24,行地址14根,因此,發過來的列地址是24-14=10
因此說,這個操做順序是,先片選,CS拉低,而後,當RAS拉低時,表示傳過的是行地址,是A0-A13,14位;當CAS拉低時,表示傳過來的是列地址,是A0-A9,那列地址多的幾位怎麼辦呢?很簡單,用來接大內存的嗎,誰說一片內存芯片就只能128M了?

2.2 從處理器角度
地址線 Xm1ADDR0-Xm1ADDR13
Xm1BA0, Xm1BA1, Xm1CSn1/BA2
數據線 Xm1DATA0-Xm1DATA31
控制線 Xm1CSn0, Xm1RASn, Xm1CASn  
對於地址線前面的Xm1,能夠參看核心板的原理圖中內存的總圖,整個內存分爲三塊,Memory Port0/Memory Port1/Memory Port2,Memory Port1/Memory Port2分別對應於芯片手冊P29頁中Memory Map中的DRAM0/DRAM1,能夠分別接兩組不一樣的DDR內存。
<ignore_js_op>


而Memory Port0接的是SRAM,他的地址線是0~15,沒有行地址,列地址,沒有複用,因此他的容量就是2^16=64K
[size=14.399999618530273px]<ignore_js_op>



2.3DDR芯片手冊
前面在查看芯片的型號命名規則時最後兩位,咱們的板子上是F7
14~15. Speed (Wafer/Chip Biz/BGD: 00)
F7 : DDR2-800 (400MHz@CL=6, tRCD=6, tRP=6),表示爲DDR2-800的內存

打開K4T1G084QF.pdf的DDR芯片手冊,P4的Key Features,由此咱們得知,咱們要看的是DDR2-800 6-6-6
<ignore_js_op>


這裏又多出和幾個咱們不認識的東西----CL,tRCD,tRP,看字面意思,大概來猜應該是一些時間,從芯片手冊的命名規則上特地用一個字母代號來標識這三個東西,這確定是很是重要的影響內存性能的某些時間參數。這裏,咱們有必要把DDR的工做原理給你們梳理一下,這樣,才能理解這些參數的意義,OK,請聽下回分解。

 

 

 

Tiny210 U-BOOT(七)----SDRAM工做時序與原理

什麼不是DDR嗎,怎麼又變成SDRAM,DDR出身自SDRAM,嚴格的說應該叫DDR SDRAM,DDR SDRAM是Double Data Rate SDRAM的縮寫,是雙倍速率同步動態隨機存儲器的意思,因此,有很大一部分,二者是同樣的,後面我會介紹二者的不一樣,接着看吧,先上一張SDRAM的結構圖。
<ignore_js_op>


下面上一張我畫的簡易的SDRAM工做流程圖
<ignore_js_op>

圖中用紅色標明的就是咱們須要找的幾個主要時間,如今開始看圖說話。
1.芯片初始化
SDRAM 芯片內部有一個邏輯控制單元,而且有一個模式寄存器爲其提供控制參數。所以,每次開機時 SDRAM 都要先對這個控制邏輯核心進行初始化。

2.行有效 
初始化完成後,要想對一個 L-Bank 中的陣列進行尋址,首先就要肯定行(Row),使之處於活動狀態(Active),而後再肯定列。簡單點理解就先傳行地址過來。

3.列讀寫
行地址肯定以後,就要對列地址進行尋址了。讀寫的信號和列地址是同時發過來的,讀寫的操做取決於WE#引腳,當他使能則爲寫,不然爲讀。
在發送列讀寫命令時必需要與行有效命令有一個間隔,這個間隔被定義爲 tRCD,即RAS to CAS Delay(RAS 至 CAS 延遲),你們也能夠理解爲行選通週期,簡單點理解就是說,在發完行地址後,再發列地址和讀寫信號時,須要延遲一下,這應該是根據芯片存儲陣列電子元件響應時間(從一種狀態到另外一種狀態變化的過程)所制定的延遲。
廣義的 tRCD 以時鐘週期(tCK,Clock Time)數爲單位,好比 tRCD=2,就表明延遲週期爲兩個時鐘週期,具體到確切的時間,則要根據時鐘頻率而定,對於PC100 SDRAM,tRCD=2,表明1000/100 * 2 = 20ns 的延遲,下圖是tRCD=3的時序圖。
<ignore_js_op>


4.數據輸出(讀)
在選定列地址後,就已經肯定了具體的存儲單元,剩下的事情就是數據經過數據 I/O 通道(DQ)輸出到內存總線上了。
可是在CAS發出以後,仍要通過必定的時間纔能有數據輸出,從CAS與讀取命令發出到第一筆數據輸出的這段時間,被定義爲 CL(CAS Latency,CAS 潛伏期)。因爲CL只在讀取時出現,因此 CL 又被稱爲讀取潛伏期(RL,Read Latency),下圖是CL=2的示意圖。
<ignore_js_op>


5.數據輸入(寫)
數據寫入的操做也是在 tRCD 以後進行,但此時沒有了 CL(記住,CL 只出如今讀取操做中),行尋址與列尋址的時序圖和上文同樣,只是在列尋址時,WE#爲有效狀態。 
爲了保證數據的可靠寫入,都會留出足夠的寫入/校訂時間(tWR,Write Recovery Time),這個操做也被稱做寫回(Write Back)。tWR 至少佔用一個時鐘週期或再多一點(時鐘頻率越高,tWR 佔用週期越多)

6.預充電
因爲 SDRAM 的尋址具體獨佔性,因此在進行完讀寫操做後,若是要對同一個Bank的另外一行進行尋址,就要將原來有效(工做)的行關閉,從新發送行/列地址。Bank 關閉現有工做行,準備打開新行的操做就是預充電(Precharge)。 
在發出預充電命令以後,要通過一段時間才能容許發送 RAS 行有效命令打開新的工做行,這個間隔被稱爲tRP(Precharge command Period,預充電有效週期)。和 tRCD、CL 同樣,tRP 的單位也是時鐘週期數,具體值視時鐘頻率而定。

7.刷新
之因此稱爲 DRAM,就是由於它要不斷進行刷新(Refresh)才能保留住數據,所以它是 DRAM 最重要的操做。刷新操做與預充電中重寫的操做同樣,都是用 S-AMP 先讀再寫。 
但爲何有預充電操做還要進行刷新呢?由於預充電是對一個或全部L-Bank 中的工做行操做,而且是不按期的,而刷新則是有固定的週期,依次對全部行進行操做,以保留那些久久沒經歷重寫的存儲體中的數據。但與全部 L-Bank 預充電不一樣的是,這裏的行是指全部 L-Bank 中地址相同的行,而預充電中各 L-Bank 中的工做行地址並非必定是相同的。好比我有四片,刷新是我依次刷新四片內存中的某個地址,而後再刷下一個;而預充電的工做行地址能夠不一樣。
那麼要隔多長時間重複一次刷新呢?目前公認的標準是,存儲體中電容的數據有效保存期上限是64ms(毫秒,1/1000 秒),也就是說每一行刷新的循環週期是 64ms。這樣刷新速度就是:行數量/64ms。咱們在看內存規格時,常常會看到 4096 Refresh Cycles/64ms 或 8192 RefreshCycles/64ms 的標識,這裏的 4096 與 8192 就表明這個芯片中每一個 L-Bank 的行數。刷新命令一次對一行有效,發送間隔也是隨總行數而變化,4096 行時爲 15.625μs(微秒,1/1000 毫秒),8192 行時就爲 7.8125μs。 
刷新操做分爲兩種:自動刷新(Auto Refresh,簡稱 AR)與自刷新(Self Refresh,簡稱 SR)。
SR 則主要用於休眠模式低功耗狀態下的數據保存,這方面最著名的應用就是 STR(Suspend to RAM,休眠掛起於內存)。在發出 AR 命令時,將 CKE 置於無效狀態,就進入了 SR 模式,此時再也不依靠系統時鐘工做,而是根據內部的時鐘進行刷新操做。在 SR 期間除了 CKE 以外的全部外部信號都是無效的(無需外部提供刷新指令),只有從新使 CKE 有效才能退出自刷新模式並進入正常操做狀態。

以上就是SDRAM是主要工做步驟,對比一下最上面的簡易工做流程圖,時間是否是就很清楚了呢?
CL=6:CAS Latency,CAS 潛伏期,CAS與讀取命令發出到第一筆數據輸出的時間  ----讀操做
tRCD=6:RAS to CAS Delay(RAS 至 CAS 延遲),行地址發完後,再發列地址的延遲時間  ----行列地址延遲
tRP=6:關閉現有工做行,準備打開新行,通過一段時間才能容許發送 RAS 行有效命令打開新的工做行的時間 ----預充電時間
OK,至此三個時間所有清清楚楚了。

 

Tiny210 U-BOOT(八)----DDR工做時序與原理 

DDR SDRAM 全稱爲 Double Data Rate SDRAM,中文名爲「雙倍數據流 SDRAM」。DDR SDRAM 在原有的 SDRAM的基礎上改進而來。下圖是DDR和SDRAM的數據傳輸對比圖
<ignore_js_op>


圖上能夠清楚的看到,DDR SDRAM可在一個時鐘週期內傳送兩次數據,上升沿傳一次,降低沿傳一次。

1.DDR的基本原理
先來看一張DDR讀操做時序圖

<ignore_js_op>

從中能夠發現它多了兩個信號:CLK#與DQS,CLK#與正常 CLK 時鐘相位相反,造成差分時鐘信號。而數據的傳輸在 CLK 與 CLK#的交叉點進行,可見在 CLK 的上升與降低沿(此時正好是 CLK#的上升沿)都有數據被觸發,從而實現雙倍數據傳輸,也就是DDR。下面來看DDR的內部結構圖的SDRAM有什麼不一樣。
<ignore_js_op>


這也是一顆 128Mbit 的內存芯片,標稱規格爲 32×4bit,右邊紅框區域就是DDR不一樣的地方:首先就是內部的L-Bank 規格。SDRAM 中L-Bank 存儲單元的容量與芯片位寬相同,但在DDR SDRAM 中並非這樣,存儲單元的容量是芯片位寬的一倍,因此在此不能再套用講解 SDRAM時「芯片位寬=存儲單元容量」的公式了。也所以,真正的行、列地址數量也與同規格 SDRAM 不同了。 
以本芯片爲例,在讀取時,L-Bank 在內部時鐘信號的觸發下一次傳送 8bit 的數據給讀取鎖存器,再分紅兩路 4bit 數據傳給複用器,由後者將它們合併爲一路 4bit 數據流,而後由發送器在 DQS 的控制下在外部時鐘上升與降低沿分兩次傳輸 4bit 的數據給北橋的內存控制器(在ARM和如今的CPU中,內存控制器是集成在CPU中的,如今的PC機中北橋已無內存控制器)。這樣,若是時鐘頻率爲 100MHz,那麼在 I/O 端口處,因爲是上下沿觸發,那麼就是傳輸頻率就是 200MHz。
如今你們基本明白 DDR SDRAM 的工做原理了吧,這種內部存儲單元容量(也能夠稱爲芯片內部總線位寬)=2×芯片位寬(也可稱爲芯片 I/O 總線位寬)的設計,就是所謂的兩位預取(2-bit Prefetch)。  

2.DDR與SDRAM的異同
DDR SDRAM 與 SDRAM 同樣,在開機時也要進行 MRS(ModeRegister Set,模式寄存器的設置),不過因爲操做功能的增多,DDR SDRAM 在 MRS 以前還多了一 EMRS 階段(Extended Mode Register Set,擴展模式寄存器設置),這個擴展模式寄存器控制着 DLL 的有效/禁止、輸出驅動強度、QFC 有效/無效等。

3.差分時鐘
CK#的做用,並不能理解爲第二個觸發時鐘,而是起到觸發時鐘校準的做用。
因爲數據是在 CK 的上下沿觸發,形成傳輸週期縮短了一半,所以必需要保證傳輸週期的穩定以確保數據的正確傳輸,這就要求 CK 的上下沿間距要有精確的控制。但由於溫度、電阻性能的改變等緣由,CK 上下沿間距可能發生變化,此時與其反相的 CK#就起到糾正的做用(CK 上升快降低慢,CK#則是上升慢降低快)。而因爲上下沿觸發的緣由,也使 CL=1.5 和 2.5 成爲可能,並容易實現。
<ignore_js_op>


4.數據選取脈衝(DQS)

DQS 是 DDR SDRAM 中的重要功能,它的功能主要用來在一個時鐘週期內準確的區分出每一個傳輸週期,並便於接收方準確接收數據。每一顆芯片都有一個 DQS 信號線,它是雙向的,在寫入時它用來傳送由內存控制器發來的 DQS 信號,讀取時,則由DDR芯片生成 DQS 向內存控制器發送。徹底能夠說,它就是數據的同步信號。 

在讀取時,DQS 與數據信號同時生成(也是在 CK 與 CK#的交叉點)。而 DDR 內存中的 CL 也就是從 CAS 發出到 DQS 生成的間隔,數據真正出如今數據 I/O 總線上相對於 DQS 觸發的時間間隔被稱爲tAC實際上,DQS 生成時,芯片內部的預取已經完畢了,tAC 是指上文結構圖中灰色部分的數據輸出時間,因爲預取的緣由,實際的數據傳出可能會提早於 DQS 發生數據提早於 DQS 傳出)。

DQS 在讀取時與數據同步傳輸,那麼接收時也是以 DQS 的上下沿爲準嗎?不,若是以 DQS 的上下沿區分數據週期的危險很大。因爲芯片有預取的操做,因此輸出時的同步很難控制,只能限制在必定的時間範圍內,數據在各 I/O 端口的出現時間可能有快有慢,會與 DQS 有必定的間隔,這也就是爲何要有一個 tAC 規定的緣由(DDR中的tAC是在DQS觸發和數據真正出如今I/O總線上的間隔時間)。而在接收方,一切必須保證同步接收,不能有 tAC 之類的誤差。這樣在寫入時,芯片再也不本身生成 DQS,而以發送方傳來的 DQS 爲基準,並相應延後必定的時間,在 DQS 的中部爲數據週期的選取分割點(在讀取時分割點就是上下沿),從這裏分隔開兩個傳輸週期。這樣作的好處是,因爲各數據信號都會有一個邏輯電平保持週期,即便發送時不一樣步,在 DQS 上下沿時都處於保持週期中,此時數據接收觸發的準確性無疑是最高的。
<ignore_js_op>

在寫入時,以 DQS 的高/低電平期中部爲數據週期分割點,而不是上/下沿,但數據的接收觸發仍爲 DQS 的上/下沿。

5.寫入延遲 
在上面的 DQS 寫入時序圖中,能夠發現寫入延遲已經不是0了,在發出寫入命令後,DQS與寫入數據要等一段時間纔會送達。這個週期被稱爲 DQS 相對於寫入命令的延遲時間tDQSS, WRITE Command to the first corresponding rising edge of DQS)。 
    爲何要有這樣的延遲設計呢?緣由也在於同步,畢竟一個時鐘週期兩次傳送,須要很高的控制精度,它必需要等接收方作好充分的準備才行。tDQSS 是 DDR 內存寫入操做的一個重要參數,過短的話恐怕接受有誤,太長則會形成總線空閒。tDQSS 最短不能小於 0.75 個時鐘週期,最長不能超過 1.25 個時鐘週期。
    正常狀況下,tDQSS 是一個時鐘週期,但寫入時接受方的時鐘只用來控制命令信號的同步,而數據的接受則徹底依靠 DQS 進行同步,因此 DQS 與時鐘不一樣步也無所謂。不過,tDQSS產生了一個不利影響— — 讀後寫操做延遲的增長,若是 CL=2.5,還要在 tDQSS 基礎上加入半個時鐘週期,由於命令都要在 CK 的上升沿發出。下圖中,當 CL=2.5 時,讀後寫的延遲將爲 tDQSS+0.5 個時鐘週期(圖中 BL=2)。 

<ignore_js_op>


另外,DDR 內存的數據真正寫入因爲要通過更多步驟的處理,因此寫回時間(tWR)也明顯延長,通常在3個時鐘週期左右,而在 DDR-Ⅱ規範中更是將 tWR 列爲模式寄存器的一項,可見它的重要性。 

6.突發長度
在 DDR SDRAM 中,突發長度只有 二、四、8 三種選擇,沒有了隨機存取的操做(突發長度爲 1)和全頁式突發。這是爲何呢?由於 L-Bank一次就存取兩倍於芯片位寬的數據,因此芯片至少也要進行兩次傳輸才能夠,不然內部多出來的數據怎麼處理?可是,突發長度的定義也與 SDRAM 的不同了,它再也不指所連續尋址的存儲單元數量,而是指連續的傳輸週期數,每次是一個芯片位寬的數據。 

 

 

7.延遲鎖定迴路(DLL) 
DDR SDRAM 對時鐘的精確性有着很高的要求,而 DDR SDRAM 有兩個時鐘,一個是外部的總線時鐘,一個是內部的工做時鐘,在理論上 DDR SDRAM 這兩個時鐘應該是同步的,但因爲種種緣由,如溫度、電壓波動而產生延遲使二者很難同步,更況且時鐘頻率自己也有不穩定的狀況(SDRAM 也有內部時鐘,不過由於它的工做/傳輸頻率較低,因此內外同步問題並不突出)。
DDR SDRAM 的 tAC 就是由於內部時鐘與外部時鐘有誤差而引發的,它極可能形成因數據不一樣步而產生錯誤的惡果。實際上,不一樣步就是一種正/負延遲,若是延遲不可避免,那麼如果 設定一個延遲值,如一個時鐘週期,那麼內外時鐘的上升與降低沿仍是同步的。鑑於外部時鐘週期也不會絕對統一,因此須要根據外部時鐘動態修正內部時鐘的延遲 來實現與外部時鐘的同步,這就是 DLL 的任務。 
DLL 不一樣於主板上的 PLL,它不涉及頻率與電壓轉換,而是生成一個延遲量給內部時鐘。目前 DLL 有兩種實現方法,一個是時鐘頻率測量法(CFM,Clock Frequency Measurement),一個是時鐘比較法(CC,Clock Comparator)。 
CFM 是測量外部時鐘的頻率週期,而後以此週期爲延遲值控制內部時鐘,這樣內外時鐘正好就相差了一個時鐘週期,從而實現同步。DLL 就這樣反覆測量反覆控制延遲值,使內部時鐘與外部時鐘保持同步。
CC的方法則是比較內外部時鐘的長短,若是內部時鐘週期短了,就將所少的延遲加到下一個內部時鐘週期裏,而後再與外部時鐘作比較,如果內部時鐘週期長了,就將多出的延遲從下一個內部時鐘中刨除,如此往復,最終使內外時鐘同步。
CFM 式 DLL 工做示意圖
<ignore_js_op>


CC 式 DLL 工做示意圖
<ignore_js_op>


CFM 與 CC 各有優缺點,CFM 的校訂速度快,僅用兩個時鐘週期,但容易受到噪音干擾,而且若是測量失誤,則內部的延遲就永遠錯下去了。CC 的優勢則是更穩定可靠,若是比較失敗,延遲受影響的只是一個數據(並且不會太嚴重),不會涉及到後面的延遲修正,但它的修正時間要比 CFM 長。DLL 功能在 DDR SDRAM 中能夠被禁止,但僅限於除錯與評估操做,正常工做狀態是自動有效的。

 

 

Tiny210(S5PV210) U-BOOT(九)----DDR2工做時序與原理
DDR的發展沿着更高數據傳輸頻率,更大內存容量的方向發展,DDR2中作到更高數據傳輸頻率,由DDR的2-bit pretetch向4-bit pretetch發展,而擴展容量,除了增長每一個L-Bank的容量之外,另外就是增長L-Bank數,也就是說在內存中,原來DDR中Bank線只有2根,一塊內存芯片最多2^2=4片L-Bank,而在DDR2中變成了2^3=8片L-Bank。(下面的一些圖和文檔出自爾必達的芯片手冊)

 

1.4-bit Prefetch

直接上一個表,看看DDR2的三個頻率的關係,下圖是內部時鐘均爲133MHz的DDR2/DDR/SDRAM的比較,由圖能夠看到,相比於DDR,DDR2因爲是4-bit Prefetch,外部時鐘是內部總線時鐘的2倍,而DDR和SDRAM中,這兩個時鐘頻率相等
<ignore_js_op>


上一個對比圖,看的會更清楚一點兒: 
<ignore_js_op>


在 SDRAM 與 DDR 時代,這兩個時鐘頻率是相同的,但在 DDR-Ⅱ內存中,內部時鐘變成了外部時鐘的一半。以 DDR-Ⅱ 533 爲例,數據傳輸頻率爲 533MHz( 對於每一個數據引腳,則是 533Mbps/pin),外部時鐘頻率爲 266MHz,內部時鐘頻率爲 133MHz。由於內部一次傳輸的數據就可供外部接口傳輸 4 次,雖然以 DDR 方式傳輸,但數據傳輸頻率的基準— — 外部時鐘頻率仍要是內部時鐘的兩倍才行。 

[size=14.399999618530273px]2. DDR-Ⅱ的新操做與新時序設計
[size=14.399999618530273px]2.1片外驅動調校(OCD,Off-Chip Driver)
DDR-Ⅱ內存在開機時也會有初始化過程,同時在 EMRS 中加入了新設置選項,因爲大同小異,此就很少說了。在 EMRS 階段,DDR-Ⅱ加入了可選的 OCD 功能。 
OCD 的主要用意在於調整 I/O 接口端的電壓,來補償上拉與下拉電阻值。目的是讓 DQS 與 DQ 數據信號之間的誤差下降到最小。調校期間,分別測試 DQS 高電平/DQ 高電平,與 DQS 低電平/DQ 高電平時的同步狀況,若是不知足要求,則經過設定突發長度的地址線來傳送上拉/下拉電阻等級(加一檔或減一檔),直到測試合格才退出 OCD 操做。
<ignore_js_op>


[size=14.399999618530273px]2.2 片內終結(ODT,On-Die Termination) 
[size=14.399999618530273px]所謂的終結,就是讓信號被電路的終端吸取掉,而不會在電路上造成反射,形成對後面信號[size=14.399999618530273px]的影響。 


在 DDR 時代,控制與數據信號的終結在主板上完成,每塊 DDR 主板在 DIMM 槽的旁邊都會有一個終結電壓島的設計,它主要由一排終結電阻構成。長期以來,這個電壓島一直是 DDR 主板設計上的一個難點。而 ODT 的出現,則將這個難點消滅了。[size=14.399999618530273px]ODT 將終結電阻從主板上移植到了內存芯片內部,主板上不在有終結電路。ODT 的功能與禁止由內存控制器控制,ODT 所終結的信號包括 DQS、RDQS(爲 8bit 位寬芯片增設的專用 DQS 讀取信號,主要用來簡化一個模組中同時使用 4 與 8bit 位寬芯片時的控制設計)、DQ、DM 等。 
<ignore_js_op>


[size=14.399999618530273px]上圖中,左邊就是DDR時代,在主板上完成信號終結,右邊就是從DDR2開始,在內存芯片內部終結信號。在內存芯片工做時系統會把終結電阻器屏蔽,而對於暫時不工做的內存芯片則打開終結電阻器以減小信號的反射。由此DDR2內存控制器能夠經過ODT同時管理全部內存引腳的信號終結。而且阻抗值也能夠有多種選擇。如0Ω、50Ω、75Ω、150Ω等等。而且內存控制器能夠根據系統內干擾信號的強度自動調整阻值的大小。

[size=14.399999618530273px]2.3前置 CAS、附加潛伏期與寫入潛伏期 
[size=14.399999618530273px]前置 CAS(Posted CAS)是爲了解決 DDR 內存中指令衝突而設計的功能。它容許 CAS 信號緊隨 RAS 發送,相對於以往的 DDR 等於將 CAS 前置了。
這樣,地址線能夠馬上空出來,便於後面的行有效命令發出,避免形成命令衝突而被迫延後的狀況發生,但讀/寫操做並無所以而提早,仍有要保證有足夠的延遲/潛伏期,爲此,DDR-Ⅱ引入了附加潛伏期的概念(AL,Additive Latency),與 CL 同樣,單位爲時鐘週期數。AL+CL 被定義爲讀取潛伏期(RL,Read Latency),相應的,DDR-Ⅱ還對寫入潛伏期(WL,Write Latency)制定了標準,WL是指從寫入命令發出到第一筆數據輸入的潛伏期,不要將它和 tDQSS 弄混了,後者是指 DQS 而不是數據。按規定,WL=RL-1,即 AL+CL-1。
<ignore_js_op>


上圖中,ACT表示的是激活信號,在沒有前置 CAS 功能時,對其餘 L-Bank 的尋址操做可能會因當前行的 CAS 命令佔用地址線而延後,並使數據 I/O 總線出現空閒(上圖中的BUBBLE處),當使用前置 CAS 後,消除了命令衝突並使數據 I/O 總線的利率提升。 
<ignore_js_op>


[size=14.399999618530273px]設置 Posted-CAS 後,必須附加潛伏期以保證應有延遲,此時讀取潛伏期(RL)就等於 AL+CL,從中能夠看出 AL 的值爲 CL+tRCD-1。

至此,DDR2的前因後果所有分析完畢,如今能夠開始打開芯片手冊,開始分析源碼了。

 

 

Tiny210(S5PV210) U-BOOT(十)----DDR2初始化順序

如今網上的S5PV210的u-boot源碼中關於內存的初始化過程,基本上我沒有找到任何資料有過度析DDR2的內存初始化代碼的。在看u-boot的這段代碼時,也徘徊了好久,不知道以下手,不少文章或資料都將這一段分析過程有意無心的隱藏掉了,最多也只是提一下說參考裸板的代碼,在找不到任何資料的狀況下,我只能依靠芯片手冊上,三星在內存控制器這一章,寫的關於DDR2的初始化順序的28個步驟來一條一條去讀去看,在安靜下來看了芯片手冊之後,我發現三星給的裸板的DDR初始化代碼和芯片手冊上的初始化步驟徹底一致,有的時候,最好的資料其實就在手邊,只是我一直在想着找捷徑,學習哪有那麼多捷徑?


如今開始關注一下芯片手冊上關於DDR2的初始化流程,P598頁:

1.查看芯片手冊DDR2的初始化順序

Initialization sequence for DDR2 memory type
1.  To provide stable power for controller and memory device, the controller must assert and hold CKE to a logic low level. Then apply stable clock. Note: XDDR2SEL should be High level to hold CKE to low. 
2. Set the PhyControl0.ctrl_start_point and PhyControl0.ctrl_incbit-fields to correct value according to clock frequency. Set the PhyControl0.ctrl_dll_onbit-field to ‘1’ to turn on the PHY DLL. 
3. DQS Cleaning: Set the PhyControl1.ctrl_shiftc and PhyControl1.ctrl_offsetcbit-fields to correct value according to clock frequency and memory tAC parameters. 
4. Set the PhyControl0.ctrl_start bit-field to ‘1’.  
5. Set the ConControl. At this moment, an auto refresh counter should be off.  
6. Set the MemControl. At this moment, all power down modes should be off. 
7. Set the MemConfig0 register. If there are two external memory chips, set the MemConfig1 register. 
8. Set the PrechConfigand PwrdnConfigregisters. 
9. Set the TimingAref, TimingRow, TimingDataand TimingPower registers according to memory AC parameters. 
10. If QoS scheme is required, set the QosControl0~15and QosConfig0~15registers. 
11. Wait for thePhyStatus0.ctrl_lockedbit-fields to change to ‘1’. Check whether PHY DLL is locked. 
12. PHY DLL compensates the changes of delay amountcaused by Process, Voltage and Temperature (PVT) variation during memory operation. Therefore, PHY DLL should not be off for reliable operation. It can be off except runs at low frequency. If off mode is used, set thePhyControl0.ctrl_forcebit-field to correct value according to thePhyStatus0.ctrl_lock_value[9:2]bit-field to fix delay amount. Clear the PhyControl0.ctrl_dll_on bit-field to turn off PHY DLL. 
13. Confirm whether stable clock is issued minimum 200us after power on 
14. Issue a NOPcommand using the DirectCmdregister to assert and to hold CKE to a logic high level.
15. Wait for minimum 400ns. 
16. Issue a PALL command using the DirectCmd register. 
17. Issue an EMRS2 command using the DirectCmd register to program the operating parameters. 
18. Issue an EMRS3 command using the DirectCmd register to program the operating parameters. 
19. Issue an EMRS command using the DirectCmd register to enable the memory DLLs. 
20. Issue a MRS command using the DirectCmd register to reset the memory DLL. 
21. Issue a PALL command using the DirectCmd register. 
22. Issue two Auto Refreshcommands using the DirectCmd register. 
23. Issue a MRS command using the DirectCmd register to program the operating parameters without resetting the memory DLL. 
24. Wait for minimum 200 clock cycles. 
25. Issue an EMRS command using the DirectCmd register to program the operating parameters. If OCD calibration is not used, issue an EMRS command to set OCD Calibration Default. After that, issue an EMRS command to exit OCD Calibration Mode and to program the operating parameters. 
26. If there are two external memory chips, perform steps 14~25 for chip1 memory device. 
27. Set the ConControlto turn on an auto refresh counter. 
28. If power down modes is required, set the MemControl registers.

 

 譯文以下
1. 提供穩壓電源給內存控制器和內存芯片,內存控制器必須保持CLE在低電平,此時就會提供穩壓電源。注:當CKE引腳爲低電平時,XDDR2SEL應該處於高電平
2. 依照時鐘頻率正確配置PhyControl0.ctrl_start_point和PhyControl0.ctrl_incbit-fields的值。配置的PhyControl0.ctrl_dll_on值爲'1'以打開PHY DLL。
3. DQS Cleaning:依照時鐘頻率和內存的tAC參數正確設置PhyControl1.ctrl_shiftc and PhyControl1.ctrl_offsetcbit-fields位的值。
4. 配置PhyControl0.ctrl_start位的值爲'1'
5. 配置ConControl,與此同時,auto refresh自動刷新計數器應該關閉
6. 配置MemControl,與此同時,全部的power down(休眠模式)應該閉關
7. 配置MemConfig0寄存器。若是有兩組內存芯片(好比有8片DDR,這8片DDR是分別掛在Memory Port1和Memory Port2上),再配置MemConfig1寄存器。
8. 配置PrechConfigPwrdnConfig寄存器
9. 依照內存的tAC參數配置TimingArefTimingRowTimingDataTimingPower寄存器
10. 若是須要QoS標準,配置QosControl0~15QosConfig0~15r寄存器
11. 等待PhyStatus0.ctrl_locked位變爲'1'。檢查是否PHY DLL是否已鎖
12. PHY DLL補償在內存操做時由PVT(Process, Voltage and Temperature,處理器、電壓和溫度)變化引發的延遲量。可是,PHY DLL不能因某些可靠的內存操做而切斷,除非是工做在低頻率下。若是關閉PHY DLL,依照PhyStatus0.ctrl_lock_value[9:2]位的值正確配置PhyControl0.ctrl_force位的值來彌補延遲量(fix delay amount)。清除PhyControl0.ctrl_dll_on位的值來關閉PHY DLL。
13. 上電後,肯定最小值爲200us的穩定時鐘是否發出
14. 使用DirectCmd寄存器發出一個NOP命令,保證CKE引腳爲高電平
15. 等最小400ns

16. 使用DirectCmd寄存器發出一個PALL命令
17. 使用DirectCmd寄存器發出一個EMRS2命令,program操做參數
18. 使用DirectCmd寄存器發出一個EMRS3命令,program操做參數
19. 使用DirectCmd寄存器發出一個EMRS命令來使能內存DLLs
20. 使用DirectCmd寄存器發出一個MRS命令,重啓內存DLL
21. 使用DirectCmd寄存器發出一個PALL命令
22. 使用DirectCmd寄存器發出兩個Auto Refresh(自動刷新)命令
23. 使用DirectCmd寄存器發出一個MRS命令,program操做參數,不要重啓內存DLL
24. 等待最小200時鐘週期
25. 使用DirectCmd寄存器發出一個EMRS命令給程序的運行參數。若是OCD校訂(Off-Chip Driver,片外驅動調校)沒有使用,改善一個EMRS命令去設置OCD校準的默認值。在此以後,發送一個EMRS指令去退出OCD校準模式,繼續program操做參數
26. 若是有兩組DDR芯片,重複14-25步配置chip1的內存,剛剛配置的是chip0,也就是第一組內存芯片
27. 配置ConControlto來打開自動刷新計數器
28. 若是須要power down(休眠)模式,配置MemControl寄存器.




知道了上面的這些初始化步驟,如今再去看三星給的裸板代碼中關於SDRAM的BL1的代碼,就比較清楚了。

 

 

Tiny210(S5PV210) U-BOOT(十一)----DDR2初始化源碼分析


1.u-boot關於DDR的源碼分析在mem_setup.S中,一點一點來,先看第一段

  1.    /* DMC0 Drive Strength (Setting 2X) */
  2.     ldr    r0, =ELFIN_GPIO_BASE
  3.     ldr    r1, =0x0000AAAA
  4.     str    r1, [r0, #MP1_0DRV_SR_OFFSET]
  5.     ldr    r1, =0x0000AAAA
  6.     str    r1, [r0, #MP1_1DRV_SR_OFFSET]
  7.     ldr    r1, =0x0000AAAA
  8.     str    r1, [r0, #MP1_2DRV_SR_OFFSET]
  9.     ldr    r1, =0x0000AAAA
  10.     str    r1, [r0, #MP1_3DRV_SR_OFFSET]
  11.     ldr    r1, =0x0000AAAA
  12.     str    r1, [r0, #MP1_4DRV_SR_OFFSET]
  13.     ldr    r1, =0x0000AAAA
  14.     str    r1, [r0, #MP1_5DRV_SR_OFFSET]
  15.     ldr    r1, =0x0000AAAA
  16.     str    r1, [r0, #MP1_6DRV_SR_OFFSET]
  17.     ldr    r1, =0x0000AAAA
  18.     str    r1, [r0, #MP1_7DRV_SR_OFFSET]
  19.     ldr    r1, =0x00002AAA
  20.     str    r1, [r0, #MP1_8DRV_SR_OFFSET]
複製代碼

首先在初始化DDR以前,引入一個關於DRAM Drive Strength的概念----DRAM Drive Strength(也被稱爲:driving strength),表示「DRAM驅動強度」。這個參數用來控制內存數據總線的信號強度,數值越高表明信號強度越高,增長信號強度能夠提升超頻的穩定性。可是並不是信號強度高就必定好。
因此,這裏咱們須要配置這個Drive Striegth,DDR2內存的全部線都須要配置,這裏咱們從地址線開始看,咱們的內存是掛載在Memory Port1(如下簡稱MP1)上,因此,先在S5PV210的芯片手冊搜索Xm1ADDR。
<ignore_js_op>


在P63頁,找到MP1的各個pin腳的描述,繼續向下搜索。
<ignore_js_op>


在P107頁,Pin腳複用的描述表裏找到他的GPIO口定義
<ignore_js_op>


Xm1ADDR[0]~[7]的GPIO的MP1_0[0]~7是複用的,OK,搜索MP1_0。
<ignore_js_op>


在P122頁最終找到MP1_0DRV,在這個寄存器的描述中,顯示這個寄存器就是用來配置咱們的內存的Drive Strength的寄存器,搜索MP1_0DRV。
<ignore_js_op>


找到MP1_0DRV的寄存器的配置表,這裏給出的初始值是AAAA,我總共是0-7,8根線,每根線我配的值都是10,也就是2x,符合咱們上面介紹DRAM Driver Strength的描述,OK,到這裏咱們的MP1_0寄存器----Xm1ADDR[0]~[7]的Driver Strength配置完畢。剩下的就按照上面的方法,再把剩下的線所有配好,初始均按芯片手冊的參考值配爲2x,也就是10。如今再回過頭來看上面的初始化代碼,清楚多了,整個這麼一長串代碼,其實只作了一件事,就是給MP1上接的內存的每一根線都配置Driver Strength的值爲2x,這裏有一個地方,須要注意一下,最後一個MP1_8他的參考配置爲0x2AAA。照樣的,再配置DM1,把Memory Port2也配置完畢,這裏咱們沒有用到MP2,不配置,應該也沒有關係。繼續看下一段代碼:

  1. /* DMC0 initialization at single Type*/
  2.     ldr    r0, =APB_DMC_0_BASE
  3.     ldr    r1, =0x00101000                @PhyControl0 DLL parameter setting, manual 0x00101000
  4.     str    r1, [r0, #DMC_PHYCONTROL0]
  5.     ldr    r1, =0x00000086                @PhyControl1 DLL parameter setting, LPDDR/LPDDR2 Case
  6.     str    r1, [r0, #DMC_PHYCONTROL1]
  7.     ldr    r1, =0x00101002                @PhyControl0 DLL on
  8.     str    r1, [r0, #DMC_PHYCONTROL0]
  9.     ldr    r1, =0x00101003                @PhyControl0 DLL start
  10.     str    r1, [r0, #DMC_PHYCONTROL0]
複製代碼

 

根據芯片手冊給出的參考步驟,第2步以下:

 

2.依照時鐘頻率正確配置PhyControl0.ctrl_start_point和PhyControl0.ctrl_inc bit-fields的值。配置的PhyControl0.ctrl_dll_on值爲'1'以打開PHY DLL。

 

那開始配置PhyControl0的相關位,P614頁找到DRAM的寄存器配置表,找到有關PhyControl0的寄存器爲PHYCONTROL0
<ignore_js_op>

 


OK,咱們須要正確配置PhyControl0寄存器的ctrl_start_point和ctrl_inc這兩個位的值,OK,查看寄存器的描述
<ignore_js_op>


 

[size=14.399999618530273px]ctrl_start_point和ctrl_inc這兩位芯片手冊上給的參考值爲0x10,先配這兩位,則爲:
ctrl_start_point: 0x10 ---- 10000(二進制)

[size=14.399999618530273px]ctrl_inc: 0x10 ---- 10000(二進制)
[size=14.399999618530273px]其他的位所有配成0,最終爲10000 00010000 00000000 ---- 0x00101000,代碼以下:

  1. ldr    r0, =APB_DMC_0_BASE  
  2.   
  3. ldr    r1, =0x00101000                @PhyControl0 DLL parameter setting, manual 0x00101000  
  4. str    r1, [r0, #DMC_PHYCONTROL0]  
複製代碼

[size=14.399999618530273px]第2步配置完成,接着往下看第3步--3.DQS Cleaning:依照時鐘頻率和內存的tAC參數正確設置PhyControl1.ctrl_shiftc and PhyControl1.ctrl_offsetc bit-fields位的值。

查看PhyControl1的ctrl_shiftc和ctrl_offsetc這兩位的描述
<ignore_js_op>

 


咱們的內存是DDR2-800,因此ctrl_shiftc配置爲0x6 ---- 110,ctrl_offsetc的配置暫時參考三星的裸板參數配置爲0,ctrl_ref配置爲1000,整理代碼以下:

 

 

  1. ldr    r1, =0x00000086                @PhyControl1 DLL parameter setting, LPDDR/LPDDR2 Case  
  2. str    r1, [r0, #DMC_PHYCONTROL1]  
複製代碼

 

按照第2步的要求,打開PLL,將PhyControl0.ctrl_dll_on配置爲1

 

  1. ldr    r1, =0x00101002                @PhyControl0 DLL on  
  2. str    r1, [r0, #DMC_PHYCONTROL0]  
複製代碼

 

繼續第4步--4.配置PhyControl0.ctrl_start位的值爲'1'

 

  1. ldr    r1, =0x00101003                @PhyControl0 DLL start  
  2. str    r1, [r0, #DMC_PHYCONTROL0]  
複製代碼

 

後面的步驟,所有是按照三星的芯片手冊上的那28步來一步一步的在操做寄存器,沒有一步有漏掉,因此,對比着芯片手冊上的那28步一行一行的查看代碼就OK了。有一點,在配置完16-25步後,到第26步時--配置第26步--26.若是有兩組DDR芯片,重複14-25步配置chip1的內存,剛剛配置的是chip0,也就是第一組內存芯片。這裏就把第16-25步從新再作一次,初始化chip1就OK了。代碼太多,這裏就不詳細重複了,我這裏參考的是三星的裸板的DDR源碼。至此,DDR的源碼也分析完成,下面,應該能夠開始將這些移植進一個新的u-boot中去了。

  

 Tiny210(S5PV210) U-BOOT(十二)----編譯出u-boot.bin
通過前面十一篇帖子的基礎知識的梳理準備,如今咱們能夠正式開始移植了,所謂磨刀不誤砍柴工,若是前面的一些知識沒有準備好,後面你照着步驟來作這些事,毫無心義,試問在工做中,有人會寫一個步驟與手冊來教給你,讓你照着作就OK了嗎,那若是有這樣的工做,並且薪水可觀的話,請聯繫我,不甚感激!,言歸正傳,開始正式移植的第一天的任務。
第一天
任務:配置板文件,編譯出u-boot.bin

1.cp -a board/samsung/smdkc100 board/samsung/tiny210

2.cp include/configs/smdkc100.h include/configs/tiny210.h

3.指定平臺
約定以下:
Target(目標):tiny210
ARCH(架構):arm
CPU(芯片):armv7
Board name(板子名稱):tiny210
Vendo(生產廠家):samsung
Soc(CPUO類型):s5pc1xx
Options(可選項)

#vim boards.cfg,在270行添加
tiny210    arm    armv7    tiny210    samsung    s5pc1xx
上面這句話主要是在選編譯時的代碼目錄

4.修改頂層目錄下的Makefile指定交叉編譯工具鏈添加:

  1. ifeq(arm,$(ARCH))
  2. CROSS_COMPILE?=/opt/FriendlyARM/toolschain/4.5.1/bin/arm-linux-
  3. endif
複製代碼

上面是個人編譯工具鏈的路徑,你換成本身的就OK了。

5.修改tiny210單板目錄下的的相關文件--board/samsung/tiny210/
1)修改Makefile,編譯tiny210.c文件,將smdkc100.c更名爲tiny210.c
COBJS-y        := tiny210.o
2)修改board/samsung/tiny210/lowlevel_init.S
添加頭文件引用

#include <s5pc110.h>

6.開始編譯
#make tiny210_config
#make
上面的命令是調用u-boot根目錄下的mkconfig腳原本完成,用來生成config.mk, config.h

錯誤1:
<ignore_js_op>


解決辦法:include目錄下添加s5pc110.h頭文件(在lowlevel_init.S中包含的頭文件沒有添加,因此報錯,加上)
錯誤2:
<ignore_js_op>


解決辦法:從include/asm/arch-s5pc1xx下拷貝一個hardware.h到include/asm/arch目錄
錯誤3:
<ignore_js_op>


解決辦法:include目錄下添加s5pc11x.h頭文件(在s5pc110.h中包含有此頭文件沒有添加,因此報錯,加上)

7.編譯成功,出現u-boot.bin

相關文章
相關標籤/搜索