Try   HackMD

Appendix D : SDRAM

tags: gnitnaw twlkh

本附錄講的是SDRAM的底層操作。

D.1: Basics

動態隨機存取記憶體(Dynamic Random Access Memory,DRAM)是一種半導體記憶體,每個bit由一個電容和一個電晶體組成,主要是利用電容內儲存電荷的多寡來代表一個二進位位元(bit)是1還是0。不過正常電容會漏電,所以要常常充電(refresh),當沒電的時候當然資料也會通通消失。同步動態隨機存取記憶體(SDRAM)則是有一個同步接口的DRAM。

SDRAM其實是由一些DRAM陣列組成,像這樣:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

要找到某一塊基本儲存單元(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。

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. "

目前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-wangDRAM.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(線路並聯就解決了)就能一次解決了。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

有些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固定倍數的頻率。

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則如下圖,

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

其行為大略描述為:

  • f(vco clock) = f(HSI or HSE)
    ×
    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。以下為可能設定方式:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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等。以下為初始化的過程(不是完全懂,勉強讀):

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);
}

參考資料: