Skip to content

Funny Monkey Coding (FMC) Time#131

Open
ChongOscar wants to merge 26 commits intomainfrom
feature/ChongOscar/fmc-support
Open

Funny Monkey Coding (FMC) Time#131
ChongOscar wants to merge 26 commits intomainfrom
feature/ChongOscar/fmc-support

Conversation

@ChongOscar
Copy link
Copy Markdown

FMC support

Added support for configuring and reading SDRAM through a Flexible Memory Controller (FMC)

Change list:

  • FMC: is an abstract class containing only the base address for future chip support
  • FMCf4xx: defines a FMC implementation for F4 boards

Copy link
Copy Markdown
Member

@ActuallyTaylor ActuallyTaylor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have quite a few comments, my main note is that the PR needs to use more EVT-Core abstractions. There is a re-implementation of GPIO handling, which is already covered by our GPIO class. If it is not completely covered, the GPIO class should be subclassed and adapted.

Comment on lines +26 to +29
typedef struct {
uint16_t PIN;
GPIO_TypeDef* PORT;
} GPIO;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EVT Core has a GPIO implementation already... We should always stray away from re-defining hardware abstraction since many edge cases and behaviors are handled in our existing abstractions.

https://github.com/RIT-EVT/EVT-core/blob/main/include/core/io/GPIO.hpp

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is directly taken from the STM32 Cube HUDL project's FMC I am working. It can definitely be removed without causing conflicts

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, it will be removed

