瘋雨-版權所有,轉載請註明【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]
這一步簡單 直接給出代碼:
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
ldr r1, =0x20E00323
str r1,[r0,#0x8]
ldr r1, =0xFF000000
ldr r1,[r0,#0x14]
ldr r1, =0xFFFF00FF
str r1,[r0,#0x28]
這幾個寄存器的值要根據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]
這裏我們就不設置了
完整的初始化代碼如下:
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
最後燒寫,測試效果截圖如下: