瘋雨-版權所有,轉載請注明【http://blog.csdn.net/u010346967】
這麼久了代碼還沒進入記憶體怎麼行,接下來我們就來初始化DDR2。
1.DDR2的初始化流程
DDR2的初始化流程datasheet裡面已經寫得很清楚了,總共27步。直接看datasheet 598頁,DDR2初始化部分截圖如下:
2.硬體相關
首先,你得知道我們的闆子用的是什麼記憶體晶片,然後用了幾塊,接在哪了?
好了檢視原理圖x210cv3.pdf,搜尋DDR2,你應該可以發現有4塊記憶體晶片,而且是FBGA封裝的。晶片型号是K4T1G164QQ,四塊一樣的晶片。三星産的,去下載下傳這塊晶片的文檔,後面配置一些時序參數要用到。
然後,還得補充一些DDR2記憶體的知識,個人建議從DDR2的早起發展開始看,看産品是怎麼樣一代代更新的。但是這裡我們隻談怎麼操作,不說原理,不然沒完沒了了。有興趣自己去了解吧。這裡推薦些資料資訊:
3.初始化細節
那我我們對照着一步步來。
3.1 為了向控制器和記憶體裝置提供穩定的電源,控制器必須保持CKE為低電平。然後才能運作穩定的時鐘。注意:XDDR2SEL引腳應為高電平把CKE電平拉低。
先看核心闆原理圖x210cv3.pdf(後面簡稱原理圖),确定XDDR2SEL和CKE是什麼?(DDR2位于核心闆上面應該知道吧,如果不知道那你得好好檢討了,每個硬體在哪還是得清楚)
先看XDDR2SEL,在原理圖中搜尋一下,發現下圖:直接接到了VDD_IO,是以應該與程式設計無關,硬體上提供了高電平。是以第一步硬體做了。
3.2 根據時鐘頻率設定PhyControl0.ctrl_start_pointandPhyControl0.ctrl_inc為正确的值,設定PhyControl0.ctrl_dll_on為1打開PHY DLL
代碼如下:
ldr r0, =0xF0000000
ldr r1, =0x00101002
str r1,[r0,#0x18]
3.3 根據時鐘頻率和記憶體t_AC參數設定PhyControl1.ctrl_shiftcandPhyControl1.ctrl_offsetc為正确的值
代碼如下:
ldr r1, =0x2046
str r1,[r0,#0x1C]
3.4 設定PhyControl0.ctrl_start為1
這一步簡單 直接給出代碼:
ldr r1, =0x00101003
str r1,[r0,#0x18]
3.5 設定ConControl關閉自動重新整理
代碼如下:
ldr r1, =0x0FFF13A8
str r1,[r0,#0x0]
3.6 設定MemControl關閉所有的掉電模式
代碼如下:
ldr r1, =0x00212400
str r1,[r0,#0x4]
3.7 設定MemConfig0
因為我們隻使用了一個記憶體控制器,DMC0是以配置MemConfig0,如果使用了兩個DMC0和DMC1那麼要配置MemConfig1
這個寄存器的配置要看K4T1G164QE.pdf手冊:K4T1G164QE名字裡面的4就說明了是8個bank。位址資料線條數可以檢視原理圖。
代碼如下:
ldr r1, =0x20E00323
str r1,[r0,#0x8]
3.8 設定PrechConfigandPwrdnConfig
代碼如下:
ldr r1, =0xFF000000
ldr r1,[r0,#0x14]
ldr r1, =0xFFFF00FF
str r1,[r0,#0x28]
3.9 設定TimingAref,TimingRow,TimingDataandTimingPower
這幾個寄存器的值要根據K4T1G164QQ記憶體晶片手冊配置,手冊上有詳細的時序參數 代碼如下:
ldr r1, =0x618
str r1,[r0,#0x30]
ldr r1, =0x1C24434A
str r1,[r0,#0x34]
ldr r1, =0x24240000
str r1,[r0,#0x38]
ldr r1, =0x08C90343
str r1,[r0,#0x3C]
下圖(時序參數)來自于記憶體晶片手冊
3.10 QoS沒用到,這一步跳過
3.11 通過讀PhyStatus0.ctrl_locked 是否為1檢測PHY DLL是否鎖定
代碼如下:
1:
ldr r1,[r0,#0x40]
and r2,r1,#0x2
cmp r2,#0
beq 1b
3.12 強制延時
代碼如下:
and r1,r1,#0x3fc0
mov r2,r1,LSL #18
orr r2,r2,#0x00100000
orr r2,r2,#0x1000
orr r2,r2,#0x3
str r2,[r0,#0x18]
3.13 開機等待200us是時鐘平穩,這一步不用開機到記憶體初始化肯定時鐘平穩了
3.14 使用DirectCmd發送NOP指令,確定CKE保持為高電平
代碼如下:
ldr r1, =0x07000000
str r1,[r0,#0x10]
3.15 等待最少400ns
3.16 使用DirectCmd發送PALL指令
代碼如下:
ldr r1, =0x01000000
str r1,[r0,#0x10]
3.17 使用 DirectCmd發送EMRS2指令去寫入運作參數
這個部分就要去看DDR2規範文檔了,自己去下載下傳吧。
代碼如下:
ldr r1, =0x00020000
str r1,[r0,#0x10]
3.18 使用DirectCmd發送EMRS3指令去寫入運作參數
代碼如下:
ldr r1, =0x00030000
str r1,[r0,#0x10]
3.19 使用 DirectCmd發送EMRS指令,使能記憶體DLLs
代碼如下:
ldr r1, =0x00010000
str r1,[r0,#0x10]
3.20 使用 DirectCmd發送MRS指令,複位記憶體DLLs
代碼如下:
ldr r1, =0x542
str r1,[r0,#0x10]
3.21 使用 DirectCmd發送PALL指令
3.22 使用DirectCmd發送兩次auto refresh指令
代碼如下:
ldr r1, =0x05000000
str r1,[r0,#0x10]
str r1,[r0,#0x10]
3.23 使用DirectCmd發送MRS指令,運作無複位的記憶體DLL
代碼如下:
ldr r1, =0x442
str r1,[r0,#0x10]
3.24 最少等200個時鐘周期
3.25使用DirectCmd發送EMRS指令,設定OCD校正
代碼如下:
ldr r1, =0x00010780
str r1,[r0,#0x10]
3.26 無
3.27 設定ConControl啟動自動重新整理計數器
代碼如下:
ldr r1, =0x0FFF13B8
str r1,[r0,#0x0]
3.28 如果有必要的話設定MemControl開啟掉電模式
這裡我們就不設定了
完整的初始化代碼如下:
mem_init:
//2.設定PhyControl0.ctrl_start_pointandPhyControl0.ctrl_inc
ldr r0, =0xF0000000
ldr r1, =0x00101002
str r1,[r0,#0x18]
//3.設定PhyControl1.ctrl_shiftcandPhyControl1.ctrl_offsetc
ldr r1, =0x2046
str r1,[r0,#0x1C]
//4.設定PhyControl0.ctrl_start為1
ldr r1, =0x00101003
str r1,[r0,0x18]
//5.設定ConControl關閉自動重新整理
ldr r1, =0x0FFF13A8
str r1,[r0,#0x0]
//6.設定MemControl關閉所有的掉電模式
ldr r1, =0x00212400
str r1,[r0,#0x4]
//7.設定MemConfig0
ldr r1, =0x20E00323
str r1,[r0,#0x8]
//8.設定PrechConfigandPwrdnConfig
ldr r1, =0xFF000000
ldr r1,[r0,#0x14]
ldr r1, =0xFFFF00FF
str r1,[r0,#0x28]
//9.設定TimingAref,TimingRow,TimingDataandTimingPower
ldr r1, =0x618
str r1,[r0,#0x30]
ldr r1, =0x1C24434A
str r1,[r0,#0x34]
ldr r1, =0x24240000
str r1,[r0,#0x38]
ldr r1, =0x08C90343
str r1,[r0,#0x3C]
//10.QoS沒用到,這一步跳過
//11.通過讀PhyStatus0.ctrl_locked 是否為1檢測PHY DLL是否鎖定
1:
ldr r1,[r0,#0x40]
and r2,r1,0x2
cmp r2,#0
beq 1b
//12.強制延時
and r1,r1,#0x3fc0
mov r2,r1,LSL #18
orr r2,r2,#0x100000
orr r2,r2,#0x1000
orr r2,r2,#0x3
str r2,[r0,#0x18]
//13.開機等待200us是時鐘平穩,這一步不用開機到記憶體初始化肯定時鐘平穩了
//14.使用DirectCmd發送NOP指令,確定CKE保持為高電平
ldr r1, =0x07000000
str r1,[r0,#0x10]
//15.等待最少400ns
//16.使用DirectCmd發送PALL指令
ldr r1, =0x01000000
str r1,[r0,#0x10]
//17.使用DirectCmd發送EMRS2指令去寫入運作參數
ldr r1, =0x00020000
str r1,[r0,#0x10]
//18.使用DirectCmd發送EMRS3指令去寫入運作參數
ldr r1, =0x00030000
str r1,[r0,#0x10]
//19. 使用DirectCmd發送EMRS指令,使能記憶體DLLs
ldr r1, =0x00010000
str r1,[r0,#0x10]
//20.使用DirectCmd發送MRS指令,複位記憶體DLLs
ldr r1, =0x542
str r1,[r0,#0x10]
//21.使用DirectCmd發送PALL指令
ldr r1, =0x01000000
str r1,[r0,#0x10]
//22.使用DirectCmd發送兩次auto refresh指令
ldr r1, =0x05000000
str r1,[r0,#0x10]
str r1,[r0,#0x10]
//23.使用DirectCmd發送MRS指令,運作無複位的記憶體DLL
ldr r1, =0x442
str r1,[r0,#0x10]
//24.最少等200個時鐘周期
//25.使用DirectCmd發送EMRS指令,設定OCD校正
ldr r1, =0x00010780
str r1,[r0,#0x10]
//26.不用設定
//27.設定ConControl啟動自動重新整理計數器
ldr r1, =0x0FFF13B8
str r1,[r0,#0x0]
//28如果有必要的話設定MemControl開啟掉電模式
mov pc,lr
在序列槽初始化代碼後添加調用DDR2初始化代碼,修改代碼如下:
bl system_clock_init
/* for UART */
bl uart_init
bl ddr2mem_init
最後是測試工作:
代碼測試始終伴随程式開發而存在,如何測試記憶體能不能用呢?我們可以這麼做,往記憶體單元裡面寫入資料,然後讀出來,比較寫入的資料與讀出的資料。
最簡單的方式是利用led來調試。你可以自己想想怎麼做?這裡我們采用序列槽把指定的位址内容列印出來。
測試的函數如下(這個函數是從别人視訊那裡偷學過來的):用法:把要列印的位址賦給r0,就是列印相應位址内容。
display_mem:
ldr r0,[r0]
ldr r1, =0xE2900020
ldr r2, =0x30
str r2,[r1]
ldr r2,=0x78
str r2,[r1]
ldr r3, =28
display_loop_cnt:
lsr r2,r0,r3
and r2,r2,#0xF
cmp r2,#10
addmi r2,r2,#0x30
addpl r2,r2,#0x37
str r2,[r1]
sub r3,r3,#4
cmp r3,#0
bpl display_loop_cnt
ldr r2, =0xA
str r2,[r1] @UTH0='\r'
ldr r2,=0xD
str r2,[r1]
mov pc,lr
将測試代碼(往0x20000000内容單元寫入0x1234ABCD并通過序列槽列印出來)添加到lowlevel_init.S檔案,修改如下:
bl ddr2mem_init
ldr r1, =0x1234abcd
ldr r0, =0x20000000
str r1,[r0]
bl display_mem
bl internal_ram_init
最後燒寫,測試效果截圖如下: