write to SDRAM in STM32f7xxx

229 views Asked by At

I'm trying to write data to SDRAM on an STM32f746ZG board in STM32CubeIde. here is the code:

  /* Enable the CPU Cache */
  CPU_CACHE_Enable();
  ...
  SDRAM_Timing.LoadToActiveDelay    = 2; //TMRD
  SDRAM_Timing.ExitSelfRefreshDelay = 7; //TXSR
  SDRAM_Timing.SelfRefreshTime      = 5; //TRAS
  SDRAM_Timing.RowCycleDelay        = 7; //TRC
  SDRAM_Timing.WriteRecoveryTime    = 3; //TWR
  SDRAM_Timing.RPDelay              = 3; //TRP
  SDRAM_Timing.RCDDelay             = 3; //TRCD

  hsdram.Init.SDBank             = FMC_SDRAM_BANK1;
  hsdram.Init.ColumnBitsNumber   = FMC_SDRAM_COLUMN_BITS_NUM_8;
  hsdram.Init.RowBitsNumber      = FMC_SDRAM_ROW_BITS_NUM_12;
  hsdram.Init.MemoryDataWidth    = FMC_SDRAM_MEM_BUS_WIDTH_16;
  hsdram.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
  hsdram.Init.CASLatency         = FMC_SDRAM_CAS_LATENCY_2;
  hsdram.Init.WriteProtection    = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
  hsdram.Init.SDClockPeriod      = FMC_SDRAM_CLOCK_PERIOD_2;
  hsdram.Init.ReadBurst          = FMC_SDRAM_RBURST_ENABLE;
  hsdram.Init.ReadPipeDelay      = FMC_SDRAM_RPIPE_DELAY_0;

  /* Initialize the SDRAM controller */
  if(HAL_SDRAM_Init(&hsdram, &SDRAM_Timing) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler();
  }

  /* Program the SDRAM external device */
  BSP_SDRAM_Initialization_Sequence(&hsdram, &command);

snippet of code that writes data:

 uint32_t aTxBuffer[BUFFER_SIZE];
 /* Fill the buffer to write */
  Fill_Buffer(aTxBuffer, BUFFER_SIZE, 0);

  /* Write data to the SDRAM memory */
  for (uwIndex = 0; uwIndex < BUFFER_SIZE; uwIndex++)
  {
    *(__IO uint32_t*) (SDRAM_BANK_ADDR + WRITE_READ_ADDR + 4*uwIndex) = aTxBuffer[uwIndex];
  }

but for some reason the recording is not happening correctly enter image description here

That is, it writes 16 bytes but doesn’t write the next 8, then writes again, then doesn’t write again. Where is the mistake? I think the problem is SDRAM_Timing. P.S. Please don't judge me harshly, this is my first time working with SDRAM.
Addition:
It seems to me that the problem is with SDRAM_Timing. when I change at least one of its values, the result is different. For example:

  SDRAM_Timing.LoadToActiveDelay    = 2; //TMRD
  SDRAM_Timing.ExitSelfRefreshDelay = 7; //TXSR
  SDRAM_Timing.SelfRefreshTime      = 5; //TRAS
  SDRAM_Timing.RowCycleDelay        = 7; //TRC
  SDRAM_Timing.WriteRecoveryTime    = 3; //TWR
  SDRAM_Timing.RPDelay              = 2; //TRP
  SDRAM_Timing.RCDDelay             = 2; //TRCD

the result will be like this enter image description here

2

There are 2 answers

1
Yael On BEST ANSWER

I decreased the BUFFER_SIZE value and everything started working as it should. (to be honest, it’s not entirely clear why and what this is connected with) enter image description here

3
Ilya On

Your SDRAM initialization sequence is incomplete. After your initialization, the hardware is not ready to be used just yet.

