[STM32] using FMC/FSMC to increase system RAM

Discussion and questions about boards that can run MicroPython but don't have a dedicated forum.
Target audience: Everyone interested in running MicroPython on other hardware.
Post Reply
Eoin
Posts: 3
Joined: Wed Sep 16, 2020 1:07 am

[STM32] using FMC/FSMC to increase system RAM

Post by Eoin » Wed Sep 23, 2020 12:29 am

I'm currently using the STM32F413H-DISCO board, but this presumably also applies to the STM32F746G-DISCO, STM32L4P5G-DK, and any other STM32-based board with some form of RAM attached to the flexible memory controller.

What I'm doing is using the onboard RAM to increase the ram available to micropython - this involves adding a board_init.c file to setup the FSMC, a MICROPY_BOARD_EARLY_INIT define in mpconfigboard.h to ensure it gets called, and then I have two possible approaches for getting micropython to use the external RAM as heap:
Option 1 is to modify the loader file (copied from stm32f413xg.ld) to add a new SRAM3 location at the appropriate address (0x6000000) and set _heap_start and _heap_end to ORIGIN(SRAM3) and ORIGIN(SRAM3) + LENGTH(SRAM3) respectively.
Option 2 is to modify mpconfigboard.h to add defines for MICROPY_HEAP_START and MICROPY_HEAP_END

All this works broadly as intended except that the board I'm working with has an 8Mbit PSRAM on it (IS66WV51216EBLL) organised as 512K words by 16 bits - which should give me 1Mbyte of working RAM for the heap. If I set the length of SRAM3 to 1M or 1024K (option 1) or set MICROPY_HEAP_END to 1024*1024 above MICROPY_HEAP_START (option 2) then sure enough gc.mem_free() reports a figure close to 1M, but if I try to allocate more than half of it then the system crashes and resets. If I set the length to 512k everything works but I have half as much memory as I'm expecting.

My question is: Is this a problem with my expectations (i.e. does micropython work with 16-bit words and report how many words are free rather than how many bytes), or with my FSMC configuration? (I have tried setting FSMC_BCR1 for both 8-bit and 16-bit mode, and 8-bit mode appears to cause memory corruption)

User avatar
jimmo
Posts: 2015
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: [STM32] using FMC/FSMC to increase system RAM

Post by jimmo » Wed Sep 23, 2020 3:21 am

Eoin wrote:
Wed Sep 23, 2020 12:29 am
What I'm doing is using the onboard RAM to increase the ram available to micropython - this involves adding a board_init.c file to setup the FSMC, a MICROPY_BOARD_EARLY_INIT define in mpconfigboard.h to ensure it gets called,...
Hi Eoin,
Is it possible to share the code in board_init.c to configure the PSRAM via the FSMC?
Eoin wrote:
Wed Sep 23, 2020 12:29 am
...and then I have two possible approaches for getting micropython to use the external RAM as heap:
Option 1 is to modify the loader file (copied from stm32f413xg.ld) to add a new SRAM3 location at the appropriate address (0x6000000) and set _heap_start and _heap_end to ORIGIN(SRAM3) and ORIGIN(SRAM3) + LENGTH(SRAM3) respectively.
Option 2 is to modify mpconfigboard.h to add defines for MICROPY_HEAP_START and MICROPY_HEAP_END
Both options are pretty much identical at the end of the day. I'd probably go with the linker script though (but for no real strong reason).

Eoin wrote:
Wed Sep 23, 2020 12:29 am
My question is: Is this a problem with my expectations (i.e. does micropython work with 16-bit words and report how many words are free rather than how many bytes), or with my FSMC configuration? (I have tried setting FSMC_BCR1 for both 8-bit and 16-bit mode, and 8-bit mode appears to cause memory corruption)
MicroPython (specifically the heap management, i.e. gc.c) doesn't really know about any of this. It just expects it to behave like read/write memory.

One thing that might be worth taking a look at to help debugging is the sdram_test() function used by the sdram controller. You could adapt that and call it from main.c after the UART is initialised.

Eoin
Posts: 3
Joined: Wed Sep 16, 2020 1:07 am

Re: [STM32] using FMC/FSMC to increase system RAM

Post by Eoin » Wed Sep 23, 2020 10:14 pm

Code in board_init is:

Code: Select all

