Appendix D : SDRAM === ###### tags: `gnitnaw` `twlkh` 本附錄講的是SDRAM的底層操作。 ## D.1: Basics [動態隨機存取記憶體(Dynamic Random Access Memory,DRAM)](https://zh.wikipedia.org/zh-tw/%E5%8A%A8%E6%80%81%E9%9A%8F%E6%9C%BA%E5%AD%98%E5%8F%96%E5%AD%98%E5%82%A8%E5%99%A8)是一種半導體記憶體,每個bit由一個電容和一個電晶體組成,主要是利用電容內儲存電荷的多寡來代表一個二進位位元(bit)是1還是0。不過正常電容會漏電,所以要常常充電(refresh),當沒電的時候當然資料也會通通消失。[同步動態隨機存取記憶體(SDRAM)](https://zh.wikipedia.org/zh-tw/SDRAM)則是有一個同步接口的DRAM。 SDRAM其實是由一些DRAM陣列組成,像這樣: ![](http://unicornx.github.io/img/20150728-mini2440-sdram/sdram_address_map.png) 要找到某一塊基本儲存單元(cell)的資料(通常大小為8-16bit),就要給定行(row),然後給定列(column)。這樣的儲存單位被稱為Physical Bank(簡稱P-Bank)。 那如果要結合32bit或64bit該怎麼辦?通常我們會用數個P-Bank湊成32bit或64bit(然後這一個大塊被稱為Logical Bank, L-Bank);假設一個P-Bank的cell大小為8bit,那四個P-Bank組成的L-Bank就有32bit。 :::success stm32F429 Discovery的SDRAM規格如下: "The 64-Mbit SDRAM is a high speed CMOS, dynamic random-access memory designed to operate in 3.3 V memory systems containing 67,108,864 bits. It is internally configured as a quad-bank DRAM with a synchronous interface. Each 16,777,216-bit bank is organized as 4,096 rows (12bit) by 256 (8 bit) columns by 16 bits. The 64-Mbit SDRAM includes an auto-refresh, a power-saving and a power-down modes. All signals are registered on the positive edge of the clock signal, CLK. " ::: :::info 目前SDRAM標準是最多4個L-Bank,DDR-II的標準則是8個。由於成本跟實用性的考量,一般來說不會去用含有大cell(32bit或以上)的L-Bank。並不是說做不出來,而是這種大cell的P-Bank還會增加Addressing Collision的機率,大幅降低效率。 Viller Hsiao : address collision 看起來比較像是連取存取同一個 Bank 的話大家就要排隊的問題。 這篇論文 p.59 有一段敘述 http://www.ece.umd.edu/~blj/papers/thesis-PhD-wang--DRAM.pdf "multiple banks so that multiple, independent accesses to different DRAM arrays can occur in parallel." ::: 下圖為讀取L-Bank的示意圖:先把L-Bank分組(每組32bit,cell=8bit的情況就是一組4個),每次讀取先決定要讀哪組L-Bank,然後讀出那組同樣field的cell(線路並聯就解決了)就能一次解決了。 ![](http://www.pctechguide.com/wp-content/uploads/2011/09/14sdram.gif) :::info 有些Memory controller是以B(Bank)->R(Row)->C(Column)找尋對應的資料,其優點為功耗低;有些是R->B->C,好處是性能高。stm32f429用的是B->R->C。 ::: Memory Controller要讀取存在某physical address的資料,就要先把physical address轉換成行列資訊,然後發出行列訊號讀寫該處的資料:行方面為Row Address Select(RAS)訊號,列為Column Address Select(CAS)訊號。 ## D.2: Clocking 既然要頻繁送資料給Memory Controller,那使用clocking去同步信號是必要的。 Single Data Rate (SDR) SDRAM在一個clock週期內只傳輸(讀或寫)一次資料,它是在clock上升期傳輸資料;而Double Data Rate (DDR)則是一個clock週期內可傳輸兩次資料(clock上升期和下降期各一次)。 通常CPU/SoC本身的時脈會大幅高於該系統其他的部份,所以處理器一般都會另外使用以震盪器產生,頻率較低的clock source為主要的clock signal(控制SDRAM等重要裝置用),然後搭配鎖相環(PLL: Phase-locked loops)產生該clock singal固定倍數的頻率。 :::success Discovery kit with STM32F429ZI MCU 的Timing簡述如下: - 內建RC震盪器(HSI: High Speed Internal clock): 16MHz (1% accuracy),搭配PLL - 外接RC震盪器(HSE: High Speed External clock): 8MHz - 低速內建時鐘(LSI: Low Speed Internal clock): RC震盪器,32kHz,給Watchdog用。 - 低速外部時鐘(LSE: Low Speed External clock): 為石英震盪器,32.768kHz,主要給RTC用。 - PLL: PLL_M為divider設定(最多為8,輸出頻率即8MHz/8=1MHz),PLL_N為倍頻倍數(最多為360,不過一般來說沒法設那麼多) - CPU(ARM 32-bit Cortex-M4) 時脈 : 由外頻(HSI或HSE)與倍頻(PLL)決定。官方說法最高為180MHz(沒超頻過不知道行不行,不過既然RPi都能超頻了這沒道理不行)。 至於clock tree則如下圖, ![](http://www.developpez.net/forums/attachments/p187740d1442079374/general-developpement/programmation-systeme/embarque/firmware-stm32f4-gpio-question-n-1/clock-tree.png/) 其行為大略描述為: - f(vco clock) = f(HSI or HSE) $\times$ PLL_N / PLL_M 。根據手冊f(vco clock)建議設為1-2MHz。 - f(system clock) = f(vco clock) / PLL_P - f(USB OTG FS, SDIO, RNG clock output) = f(system clock)/PLL_Q。此值根據USB標準為48MHz。 System clock提供所有週邊或外設的clock source。根據stm32f429i discovery其建議設定為180MHz。以下為可能設定方式: ![](http://scaluza.com/wp-content/uploads/2016/03/clock.jpg) System clock透過AHB(Advanced High-performance Bus)與APB(Advanced Peripheral Bus)提供clock給週邊或外設。AHB是給RAM或DMA等高速裝置,APB就是給低速外設。給RAM用的clock為HCLK,是system clock經過AHB divider後得到的值。 ::: ## D.3: Setup STM32F429使用Flexible Memory Controller(FMC)來管理SRAM、SDRAM、NOR FLASH以及NAND FLSAH等。以下為初始化的過程(不是完全懂,勉強讀): ```C void BSP_SDRAM_Init(void) { /* SDRAM device configuration */ SdramHandle.Instance = FMC_SDRAM_DEVICE; /* FMC Configuration -------------------------------------------------------*/ /* FMC SDRAM Bank configuration */ /* Timing configuration for 90 Mhz of SD clock frequency (180Mhz/2) */ /* TMRD: load register 與開始執行refresh或column指令之間需要兩個Clock cycles 的延遲 */ Timing.LoadToActiveDelay = 2; /* TXSR: min=70ns (7x11.11ns) ,為refresh後需等待的時間 */ Timing.ExitSelfRefreshDelay = 7; /* TRAS: min=42ns (4x11.11ns) max=120k (ns) ,column指令後,過了這段時間才能refresh */ Timing.SelfRefreshTime = 4; /* TRC: min=70 (7x11.11ns) ,兩個column指令間的間隔時間,或兩個refresh之間的間隔時間 */ Timing.RowCycleDelay = 7; /* TWR: min=1+ 7ns (1+1x11.11ns) ,寫入命令和refresh之間的延遲 */ Timing.WriteRecoveryTime = 2; /* TRP: 20ns => 2x11.11ns,refresh與其它命令間的延遲 */ Timing.RPDelay = 2; /* TRCD: 20ns => 2x11.11ns ,column指令到row指令間的延遲*/ Timing.RCDDelay = 2; /* FMC SDRAM control configuration , 選擇FMC mapping的SDRAM區域*/ SdramHandle.Init.SDBank = FMC_SDRAM_BANK2; /* Row addressing: [7:0] ,SDRAM的列地址寬度*/ SdramHandle.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8; /* Column addressing: [11:0] ,SDRAM的行地址寬度*/ SdramHandle.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12; /* SDRAM的資料寬度 */ SdramHandle.Init.MemoryDataWidth = SDRAM_MEMORY_WIDTH; /* SDRAM的L-Bank 數目 */ SdramHandle.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; /* CASLatency,發出讀取命令後與得到資料時間之間的延遲 */ SdramHandle.Init.CASLatency = SDRAM_CAS_LATENCY; /* 是否能使用保護模式 */ SdramHandle.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE; /* 同步時鐘參數 */ SdramHandle.Init.SDClockPeriod = SDCLOCK_PERIOD; SdramHandle.Init.ReadBurst = SDRAM_READBURST; SdramHandle.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1; /* SDRAM controller initialization */ MspInit(); HAL_SDRAM_Init(&SdramHandle, &Timing); /* SDRAM initialization sequence */ BSP_SDRAM_Initialization_sequence(REFRESH_COUNT); } void BSP_SDRAM_Initialization_sequence(uint32_t RefreshCount) { __IO uint32_t tmpmrd =0; /* Step 1: Configure a clock configuration enable command */ Command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE; Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; Command.AutoRefreshNumber = 1; Command.ModeRegisterDefinition = 0; /* Send the command */ HAL_SDRAM_SendCommand(&SdramHandle, &Command, SDRAM_TIMEOUT); /* Step 2: Insert 100 us minimum delay */ /* Inserted delay is equal to 1 ms due to systick time base unit (ms) */ HAL_Delay(1); /* Step 3: Configure a PALL (precharge all) command */ Command.CommandMode = FMC_SDRAM_CMD_PALL; Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; Command.AutoRefreshNumber = 1; Command.ModeRegisterDefinition = 0; /* Send the command */ HAL_SDRAM_SendCommand(&SdramHandle, &Command, SDRAM_TIMEOUT); /* Step 4: Configure an Auto Refresh command */ Command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE; Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; Command.AutoRefreshNumber = 4; Command.ModeRegisterDefinition = 0; /* Send the command */ HAL_SDRAM_SendCommand(&SdramHandle, &Command, SDRAM_TIMEOUT); /* Step 5: Program the external memory mode register */ tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 | SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL | SDRAM_MODEREG_CAS_LATENCY_3 | SDRAM_MODEREG_OPERATING_MODE_STANDARD | SDRAM_MODEREG_WRITEBURST_MODE_SINGLE; Command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE; Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; Command.AutoRefreshNumber = 1; Command.ModeRegisterDefinition = tmpmrd; /* Send the command */ HAL_SDRAM_SendCommand(&SdramHandle, &Command, SDRAM_TIMEOUT); /* Step 6: Set the refresh rate counter */ /* Set the device refresh rate */ HAL_SDRAM_ProgramRefreshRate(&SdramHandle, RefreshCount); } ``` ## 參考資料: - [[技術] SDRAM研究心得](http://lazyflai.blogspot.fr/2010/09/sdram.html) - [SDRAM](https://www.pctechguide.com/computer-memory/sdram) - [Memory Controller](http://ivanboyblog.blogspot.fr/2015/08/memory-controller.html) - [基于mini2440开发板学习S3C2440的内存控制器 ](http://unicornx.github.io/2015/07/28/20150728-mini2440-sdram/) - [mini2440 貌似复杂的mmu](http://www.programgo.com/article/35455438413/;jsessionid=5D282F08BD28D23AEE09237BD426D6D9) - [S3C2440的MMU](http://www.cnblogs.com/dudu1990/p/3406265.html) - [Scalable Many-Core Memory Systems Topic 1: DRAM Basics and DRAM Scaling Prof. Onur Mutlu HiPEAC ACACES Summer.](http://slideplayer.com/slide/7534141/) - [时钟,分频与倍频](http://www.enixyu.com/blog/2015/10/25/ee-clock-and-pll/) - [How to properly set clock speed for STM32F4xx devices](https://stm32f4-discovery.net/2015/01/properly-set-clock-speed-stm32f4xx-devices/) - [第26章 FMC—扩展外部SDRAM](http://www.cnblogs.com/firege/p/5805812.html) - [Initialization of ILI9341 LCD and STMPE811 TS for STM32F4](http://scaluza.com/stm32f4/getting_started/initialization-of-ili9341-lcd-and-stmpe811-ts-for-stm32f4/#.WGVFpWeVvZs) - [stm32f407时钟配置方法(感觉很好,分享一下)](http://blog.chinaunix.net/uid-27680183-id-3784602.html) - [关于STM32里PLL时钟配置PLL_M PLL_N PLL_P PLL_Q](http://blog.sina.com.cn/s/blog_9361c9220102vf5c.html) - [ [原创] 强仔教你玩stm32f401 Nucleo之系统时钟的配置 ](http://www.eeboard.com/bbs/thread-38051-1-1.html) - [详解】SDRAM的地址映射方式BRC(Bank Row Column)和RBC(Row Bank Column)](http://www.crifan.com/detailed_sdram_address_mapping_brc_bank_row_column_and_rbc_row_bank_column/) - [RM0090](http://l.facebook.com/l.php?u=http%3A%2F%2Fwww.st.com%2Fresource%2Fen%2Freference_manual%2FDM00031020.pdf&h=GAQHzgl3O) - Discovery kit with STM32F429ZI MCU User manual : UM1670, UM1662