2024-02-12 06:51 AM
I've tried to see if I can get away with using just one bank address pin for a 4-bank SDRAM (IS42/45S16160J). My understanding is that this would half the addressable memory, as I could only access two banks instead of four.
In the STM32H7 user manual I find the following table that matches my SDRAM:
This indicates to me that the banks are laid out as contiguous memory blocks.
However, when I try this by configuring BA1 as GPIO output and set it to either low or high, I find that I can access the whole memory but only using 1- or 2-byte access, while 4-byte access are not working anymore (atleast not on 0xd0000000). It also doesn't seem to matter how I init the InternalBankNumber field for the FMC HAL API, both FMC_SDRAM_INTERN_BANKS_NUM_2 and FMC_SDRAM_INTERN_BANKS_NUM_4 seem to lead to the same result.
Can anyone explain what's going on here? Am I missing something?
Bonus question: How are 4-byte accesses handled in general over a 16-bit bus? Does the FMC send two READ/WRITE commands? In the code below a burst length of 1 is used.
Below is my init and test code.
/* FMC initialization function */
static void MX_FMC_Init(void)
{
FMC_SDRAM_TimingTypeDef SdramTiming = {0};
/** Perform the SDRAM1 memory initialization sequence
*/
hsdram1.Instance = FMC_SDRAM_DEVICE;
/* hsdram1.Init */
hsdram1.Init.SDBank = FMC_SDRAM_BANK2;
hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_2;
hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_2;
/* SdramTiming */
SdramTiming.LoadToActiveDelay = 2;
SdramTiming.ExitSelfRefreshDelay = 8;
SdramTiming.SelfRefreshTime = 5;
SdramTiming.RowCycleDelay = 6;
SdramTiming.WriteRecoveryTime = 3;
SdramTiming.RPDelay = 2;
SdramTiming.RCDDelay = 2;
if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
{
Error_Handler( );
}
FMC_SDRAM_CommandTypeDef Command;
/* Step 1 and Step 2 already done in HAL_SDRAM_Init() */
/* Step 3: Configure a clock configuration enable command */
Command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE; /* Set MODE bits to "001" */
Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2; /* configure the Target Bank bits */
Command.AutoRefreshNumber = 1;
Command.ModeRegisterDefinition = 0;
HAL_SDRAM_SendCommand(&hsdram1, &Command, 0xfff);
HAL_Delay(1); /* Step 4: Insert 100 us minimum delay - Min HAL Delay is 1ms */
/* Step 5: Configure a PALL (precharge all) command */
Command.CommandMode = FMC_SDRAM_CMD_PALL; /* Set MODE bits to "010" */
HAL_SDRAM_SendCommand(&hsdram1, &Command, 0xfff);
/* Step 6: Configure an Auto Refresh command */
Command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE; /* Set MODE bits to "011" */
Command.AutoRefreshNumber = 2;
HAL_SDRAM_SendCommand(&hsdram1, &Command, 0xfff);
/* Step 7: Program the external memory mode register */
Command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;/*set the MODE bits to "100" */
Command.ModeRegisterDefinition = (uint32_t)0 | 0<<3 | 2<<4 | 0<<7 | 1<<9;
HAL_SDRAM_SendCommand(&hsdram1, &Command, 0xfff);
/* Step 8: Set the refresh rate counter - refer to section SDRAM refresh timer register in RM0455 */
/* Set the device refresh rate
* COUNT = [(SDRAM self refresh time / number of row) x SDRAM CLK] – 20
= [(64ms/4096) * 100MHz] - 20 = 1562.5 - 20 ~ 1542 */
HAL_SDRAM_ProgramRefreshRate(&hsdram1, 1542);
}
//Test code
volatile uint8_t* sdram_start = (uint8_t*)0xD0000000;
volatile uint32_t sdram_size = 32*1024*1024;
static bool check_1byte(volatile uint32_t index)
{
return ((volatile uint8_t*)sdram_start)[index] != (volatile uint8_t)index;
}
static bool check_2byte(volatile uint32_t index)
{
return ((volatile uint16_t*)sdram_start)[index] != (volatile uint16_t)index;
}
static bool check_4byte(volatile uint32_t index)
{
return ((volatile uint32_t*)sdram_start)[index] != (volatile uint32_t)index;
}
static void set_1byte(volatile uint32_t index)
{
((volatile uint8_t*)sdram_start)[index] = (volatile uint8_t)index;
}
static void set_2byte(volatile uint32_t index)
{
((volatile uint16_t*)sdram_start)[index] = (volatile uint16_t)index;
}
static void set_4byte(volatile uint32_t index)
{
((volatile uint32_t*)sdram_start)[index] = (volatile uint32_t)index;
}
static bool sdram_test_with_byte_size(bool (*check_fn)(volatile uint32_t), void (*set_fn)(volatile uint32_t), size_t byte_size, bool breakpoint)
{
volatile uint32_t max_index = sdram_size / byte_size;
//Write memory
for(volatile uint32_t i = 0; i < max_index; i++)
{
set_fn(i);
}
//Invalidate cache
SCB_CleanDCache();
SCB_InvalidateDCache();
//Wait some time to test retention
for(volatile uint64_t i = 0; i < 1ull * 100 * 1000 * 1000; i++)
{
}
//Check memory
for(volatile uint32_t i = 0; i < max_index; i++)
{
if(check_fn(i))
{
if(breakpoint)
__BKPT(0);
else
return false;
}
}
return true;
}
//Called as sdram_test((uint8_t*)0xd0000000, 32*1024*1024, 1, true);
bool sdram_test(uint8_t* start, uint32_t size, uint32_t n, bool breakpoint)
{
sdram_start = start;
sdram_size = size;
for(uint32_t i = 0; i < n; i++)
{
bool result = sdram_test_with_byte_size(check_1byte, set_1byte, 1, breakpoint);
if(!breakpoint && !result)
return false;
result = sdram_test_with_byte_size(check_2byte, set_2byte, 2, breakpoint);
if(!breakpoint && !result)
return false;
result = sdram_test_with_byte_size(check_4byte, set_4byte, 4, breakpoint);
if(!breakpoint && !result)
return false;
}
return true;
}
2024-02-14 01:22 PM
Hi All,
This case has been routed to our online support center for direct support from our team.
Regards,
Jake
ST Support