void STM32F413DISC_board_early_init(void) {
  __IO uint32_t tmp = 0x00;
/*-- GPIOs Configuration -----------------------------------------------------*/
   /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */
  RCC->AHB1ENR   |= 0x00000078;
  /* Delay after an RCC peripheral clock enabling */
  tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN);

  /* Connect PDx pins to FMC Alternate function */
  GPIOD->AFR[0]  = 0xC0CC00CC; //or A0AA00AA
  GPIOD->AFR[1]  = 0xCC0CCCCC;
  /* Configure PDx pins in Alternate function mode */
  GPIOD->MODER   = 0xA2AA8A0A;
  /* Configure PDx pins speed to 100 MHz */
  GPIOD->OSPEEDR = 0xF3FFCF0F;
  /* Configure PDx pins Output type to push-pull */
  GPIOD->OTYPER  = 0x00000000;
  /* No pull-up, pull-down for PDx pins */
  GPIOD->PUPDR   = 0x51554505;

  /* Connect PEx pins to FMC Alternate function */
  GPIOE->AFR[0]  = 0xC00000CC;
  GPIOE->AFR[1]  = 0xCCCCCCCC;
  /* Configure PEx pins in Alternate function mode */
  GPIOE->MODER   = 0xAAAA800A;
  /* Configure PEx pins speed to 100 MHz */
  GPIOE->OSPEEDR = 0xFFFFC00F;
  /* Configure PEx pins Output type to push-pull */
  GPIOE->OTYPER  = 0x00000000;
  /* No pull-up, pull-down for PEx pins */
  GPIOE->PUPDR   = 0x55554005;

  /* Connect PFx pins to FMC Alternate function */
  GPIOF->AFR[0]  = 0x00CCCCCC;
  GPIOF->AFR[1]  = 0xCCCC0000;
  /* Configure PFx pins in Alternate function mode */
  GPIOF->MODER   = 0xAA000AAA;
  /* Configure PFx pins speed to 100 MHz */
  GPIOF->OSPEEDR = 0xFF000FFF;
  /* Configure PFx pins Output type to push-pull */
  GPIOF->OTYPER  = 0x00000000;
  /* No pull-up, pull-down for PFx pins */
  GPIOF->PUPDR   = 0x55000555;

  /* Connect PGx pins to FMC Alternate function */
  GPIOG->AFR[0]  = 0x00CCCCCC; //or 0AAAAAAA
  GPIOG->AFR[1]  = 0x00000000;
  /* Configure PGx pins in Alternate function mode */
  GPIOG->MODER   = 0x00000AAA;
  /* Configure PGx pins speed to 100 MHz */
  GPIOG->OSPEEDR = 0x00000FFF;
  /* Configure PGx pins Output type to push-pull */
  GPIOG->OTYPER  = 0x00000000;
  /* No pull-up, pull-down for PGx pins */
  GPIOG->PUPDR   = 0x00000555;

/*-- FMC/FSMC Configuration --------------------------------------------------*/
  /* Enable the FMC/FSMC interface clock */
  RCC->AHB3ENR         |= 0x00000001;
  /* Delay after an RCC peripheral clock enabling */
  tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FSMCEN);

  /* Configure and enable Bank1_PSRAM1 */
  // to do this properly, reserved bits should be left unmodified
  tmp = FSMC_Bank1->BTCR[0];
  tmp &= 0xFFC00480; // clear non-reserved bits
  tmp |= 0x00001015; // set relevant bits - 00001015 for 16-bit PSRAM, 00001005 for 8-bit PSRAM, 00001001 for 8-bit SRAM
  FSMC_Bank1->BTCR[0]  = tmp; //BCR[31:22]=rsvd, [21-16]=WFDIS|CCLKEN|CBURSTRW|CPSIZE[2:0], [15:0]=ASYNCWAIT|EXTMOD|WAITEN|WREN|WAITCFG|RSVD|WAITPOL|BURSTEN|RSVD|FACCEN|MWID[1:0]|MTYP[1:0]|MUXEN|MBKEN
  //0000=write FIFO en, sync clk only, async write, no split over pages
  //1015=no async wait, no extended mode, no wait,write enabled, [not used for PSRAM], wait active low, no burst, NOR flash disabled, 16 bit, PSRAM, no mux, bank enabled
  tmp = FSMC_Bank1->BTCR[1];
  tmp &= 0xC0000000; // clear non-reserved bits
  tmp |= 0x00110413; // set relevant bits
  FSMC_Bank1->BTCR[1]  = tmp; //BTR[31:30]=rsvd, [29-16]=ACCMOD[1:0]|DATLAT[3:0]|CLKDIV[3:0]|BUSTURN[3:0], [15:0]=DATAST[7:0]|ADDHLD[3:0]|ADDSET[3:0]
  tmp = FSMC_Bank1E->BWTR[0];
  tmp &= 0xCFF00000; // clear non-reserved bits
  tmp |= 0x000FFFFF; // set relevant bits
  FSMC_Bank1E->BWTR[0] = tmp; //BWTR[31:30]=rsvd, [29-16]=ACCMOD[1:0]|rsvd[7:0]|BUSTURN[3:0], [15:0]=DATAST[7:0]|ADDHLD[3:0]|ADDSET[3:0]
  (void)(tmp);
}
I agree the linker script route is best - mostly because it means changes only require re-linking rather than recompiling (modifying the header means a large number of files need recompiled), but it does mean you need to manually delete the .elf file so the build script doesn't conclude there's nothing to do.
I'll probably have a look at the sdram_test() function tomorrow or next week.

Eoin
Posts: 3
Joined: Wed Sep 16, 2020 1:07 am

Re: [STM32] using FMC/FSMC to increase system RAM [Solved]

Post by Eoin » Fri Sep 25, 2020 5:25 am

Tried using a modification to the sdram_test() function and confirmed it fails with a bus overlap at 0x60080000 if I set the memory size above 512k. That led me to check back on the schematic for the discovery board, and it turns out the final address pin on the memory is tied to ground rather than connecting to the corresponding FSMC pin. :oops:

User avatar
jimmo
Posts: 2015
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: [STM32] using FMC/FSMC to increase system RAM

Post by jimmo » Fri Sep 25, 2020 5:28 am

Eoin wrote:
Fri Sep 25, 2020 5:25 am
d it turns out the final address pin on the memory is tied to ground rather than connecting to the corresponding FSMC pin.
What!!

Well...glad it's resolved, but gee that's annoying.

Post Reply