Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eeprom emulation for stm32f4 #7803

Closed
wants to merge 9 commits into from
Closed

eeprom emulation for stm32f4 #7803

wants to merge 9 commits into from

Conversation

yulei
Copy link
Contributor

@yulei yulei commented Jan 6, 2020

Description

Using flash to emulate EEPROM for STM32F4, every byte of eeprom's address and data will be composed to a WORD(4bytes) and then write to the flash. The writing operation will from the start address to the end, and the reading operation will from the end address to the begin. Thus we can write the eeprom byte with the same eeprom address many times without erase the the flash page.
I have tested this implementation on the NOAH keyboard.

Types of Changes

  • Core
  • Bugfix
  • New feature
  • Enhancement/optimization
  • Keyboard (addition or update)
  • Keymap/layout/userspace (addition or update)
  • Documentation

Issues Fixed or Closed by This PR

Checklist

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • I have tested the changes and verified that they work and don't break anything (as well as I can manage).

@yanfali
Copy link
Contributor

yanfali commented Jan 6, 2020

Can you tell us what the origin of this code is and what license it is under?

@yulei
Copy link
Contributor Author

yulei commented Jan 6, 2020

Can you tell us what the origin of this code is and what license it is under?

It's written by myself, should I adding the license on the file?

Copy link
Contributor

@yanfali yanfali left a comment

Choose a reason for hiding this comment

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

We're going to need some docs to go with this and a link to where it came from and what license the code is under. Thanks

@yanfali
Copy link
Contributor

yanfali commented Jan 6, 2020

Can you tell us what the origin of this code is and what license it is under?

It's written by myself, should I adding the license on the file?

Oh, that's awesome. Yeah, you should probably add a GPL compatible license to this. Thanks

#include "hal.h"

#define EE_SECTOR_ID 3
#define EE_SECTOR_SIZE 0x4000
Copy link
Contributor

Choose a reason for hiding this comment

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

some pointers to documentation where you got these magic values from would be great too. thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

description for this was added

Copy link
Member

Choose a reason for hiding this comment

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

Doesn't this limit the executable flash space (starting at 0x08000000) to 48kB unless each stm32f4 project's ldscripts get modified?
If they're not changed, then this eeprom implementation will potentially overwrite program code, will it not?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this limits the flash space, currently the qmk need about 30K flash(with all rgb lights enabled). The flash sector was not uniform distributed as the F1 or F3. the sector 0-3 are 16KB each, sector 4 has 64KB, sector 5 to last were 128KB. maybe we can use the last sector and use the DataBuf for caches.

Copy link
Member

Choose a reason for hiding this comment

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

Unsure what you're including in your "normal" builds, but I have an ARM board that uses RGB, OLED, external EEPROM and custom matrix -- it's 52kB of flash used. I'm currently working on one that includes colour images for an LCD -- 30kB would get used very quickly.

Now, I'm not targeting F4 for this build, but I don't feel like we should be artifically placing a restriction on the amount of flash available in general. The assertion that around 30kB is "normal" doesn't really hold true now, and certainly won't do so in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Normally I'll flash an small bootloader for IAP at the start of flash space. And the qmk firmware will sit behind the bootloader and the emulated eeprom area. This can remove the hard limit of flash space for application. Anyway the latest codes have used the last flash sector for eeprom emulation, so the qmk can programmed at start of the flash for common usage.

@@ -47,6 +49,9 @@
# elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) || defined(MCU_STM32F103RD) || defined(MCU_STM32F303CC) || defined(MCU_STM32F072CB)
# define FEE_PAGE_SIZE (uint16_t)0x800 // Page size = 2KByte
# define FEE_DENSITY_PAGES 4 // How many pages are used
# elif defined(MCU_STM32F411xE)
Copy link
Contributor

Choose a reason for hiding this comment

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

Some docs explaining why you had to add a new flash driver rather than re-use the existing one would also be helpful for review.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the explanation was added on the eeprom_stm32f4.c

Copy link
Member

@zvecr zvecr Jan 6, 2020

Choose a reason for hiding this comment

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

I would rather use the existing code if possible, and target an "improved" version across supported chips, instead of fragmenting implementations.

We also have #7274 looking close to completion. If there is a hard requirement for another implementation, it would be nice to target the newer structure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

stm32F4 has different register bits for flash programming compared with stm32f1 and f3. It will introduce many hardware specific #define on the existing code. The new structure introduced by #7274 will not conflict the current EEPROM emulation implementation.

Copy link
Member

Choose a reason for hiding this comment

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

I am just putting it on record that:

  1. I'm not keen on having a non-uniform approach to the eeprom emulation strategy
  2. I'm not keen on the code duplication this PR introduces

@zvecr
Copy link
Member

zvecr commented Jan 6, 2020

@tzarc I know you have a eeprom PR in the queue, any chance you could give this one a once over?

#include "hal.h"

