Stores data on the flash memory of stm32f4xx

Files at this revision

API Documentation at this revision

Comitter:
hendreh
Date:
Fri Nov 18 14:37:15 2016 +0000
Commit message:
working

Changed in this revision

SOFBlock.cpp Show annotated file Show diff for this revision Revisions of this file
SOFBlock.h Show annotated file Show diff for this revision Revisions of this file
SOF_block.cpp Show annotated file Show diff for this revision Revisions of this file
SOF_dev.h Show annotated file Show diff for this revision Revisions of this file
SOF_dev_stm32_f4xx.cpp Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 4cb438b58dc2 SOFBlock.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOFBlock.cpp	Fri Nov 18 14:37:15 2016 +0000
@@ -0,0 +1,153 @@
+/*
+* @file SOFBlock.cpp
+* @author hillkim7@gmail.com
+*
+* @brief Simple storage implementation on internal MCU flash memory.
+*
+*/
+
+#include "SOFBlock.h"
+#include <string.h>
+#include <stdlib.h>
+
+
+SOFBlock::SOFBlock()
+    : hblock_(NULL)
+{
+}
+
+SOFBlock::~SOFBlock()
+{
+    close();
+}
+
+void SOFBlock::close()
+{
+    if (hblock_ != NULL) {
+        SOF_block_close(hblock_);
+        hblock_ = NULL;
+    }
+}
+
+bool SOFBlock::format( uint8_t sector_index )
+{
+    return SOF_block_format(sector_index);
+}
+
+bool SOFBlock::get_stat( uint8_t sector_index, SOF_Statics_t &statics )
+{
+    return SOF_block_get_statics(sector_index, &statics) ==  kSOF_ErrNone;
+}
+
+SOFWriter::SOFWriter()
+{
+
+}
+
+SOFWriter::~SOFWriter()
+{
+
+}
+
+SOF_Error_t SOFWriter::open( uint8_t sector_index )
+{
+    if (is_open()) {
+        return kSOF_ErrBusyBlock;
+    }
+
+    SOF_Error_t err;
+
+    hblock_ = SOF_block_create_storage(sector_index, &err);
+
+    return err;
+}
+
+bool SOFWriter::write_byte_data( uint8_t c )
+{
+    if (!is_open()) {
+        return false;
+    }
+
+    return SOF_block_putc(hblock_, c);
+}
+
+size_t SOFWriter::write_data( const uint8_t *p, size_t p_size )
+{
+    if (!is_open()) {
+        return false;
+    }
+
+    return SOF_block_write(hblock_, p, p_size);
+}
+
+size_t SOFWriter::get_free_size()
+{
+    if (!is_open()) {
+        return 0;
+    }
+
+    return SOF_block_get_free_size(hblock_);
+}
+
+
+SOFReader::SOFReader()
+{
+
+}
+
+SOFReader::~SOFReader()
+{
+
+}
+
+SOF_Error_t SOFReader::open( uint8_t sector_index )
+{
+    if (is_open()) {
+        return kSOF_ErrBusyBlock;
+    }
+
+    SOF_Error_t err;
+
+    hblock_ = SOF_block_open_storage(sector_index, &err);
+
+    return err;
+}
+
+uint8_t * SOFReader::get_physical_data_addr()
+{
+    if (!is_open()) {
+        return NULL;
+    }
+
+    return SOF_block_base_addr(hblock_);
+}
+
+size_t SOFReader::get_data_size()
+{
+    if (!is_open()) {
+        return 0;
+    }
+
+    return SOF_block_storage_size(hblock_);
+}
+
+bool SOFReader::read_byte_data( uint8_t *c )
+{
+    if (!is_open()) {
+        return false;
+    }
+
+    return SOF_block_getc(hblock_, c);
+}
+
+size_t SOFReader::read_data( uint8_t *p, size_t p_size )
+{
+    if (!is_open()) {
+        return 0;
+    }
+
+    return SOF_block_read(hblock_, p, p_size);
+}
+
+
+
diff -r 000000000000 -r 4cb438b58dc2 SOFBlock.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOFBlock.h	Fri Nov 18 14:37:15 2016 +0000
@@ -0,0 +1,184 @@
+/**
+ * @file SOFBlock.h
+ *
+ * @author hillkim7@gmail.com
+ * @brief Simple storage implementation on internal MCU flash memory.
+ *
+ * The SOF in SOFBlock is abbreviation of "Storage On Flash".
+ * The purpose of SOFBlock class is to provide a way to write data on flash memory
+ * in the same way of file handling class in the file system.
+ * It manages a chunk of data on the Flash memory efficiently by minimizing flash erase operation as little as possible.
+ * Note: Currently it only supports STM32F4xx series platforms.
+ *       - NUCLEO-F401RE, NUCLEO-F411RE, Seeed Arch Max
+ * The STM32 F4xx series from ST have plenty of internal Flash memory inside MCU core.
+ * For example STM32 401RE has 512Kbyts Flash.
+ * Typical size of firmware file is less than 256KB, so remaining area is free to use.
+ * The simplest way of flash utilization as data storage is to use a chunk of Flash area as an unit of storage.
+ * A block of flash is called sector in STM32F4xx domain. It requires to erase a sector before update bits in flash.
+ *
+ * Conceptually it is quite simple.
+ * Here is typical write operation:
+ * 1) Erase sector #n
+ * 2) Write data to sector #n
+ * Read operation:
+ * 1) Just read physical memory address of sector #n
+ *    The base physical address of STM32 flash is 0x08000000.
+ *
+ * There may be inefficiency in this flash usage scenario when size of data is too small compared with sector size.
+ * The size of sectors from #5 to #7 of STM32-F4xx Flash is 128KB. For example, if I only need to maintain 1KB data,
+ * whenever I need to update data I need to erase whole 128KB of sector.
+ * This produces two problems.
+ * One is time consumption of the erase operation. The operation of ERASE128KB takes 1~4 seconds long.
+ * The other is related to lifetime of Flash memory.
+ * More you erase and write and lifetime of flash is shorter.
+ *
+ * To overcome such problems, here simple flash management algorithm is used for.
+ * By tracking data offset and size it can hold multiple data in a sector.
+ * Bear in mind that is impossible rewriting data on Flash.
+ * Keeping tracking data along with data itself without frequent erase operation is crucial.
+ * To do this, data itself is growing from low address.
+ * On the other hand tracking data is growing down from high address.
+ * Let's assume the size of data is 1KB and store it in sector #6 which address range is from 0x08040000 to 0x0805ffff.
+ * +-------------+----------------------------------------------------------------------+-----+
+ * <data>                                                                        <tracking data>
+ * +-------------+----------------------------------------------------------------------+-----+
+ * data grows ->                                                           <- tracking data grows
+ * Writing data will be placed at the end of data always and reading data will pick the last data.
+ * It is like simple file system that only keep a file only.
+ *
+ * Unlike file manipulation operation, there is caution you need to check if write operation fails
+ * or need to check free size before you start to write data.
+ * It is required to format flash sector when there is no more free space.
+ */
+
+#pragma once
+
+#include "SOF_dev.h"
+
+/** SOF(Storage On Flash) usage example
+ *
+ * Example:
+ * @code
+ * #include "mbed.h"
+ * #include "SOFBlock.h"
+ *
+ * int main()
+ * {
+ * 	const uint8_t sector_index = 7;
+ * 	SOFBlock::format(sector_index);	// Erase flash sector 7 and make structure for storage.
+ *
+ * 	SOFWriter writer;
+ * 	SOFReader reader;
+ *
+ * 	writer.open(sector_index);
+ * 	writer.write_data((uint8_t*)"First Data", 10);
+ * 	writer.close();
+ *
+ * 	reader.open(sector_index);
+ * 	printf("data %d bytes at %p :\r\n", reader.get_data_size(), reader.get_physical_base_addr());
+ * 	printf("%.*s\r\n", reader.get_data_size(), reader.get_physical_base_addr());
+ * 	// "First Data" printed
+ * 	reader.close();
+ *
+ *  SOF_Statics_t statics;
+ *  if (!SOFBlock(sector_index, statics) || statics.free_size < 11) { // check available byte
+ *     SOFBlock::format(sector_index);
+ *  }
+ * 	writer.open(sector_index);
+ * 	// Overwrite previous data without erasing flash.
+ * 	writer.write_data((uint8_t*)"Second Data", 11);
+ * 	writer.close();
+ *
+ * 	reader.open(sector_index);
+ * 	printf("data %d bytes at %p :\r\n", reader.get_data_size(), reader.get_physical_base_addr());
+ * 	printf("%.*s\r\n", reader.get_data_size(), reader.get_physical_base_addr());
+ * 	// "Second Data" printed
+ * 	reader.close();
+ * }
+ */
+
+/**
+ * Base class of SOF(Storage On Flash)
+ */
+class SOFBlock
+{
+public:
+    SOFBlock();
+
+    virtual ~SOFBlock();
+
+    void close();
+
+public:
+    /*** Returns whether instance of SOFBlock is currently associated to flash storage.  */
+    bool is_open() const {
+        return hblock_ != NULL;
+    }
+
+public:
+    /*** Erase flash sector and put signature to setup file system struct */
+    static bool format(uint8_t sector_index);
+
+    /*** Get statistics of storage */
+    static bool get_stat(uint8_t sector_index, SOF_Statics_t &statics);
+
+protected:
+    SOF_BlockHandle_t hblock_;
+};
+
+
+/**
+ * It provides interface for writing data to flash memory.
+ */
+class SOFWriter : public SOFBlock
+{
+public:
+    SOFWriter();
+    virtual ~SOFWriter();
+
+    /*** Open for writing mode */
+    SOF_Error_t open(uint8_t sector_index);
+
+    /*** Return max available for writing */
+    size_t get_free_size();
+
+    /*** Write one byte of data.
+     * Note: in case of storage full, it can't write data any more.
+     * It is required to format sector and write it again.
+     */
+    bool write_byte_data(uint8_t c);
+
+    /*** Write n bytes of data */
+    size_t write_data(const uint8_t *p, size_t p_size);
+};
+
+
+/**
+* It provides interface for reading data from flash memory.
+* It can read data directly by accessing physical flash address or
+* calling function like traditional file API style.
+*/
+class SOFReader : public SOFBlock
+{
+public:
+    SOFReader();
+    virtual ~SOFReader();
+
+    /*** Open for read mode */
+    SOF_Error_t open(uint8_t sector_index);
+
+    /*** Return flash physical address of data for direct access */
+    uint8_t *get_physical_data_addr();
+
+    /*** Return data size */
+    size_t get_data_size();
+
+    /*** Return one byte of data */
+    bool read_byte_data(uint8_t *c);
+
+    /*** Return n bytes of data */
+    size_t read_data( uint8_t *p, size_t p_size);
+};
+
+
+
diff -r 000000000000 -r 4cb438b58dc2 SOF_block.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOF_block.cpp	Fri Nov 18 14:37:15 2016 +0000
@@ -0,0 +1,523 @@
+/*
+* @file SOF_block.cpp
+*
+* @brief MTD device handling of STM32 internal flash memory.
+*
+*
+* History:
+*/
+
+#include <stdio.h>
+#include <assert.h>
+#include "SOF_dev.h"
+#include <string.h>
+
+#define LOCAL_DEBUG 0 // turn on local debug
+
+#define DCRLF	"\r\n"
+
+#if LOCAL_DEBUG
+#define DPRINTF printf
+#define EPRINTF printf
+#define DUMP_BLOCK dump_block
+#define DASSERT assert
+#else
+#define DPRINTF(...)
+#define EPRINTF(...)
+#define DUMP_BLOCK(...)
+#define DASSERT(...)
+#endif
+
+static uint16_t checksum(const uint8_t* data, int count);
+
+#define SYNC_MARK_BYTE_IN_LEN	0x07	// signature mark for upper byte in the storage_len
+
+#define RESERVED_BLOCK_INFO_SIZE sizeof(BlockInfo_t)
+
+typedef struct {
+    uint16_t for_storage;
+    uint16_t for_info;
+} BlockChecksum_t;
+
+typedef struct {
+    BlockChecksum_t		csum;
+    uint32_t			storage_len;
+} BlockInfo_t;
+
+typedef struct {
+    uint32_t	begin_offset;
+    uint32_t	len;
+    uint16_t	storage_csum;
+} StorageInfo_t;
+
+class SOF_BlockHandle
+{
+public:
+    SOF_BlockHandle()
+        : write_mode_(false)
+        , cur_pos_(0)
+        , hdev_(SOF_INVALID_HANDLE)
+        , storage_max_offset_(0)
+        , storage_begin_offset_(0)
+        , storage_end_offset_(0) {
+    }
+
+    bool is_writable() const {
+        return write_mode_;
+    }
+
+    uint32_t total_physical_block_size() const {
+        return SOF_dev_info(hdev_)->sec_size;
+    }
+
+public:
+    bool				write_mode_;
+    size_t				cur_pos_;
+    SOF_DevHandle_t		hdev_;
+    uint32_t			storage_max_offset_;
+    uint32_t			storage_begin_offset_;
+    uint32_t			storage_end_offset_;
+};
+
+
+static bool get_block_info(const SOF_BlockHandle_t handle, size_t seq, BlockInfo_t *info, uint32_t *loc_offset)
+{
+    uint32_t check_pos = ((seq+1) * sizeof(BlockInfo_t));
+    uint32_t info_pos;
+
+    DASSERT(check_pos < handle->total_physical_block_size());
+    if (check_pos >= handle->total_physical_block_size())
+        return false;
+
+    *loc_offset = info_pos = handle->total_physical_block_size() - check_pos;
+
+    // checksum in the first word
+    *((uint32_t*)&info->csum) = SOF_dev_read_word(handle->hdev_, info_pos);
+    // storage len in the next word
+    info->storage_len = SOF_dev_read_word(handle->hdev_, info_pos + 4);
+
+    return true;
+}
+
+static bool is_empty_block_info(BlockInfo_t *info)
+{
+    uint8_t *p = (uint8_t*)info;
+
+    for (size_t i = 0; i < sizeof(BlockInfo_t); ++i)
+        if (p[i] != SOF_ERASED_BYTE_VALUE)
+            return false;
+
+    return true;
+}
+
+static bool is_valid_block_info(BlockInfo_t *info)
+{
+    uint16_t csum = checksum((uint8_t*)&info->storage_len, 4);
+
+    if (SYNC_MARK_BYTE_IN_LEN != (info->storage_len >> 24)) {
+        EPRINTF("no sync mark in storage_len=%#x"DCRLF,info->storage_len);
+        return false;
+    }
+
+    if (csum != info->csum.for_info) {
+        EPRINTF("CSUM mismatch %#x %#x"DCRLF,csum, info->csum.for_info);
+        return false;
+    }
+
+    return true;
+}
+
+static bool get_empty_info_location(const SOF_BlockHandle_t handle, uint32_t *loc_offset)
+{
+    BlockInfo_t info;
+    uint32_t pos;
+
+    for (size_t seq = 0; get_block_info(handle, seq, &info, &pos); ++seq) {
+        //DPRINTF("[%u] len=%#x pos=%u"DCRLF,seq, info.storage_len, pos);
+        if (is_empty_block_info(&info)) {
+            *loc_offset = pos;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static SOF_Error_t probe_active_storage_info(const SOF_BlockHandle_t handle, StorageInfo_t *storage_info)
+{
+    BlockInfo_t info, last_info;
+    uint32_t pos;
+    uint32_t storage_len_sum = 0;
+
+    for (size_t seq = 0; get_block_info(handle, seq, &info, &pos); ++seq) {
+        if (is_empty_block_info(&info)) {
+            if (seq == 0)
+                return kSOF_ErrNoInfo;
+            break;
+        }
+        if (!is_valid_block_info(&info)) {
+            if (storage_info->begin_offset + storage_info->len == pos) {
+                DPRINTF("data is full: %u"DCRLF,storage_info->begin_offset + storage_info->len);
+                break;
+            }
+
+            EPRINTF("invalid block at %u"DCRLF,pos);
+            return kSOF_ErrBadBlock;
+        }
+
+        storage_len_sum += info.storage_len & 0x00FFFFFF;
+        last_info = info;
+    }
+
+    uint32_t storage_len = last_info.storage_len & 0x00FFFFFF;
+
+    storage_info->begin_offset = storage_len_sum - storage_len;
+    storage_info->len = storage_len;
+    storage_info->storage_csum = last_info.csum.for_storage;
+
+    return kSOF_ErrNone;
+}
+
+
+#if LOCAL_DEBUG
+static void dump_block(SOF_BlockHandle_t handle)
+{
+    DPRINTF("sector(%u)"DCRLF, SOF_dev_info(handle->hdev_)->sec_no);
+    DPRINTF("	offset               =%u"DCRLF, handle->cur_pos_);
+    DPRINTF("	writemode            =%d"DCRLF, handle->write_mode_);
+    DPRINTF("	storage_max_offset   =%u"DCRLF, handle->storage_max_offset_);
+    DPRINTF("	storage_begin_offset =%u"DCRLF, handle->storage_begin_offset_);
+    DPRINTF("	storage_end_offset   =%u"DCRLF, handle->storage_end_offset_);
+    DPRINTF("	free=%u total=%u"DCRLF,SOF_block_get_free_size(handle), handle->total_physical_block_size());
+}
+#endif
+
+size_t SOF_block_get_free_size(SOF_BlockHandle_t handle)
+{
+    DASSERT(handle != NULL);
+    if (handle->storage_end_offset_ <= handle->storage_max_offset_-RESERVED_BLOCK_INFO_SIZE)
+        return (handle->storage_max_offset_- RESERVED_BLOCK_INFO_SIZE) - handle->storage_end_offset_;
+    else {
+        return 0;
+    }
+}
+
+uint32_t SOF_block_storage_size(SOF_BlockHandle_t handle)
+{
+    DASSERT(handle != NULL);
+    return handle->storage_end_offset_ - handle->storage_begin_offset_;
+}
+
+static uint16_t checksum(const uint8_t* data, int count)
+{
+    // Fletcher's checksum algorithm
+    uint16_t sum1 = 0;
+    uint16_t sum2 = 0;
+    int index;
+
+    for( index = 0; index < count; ++index ) {
+        sum1 = (sum1 + data[index]) % 255;
+        sum2 = (sum2 + sum1) % 255;
+    }
+
+    return (sum2 << 8) | sum1;
+}
+
+static uint16_t compute_storage_checksum(SOF_BlockHandle_t handle)
+{
+    uint8_t *addr = SOF_dev_get_hw_addr(handle->hdev_);
+
+    return checksum(addr+handle->storage_begin_offset_, SOF_block_storage_size(handle));
+}
+
+static bool write_storage_info(SOF_BlockHandle_t handle)
+{
+    BlockInfo_t cs;
+
+    cs.storage_len = (SYNC_MARK_BYTE_IN_LEN << 24) | SOF_block_storage_size(handle);
+    cs.csum.for_info = checksum((uint8_t*)&cs.storage_len, 4);
+    cs.csum.for_storage = compute_storage_checksum(handle);
+
+    DPRINTF("write %#x at %#x"DCRLF,*((uint32_t*)&cs.csum), handle->storage_max_offset_);
+    if (SOF_dev_write_word(handle->hdev_, handle->storage_max_offset_, *((uint32_t*)&cs.csum)) < 0)
+        return false;
+
+    if (SOF_dev_write_word(handle->hdev_, handle->storage_max_offset_+4, *((uint32_t*)&cs.storage_len)) < 0)
+        return false;
+
+    return true;
+}
+
+static bool create_empty_storage(SOF_DevHandle_t hdev, uint8_t sector_index)
+{
+    SOF_BlockHandle handle_data;
+
+    handle_data.hdev_ = hdev;
+
+    uint32_t info_begin_offset;
+
+    if (!get_empty_info_location(&handle_data, &info_begin_offset)) {
+        EPRINTF("no info"DCRLF);
+        SOF_block_close(&handle_data);
+        return false;
+    }
+
+    handle_data.storage_max_offset_ = info_begin_offset;
+    handle_data.storage_begin_offset_ = 0;
+    handle_data.storage_end_offset_ = handle_data.storage_begin_offset_;
+    handle_data.cur_pos_ = handle_data.storage_begin_offset_;
+
+    DPRINTF("storage created: begin=%d end=%d free=%d"DCRLF,
+            handle_data.storage_begin_offset_, handle_data.storage_end_offset_, SOF_block_get_free_size(&handle_data));
+
+    write_storage_info(&handle_data);
+
+    return true;
+}
+
+
+bool SOF_block_format(uint8_t sector_index)
+{
+    if (!SOF_dev_is_valid_sector(sector_index)) {
+        DPRINTF("invalid sector_index=%d"DCRLF, sector_index);
+        return false;
+    }
+
+    SOF_DevHandle_t hdev = SOF_dev_open(sector_index);
+
+    if (hdev == SOF_INVALID_HANDLE) {
+        DPRINTF("SOF_dev_open(%d) failed"DCRLF, sector_index);
+        return false;
+    }
+
+    DPRINTF("Flash erase %d"DCRLF, sector_index);
+    SOF_dev_erase(hdev);
+    create_empty_storage(hdev, sector_index);
+    SOF_dev_close(hdev);
+
+    return true;
+}
+
+SOF_BlockHandle_t SOF_block_open_storage(uint8_t sector_index, SOF_Error_t *err)
+{
+    if (!SOF_dev_is_valid_sector(sector_index)) {
+        DPRINTF("invalid sector_index=%d"DCRLF, sector_index);
+        return false;
+    }
+
+    SOF_DevHandle_t hdev = SOF_dev_open(sector_index);
+
+    if (hdev == SOF_INVALID_HANDLE) {
+        DPRINTF("SOF_dev_open(%d) failed"DCRLF, sector_index);
+        return false;
+    }
+
+    SOF_BlockHandle_t handle = new SOF_BlockHandle();
+
+    handle->hdev_ = hdev;
+
+    StorageInfo_t storage_info;
+
+    if ((*err=probe_active_storage_info(handle, &storage_info)) != kSOF_ErrNone) {
+        delete handle;
+        return NULL;
+    }
+
+    uint32_t info_begin_offset;
+
+    if (!get_empty_info_location(handle, &info_begin_offset)) {
+        *err = kSOF_ErrBadBlock;
+        delete handle;
+        return NULL;
+    }
+
+    // set max offset that storage grows.
+    handle->storage_max_offset_ = info_begin_offset;
+
+    handle->storage_begin_offset_ = storage_info.begin_offset;
+    handle->storage_end_offset_ = storage_info.begin_offset + storage_info.len;
+
+    handle->cur_pos_ = handle->storage_begin_offset_;
+
+    DPRINTF("open for read: begin=%d end=%d len=%d free=%d"DCRLF,
+            handle->storage_begin_offset_, handle->storage_end_offset_, storage_info.len,
+            SOF_block_get_free_size(handle));
+    if (compute_storage_checksum(handle) != storage_info.storage_csum) {
+        EPRINTF("checksum error %#x != %#x"DCRLF, compute_storage_checksum(handle), storage_info.storage_csum);
+        *err = kSOF_ErrDataCurrupted;
+        delete handle;
+        return NULL;
+    }
+
+    DUMP_BLOCK(handle);
+    *err = kSOF_ErrNone;
+
+    return handle;
+}
+
+SOF_BlockHandle_t SOF_block_create_storage(uint8_t sector_index, SOF_Error_t *err)
+{
+    if (!SOF_dev_is_valid_sector(sector_index)) {
+        DPRINTF("invalid sector_index=%d"DCRLF, sector_index);
+        return false;
+    }
+
+    SOF_DevHandle_t hdev = SOF_dev_open(sector_index);
+
+    if (hdev == SOF_INVALID_HANDLE) {
+        DPRINTF("SOF_dev_open(%d) failed"DCRLF, sector_index);
+        return false;
+    }
+
+    SOF_BlockHandle_t handle = new SOF_BlockHandle();
+
+    handle->hdev_ = hdev;
+
+    StorageInfo_t storage_info;
+
+    if ((*err=probe_active_storage_info(handle, &storage_info)) != kSOF_ErrNone) {
+        delete handle;
+        return NULL;
+    }
+
+    uint32_t info_begin_offset;
+
+    if (!get_empty_info_location(handle, &info_begin_offset)) {
+        *err = kSOF_ErrBadBlock;
+        delete handle;
+        return NULL;
+    }
+
+    // set max offset that storage grows.
+    handle->storage_max_offset_ = info_begin_offset;
+
+    // writing position is just after previous storage
+    handle->storage_begin_offset_ = storage_info.begin_offset + storage_info.len;
+    handle->storage_end_offset_ = handle->storage_begin_offset_;
+
+    handle->cur_pos_ = handle->storage_begin_offset_;
+    handle->write_mode_ = true;
+    DPRINTF("open for write: begin=%d end=%d free=%d"DCRLF,
+            handle->storage_begin_offset_, handle->storage_end_offset_, SOF_block_get_free_size(handle));
+
+    DUMP_BLOCK(handle);
+    *err = kSOF_ErrNone;
+
+    return handle;
+}
+
+bool SOF_block_close(SOF_BlockHandle_t handle)
+{
+    bool r = true;
+
+    DASSERT(handle != NULL);
+    if (handle->write_mode_)
+        r = (bool)write_storage_info(handle);
+    SOF_dev_close(handle->hdev_);
+    delete handle;
+
+    return r;
+}
+
+uint8_t *SOF_block_base_addr(SOF_BlockHandle_t handle)
+{
+    DASSERT(handle != NULL);
+    return SOF_dev_get_hw_addr(handle->hdev_) + handle->cur_pos_;
+}
+
+bool SOF_block_putc(SOF_BlockHandle_t handle, uint8_t c)
+{
+    DASSERT(handle != NULL);
+    DASSERT(handle->is_writable());
+
+    if (SOF_block_get_free_size(handle) == 0) {
+        DPRINTF("no free space"DCRLF);
+        DUMP_BLOCK(handle);
+
+        return false;
+    }
+
+    bool b = SOF_dev_write_byte(handle->hdev_, handle->cur_pos_, c) != -1;
+    if (b) {
+        handle->cur_pos_++;
+        handle->storage_end_offset_++;
+    }
+
+    return b;
+}
+
+size_t SOF_block_write(SOF_BlockHandle_t handle, const uint8_t *p, size_t p_size)
+{
+    size_t i;
+
+    for (i = 0; i < p_size; ++i)
+        if (SOF_block_putc(handle, *p++) != true)
+            return i;
+
+    return i;
+}
+
+bool SOF_block_getc(SOF_BlockHandle_t handle, uint8_t *c)
+{
+    DASSERT(handle != NULL);
+    DASSERT(handle->is_writable());
+
+    if (handle->cur_pos_ >= handle->storage_end_offset_) {
+        DPRINTF("end of data\n"DCRLF);
+        DUMP_BLOCK(handle);
+
+        return false;
+    }
+
+    *c = SOF_dev_read_byte(handle->hdev_, handle->cur_pos_++);
+
+    return true;
+}
+
+size_t SOF_block_read(SOF_BlockHandle_t handle, uint8_t *p, size_t p_size)
+{
+    size_t i;
+
+    for (i = 0; i < p_size; ++i)
+        if (!SOF_block_getc(handle, p++))
+            break;
+
+    return i;
+}
+
+SOF_Error_t SOF_block_get_statics(uint8_t sector_index, SOF_Statics_t *stat)
+{
+    if (!SOF_dev_is_valid_sector(sector_index)) {
+        DPRINTF("invalid sector_index=%d"DCRLF, sector_index);
+        return kSOF_ErrParam;
+    }
+
+    SOF_Error_t err;
+    SOF_BlockHandle_t hblk = SOF_block_open_storage(sector_index, &err);
+
+    if (hblk == NULL) {
+        DPRINTF("SOF_block_open_storage(%d) failed"DCRLF, sector_index);
+        return err;
+    }
+
+    stat->data_addr = SOF_block_base_addr(hblk);
+    stat->data_size = SOF_block_storage_size(hblk);
+    stat->free_size = SOF_block_get_free_size(hblk);
+
+    SOF_block_close(hblk);
+
+    return kSOF_ErrNone;
+}
+
+const SOF_SectorSpec_t *SOF_block_get_info(uint8_t sector_index)
+{
+    if (!SOF_dev_is_valid_sector(sector_index)) {
+        DPRINTF("invalid sector_index=%d"DCRLF, sector_index);
+        return NULL;
+    }
+
+    return SOF_dev_info_by_index(sector_index);
+}
+
+
+
diff -r 000000000000 -r 4cb438b58dc2 SOF_dev.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOF_dev.h	Fri Nov 18 14:37:15 2016 +0000
@@ -0,0 +1,82 @@
+/**
+* @file SOF_dev.c
+*
+* @author hillkim7@gmail.com
+* @brief Storage On Flash device interface
+*
+*/
+
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+
+/** The default value of erased flash memory */
+#define SOF_ERASED_BYTE_VALUE	0xFF
+
+#define SOF_INVALID_HANDLE	0xFFFFFFFF
+
+/** Error code definition */
+typedef enum {
+    kSOF_ErrNone = 0,
+    kSOF_ErrParam,
+    kSOF_ErrNoInfo,
+    kSOF_ErrBusyBlock,
+    kSOF_ErrBadBlock,
+    kSOF_ErrDataCurrupted,
+    kSOF_ErrFatal,
+} SOF_Error_t;;
+
+/** Flash sector specification */
+typedef struct {
+    uint32_t	sec_no;
+    uint32_t	sec_addr;
+    uint32_t	sec_size;
+} SOF_SectorSpec_t;
+
+/** statics of SOF block */
+typedef struct {
+    uint8_t		*data_addr;
+    uint32_t	data_size;
+    uint32_t	free_size;
+} SOF_Statics_t;
+
+typedef uint32_t SOF_DevHandle_t;
+
+class SOF_BlockHandle;
+typedef SOF_BlockHandle* SOF_BlockHandle_t;
+
+/*-----------------------------------------------------------------------------------------------------*/
+/* Flash device interface */
+/*-----------------------------------------------------------------------------------------------------*/
+int SOF_dev_is_valid_sector(uint8_t sector_index);
+const SOF_SectorSpec_t *SOF_dev_info(SOF_DevHandle_t hdev);
+const SOF_SectorSpec_t *SOF_dev_info_by_index(uint8_t sector_index);
+SOF_DevHandle_t SOF_dev_open(uint8_t sector_index);
+void SOF_dev_close(SOF_DevHandle_t hdev);
+uint8_t *SOF_dev_get_hw_addr(SOF_DevHandle_t hdev);
+void SOF_dev_erase(SOF_DevHandle_t handle);
+int SOF_dev_write_word(SOF_DevHandle_t handle, uint32_t offset_addr, uint32_t data);
+uint32_t SOF_dev_read_word(SOF_DevHandle_t handle, uint32_t offset_addr);
+int SOF_dev_write_byte(SOF_DevHandle_t handle, uint32_t offset_addr, uint8_t data);
+uint8_t SOF_dev_read_byte(SOF_DevHandle_t handle, uint32_t offset_addr);
+
+/*-----------------------------------------------------------------------------------------------------*/
+/* Flash block device interface */
+/*-----------------------------------------------------------------------------------------------------*/
+bool SOF_block_format(uint8_t sector_index);
+SOF_BlockHandle_t SOF_block_open_storage(uint8_t sector_index, SOF_Error_t *err);
+SOF_BlockHandle_t SOF_block_create_storage(uint8_t sector_index, SOF_Error_t *err);
+bool SOF_block_close(SOF_BlockHandle_t handle);
+uint8_t *SOF_block_base_addr(SOF_BlockHandle_t handle);
+bool SOF_block_putc(SOF_BlockHandle_t handle, uint8_t c);
+size_t SOF_block_write(SOF_BlockHandle_t handle, const uint8_t *p, size_t p_size);
+bool SOF_block_getc(SOF_BlockHandle_t handle, uint8_t *c);
+size_t SOF_block_read(SOF_BlockHandle_t handle, uint8_t *p, size_t p_size);
+size_t SOF_block_get_free_size(SOF_BlockHandle_t handle);
+uint32_t SOF_block_storage_size(SOF_BlockHandle_t handle);
+SOF_Error_t SOF_block_get_statics(uint8_t sector_index, SOF_Statics_t *stat);
+const SOF_SectorSpec_t *SOF_block_get_info(uint8_t sector_index);
+
+
+
diff -r 000000000000 -r 4cb438b58dc2 SOF_dev_stm32_f4xx.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SOF_dev_stm32_f4xx.cpp	Fri Nov 18 14:37:15 2016 +0000
@@ -0,0 +1,186 @@
+/**
+* @file SOF_dev_stm32.c
+*
+* @brief Flash device access interface for STM32 F4xx series
+*
+*
+* History:
+*/
+
+#include <stdio.h>
+#include "SOF_dev.h"
+#include <string.h>
+
+#include "mbed.h"
+#include "stm32f4xx_hal_flash.h"
+
+#define DCRLF	"\r\n"
+
+#if 0
+#define DPRINTF	printf
+#define DASSERT(cond)  \
+	if (!(cond)) { \
+		printf("%s:%d assertion failed! '%s\r\n"\
+			, __FILE__, __LINE__, #cond); \
+	}
+#else
+#define DPRINTF(...)
+#define DASSERT(...)
+#endif
+
+
+#if defined(STM32F401xE) || defined(STM32F411xE) || defined(STM32F407xx)
+static const SOF_SectorSpec_t _sec_spec[] = {
+    {FLASH_SECTOR_0, 0x08000000, 16*1024},
+    {FLASH_SECTOR_1, 0x08004000, 16*1024},
+    {FLASH_SECTOR_2, 0x08008000, 16*1024},
+    {FLASH_SECTOR_3, 0x0800C000, 16*1024},
+    {FLASH_SECTOR_4, 0x08010000, 64*1024},
+    {FLASH_SECTOR_5, 0x08020000, 128*1024},
+    {FLASH_SECTOR_6, 0x08040000, 128*1024},
+    {FLASH_SECTOR_7, 0x08060000, 128*1024},
+};
+#else
+#error "Not supported device"
+#endif
+
+#define N_SECTOR_SPEC			(sizeof(_sec_spec)/sizeof(_sec_spec[0]))
+
+#define SECTOR_NO(sector)		_sec_spec[sector].sec_no
+#define SECTOR_ADDR(sector)	_sec_spec[sector].sec_addr
+#define SECTOR_SIZE(sector)		_sec_spec[sector].sec_size
+
+
+static inline size_t handle_to_sector_index(SOF_DevHandle_t hdev)
+{
+    DASSERT(hdev < N_SECTOR_SPEC);
+    return hdev;
+}
+
+const SOF_SectorSpec_t *SOF_dev_info(uint8_t sector_index)
+{
+    DASSERT(sector_index < N_SECTOR_SPEC);
+    return &_sec_spec[sector_index];
+}
+
+int SOF_dev_is_valid_sector(uint8_t sector_index)
+{
+    return sector_index < N_SECTOR_SPEC;
+}
+
+const SOF_SectorSpec_t *SOF_dev_info_by_index(uint8_t sector_index)
+{
+    DASSERT(SOF_dev_is_valid_sector(sector_index));
+    return &_sec_spec[sector_index];
+}
+
+const SOF_SectorSpec_t *SOF_dev_info(SOF_DevHandle_t hdev)
+{
+    uint8_t sector_index = handle_to_sector_index(hdev);
+
+    return SOF_dev_info_by_index(sector_index);
+}
+
+SOF_DevHandle_t SOF_dev_open(uint8_t sector_index)
+{
+    DASSERT(sector_index < N_SECTOR_SPEC);
+    return (SOF_DevHandle_t)sector_index;
+}
+
+void SOF_dev_close(SOF_DevHandle_t hdev)
+{
+}
+
+uint8_t *SOF_dev_get_hw_addr(SOF_DevHandle_t hdev)
+{
+    uint8_t sector_index = handle_to_sector_index(hdev);
+
+    return (uint8_t *)SECTOR_ADDR(sector_index);
+}
+
+
+void SOF_dev_erase(SOF_DevHandle_t hdev)
+{
+    uint8_t sector_index = handle_to_sector_index(hdev);
+    FLASH_EraseInitTypeDef ei;
+    uint32_t error = 0;
+
+    DPRINTF("FLASH_Erase_Sector %d"DCRLF, SECTOR_NO(sector_index));
+    HAL_FLASH_Unlock();
+
+    ei.TypeErase = TYPEERASE_SECTORS;
+    ei.Sector = SECTOR_NO(sector_index);
+    ei.NbSectors = 1;
+    ei.Banks = 0;
+    ei.VoltageRange = VOLTAGE_RANGE_3;
+    HAL_FLASHEx_Erase(&ei, &error);
+    HAL_FLASH_Lock();
+    DPRINTF("FLASH_Erase_Sector ok"DCRLF);
+}
+
+
+int SOF_dev_write_word(SOF_DevHandle_t hdev, uint32_t offset_addr, uint32_t data)
+{
+    uint8_t sector_index = handle_to_sector_index(hdev);
+    uint32_t dst = SECTOR_ADDR(sector_index) + offset_addr;
+
+    DASSERT((offset_addr%sizeof(uint32_t)) == 0);
+    HAL_FLASH_Unlock();
+    if (HAL_FLASH_Program(TYPEPROGRAM_WORD, dst, data) != HAL_OK) {
+        DPRINTF("FLASH_ProgramWord failed: %#x"DCRLF, dst);
+        HAL_FLASH_Lock();
+        return -1;
+    }
+
+    HAL_FLASH_Lock();
+
+    if (data != SOF_dev_read_word(hdev, offset_addr)) {
+        DPRINTF("addr=%x %#04x %#04x"DCRLF, dst, data, SOF_dev_read_word(hdev, offset_addr));
+        return -1;
+    }
+
+    return 0;
+}
+
+uint32_t SOF_dev_read_word(SOF_DevHandle_t hdev, uint32_t offset_addr)
+{
+    uint8_t sector_index = handle_to_sector_index(hdev);
+    uint32_t src = SECTOR_ADDR(sector_index) + offset_addr;
+
+    DASSERT((offset_addr%sizeof(uint32_t)) == 0);
+
+    return *(volatile uint32_t*)src;
+}
+
+int SOF_dev_write_byte(SOF_DevHandle_t hdev, uint32_t offset_addr, uint8_t data)
+{
+    uint8_t sector_index = handle_to_sector_index(hdev);
+    uint32_t dst = SECTOR_ADDR(sector_index) + offset_addr;
+
+    HAL_FLASH_Unlock();
+    if (HAL_FLASH_Program(TYPEPROGRAM_BYTE, dst, data) != HAL_OK) {
+        DPRINTF("FLASH_ProgramWord failed: %#x"DCRLF, dst);
+        HAL_FLASH_Lock();
+        return -1;
+    }
+
+    HAL_FLASH_Lock();
+
+    if (data != SOF_dev_read_byte(hdev, offset_addr)) {
+        DPRINTF("addr=%x %#02x %#02x"DCRLF, dst, data, SOF_dev_read_byte(hdev, offset_addr));
+        return -1;
+    }
+
+    return 0;
+}
+
+uint8_t SOF_dev_read_byte(SOF_DevHandle_t hdev, uint32_t offset_addr)
+{
+    uint8_t sector_index = handle_to_sector_index(hdev);
+    uint32_t src = SECTOR_ADDR(sector_index) + offset_addr;
+
+    return *(volatile uint8_t*)src;
+}
+
+
+