Comment on lines +140 to +152
struct SdramInitConfig {
FMC_SDRAM_TypeDef* sdramDevice = FMC_SDRAM_DEVICE;
uint32_t sdBank = FMC_SDRAM_BANK1;
uint32_t columnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8;
uint32_t rowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;
uint32_t memoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
uint32_t internalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
uint32_t casLatency = FMC_SDRAM_CAS_LATENCY_2;
uint32_t writeProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
uint32_t sdClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
uint32_t readBurst = FMC_SDRAM_RBURST_ENABLE;
uint32_t readPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These structures are not going to be unique for the F4. They should be a part of the core FMC class. Initialization of the values can happen within the FMCf4xx.hpp but the struct definition should happen in the main FMC.hpp

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These also might change based on what RAM on what platform we are targeting. This was generated by CubeMX, so it might be worthwhile to make this a passed in struct

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, the sdramDevice is a HAL specific macro but that can be separated from the init config so that the config can be defined in the base FMC.hpp.

Comment on lines +154 to +167
/**
* Structure to simplify SDRAM timing initialization, pre-filled with default values
*
* Contains all required SDRAM timing delays in clock cycles.
*/
struct SdramTimingConfig {
uint32_t loadToActiveDelay = LOAD_MODE_REGISTER_TO_ACTIVE;
uint32_t exitSelfRefreshDelay = EXIT_SELF_REFRESH_DELAY;
uint32_t selfRefreshTime = SELF_REFRESH_TIME;
uint32_t rowCycleDelay = ROW_CYCLE_DELAY;
uint32_t writeRecoveryTime = RECOVERY_DELAY;
uint32_t rpDelay = ROW_PRECHARGE_DELAY;
uint32_t rcdDelay = ROW_TO_COLUMN_DELAY;
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also be located within the FMC base class.

Comment on lines +33 to +45
void FMCf4xx::write32(uint32_t offset, uint32_t value) const {
// Ensure 4-byte alignment
if (offset % sizeof(uint32_t) != 0)
return;

// Ensure within SDRAM bounds
if (offset >= RAM_SIZE)
return;

auto* const ptr = reinterpret_cast<volatile uint32_t*>(sdramMemoryAddress + offset);

*ptr = value;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return a STATUS enumeration that will tell someone if it succeeded or not.

Copy link
Copy Markdown
Contributor

@DannyCato DannyCato Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should not exist

Comment on lines +47 to +59
uint32_t FMCf4xx::read32(uint32_t offset) const {
// Ensure 4-byte alignment
if (offset % sizeof(uint32_t) != 0)
return 0;

// Ensure within SDRAM bounds
if (offset >= RAM_SIZE)
return 0;

auto* const ptr = reinterpret_cast<volatile uint32_t*>(sdramMemoryAddress + offset);

return *ptr;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return a STATUS enumeration that will tell a user if it was able to read or not. The value should be passed back through a pointer in the function parameters. See how we do this in the BMS: https://github.com/RIT-EVT/BMS/blob/00bc33d695f8865c8ae2ff0de5e493eb70ec08ca/src/dev/BQ76952.cpp#L241

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should not exist either

Comment on lines +5 to +7
FMC::FMC(uint32_t sdramMemoryAddress) {
this->sdramMemoryAddress = sdramMemoryAddress;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a couple file wide notes.

  • Existing EVT-Core abstractions should be used. The code is using a custom GPIO implementation which is more work than necessary as well as duplicating existing code.
  • All pins in this file should use the EVT-Core pin class

@@ -0,0 +1,9 @@
#include "core/io/FMC.hpp"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use angled bracket inclusion <>

Comment on lines +18 to +22
#include "HALf4/stm32f4xx_hal.h"
#include "HALf4/stm32f4xx_hal_sdram.h"
#include "HALf4/stm32f4xx_ll_fmc.h"

#include "core/io/FMC.hpp"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use angled bracket inclusion <>

* - SDRAM timing configuration
* - SDRAM read/write operations
*
* @note Requires STM32 HAL libraries.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of our platforms require STM32 HAL libraries, so this comment is not really applicable.

#define FMC_SDNWE ((FMC_CMD){GPIO_PIN_5, GPIOH})
#define FMC_SDNRAS ((FMC_CMD){GPIO_PIN_11, GPIOF})

// #define FMC_ ((GPIO) {GPIO_PIN_x, GPIOx})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the above defines can definitely be removed for the future. They did not prove helpful in the HUDL project, and only add to confusion

#define LOAD_MODE_REGISTER_TO_ACTIVE (NS_TO_SDRAM_CLK_CYCLES(tMRD))

#define RAM_SIZE (0x4000000) // 64 megabits
#define STARTING_ADDR ((uint32_t*) 0xC000000)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some additional defines in the HUDL project that could prove useful, but I am wondering if we can simply turn these into functions that get executed on construction of the object.

In any case, a method to simply find the clock speed of the FMC could be helpful, and at that point just make everything happen at runtime. No preprocessing magic

FMC_BANK* pins;
uint8_t count;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change these to be wrappers of the evt core GPIO pins instead of the above macros


void write32(uint32_t offset, uint32_t value) const;

uint32_t read32(uint32_t offset) const;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should not be explicitly be defined. If you try to write or read from a value at the address of FMC Bank (the STARTING_ADDRESS macro), then it should simply work with some hardware magic from the peripheral

* @param[in] offset Byte offset from the SDRAM base address
*/
uint32_t read32(uint32_t offset) const;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading or writing data of any size to the address of the FMC will cause reads and writes to the underlying RAM

uint32_t read32(uint32_t offset) const;

protected:
uint32_t sdramMemoryAddress;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be a void pointer to signify that it is a memory address. That's what you would do in C, but maybe there is some other convention for C++

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I still need this if im removing the read and write functions?


protected:
uint32_t sdramMemoryAddress;
};
Copy link
Copy Markdown
Contributor

@DannyCato DannyCato Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The public section of this should also include wrappers for the methods that can be seen in stm32f4xx_ll_fmc.h. Namely, FMC_SDRAM_WriteProtection_Enable and FMC_SDRAM_WriteProtection_Disable. I have not checked if those are available on the F3, but I would be shocked if they weren't

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The F3 FMC doesn't seem to support any SDRAM operations at all

oc6723 and others added 11 commits March 5, 2026 00:11
… the base class and finally figured out how GPIO works
…into feature/ChongOscar/fmc-support

# Conflicts:
#	include/core/io/FMC.hpp
#	include/core/io/platform/f4xx/FMCf4xx.hpp
#	src/core/io/platform/f4xx/FMCf4xx.cpp
…into feature/ChongOscar/fmc-support

# Conflicts:
#	include/core/io/FMC.hpp
#	include/core/io/platform/f4xx/FMCf4xx.hpp
#	src/core/io/FMC.cpp
#	src/core/io/platform/f4xx/FMCf4xx.cpp
@ChongOscar ChongOscar requested a review from DannyCato March 28, 2026 18:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants