從上圖可知,外部內存類的設備與存儲管理器相連,那麼CPU是怎樣訪問到內存的呢?經過存儲管理器。CPU比較單純,只會按照指令執行,CPU只負責發出地址,怎樣找到內存類設備呢?這些都交給存儲管理器來管理。linux
s3c2440對外引出的27根地址線ADDR0-ADDR26的訪問範圍只有128M,那麼如何達到1G呢,CPU還對外引出了8條片選線nGCS0-nGCS7,對應於bank0-bank7,當訪問BANKX的地址空間時,nGCSx輸出低電平選中外部芯片,這樣就能達到1G的地址空間。以下圖性能
s3c2440做爲32位的CPU,可使用的地址理論上是4G,除了存儲器管理的1G,還有內部的寄存器佔用地址外,其餘的地址剩下沒用。s3c2440的內部寄存器地址範圍是0x48000000-0x5FFFFFFF,各功能部件的功能大致相同。
下圖是內部寄存器地址分佈:spa
SDRAM內存工做原理
SDRAM的內部是一個存儲陣列。陣列就如同表格同樣,將數據「填」進去。在數據讀寫時和表格的檢索原理同樣,先指定一個行(Row),再指定一個列(Column),咱們就能夠準確地找到所須要的單元格,這就是內存芯片尋址的基本原理,如圖所示。prototype
這個單元格(存儲陣列)就叫邏輯 Bank(Logical Bank,下文簡稱 L-Bank)。 因爲技術、成本等緣由,不可能只作一個全容量的 L-Bank,並且最重要的是,因爲SDRAM的工做原理限制,單一的 L-Ban k將會形成很是嚴重的尋址衝突,大幅下降內存效率。因此人們在 SDRAM內部分割成多個 L-Bank,目前基本都是 4個(這也是SDRAM規範中的最高L-Bank數量),因而可知,在進行尋址時就要先肯定是哪一個 L-Bank,而後在這個選定的 L-Bank中選擇相應的行與列進行尋址。所以對內存的訪問,一次只能是一個 L-Bank工做。
當對內存進行操做時,先要肯定操做L-Bank,所以要對L-Bank進行選擇。在內存芯片的外部管腳上多出了兩個管腳BA0, BA1,用來片選4個L-Bank。如前所述, 32位的地址長度因爲其存儲結構特色,分紅了行地址和列地址。經過下面的內存結構圖可知,內存外接管腳地址線只有13根地址線A0~A12,它最多隻能尋址8M內存空間,到底使用什麼機制來實現對64M內存空間進行尋址的呢?SDRAM的行地址線和列地址線是分時複用的,即地址要分兩次送出,先送出行地址(nSRAS行有效操做),再送出列地址(nSCAS列有效操做)。這樣,能夠大幅度減小地址線的數目,提升器件的性能和製做工藝複雜度。但尋址過程也會所以而變得複雜。實際上,如今的SDRAM通常都以L-Bank爲基本尋址對象的。由L-Bank地址線BAn控制L-Bank間的選擇,行地址線和列地址線貫穿鏈接全部的L-Bank,每一個L-Bank的數據的寬度和整個存儲器的寬度相同,這樣,能夠加快數據的存儲速度。同時,BAn還可使未被選中的L-Bank工做於低功耗的模式下,從而下降器件的功耗。
這些都是內存的原理部分,其實做爲工程師,可以正確配置存儲管理器的相關的寄存器,就能夠對內存進行初始化,可使用了。
下面是配置存儲管理器的思路及方法:code
以jz2440爲例:對象
一、肯定BA0、BA1的接線
經過閱讀s3c2440手冊,能夠看出CPU ADDR24和ADDR25
解釋一下名詞:
Bank Size: 外接內存容量大小(HY57561620是4Mbit*16bit*4Bank*2Chips/8=64MB)
Bus Width: 總線寬度 (兩片16位HY57561620,並聯成32位)
Base Component:單個芯片容量(bit)(256Mb)
Memory Configration:內存配置 ((4M*16*4banks)*2Chips )
使用A[25:24]兩根地址線做爲Bank片選信號,正好兩根接線能夠片選每一個存儲單元的4個BANKS。
S3C2440提供的兩片16位芯片並聯鏈接示意圖,An是CPU地址總線,其中A2~A14爲內存芯片尋址總線,之因此地址尋址總線從A2開始是由於內存地址都是按字節對齊的,,A24,A25爲L-Bank片選信號,Dn爲CPU數據總線,其它爲對應控制信號線。blog
S3C2440A爲32位CPU,也就是說其數據總線和地址總線寬度都是32位(能夠理解爲32根線一端鏈接CPU內部,另一端鏈接向內存控制器),那麼內存數據的輸入/輸出端也要保證是32位總線,jz2440上採用兩片16位寬總線內存芯片並聯構成32位總線。其中一個芯片鏈接到CPU數據總線的低16位,另一個芯片鏈接到數據總線上的高16位,並聯成32位總線,所以兩個芯片的輸入/輸出總線鏈接到CPU總線上的不一樣管腳上。ip
1、SDRAM的讀操做
SDRAM進行讀操做時,先向地址線上送上要讀取數據的地址,經過前面的知識瞭解到,地址被分紅3部分,行地址,列地址,L-Bank片選信號。片選(L-Bank的定址)操做和行有效操做能夠同時進行。
在CS、L-Bank定址的同時,RAS(nSRAS行地址選通訊號)也處於有效狀態。此時 An地址線則發送具體的行地址。A0~A12,共有13根地址線(可表示8192行),A0~A12的不一樣數值就肯定了具體的行地址。因爲行有效的同時也是相應 L-Bank有效,因此行有效也可稱爲L-Bank有效。
行地址肯定以後,就要對列地址進行尋址了。可是,地址線仍然是行地址所用的 A0~A12。沒錯,在SDRAM中,行地址與列地址線是複用的。列地址複用了A0~A8,共9根(可表示512列)。那麼,讀/寫的命令是怎麼發出的呢?其實沒有一個信號是發送讀或寫的明確命令的,而是經過芯片的可寫狀態的控制來達到讀/寫的目的。顯然WE信號(nWE)就是一個關鍵。WE無效時,固然就是讀取命令。有效時,就是寫命令。
SDRAM基本操做命令, 經過各類控制/地址信號的組合來完成(H表明高電平,L表明低電平,X表示高,低電平均沒有影響)。此表中,除了自刷新命令外,全部命令都是默認CKE(SCKEl輸入時鐘頻率有效)有效。列尋址信號與讀寫命令是同時發出的。雖然地址線與行尋址共用,但CAS(nSCAS列地址選通訊號)信號則能夠區分開行與列尋址的不一樣,配合A0~A8,A9~A11來肯定具體的列地址。
讀取命令與列地址一塊發出(當WE爲低電平是即爲寫命令)然而,在發送列讀寫命令時必需要與行有效命令有一個間隔,這個間隔被定義爲 tRCD,即RAS to CAS Delay(RAS至 CAS延遲),這個很好理解,在地址線上送完行地址以後,要等到行地址穩定定位後再送出列地址,tRCD是SDRAM的一個重要時序參數,相關數值參看對應芯片硬件手冊。一般tRCD以時鐘週期(tCK,Clock Time)數爲單位,好比筆者MINI2440所用內存芯片裏面寫到tRCD爲20nst,若是未來內存工做在100MHz,那麼RCD至少要爲2個時鐘週期, RCD=2。內存
SDRAM讀操做時序圖資源
在選定列地址後,就已經肯定了具體的存儲單元,剩下就是等待數據經過數據 I/O通道(DQ)輸出到內存數據總線上了。可是在列地址選通訊號CAS 發出以後,仍要通過必定的時間纔能有數據輸出,從CAS與讀取命令發出到第一筆數據輸出的這段時間,被定義爲 CL(CAS Latency,CAS潛伏期)。因爲CL只在讀取時出現,因此CL又被稱爲讀取潛伏期(RL,Read Latency)。CL的單位與tRCD同樣,也是時鐘週期數,具體耗時由時鐘頻率決定(筆者官方手冊CL=3)。不過,CAS並非在通過CL週期以後才送達存儲單元。實際上CAS與RAS同樣是瞬間到達的。因爲芯片體積的緣由,存儲單元中的電容容量很小,因此信號要通過放大來保證其有效的識別性,這個放大/驅動工做由S-AMP負責。但它要有一個準備時間才能保證信號的發送強度,這段時間咱們稱之爲tAC(Access Time from CLK,時鐘觸發後的訪問時間)。
2、SDRAM預充電操做
本來邏輯狀態爲1的電容在讀取操做後,會因放電而變爲邏輯0。因爲SDRAM的尋址具備獨佔性,因此在進行完讀寫操做後,若是要對同一L-Bank的另外一行進行尋址,就要將原先操做行關閉,從新發送行/列地址。在對原先操做行進行關閉時,DRAM爲了在關閉當前行時保持數據,要對存儲體中原有的信息進行重寫,這個充電重寫和關閉操做行過程叫作預充電,發送預充電信號時,意味着先執行存儲體充電,而後關閉當前L-Bank操做行。預充電中重寫的操做與刷新操做(後面詳細介紹)同樣,只不過預充電不是按期的,而只是在讀操做之後執行的。
3、SDRAM突發操做
突發(Burst)是指在同一行中相鄰的存儲單元連續進行數據傳輸的方式,連續傳輸所涉及到存儲單元(列)數量就是突發長度(Burst Length,簡稱BL)。
在目前,因爲內存控制器一次讀/寫P-Bank位寬的數據,也就是8個字節,可是在現實中小於8個字節的數據不多見,因此通常都要通過多個週期進行數據的傳輸,上文寫到的讀/寫操做,都是一次對一個存儲單元進行尋址,若是要連續讀/寫,還要對當前存儲單元的下一單元進行尋址,也就是要不斷的發送列地址與讀/寫命令(行地址不變,因此不用再對地尋址)。雖然因爲讀/寫延遲相同可讓數據傳輸在I/O端是連續的,可是它佔用了大量的內存控制資源,在數據進行連續傳輸時沒法輸入新的命令效率很低。爲此,引入了突發傳輸機制,只要指定起始列地址與突發長度,內存就會依次自動對後面相應長度數據的數據存儲單元進行讀/寫操做而再也不須要控制器連續地提供列地址,這樣,除了第一筆數據的傳輸須要若干個週期(主要是之間的延遲,通常的是tRCD + CL)外,其後每一個數據只需一個週期便可。
SDRAM寫操做
SDRAM的基本寫操做也須要控制線和地址線相配合地發出一系列命令來完成。先發出芯片有效命令,並鎖定相應的L-BANK地址(BA0、BA1給出)和行地址(A0~A12給出)。芯片有效命令發出後必須等待大於tRCD的時間後,發出寫命令數據,待寫入數據依次送到DQ(數據線)上。在最後一個數據寫入後,延遲tWR時間。發出預充電命令,關閉已經激活的頁。等待tRP時間後,能夠展開下一次操做。寫操做能夠有突發寫和非突發寫兩種。突發長度同讀操做。
寫操做時序以下圖:
4、SDRAM的刷新
SDRAM之因此成爲DRAM就是由於它要不斷進行刷新(Refresh)才能保留住數據,所以它是SDRAM最重要的操做。
刷新操做與預充電中重寫的操做同樣,都是用S-AMP先讀再寫。但爲何有預充電操做還要進行刷新呢?由於預充電是對一個或全部 L-Bank中的工做行操做,而且是不按期的,而刷新則是有固定的週期,依次對全部行進行操做,以保留那些很長時間沒經歷重寫的存儲體中的數據。但與全部L-Bank預充電不一樣的是,這裏的行是指全部L-Bank中地址相同的行,而預充電中各L-Bank中的工做行地址並非必定是相同的。那麼要隔多長時間重複一次刷新呢?目前公認的標準是,存儲體中電容的數據有效保存期上限是64ms(毫秒,1/1000秒),也就是說每一行刷新的循環週期是64ms。這樣刷新時間間隔就是: 64m/行數s。咱們在看內存規格時,常常會看到4096 Refresh Cycles/64ms或8192 Refresh Cycles/64ms的標識,這裏的4096與8192就表明這個芯片中每一個L-Bank的行數。刷新命令一次對一行有效,刷新間隔也是隨總行數而變化,4096行時爲 15.625μs(微秒,1/1000毫秒),8192行時就爲 7.8125μs。刷新操做分爲兩種:Auto Refresh,簡稱AR與Self Refresh,簡稱SR。不管是何種刷新方式,都不須要外部提供行地址信息,由於這是一個內部的自動操做。對於 AR,SDRAM內部有一個行地址生成器(也稱刷新計數器)用來自動的依次生成行地址。因爲刷新是針對一行中的全部存儲體進行,因此無需列尋址,或者說CAS在 RAS以前有效。因此,AR又稱CBR(CAS Before RAS,列提早於行定位)式刷新。因爲刷新涉及到全部L-Bank,所以在刷新過程當中,全部 L-Bank都中止工做,而每次刷新所佔用的時間爲9個時鐘週期(PC133標準),以後就可進入正常的工做狀態,也就是說在這9個時鐘期間內,全部工做指令只能等待而沒法執行。64ms以後則再次對同一行進行刷新,如此周而復始進行循環刷新。顯然,刷新操做確定會對SDRAM的性能形成影響,但這是沒辦法的事情,也是DRAM相對於 SRAM(靜態內存,無需刷新仍能保留數據)取得成本優點的同時所付出的代價。SR則主要用於休眠模式低功耗狀態下的數據保存,這方面最著名的應用就是 STR(Suspend to RAM,休眠掛起於內存)。在發出AR命令時,將CKE置於無效狀態,就進入了SR模式,此時再也不依靠系統時鐘工做,而是根據內部的時鐘進行刷新操做。在SR期間除了CKE以外的全部外部信號都是無效的(無需外部提供刷新指令),只有從新使CKE有效才能退出自刷新模式並進入正常操做狀態。
剩下的事情就是配置寄存器
(1)BWSCON寄存器(BUS WIDTH & WAIT CONTROL REGISTER)
根據開發板的存儲器配置和芯片型號,設置每一個BANK焊接芯片的位寬和等待狀態,BWSCON,每4位對應一個BANK,這4位分別表示:
STx:啓動/禁止SDRAM的數據掩碼引腳(UB/LB),SDRAM沒有高低位掩碼引腳,此位爲0,SRAM鏈接有UB/LB管腳,設置爲1
注:UB/LB數據掩碼引腳用來控制芯片讀取/寫入的高字節和低字節(對比硬件手冊SDRAM和SRAM的接線圖)
WSx:是否使用存儲器的WAIT信號,一般設爲0
DWx:設置焊接存儲器芯片的位寬,筆者開發板使用兩片容量爲32M,位寬爲16的SDRAM組成64M,32位存儲器,所以DW7,DW6位設置爲0b10,其它BANK不用設置採用默認值便可。
BANK0對應的是系統引導BANK,這4位比較特殊,它的設置是由硬件跳線決定的,所以不用設置
BWSCON設置結果:0x22000000
2)BANKCON0~BANKCON5 (BANK CONTROL REGISTER)
這6個寄存器用來設置對應BANK0~BANK5的訪問時序,採用默認值0x700便可
(3)BANKCON6~BANKCON7 (BANK CONTROL REGISTER)
因爲內存都焊接在這兩個BANK上,所以內存驅動主要是對這兩個寄存器進行設置
MT:設置BANK6~BANK7的存儲器類型,
00=ROM or SRAM 01=保留
10=保留 11=SDRAM
內存爲SDRAM,設置爲0b11,對應的應該設置Trcd和SCAN位,其它位和SDRAM無關
Trcd:RAS to CAS Delay行地址選通到列地址選通延遲,這個參數請看後面的內存工做原理擴展部分解釋,筆者內存芯片爲HY57V561620,由其芯片手冊可知其Trcd爲最少 20ns,若是內存工做在100MHz,則該值至少要爲2個時鐘週期,一般設置爲3個時鐘週期,所以設置爲0b01
SCAN:SDRAM Column Address Number SDRAM的列地址數,筆者內存芯片爲HY57V561620,列地址數爲9,設置爲0b01
BANK6,BANK7設置結果爲:0x18005
(4)REFRESH (REFRESH CONTROL REGISTER)
刷新頻率設置寄存器(REFRESH)
SDRAM的刷新有效,刷新頻率設置寄存器(刷新)
REFEN:開啓/關閉刷新功能,設置爲1,開啓刷新
TREFMD:SDRAM刷新模式,0=CBR/AutoRefresh, 1=Self Refresh,設置爲0,自動刷新
Trp:行地址選通預充電時間,通常設置爲0b00便可
Tsrc:單行刷新時間,設置爲0b11便可。
Refresh Counter:內存存儲單元刷新數,它經過下面公式計算出:
Refresh Counter = 2^11 + 1 – SDRAM時鐘頻率(MHz)* SDRAM刷新週期(uS)
SDRAM的刷新週期,也就是內存存儲單元間隔須要多久進行一次刷新,前面內存工做原理分析可知電容數據保存上限爲64ms,筆者使用內存芯片每一個L-Bank共有8192行,所以每次刷新最大間隔爲:64ms/8192 = 7.8125uS,若是內存工做在外部晶振頻率12MHz下,Refresh Counter = 1955,若是內存工做在100MHz下,那麼Refresh Counter = 1269(取大整數)
REFRESH寄存器設置爲:
0x8e0000 + 1269 = 0x008e04f5(HCLK = 100MHz)
0x8e0000 + 1955 = 0x008e07a3(HCLK = 12MHz)
(5)BANKSIZE寄存器(BANKSIZE REGISTER)
BANKSIZE寄存器(BANKSIZE)
設置內存的突發傳輸模式,省電模式和內存容量。
BURST_EN:是否開啓突發模式, 0 = ARM內核禁止突發傳輸 1 = 開啓突發傳輸,設置爲1,開啓突發傳輸
SCKE_EN:是否使用SCKE信號做爲省電模式控制信號, 0 = 不使用SCKE信號做爲省電模式控制信號 1 = 使用SCKE信號做爲省電模式控制信號,一般設置爲1
SCLK_EN: 設置向存儲器輸入工做頻率,0 = 一直輸入SCLK頻率,即便沒有內存操做也會輸入, 1 = 僅當進行內存數據操做時才輸入SCLK頻率,一般設置爲1
BK76MAP:設置Bank6/7的內存容量,筆者使用開發板內存爲兩片32M內存芯片並聯成64M,它們所有都外接到Bank6上,所以選擇0b001
BANKSIZE寄存器設置爲:0xb1
(6)SDRAM模式設置寄存器MRSRx (SDRAM MODE REGISTER SET REGISTER)
SDRAM模式設置寄存器(MRSRx)
該寄存器用於設置CAS潛伏週期,能夠手動設置的位只有CL[6:4]位,經過前面內存工做原理可知,筆者使用開發板CL=3,即0b011
MRSR6,MRSR7設置爲:0x00000030
內存實驗:
設置該工程加載時運行時地址爲0x30000000
ctr.S
.equ MEM_CTL_BASE, 0x48000000 .equ SDRAM_BASE, 0x30000000 .text .global _start _start: bl close_watchdog bl memsetup bl copy_steppingstone_to_sdram ldr pc, =on_sdram on_sdram: ldr sp,=0x34000000 bl main continue: b continue close_watchdog: mov r1,#0x53000000 mov r2,#0x0 str r2,[r1] mov pc,lr copy_steppingstone_to_sdram: mov r1,#0 ldr r2,=SDRAM_BASE mov r3,#4*1024 1: ldr r4,[r1],#4 str r4,[r2],#4 cmp r1,r3 bne 1b mov pc,lr memsetup: mov r1,#MEM_CTL_BASE adrl r2,mem_cfg_val add r3,r1,#52 1: ldr r4,[r2],#4 str r4,[r1],#4 cmp r1,r3 bne 1b mov pc,lr .align 4 mem_cfg_val: .long 0x22011110 @ BWSCON .long 0x00000700 @ BANKCON0 .long 0x00000700 @ BANKCON1 .long 0x00000700 @ BANKCON2 .long 0x00000700 @ BANKCON3 .long 0x00000700 @ BANKCON4 .long 0x00000700 @ BANKCON5 .long 0x00018005 @ BANKCON6 .long 0x00018005 @ BANKCON7 .long 0x008C07A3 @ REFRESH .long 0x000000B1 @ BANKSIZE .long 0x00000030 @ MRSRB6 .long 0x00000030 @ MRSRB7
main.c
#define GPFCON (*(volatile unsigned long *)0x56000050) #define GPFDAT (*(volatile unsigned long *)0x56000054) #define GPF4_out ( 1 << 4*2 ) #define GPF5_out ( 1 << 5*2 ) #define GPF6_out ( 1 << 6*2 ) void delay(volatile unsigned long dly) { for(;dly > 0;dly--); } int main() { unsigned i = 1; GPFCON = GPF4_out | GPF5_out | GPF6_out; while(1) { GPFDAT = ( ~( i << 4 ) ); i *=2; if( i == 8) { i = 1; } delay(30000); } return 0; }
Makefile
CFLAGS := -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -ffreestanding led_on.bin:ctr.S main.c arm-linux-gcc $(CFLAGS) -c -o ctr.o ctr.S arm-linux-gcc $(CFLAGS) -c -o main.o main.c arm-linux-ld -Ttext 0x30000000 ctr.o main.o -o led_on.elf arm-linux-objcopy -O binary -S led_on.elf led_on.bin arm-linux-objdump -D -m arm led_on.elf>led_on.dis clean: rm -f led_on.bin led_on.elf *.o led_on.dis