SDRAM initialization consists of two stages:

  1. Initialize MCU's SDRAM handling peripheral with all the timings and burst configurations (which you did).
  2. Initialize SDRAM IC itself with some of those configurations by sending it a few commands before you can actually use it for data (which you didn't do).

Here is the register you need for that, straight out of STM32F746 reference manual:

enter image description here

A while ago I wrote my little bare metal 2D graphics (proto-)engine for STM32F746 Discovery Board (it never went anywhere, but I had semi-transparent rectangles float on the screen), and it uses SDRAM for framebuffers. Here is a piece from my tested code. All timings were calculated by hand from datasheets, no "ready values from examples" used. I marked the part you didn't do:

//GPIO is already configured
//MCU runs at 216MHz
//SDRAM clock is 108MHz

/*
 * Set up SDRAM Bank 1 control register
 *
 * Number of column address bits:   8
 * Number of row address bits:      12
 * Memory data bus width:           16
 * Number of internal banks:        4
 * CAS latency:                     2 Cycles
 * Write protection:                Write access allowed
 * SDRAM Clock Config:              Tsdclk = 2xThclk (108MHz @ 216MHz CPU clock)
 * Burst read:                      Single read requests NOT managed as bursts
 * Read Pipe:                       0 HCLK cycle delay
 *
 * */
FMC_Bank5_6->SDCR[0] = (0x00 << FMC_SDCR1_NC_Pos)
        | (0x01 << FMC_SDCR1_NR_Pos) | (0x01 << FMC_SDCR1_MWID_Pos)
        | (0x01 << FMC_SDCR1_NB_Pos) | (0x02 << FMC_SDCR1_CAS_Pos)
        | (0x00 << FMC_SDCR1_WP_Pos) | (0x02 << FMC_SDCR1_SDCLK_Pos)
        | (0x00 << FMC_SDCR1_RBURST_Pos) | (0x00 << FMC_SDCR1_RPIPE_Pos);

/*
 * Set up device timing
 *
 * Set Load Mode Register to Active Tmrd:   12ns (in no. of cycles, rounded up)
 * Exit Self-refresh delay Txsr:            70ns (in no. of cycles, rounded up)
 * Self-refresh time Tras:                  42ns (in no. of cycles, rounded up)
 * Row cycle delay Trc:                     6 Cycles for CAS latency 2, 10 cycles for CAS latency 3
 * Recovery delay Twr:                      Twr>=Tras-Trcd and Twr>=TRC-Trcd-Trp in cycles
 * Row precharge delay Trp:                 18ns (in no. of cycles, rounded up)
 * Row to column delay Trcd:                18ns (in no. of cycles, rounded up)
 *
 * */
FMC_Bank5_6->SDTR[0] = ((2U - 1U) << FMC_SDTR1_TMRD_Pos)        //2
| ((8U - 1U) << FMC_SDTR1_TXSR_Pos)     //8
        | ((5U - 1U) << FMC_SDTR1_TRAS_Pos)     //5
        | ((6U - 1U) << FMC_SDTR1_TRC_Pos)      //6
        | ((3U - 1U) << FMC_SDTR1_TWR_Pos)      //3
        | ((2U - 1U) << FMC_SDTR1_TRP_Pos)      //2
        | ((2U - 1U) << FMC_SDTR1_TRCD_Pos);    //2

// =====>>>>>>>>>>>>>>>> YOU DIDN'T DO THIS PART <<<<<<<<<<<<<<<<=====
/*
 * Initialize SDRAM IC via Command Mode register
 * Writing to MODE field issues commands
 * */
FMC_Bank5_6->SDCMR = (0x01 << FMC_SDCMR_CTB1_Pos)
        | ((1U - 1U) << FMC_SDCMR_NRFS_Pos)  //number of auto-refreshes
        | (0x01 << FMC_SDCMR_MODE_Pos); //start delivering clock to SDRAM bank 1 (SDCKE->HIGH)
//system_msdelay(1U);  //wait at least 100us after power up
for (uint32_t counter = 0x00; counter < 70000; counter++);

FMC_Bank5_6->SDCMR = (0x01 << FMC_SDCMR_CTB1_Pos)  //set bank 1
| ((1U - 1U) << FMC_SDCMR_NRFS_Pos)  //number of auto-refreshes
        | (0x02 << FMC_SDCMR_MODE_Pos);  //issue precharge all

FMC_Bank5_6->SDCMR = (0x01 << FMC_SDCMR_CTB1_Pos)  //set bank 1
| ((8U - 1U) << FMC_SDCMR_NRFS_Pos)  //number of auto-refreshes
        | (0x03 << FMC_SDCMR_MODE_Pos);  //issue auto-refreshes

/*
 * Create mode register value and load it into SDRAM mode register
 * Burst length:        1 (1 2 4 8 Page)
 * Burst type:          Sequential
 * Latency mode:        CAS latency 2
 * Operating mode:      Standard operation
 * Write burst mode:    Programmed burst length
 * Reserved:            Always 0
 *
 *   * */
uint32_t SDRAM_modeRegisterContent = (0x00 << 0x00) //[0:3]         Burst length
| (0x00 << 0x03)  //[3:1]       Burst type
        | (0x02 << 0x04)  //[4:3]       Latency mode
        | (0x00 << 0x07)  //[7:2]       Operating mode
        | (0x00 << 0x09)  //[9:1]       Write burst mode
        | (0x00 << 0x0A);  //[10:4]     Reserved

FMC_Bank5_6->SDCMR = (0x01 << FMC_SDCMR_CTB1_Pos)  //set bank 1
| (SDRAM_modeRegisterContent << FMC_SDCMR_MRD_Pos) //SDRAM mode register content
        | ((1U - 1U) << FMC_SDCMR_NRFS_Pos)  //number of auto-refreshes
        | (0x04 << FMC_SDCMR_MODE_Pos); //issue set mode register in the SDRAM;

//system_msdelay(1U);
for (uint32_t counter = 0x00; counter < 70000; counter++);
/*
 * SDRAM Refresh Timer
 * Refresh period:      64ms
 * Number of rows:      4096
 * Refresh rate:        64ms/4096rows = 15.62us
 * Count value:         15.62*108MHz - 20 = 1686.96 - 20 = ~1666
 *
 * RES Interrupt disable
 * Clear refresh error flag
 * */
uint32_t SDRAM_refreshCount = 1666U;  //1666U = 0x0682
FMC_Bank5_6->SDRTR = (0x00 << FMC_SDRTR_REIE_Pos) //RES interrupt enable OFF
| (SDRAM_refreshCount << FMC_SDRTR_COUNT_Pos) //load refresh count
        | (0x01 << FMC_SDRTR_CRE_Pos); //clear refresh error flag
//system_msdelay(1U);
for (uint32_t counter = 0x00; counter < 70000; counter++);

*(uint32_t*) (0xC0000000) = 0xBEEFCAFE; //test data, remember about endianness