#define EE_SECTOR_ID 3
#define EE_SECTOR_SIZE 0x4000
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't this limit the executable flash space (starting at 0x08000000) to 48kB unless each stm32f4 project's ldscripts get modified?
If they're not changed, then this eeprom implementation will potentially overwrite program code, will it not?

}
/* Return the Erase Status */
return status;
}
Copy link
Member

Choose a reason for hiding this comment

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

Conventional erased value for an external flash is 0xFF for each byte -- is the erased value after this operation 0x00 or 0xFF?
eeconfig will not function correctly if the default value is non-zero after an erase.

Copy link
Member

Choose a reason for hiding this comment

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

I suppose writing 0xFF for everything means the iteration through the list won't actually match anything when querying values. In hindsight, this should be fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After flash was erased, all the bit will be set to "1". We can only program the bit from "1" to "0", and can't from "0" to "1" which need a full page erasing operation. Thus the 0xFF means the empty value in flash.

}

uint16_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
uint32_t addr = FLASH_FindValidAddress();
Copy link
Member

Choose a reason for hiding this comment

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

Given there's a possibility of using the "shadow" copy in the static DataBuf array, to increase the endurance perhaps use it as a cache and compare against the existing value before attempting a write. Writing the same value to the same address will needlessly add entries to flash with this implementation.
Reads can follow the same principle, consulting the shadow copy, rather than iterating through the flash?

static void FLASH_Backup(void) {
uint32_t begin = EE_SECTOR_BEGIN;
uint32_t end = EE_SECTOR_END;
memset(&DataBuf[0], 0xFF, sizeof(DataBuf));
Copy link
Member

Choose a reason for hiding this comment

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

Again, 0x00 vs. 0xFF.

Copy link
Member

Choose a reason for hiding this comment

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

Just confirm that it's writing 0xFF to flash when erasing sectors. Any attempted write with an invalid address will be skipped anyway.

}

uint8_t EEPROM_ReadDataByte(uint16_t Address) {
uint32_t begin = EE_SECTOR_BEGIN;
Copy link
Member

Choose a reason for hiding this comment

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

If using DataBuf as a shadow copy, consult that instead here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The latest implementation uses the DataBuf as cache to avoid same value writing and skip the flash iteration for the reading operation.

}

// memory emulate, for verification
#define EE_MEMORY 0
Copy link
Member

Choose a reason for hiding this comment

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

Remove this test code, it's a duplicate from other places?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this code were used for verification the implementation, they can be removed now.

@tzarc
Copy link
Member

tzarc commented Jan 6, 2020

As far as being related to #7274 -- as @yulei said it won't really collide, it'll just mean that one of the PRs will need Makefile changes to support it.

@tzarc
Copy link
Member

tzarc commented Jan 6, 2020

Also, sort of like @zvecr -- I don't really like the duplication, and I think that the implementation here with the addr+value being written as a list could be a good thing to standardise when writing to flash.

Personally, I'd look at using that functionality for F303 and the like anyway. Suggestion would be to merge that logic, then abstract out the operations for unlocking and the like with #defines selecting which implementation to use.

@yulei
Copy link
Contributor Author

yulei commented Jan 6, 2020

Also, sort of like @zvecr -- I don't really like the duplication, and I think that the implementation here with the addr+value being written as a list could be a good thing to standardise when writing to flash.

Personally, I'd look at using that functionality for F303 and the like anyway. Suggestion would be to merge that logic, then abstract out the operations for unlocking and the like with #defines selecting which implementation to use.

I'll try to use this idea (combine address and value) to implement eeprom emulation for F0,F1&F3, and it will take some time to verify on those MCUs.

@tzarc
Copy link
Member

tzarc commented Apr 8, 2020

This should probably be moved to drivers/eeprom now.

@xyzz
Copy link
Contributor

xyzz commented Apr 9, 2020

Design suggestion - store "original" contents of eeprom as the first FEE_MAX_BYTES bytes in the eeprom flash area, followed by "patches" to the data (i.e. address/data pairs same as done in this PR). This should save some space as when you do the backup/restore cycle, only FEE_MAX_BYTES bytes are wasted, as opposed to the current 3*FEE_MAX_BYTES if EEPROM_Restore writes complete eeprom data as 3-byte address/data pairs.

@tzarc tzarc changed the base branch from master to develop July 25, 2020 22:01
@tzarc
Copy link
Member

tzarc commented Jul 25, 2020

Retargeted develop due to core changes.

//#define FEE_ADDR_OFFSET(Address) (Address * 2) // 1 Byte per Word will be saved to preserve Flash
#define FEE_DATA_ADDRESS(Address) ((Address)+2) // flash address of the eeprom data
#define FEE_ADDR_ADDRESS(Address) (Address) // flash address of the eeprom address
#define FEE_MAX_BYTES ((FEE_DENSITY_BYTES+1) > 1024 ? 1024 : (FEE_DENSITY_BYTES+1)) // eeprom size
Copy link
Contributor

@LSChyi LSChyi Sep 6, 2020

Choose a reason for hiding this comment

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

I think this is a bug, it should be

#define FEE_MAX_BYTES ((FEE_DENSITY_BYTES+1) > 1024 ? (FEE_DENSITY_BYTES+1) : 1024)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The Atmega32u4's eeprom size was 1024, so it was limited to keep the compatibility.

Copy link
Contributor

@LSChyi LSChyi Sep 7, 2020

Choose a reason for hiding this comment

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

Hmm, so you do not want to use the full space? Supposedly there is no problem if you use a larger external EEPROM, so there is no reason to limit the max size to just 1024.

Copy link
Member

Choose a reason for hiding this comment

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

If VIA didn't exist, I would say that is just fine. But ... it does, so a higher max by default is absolutely beneficial. To the point that I think it's more important than compatibility.

In fact, for the emulated eeprom for the F303, isn't it 4kb?

Comment on lines 134 to 136
FLASH->AR = Page;
FLASH->CR |= FLASH_CR_PER;
FLASH->AR = Page_Address;
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

you swapped these two around, any reason for that? actually this reliably bricks my F072 board by erasing the first page (at 0x08000000)

To reproduce, I'm flashing with:

sudo dfu-util -d 0483:DF11 -a 0 -s 0x08000000:mass-erase:force -D .build/xyz_g60_via.bin

After a reboot, after the first FLASH_ErasePage is executed, the first page is gone, set to all FFs.

reference from datasheet (dm00031936), FLASH_AR must be set after setting FLASH_CR_PER:

image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When I added the page erase codes for STM32F4, the register setting order was misplaced.

@drashna
Copy link
Member

drashna commented Dec 17, 2020

There are merge conflicts here. Needs a rebase.

@@ -30,15 +31,24 @@
/* Private variables ---------------------------------------------------------*/
/* Functions -----------------------------------------------------------------*/

uint8_t DataBuf[FEE_PAGE_SIZE];
#define IS_VALID_ADDRESS(x) ((x) < FEE_MAX_BYTES)
Copy link
Contributor

Choose a reason for hiding this comment

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

suggest adding a cast to ensure unsigned x here

Comment on lines +87 to +88
#define FEE_DATA_ADDRESS(Address) ((Address)+2) // flash address of the eeprom data
#define FEE_ADDR_ADDRESS(Address) (Address) // flash address of the eeprom address
Copy link
Contributor

Choose a reason for hiding this comment

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

these two IMO don't make sense with the new algorithm and should be removed (previously, it contained a mapping from eeprom address to flash address, but currently such mapping is not required)

// restore data from DataBuf
FLASH_Status EEPROM_Restore(void) {
uint32_t cur = FEE_PAGE_BASE_ADDRESS;
for (uint8_t i = 0; i < FEE_MAX_BYTES; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this should not be uint8_t as FEE_MAX_BYTES is 1024, it would loop forever and likely brick the board

DataBuf[addr] = *(__IO uint16_t*)(FEE_DATA_ADDRESS(begin));
} else if( addr == FEE_EMPTY_WORD) {
uint16_t data = *(__IO uint16_t*)(FEE_DATA_ADDRESS(begin));
if (data == FEE_EMPTY_WORD) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this check is needed, you can bail right away

* helper functions
*******************************************************************************/
// backup the current data
void EEPROM_Backup(void)
Copy link
Contributor

Choose a reason for hiding this comment

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

suggest renaming to EEPROM_Load or similar, Backup doesn't really make sense to me as it's loaded once during initialization

DataBuf[Address] = DataByte;
#pragma GCC diagnostic pop
if (addr == FEE_EMPTY_VALUE) {
// EEPROM is full, need to erase and reprogramming
Copy link
Contributor

Choose a reason for hiding this comment

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

there's an issue here that when your eeprom is completely full, every single byte write will perform a full erase. I think it would be good to add some "slack" e.g. reserve twice the flash memory compared to emulated eeprom size (so times 8 instead of current times 4)

@xyzz xyzz mentioned this pull request Dec 26, 2020
14 tasks
@tzarc tzarc closed this Feb 27, 2021
@tzarc tzarc deleted the branch qmk:develop February 27, 2021 20:27
@noroadsleft noroadsleft reopened this Feb 27, 2021
@drashna
Copy link
Member

drashna commented Mar 14, 2021

This looks like it needs a rebase, and to fix merge conflicts.

xyzz added a commit to vial-kb/vial-qmk that referenced this pull request Jul 11, 2021
xyzz added a commit to vial-kb/vial-qmk that referenced this pull request Jul 12, 2021
xyzz added a commit to vial-kb/vial-qmk that referenced this pull request Jul 18, 2021
@zvecr
Copy link
Member

zvecr commented Aug 29, 2021

superseded by 14195

@zvecr zvecr closed this Aug 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants