Skip to content

Commit 6d98353

Browse files
committed
stmhal: Improve flash storage cache management.
Internal flash used for the filesystem is now written (from the cache) only after a 5s delay, or when a file is closed, or when the drive is unmounted from the host. This delay means that multiple writes can accumulate in the cache, and leads to less writes to the flash, making it last longer. It's implemented by a high-priority interrupt that takes care of flash erase and write, and flushing the cache. This is still only an interim solution for the flash filesystem. It eventually needs to be replaced with something that uses less RAM for the cache, something that can use more of the flash, and something that does proper wear levelling.
1 parent f6be480 commit 6d98353

File tree

11 files changed

+181
-25
lines changed

11 files changed

+181
-25
lines changed

stmhal/flash.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,81 @@ uint32_t flash_get_sector_info(uint32_t addr, uint32_t *start_addr, uint32_t *si
4949
return 0;
5050
}
5151

52+
void flash_erase(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) {
53+
// check there is something to write
54+
if (num_word32 == 0) {
55+
return;
56+
}
57+
58+
// unlock
59+
HAL_FLASH_Unlock();
60+
61+
// Clear pending flags (if any)
62+
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
63+
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
64+
65+
// erase the sector(s)
66+
FLASH_EraseInitTypeDef EraseInitStruct;
67+
EraseInitStruct.TypeErase = TYPEERASE_SECTORS;
68+
EraseInitStruct.VoltageRange = VOLTAGE_RANGE_3; // voltage range needs to be 2.7V to 3.6V
69+
EraseInitStruct.Sector = flash_get_sector_info(flash_dest, NULL, NULL);
70+
EraseInitStruct.NbSectors = flash_get_sector_info(flash_dest + 4 * num_word32 - 1, NULL, NULL) - EraseInitStruct.Sector + 1;
71+
uint32_t SectorError = 0;
72+
if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
73+
// error occurred during sector erase
74+
HAL_FLASH_Lock(); // lock the flash
75+
return;
76+
}
77+
}
78+
79+
/*
80+
// erase the sector using an interrupt
81+
void flash_erase_it(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) {
82+
// check there is something to write
83+
if (num_word32 == 0) {
84+
return;
85+
}
86+
87+
// unlock
88+
HAL_FLASH_Unlock();
89+
90+
// Clear pending flags (if any)
91+
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
92+
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
93+
94+
// erase the sector(s)
95+
FLASH_EraseInitTypeDef EraseInitStruct;
96+
EraseInitStruct.TypeErase = TYPEERASE_SECTORS;
97+
EraseInitStruct.VoltageRange = VOLTAGE_RANGE_3; // voltage range needs to be 2.7V to 3.6V
98+
EraseInitStruct.Sector = flash_get_sector_info(flash_dest, NULL, NULL);
99+
EraseInitStruct.NbSectors = flash_get_sector_info(flash_dest + 4 * num_word32 - 1, NULL, NULL) - EraseInitStruct.Sector + 1;
100+
if (HAL_FLASHEx_Erase_IT(&EraseInitStruct) != HAL_OK) {
101+
// error occurred during sector erase
102+
HAL_FLASH_Lock(); // lock the flash
103+
return;
104+
}
105+
}
106+
*/
107+
52108
void flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) {
109+
// program the flash word by word
110+
for (int i = 0; i < num_word32; i++) {
111+
if (HAL_FLASH_Program(TYPEPROGRAM_WORD, flash_dest, *src) != HAL_OK) {
112+
// error occurred during flash write
113+
HAL_FLASH_Lock(); // lock the flash
114+
return;
115+
}
116+
flash_dest += 4;
117+
src += 1;
118+
}
119+
120+
// lock the flash
121+
HAL_FLASH_Lock();
122+
}
123+
124+
/*
125+
use erase, then write
126+
void flash_erase_and_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) {
53127
// check there is something to write
54128
if (num_word32 == 0) {
55129
return;
@@ -71,13 +145,15 @@ void flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32)
71145
uint32_t SectorError = 0;
72146
if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
73147
// error occurred during sector erase
148+
HAL_FLASH_Lock(); // lock the flash
74149
return;
75150
}
76151
77152
// program the flash word by word
78153
for (int i = 0; i < num_word32; i++) {
79154
if (HAL_FLASH_Program(TYPEPROGRAM_WORD, flash_dest, *src) != HAL_OK) {
80155
// error occurred during flash write
156+
HAL_FLASH_Lock(); // lock the flash
81157
return;
82158
}
83159
flash_dest += 4;
@@ -87,3 +163,4 @@ void flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32)
87163
// lock the flash
88164
HAL_FLASH_Lock();
89165
}
166+
*/

stmhal/flash.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
uint32_t flash_get_sector_info(uint32_t addr, uint32_t *start_addr, uint32_t *size);
2+
void flash_erase(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32);
23
void flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32);

stmhal/pybstdio.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#include "obj.h"
1010
#include "stream.h"
1111
#include "pybstdio.h"
12-
#include "storage.h"
1312
#include "usb.h"
1413
#include "usart.h"
1514

@@ -50,9 +49,6 @@ int stdin_rx_chr(void) {
5049
return usart_rx_char(pyb_usart_global_debug);
5150
}
5251
__WFI();
53-
if (storage_needs_flush()) {
54-
storage_flush();
55-
}
5652
}
5753
}
5854

stmhal/pyexec.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
#include "pybstdio.h"
2222
#include "readline.h"
2323
#include "pyexec.h"
24-
#include "storage.h"
2524
#include "usb.h"
2625
#include "build/py/py-version.h"
2726

stmhal/stm32f4xx_it.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include "obj.h"
5050
#include "exti.h"
5151
#include "timer.h"
52+
#include "storage.h"
5253

5354
/** @addtogroup STM32F4xx_HAL_Examples
5455
* @{
@@ -263,6 +264,19 @@ void OTG_XX_WKUP_IRQHandler(void)
263264
{
264265
}*/
265266

267+
// Handle a flash (erase/program) interrupt.
268+
void FLASH_IRQHandler(void) {
269+
// This calls the real flash IRQ handler, if needed
270+
/*
271+
uint32_t flash_cr = FLASH->CR;
272+
if ((flash_cr & FLASH_IT_EOP) || (flash_cr & FLASH_IT_ERR)) {
273+
HAL_FLASH_IRQHandler();
274+
}
275+
*/
276+
// This call the storage IRQ handler, to check if the flash cache needs flushing
277+
storage_irq_handler();
278+
}
279+
266280
/**
267281
* @brief These functions handle the EXTI interrupt requests.
268282
* @param None

stmhal/storage.c

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,22 @@
1616
#define FLASH_PART1_NUM_BLOCKS (224) // 16k+16k+16k+64k=112k
1717
#define FLASH_MEM_START_ADDR (0x08004000) // sector 1, 16k
1818

19+
#define FLASH_FLAG_DIRTY (1)
20+
#define FLASH_FLAG_FORCE_WRITE (2)
21+
#define FLASH_FLAG_ERASED (4)
1922
static bool flash_is_initialised = false;
20-
static bool flash_cache_dirty;
23+
static __IO uint8_t flash_flags = 0;
2124
static uint32_t flash_cache_sector_id;
2225
static uint32_t flash_cache_sector_start;
2326
static uint32_t flash_cache_sector_size;
2427
static uint32_t flash_tick_counter_last_write;
2528

2629
static void flash_cache_flush(void) {
27-
if (flash_cache_dirty) {
28-
// sync the cache RAM buffer by writing it to the flash page
29-
flash_write(flash_cache_sector_start, (const uint32_t*)CACHE_MEM_START_ADDR, flash_cache_sector_size / 4);
30-
flash_cache_dirty = false;
31-
// indicate a clean cache with LED off
32-
led_state(PYB_LED_R1, 0);
30+
if (flash_flags & FLASH_FLAG_DIRTY) {
31+
flash_flags |= FLASH_FLAG_FORCE_WRITE;
32+
while (flash_flags & FLASH_FLAG_DIRTY) {
33+
NVIC->STIR = FLASH_IRQn;
34+
}
3335
}
3436
}
3537

@@ -44,9 +46,9 @@ static uint8_t *flash_cache_get_addr_for_write(uint32_t flash_addr) {
4446
flash_cache_sector_start = flash_sector_start;
4547
flash_cache_sector_size = flash_sector_size;
4648
}
47-
flash_cache_dirty = true;
48-
// indicate a dirty cache with LED on
49-
led_state(PYB_LED_R1, 1);
49+
flash_flags |= FLASH_FLAG_DIRTY;
50+
led_state(PYB_LED_R1, 1); // indicate a dirty cache with LED on
51+
flash_tick_counter_last_write = HAL_GetTick();
5052
return (uint8_t*)CACHE_MEM_START_ADDR + flash_addr - flash_sector_start;
5153
}
5254

@@ -64,11 +66,17 @@ static uint8_t *flash_cache_get_addr_for_read(uint32_t flash_addr) {
6466

6567
void storage_init(void) {
6668
if (!flash_is_initialised) {
67-
flash_cache_dirty = false;
69+
flash_flags = 0;
6870
flash_cache_sector_id = 0;
69-
flash_is_initialised = true;
7071
flash_tick_counter_last_write = 0;
72+
flash_is_initialised = true;
7173
}
74+
75+
// Enable the flash IRQ, which is used to also call our storage IRQ handler
76+
// It needs to go at a higher priority than all those components that rely on
77+
// the flash storage (eg higher than USB MSC).
78+
HAL_NVIC_SetPriority(FLASH_IRQn, 1, 1);
79+
HAL_NVIC_EnableIRQ(FLASH_IRQn);
7280
}
7381

7482
uint32_t storage_get_block_size(void) {
@@ -79,9 +87,47 @@ uint32_t storage_get_block_count(void) {
7987
return FLASH_PART1_START_BLOCK + FLASH_PART1_NUM_BLOCKS;
8088
}
8189

82-
bool storage_needs_flush(void) {
83-
// wait 2 seconds after last write to flush
84-
return flash_cache_dirty && sys_tick_has_passed(flash_tick_counter_last_write, 2000);
90+
void storage_irq_handler(void) {
91+
if (!(flash_flags & FLASH_FLAG_DIRTY)) {
92+
return;
93+
}
94+
95+
// This code uses interrupts to erase the flash
96+
/*
97+
if (flash_erase_state == 0) {
98+
flash_erase_it(flash_cache_sector_start, (const uint32_t*)CACHE_MEM_START_ADDR, flash_cache_sector_size / 4);
99+
flash_erase_state = 1;
100+
return;
101+
}
102+
103+
if (flash_erase_state == 1) {
104+
// wait for erase
105+
// TODO add timeout
106+
#define flash_erase_done() (__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY) == RESET)
107+
if (!flash_erase_done()) {
108+
return;
109+
}
110+
flash_erase_state = 2;
111+
}
112+
*/
113+
114+
// This code erases the flash directly, waiting for it to finish
115+
if (!(flash_flags & FLASH_FLAG_ERASED)) {
116+
flash_erase(flash_cache_sector_start, (const uint32_t*)CACHE_MEM_START_ADDR, flash_cache_sector_size / 4);
117+
flash_flags |= FLASH_FLAG_ERASED;
118+
return;
119+
}
120+
121+
// If not a forced write, wait at least 5 seconds after last write to flush
122+
// On file close and flash unmount we get a forced write, so we can afford to wait a while
123+
if ((flash_flags & FLASH_FLAG_FORCE_WRITE) || sys_tick_has_passed(flash_tick_counter_last_write, 5000)) {
124+
// sync the cache RAM buffer by writing it to the flash page
125+
flash_write(flash_cache_sector_start, (const uint32_t*)CACHE_MEM_START_ADDR, flash_cache_sector_size / 4);
126+
// clear the flash flags now that we have a clean cache
127+
flash_flags = 0;
128+
// indicate a clean cache with LED off
129+
led_state(PYB_LED_R1, 0);
130+
}
85131
}
86132

87133
void storage_flush(void) {
@@ -167,7 +213,6 @@ bool storage_write_block(const uint8_t *src, uint32_t block) {
167213
uint32_t flash_addr = FLASH_MEM_START_ADDR + (block - FLASH_PART1_START_BLOCK) * FLASH_BLOCK_SIZE;
168214
uint8_t *dest = flash_cache_get_addr_for_write(flash_addr);
169215
memcpy(dest, src, FLASH_BLOCK_SIZE);
170-
flash_tick_counter_last_write = HAL_GetTick();
171216
return true;
172217

173218
} else {

stmhal/storage.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
void storage_init(void);
44
uint32_t storage_get_block_size(void);
55
uint32_t storage_get_block_count(void);
6-
bool storage_needs_flush(void);
6+
void storage_irq_handler(void);
77
void storage_flush(void);
88
bool storage_read_block(uint8_t *dest, uint32_t block);
99
bool storage_write_block(const uint8_t *src, uint32_t block);

stmhal/timer.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
// the interrupts to be dispatched, so they are all collected here.
2020
//
2121
// TIM3:
22+
// - flash storage controller, to flush the cache
2223
// - USB CDC interface, interval, to check for new data
2324
// - LED 4, PWM to set the LED intensity
2425
//
@@ -29,14 +30,17 @@ TIM_HandleTypeDef TIM3_Handle;
2930
TIM_HandleTypeDef TIM5_Handle;
3031
TIM_HandleTypeDef TIM6_Handle;
3132

33+
// Used to divide down TIM3 and periodically call the flash storage IRQ
34+
static uint32_t tim3_counter = 0;
35+
3236
// TIM3 is set-up for the USB CDC interface
3337
void timer_tim3_init(void) {
3438
// set up the timer for USBD CDC
3539
__TIM3_CLK_ENABLE();
3640

3741
TIM3_Handle.Instance = TIM3;
38-
TIM3_Handle.Init.Period = (USBD_CDC_POLLING_INTERVAL*1000) - 1;
39-
TIM3_Handle.Init.Prescaler = 84-1;
42+
TIM3_Handle.Init.Period = (USBD_CDC_POLLING_INTERVAL*1000) - 1; // TIM3 fires every USBD_CDC_POLLING_INTERVAL ms
43+
TIM3_Handle.Init.Prescaler = 84-1; // for System clock at 168MHz, TIM3 runs at 1MHz
4044
TIM3_Handle.Init.ClockDivision = 0;
4145
TIM3_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
4246
HAL_TIM_Base_Init(&TIM3_Handle);
@@ -105,6 +109,13 @@ void timer_tim6_init(uint freq) {
105109
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
106110
if (htim == &TIM3_Handle) {
107111
USBD_CDC_HAL_TIM_PeriodElapsedCallback();
112+
113+
// Periodically raise a flash IRQ for the flash storage controller
114+
if (tim3_counter++ >= 500 / USBD_CDC_POLLING_INTERVAL) {
115+
tim3_counter = 0;
116+
NVIC->STIR = FLASH_IRQn;
117+
}
118+
108119
} else if (htim == &TIM5_Handle) {
109120
servo_timer_irq_callback();
110121
}

stmhal/usbd_msc_storage.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ int8_t FLASH_STORAGE_StopUnit(uint8_t lun) {
109109
return 0;
110110
}
111111

112+
int8_t FLASH_STORAGE_PreventAllowMediumRemoval(uint8_t lun, uint8_t param) {
113+
// sync the flash so that the cache is cleared and the device can be unplugged/turned off
114+
disk_ioctl(0, CTRL_SYNC, NULL);
115+
return 0;
116+
}
117+
112118
/**
113119
* @brief Read data from the medium
114120
* @param lun : logical unit number
@@ -146,7 +152,6 @@ int8_t FLASH_STORAGE_Write (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16
146152
}
147153
}
148154
*/
149-
storage_flush(); // XXX hack for now so that the cache is always flushed
150155
return 0;
151156
}
152157

@@ -165,6 +170,7 @@ const USBD_StorageTypeDef USBD_FLASH_STORAGE_fops = {
165170
FLASH_STORAGE_IsReady,
166171
FLASH_STORAGE_IsWriteProtected,
167172
FLASH_STORAGE_StopUnit,
173+
FLASH_STORAGE_PreventAllowMediumRemoval,
168174
FLASH_STORAGE_Read,
169175
FLASH_STORAGE_Write,
170176
FLASH_STORAGE_GetMaxLun,
@@ -295,6 +301,10 @@ int8_t SDCARD_STORAGE_StopUnit(uint8_t lun) {
295301
return 0;
296302
}
297303

304+
int8_t SDCARD_STORAGE_PreventAllowMediumRemoval(uint8_t lun, uint8_t param) {
305+
return 0;
306+
}
307+
298308
/**
299309
* @brief Read data from the medium
300310
* @param lun : logical unit number
@@ -340,6 +350,7 @@ const USBD_StorageTypeDef USBD_SDCARD_STORAGE_fops = {
340350
SDCARD_STORAGE_IsReady,
341351
SDCARD_STORAGE_IsWriteProtected,
342352
SDCARD_STORAGE_StopUnit,
353+
SDCARD_STORAGE_PreventAllowMediumRemoval,
343354
SDCARD_STORAGE_Read,
344355
SDCARD_STORAGE_Write,
345356
SDCARD_STORAGE_GetMaxLun,

stmhal/usbdev/class/cdc_msc_hid/inc/usbd_cdc_msc_hid.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ typedef struct _USBD_STORAGE {
5454
int8_t (* IsReady) (uint8_t lun);
5555
int8_t (* IsWriteProtected) (uint8_t lun);
5656
int8_t (* StopUnit)(uint8_t lun);
57+
int8_t (* PreventAllowMediumRemoval)(uint8_t lun, uint8_t param0);
5758
int8_t (* Read) (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
5859
int8_t (* Write)(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
5960
int8_t (* GetMaxLun)(void);

0 commit comments

Comments
 (0)