STM32 SDRAM: Why & How
Brief
This article is about how to use SDRAM on stm32, mainly, for STM32F4xx, NOT STM32F1xx.
The IDE
The using IDE is STM32CubeIDE, which has built-in STM32CubeMX.
The Version for writing this article is 1.7.0. Or the combination of System Workbench for STM32 and STM32CubeMX, it works well too.
The Board with SDRAM
After all, a STM32 board with SDRAM is needed. Here uses an old green STM32F4-Discovery(or DISCO) which has a built-in SDRAM chip of IS42S16400-7TL. Another eval board Alientek Apolo F429 from Chian just took off.
According to the datasheets, both boards are using STM42F429ZI
MCU, which can run at a max speed of 180MHz, and drive the APB2 peripheral clock for SDRAM at 90Mhz. The SDRAM space is mapped to system memory space, for my board, it's BANK2
. For STMF4xx MCU, the BANK2
SDRAM is mapped to address 0xD0000000
. The STMF1xx MCUs use a different address mapping space, but .
Generating Project
Run STM32CubeMX or STM32CubeIDE, on the MCU/MPU Selector
tab, input F429ZI, select the chips with package LQFP144. Or, on Board Selector
tab, select the board. The software only show the picture of newer Nucelo F429ZI
, which looks different from DISCO
. when the software asking if initialize components to their preset states, click No. Unfortunately, the newer Nucelo F429FI
can't generate SDRAM code correctly. The board configuration must be down by hand.
System Core
RCC
- High Speed Clock (HSE): Crystal/Ceramic Resonator
- Low Speed Clock (LSE): Crystal/Ceramic Rsonator
SYS
- Debug: Serial Wire
- Timebase Source: SysTick
Clock
Setup clock like this:
| Parameter | Value |
| :----: | --- |
| Input frequence | 8Mhz |
| /M | /4 |
| Main PLL | X180 for*N
, /2 for/P
|
| PLLCLK | Selected |
| SYSCLK(MHz) | 180 |
| AHB Prescaler | /1 |
| HCLK(MHz) | 180 |
| APB2 Prescaler | /2 |
The APB2 periperal clocks (MHz) will run at 90MHz.
SDRAM Configuration
SDRAM is under FMC
category. Select the SDRAM1
.
SDRAM 1
| Name | Value |
|---|---|
| Clock and chip enable | SDCKE1+SDNE1 |
| Internal bank number | 4 banks |
| Address | 12 bits |
| Data | 16 bits|Check the
16-bit byte enable
.SDRAM Control
| Name | Value |
| ---- | --- |
| Bank | SDRAM bank2 |
| Numberof column address bits | 8 bits |
| Number of row address bits | 12 bits |
| CAS latency | 3 memory clock cyles |
| Write protection | Disabled |
| SDRAM common clock | 2 HCLK clock cycles(for 90MHz), or 3 HCLK clock cycles for 60MHz |
| SDRAM common burst read | Disabled |
| SDRAM common read pipe delay | 0 HCLK colck cycle |- SDRAM timing in memoty clock cycles
| Name | Value |
| --- | --- |
| Load mode register to active delay | 2 |
| Exit self-refresh delay | 7 |
| Self-refresh time | 4 |
| SDRAM common row cycle delay | 7 |
| Write recovery time | 3 |
| SDRAM common row precharge delay | 2 |
| Row to column delay | 2 |
Explain about some parameters:
The reference manual is dm00031020.
About
SDCKE1+SDNE1
According to manual:
SDCKE
- 0: SDRAM Bank 1 Clock Enable
- 1: SDRAM Bank 2 Clock Enable
SDNE
- 0: SDRAM Bank 1 Chip Enable
- 1: SDRAM Bank 2 Chip Enable
Why
4 banks
the SDRAM controller has 8/16/32 bits data bus width, 13-bits address row, 11-bits address column, 4 internal banks: 4x1Mx32bit(256MB), 4x16Mx16bit(128MB), 4x16Mx8bit(64MB). So the banks should be
4
.12 bits of Address
According to
IS42S16400-7TL datasheet says the address line is from A0 to A11, total 12 bits.16 bits of Data
STM32CubeMX gives highest 16 bits.
CAS latency: 3 memory clock cycles
Accroding to
IS42S16400
datasheet, there are two CAS latencies: 3 or 2. 3 means a time of 7ns. Also you can choose 2, which will affect the timing parameters.SDRAM timing in memroy clock cycles
To get the timing paramters, we have to do some calculations. At first, keep in mind the parameters that IS42S16400-7TL datasheet says:
| Name | Value | Unit | Comments |
| --- | ---: | --- | --- |
| CAS latency = 3| 7 | ns | Clock cycle time |
| Refresh count | 4K | 64ms | 4096 refresh cycles every 64 ms |
| Tmrd | 2 | Cycle | Load mode register command to active or refresh command |
| Txsr | 70 | ns | Exit Self-Refresh to Active Time |
| Tras | 42 | ns | ACT to PRE command period |
| Trc | 63 | ns | REF to REF/ACT to ACT command period |
| Twr/TDPL | 2 | Cycle | Input data to precharge command delay time |
| Trp | 15 | ns | PRE to ACT command period |
| Trcd | 15 | ns | Active command to read/write command delay time |The relationship of s, ms, us and ns is:
1s = 1 x 1000ms = 1 x 1000 x 1000us = 1 x 1000 x 1000 x 1000 ns
The calculation of time of clock cycle is
1/Frequency
.For
90MHz
clock, one cycle takes1/90Mhz = 11.11ns
.For
60MHz
clock, one cycle takes1/60MHz = 16.67ns
.STM32CubeMX generates code for FMC timing parameters like this, in a unit of clock cycle:
/* SdramTiming */ SdramTiming.LoadToActiveDelay = 2; SdramTiming.ExitSelfRefreshDelay = 7; SdramTiming.SelfRefreshTime = 4; SdramTiming.RowCycleDelay = 6; SdramTiming.WriteRecoveryTime = 2; SdramTiming.RPDelay = 2; SdramTiming.RCDDelay = 2;
Here is the way to calculate the value of cycle:
LoadToActiveDelay
= Tmrd = 2ExtSelfRefreshDelay
= Txsr = 70ns, 70ns/11.11ns = 6.7 ~= 7SelfRefreshTime
= Tras = 42ns, 42ns/11.11ns = 3.78 ~= 4RowCycleDelay
= Trc = 63ns, 63ns/11.11ns = 5.67 ~= 6WriteRecoveryTime
= Twr = 2RPDelay
= Trp = 15ns, 15ns/11.11ns = 1.35 ~= 2RCDDelay
= Trcd = 15ns, 15/ns/11.11ns = 1.35 ~=2Samingly, for a clock of
60MHz
, the values are: 1, 5, 3, 4, 1, 1, 1.Refresh rate
The refresh count is 4K/64ms, it is:
64ms/4096 = 15.625ns
For
90MHz
, the rate is:15.625ns * 90MHz = 1406.25
For
60MHz
, the rate is:15.625ns * 60MHz = 937.5
The Trick For Hardware Fault
SDRAM Address
The code generator won't give the defination of the SDRAM address. According to dm00031020, the SDRAM BANK 2 is mapped from
0xD0000000
to0xDFFFFFFF
, 256MB total. For the using board, it's 8MB total, from0xD0000000
to0xD08000000
.Initializing Commands
To make the SDRAM work, add the following code in any place before access SDRAM, for example, between the
USER CODE BEGIN/END FMC_Init 2
:FMC_SDRAM_CommandTypeDef cmd; cmd.CommandMode= FMC_SDRAM_CMD_CLK_ENABLE; cmd.CommandTarget= FMC_SDRAM_CMD_TARGET_BANK2; cmd.AutoRefreshNumber = 1; cmd.ModeRegisterDefinition = 0; HAL_SDRAM_SendCommand(&hsdram1, &cmd, 1000); HAL_Delay(100); cmd.CommandMode= FMC_SDRAM_CMD_PALL; cmd.CommandTarget= FMC_SDRAM_CMD_TARGET_BANK2; cmd.AutoRefreshNumber = 1; cmd.ModeRegisterDefinition = 0; HAL_SDRAM_SendCommand(&hsdram1, &cmd, 1000); HAL_Delay(100); cmd.CommandMode= FMC_SDRAM_CMD_AUTOREFRESH_MODE; cmd.CommandTarget= FMC_SDRAM_CMD_TARGET_BANK2; cmd.AutoRefreshNumber = 1; // or 2, 3, 4, 8, ... you can try it. cmd.ModeRegisterDefinition = 0; HAL_SDRAM_SendCommand(&hsdram1, &cmd, 1000); HAL_Delay(100); cmd.CommandMode= FMC_SDRAM_CMD_LOAD_MODE; cmd.CommandTarget= FMC_SDRAM_CMD_TARGET_BANK2; cmd.AutoRefreshNumber = 1; cmd.ModeRegisterDefinition = 0x00000221; HAL_SDRAM_SendCommand(&hsdram1, &cmd, 1000); HAL_Delay(100);
Read/write SDRAM
The code of reading/writing SDRAM lists here:
#define SDRAM_ADDR 0xD0000000
**IO uint8_t* ptr = (**IO uint8_t *)SDRAM_ADDR;
**IO uint8_t* pend = ptr + 8 * 1024 * 1024; // 8MB
// Write
while(ptr < pend) {
*ptr++ = 0xCC; // Unfamous byte.
}
// Read & verify
ptr = (_IO uint8_t *)SDRAM_ADDR;
while(ptr < pend) {
// Verify
if (*ptr++ != 0xCC) {
Error_Handler();
}
}
Other References
Good luck.