Integrate littlefs with external parallel NOR FLASH 128-mb

I want to use littlefs file system with external parallel NOR FLASH memory(PC28640P33BF) with STM32L4

I have the example code of STM32L4 to initialize the external NOR FLASH memory using FSMC peripheral.
But I am not sure how to integrate the NOR flash memory with the MbedOS LitteFS file system.

Request you to give steps to do this

Hello,

It is still related to your previous question about block device.

So if you were not able to find a fit block device then you also can not use the LittleFileSystem or any other one.

What would by a purpose od your memories? Do you want to use them as main flash and RAM or for increase the on-chip one?

BR, Jan

we are using Nor flash as a external data storage device to store the information ( e.g. log , sensor data etc ). I could not figure out suitable block device.
could you please suggest which block device would be suitable for operation?

Hello Jan,
Are you want to say that with Mbed OS , we cant use parallel NOR FLASH & PSRAM memories ?
please clarify if any difference in understanding

How I wrote before I am not expert but I am not sure if any one else give you an answer.

MbedOs doesn’t seem to have a direct solution for parallel memory, all block device APIs are for serial (usually SPI) memory. However you can combine HAL drivers with Mbed. If you know about an example (must be on same HAL version) with STM32CubeIDE where is intialization and data trasnfer, then you can put this code to your Mbed project.

BR, Jan

I am using a IS29GL128 NOR flash as the backing memory for a LittleFS2 file system. It required writing a custom block device class derived from mbed::BlockDevice.

//-----------------------------------------------------------------------guard--
#ifndef CFI_FLASHBLOCKDEVICE_H_
#define CFI_FLASHBLOCKDEVICE_H_

//-------------------------------------------------------------------<include>--
#include <BlockDevice.h>
#include <stm32f4xx.h>
#include <stm32f4xx_hal.h>
#include <stm32f4xx_hal_flash.h>

//-------------------------------------------------------------------"include"--
#include "../Chips/FlashMemory.h"

//-----------------------------------------------------------class-declaration--
//! \brief A class to monitor the SD card.
//
class CFI_FlashBlockDevice : public mbed::BlockDevice
{
    //member type

    //member data
    private:
        Chips::IS29GL128&           NOR_FlashChip;
        unsigned int                BankOrdinal;
        NOR_HandleTypeDef           HAL_Device;
        FMC_NORSRAM_TimingTypeDef   FMC_Timing;
        int                         InitResult;

    //constructors/destructor
    public:
        CFI_FlashBlockDevice( Chips::IS29GL128& NOR_FlashChip,
                              unsigned int      BankOrdinal    );
        virtual ~CFI_FlashBlockDevice();

    //member functions
        virtual int         init();
        virtual int         deinit();
        virtual int         read   ( void*       buffer, bd_addr_t addr, bd_size_t size );
        virtual int         program( const void* buffer, bd_addr_t addr, bd_size_t size );
        virtual int         erase( bd_addr_t addr, bd_size_t size );
        virtual bd_size_t   get_read_size() const;
        virtual bd_size_t   get_program_size() const;
        virtual bd_size_t   get_erase_size() const;
        virtual bd_size_t   get_erase_size( bd_addr_t addr ) const;
        virtual bd_size_t   size() const;
        virtual const char* get_type() const;

    private:
        virtual void        lock();
        virtual void        unlock();
};

#endif 
//-------------------------------------------------------------------<include>--
#include <algorithm>

#include <errno.h>

//-------------------------------------------------------------------"include"--
#include "CFI_FlashBlockDevice.h"

#include "../SharedUtilities/BufferView.h"
#define LOGLEVEL 0
#include "../SharedUtilities/Debug.h"

//------------------------------------------------------------------------------
namespace
{
    class HalLock
    {
        NOR_HandleTypeDef&      Device;
        bool                    Claimed;

        public:
            HalLock( NOR_HandleTypeDef& Device )
                : Device ( Device )
                , Claimed( false )
            {
                Lock();
            }

            ~HalLock()
            {
                if ( this->Claimed )
                {
                    this->Device.State = HAL_NOR_STATE_READY;
                }

                Unlock();
            }

            bool SetBusy()
            {
                const auto AlreadyBusy( this->Device.State == HAL_NOR_STATE_BUSY );

                if ( !AlreadyBusy )
                {
                    this->Device.State = HAL_NOR_STATE_BUSY;
                    this->Claimed      = true;
                }

                return AlreadyBusy;
            }

        private:
            int  Lock()   { __HAL_LOCK  ( &this->Device ); return HAL_OK; }
            void Unlock() { __HAL_UNLOCK( &this->Device );                }
    };

    enum Error
    {
        ERROR_WOULD_BLOCK           = -5001, /*!< operation would block */
        ERROR_UNSUPPORTED           = -5002, /*!< unsupported operation */
        ERROR_PARAMETER             = -5003, /*!< invalid parameter */
        ERROR_NO_INIT               = -5004, /*!< uninitialized */
        ERROR_NO_DEVICE             = -5005, /*!< device is missing or not connected */
        ERROR_WRITE_PROTECTED       = -5006, /*!< write protected */
        ERROR_UNUSABLE              = -5007, /*!< unusable card */
        ERROR_NO_RESPONSE           = -5008, /*!< No response from device */
        ERROR_CRC                   = -5009, /*!< CRC error */
        ERROR_ERASE                 = -5010, /*!< Erase error: reset/sequence */
        ERROR_WRITE                 = -5011, /*!< Write error: !DATA_ACCEPTED */
        ERROR_UNSUPPORTED_BLOCKSIZE = -5012, /*!< unsupported blocksize, only 512 byte supported */
        ERROR_READBLOCKS            = -5013, /*!< read data blocks from SD failed */
        ERROR_WRITEBLOCKS           = -5014, /*!< write data blocks to SD failed */
        ERROR_ERASEBLOCKS           = -5015  /*!< erase data blocks to SD failed */
    };
}

//-----------------------------------------------------------------constructor--
CFI_FlashBlockDevice::CFI_FlashBlockDevice( Chips::IS29GL128& NOR_FlashChip,
                                            unsigned int      BankOrdinal    )
    : NOR_FlashChip( NOR_FlashChip )
    , BankOrdinal  ( BankOrdinal )
    , FMC_Timing   ( { 0 } )
    , InitResult   ( BD_ERROR_OK )
{
    this->HAL_Device.Instance                = FMC_NORSRAM_DEVICE;
    this->HAL_Device.Extended                = FMC_NORSRAM_EXTENDED_DEVICE;
    this->HAL_Device.State                   = HAL_NOR_STATE_RESET;
    this->HAL_Device.Init.NSBank             = BankOrdinal;
    this->HAL_Device.Init.DataAddressMux     = FMC_DATA_ADDRESS_MUX_DISABLE;
    this->HAL_Device.Init.MemoryType         = FMC_MEMORY_TYPE_NOR;
    this->HAL_Device.Init.MemoryDataWidth    = FMC_NORSRAM_MEM_BUS_WIDTH_8;
    this->HAL_Device.Init.BurstAccessMode    = FMC_BURST_ACCESS_MODE_DISABLE;
    this->HAL_Device.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;
    this->HAL_Device.Init.WrapMode           = FMC_WRAP_MODE_DISABLE;
    this->HAL_Device.Init.WaitSignalActive   = FMC_WAIT_TIMING_BEFORE_WS;
    this->HAL_Device.Init.WriteOperation     = FMC_WRITE_OPERATION_ENABLE;
    this->HAL_Device.Init.WaitSignal         = FMC_WAIT_SIGNAL_ENABLE;
    this->HAL_Device.Init.ExtendedMode       = FMC_EXTENDED_MODE_DISABLE;
    this->HAL_Device.Init.AsynchronousWait   = FMC_ASYNCHRONOUS_WAIT_ENABLE;
    this->HAL_Device.Init.WriteBurst         = FMC_WRITE_BURST_DISABLE;
    this->HAL_Device.Init.ContinuousClock    = FMC_CONTINUOUS_CLOCK_SYNC_ONLY;
    this->HAL_Device.Init.PageSize           = FMC_PAGE_SIZE_NONE;
    /* Timing */
    //@AHB clock == 168MHz --> t_HCLK == 6ns
    this->FMC_Timing.AddressSetupTime      = 0;
    this->FMC_Timing.AddressHoldTime       = 45/6 + 1;
    this->FMC_Timing.DataSetupTime         = 12; // 11 fails (just)
    this->FMC_Timing.BusTurnAroundDuration = 15;
    this->FMC_Timing.CLKDivision           = 16;
    this->FMC_Timing.DataLatency           = 17;
    this->FMC_Timing.AccessMode            = FMC_ACCESS_MODE_A;

    if ( HAL_NOR_Init( &this->HAL_Device, &this->FMC_Timing, nullptr ) != HAL_OK )
    {
        LOG_0( "HAL_NOR_INIT failed.\n" );
        this->InitResult = BD_ERROR_DEVICE_ERROR;
    }
}

//------------------------------------------------------------------destructor--
CFI_FlashBlockDevice::~CFI_FlashBlockDevice()
{
}

//------------------------------------------------------------------------------
int CFI_FlashBlockDevice::init()
{
    LOG_1( "CFI_FlashBlockDevice::init\n" );
    return this->InitResult;
}

//------------------------------------------------------------------------------
int CFI_FlashBlockDevice::deinit()
{
    LOG_1( "CFI_FlashBlockDevice::deinit\n" );
    return this->InitResult;
}

//------------------------------------------------------------------------------
int CFI_FlashBlockDevice::read( void* buffer, bd_addr_t addr, bd_size_t size )
{
    HalLock Lock( this->HAL_Device );

    LOG_1( "CFI_FlashBlockDevice::read --> buffer = {:08x} addr = {:08x} size = {}\n", buffer, addr, size );

    if ( !is_valid_read( addr, size ) )
    {
        LOG_0( "CFI_FlashBlockDevice::read --> invalid parameter addr = {:08x} size = {}\n", addr, size );
        return ERROR_PARAMETER;
    }

    if ( BD_ERROR_OK != this->InitResult )
    {
        LOG_0( "CFI_FlashBlockDevice::read --> not init\n" );
        return ERROR_NO_INIT;
    }

    auto       Dest = static_cast< unsigned char* >( buffer );
    const auto Src  = this->NOR_FlashChip.cbegin() + addr;
    const auto Itr  = std::copy_n( Src, size, Dest ); 

    if ( Itr != Dest + size )
    {
       LOG_0( "CFI_FlashBlockDevice::read --> bad count {} expected {}\n", Itr - Dest, size );
       return ERROR_READBLOCKS;
    }

//=    BufferView v( Dest, size );
//=    v.Dump();

    return BD_ERROR_OK;
}

//------------------------------------------------------------------------------
int CFI_FlashBlockDevice::program( const void* buffer, bd_addr_t addr, bd_size_t size )
{
    HalLock Lock( this->HAL_Device );

    LOG_1( "CFI_FlashBlockDevice::program --> buffer = {:08x} addr = {:08x} size = {}\n", buffer, addr, size );

    if ( !is_valid_program( addr, size ) )
    {
        LOG_0( "CFI_FlashBlockDevice::program --> invalid parameter addr = {:08x} size = {}\n", addr, size );
        return ERROR_PARAMETER;
    }

    if ( BD_ERROR_OK != this->InitResult )
    {
        LOG_0( "CFI_FlashBlockDevice::program --> not init\n" );
        return ERROR_NO_INIT;
    }

    //Claim the NOR controller
    if ( Lock.SetBusy() )
    {
        LOG_0( "CFI_FlashBlockDevice::program --> already busy\n" );
        return ERROR_WOULD_BLOCK;
    }

    // Get block count
    const auto BlockCnt( size / this->NOR_FlashChip.WriteSize() );
    auto       Offset  ( static_cast< uint32_t >( addr ) );
    const auto Dest    ( static_cast< const unsigned char* >( buffer ) );
    BufferView Slice   ( Dest, this->NOR_FlashChip.WriteSize() );

    for ( bd_size_t Block = 0u; Block < BlockCnt; ++Block )
    {
        const auto WriteResult = this->NOR_FlashChip.WriteBuffer( Offset, Slice );

        if ( Chips::IS29GL128::WriteOperationStatus::Done != WriteResult )
        {
            this->NOR_FlashChip.ReturnToReadMode();

            LOG_0( "CFI_FlashBlockDevice::program --> failed\n" );
            return ERROR_WRITE;
        }

        Slice.MoveUp( this->NOR_FlashChip.WriteSize() );
        Offset += this->NOR_FlashChip.WriteSize();
    }

    this->NOR_FlashChip.ReturnToReadMode();

    return BD_ERROR_OK;
}

//------------------------------------------------------------------------------
int CFI_FlashBlockDevice::erase( bd_addr_t addr, bd_size_t size )
{
    HalLock Lock( this->HAL_Device );

    LOG_1( "CFI_FlashBlockDevice::erase --> addr = {:08x} size = {}\n", addr, size );

    if ( !is_valid_erase( addr, size ) )
    {
        const bool StartAligned = addr % get_erase_size(addr) == 0;
        const bool EndAligned   = (addr + size) % get_erase_size(addr + size - 1) == 0;
        const bool Fit          =  addr + size <= this->size();
        LOG_0( "CFI_FlashBlockDevice::erase --> invalid parameter S={} E={}, F={}\n", StartAligned, EndAligned, Fit );
        return ERROR_PARAMETER;
    }

    if ( BD_ERROR_OK != this->InitResult )
    {
        LOG_0( "CFI_FlashBlockDevice::erase --> not int\n" );
        return ERROR_NO_INIT;
    }

    //Claim the NOR controller
    if ( Lock.SetBusy() )
    {
        LOG_0( "CFI_FlashBlockDevice::erase --> already busy\n" );
        return ERROR_WOULD_BLOCK;
    }

    // Get block count
    const auto BlockCnt( size / this->NOR_FlashChip.EraseSize() );
    auto       Offset  ( static_cast< uint32_t >( addr ) );

    for ( bd_size_t Block = 0u; Block < BlockCnt; ++Block )
    {
        const auto EraseResult = this->NOR_FlashChip.EraseSector( Offset );

        if ( Chips::IS29GL128::WriteOperationStatus::Done != EraseResult )
        {
            this->NOR_FlashChip.ReturnToReadMode();

            LOG_0( "CFI_FlashBlockDevice::erase --> failed\n" );
            return ERROR_ERASEBLOCKS;
        }

        Offset += this->NOR_FlashChip.EraseSize();
    }

    this->NOR_FlashChip.ReturnToReadMode();

    return BD_ERROR_OK;
}

//------------------------------------------------------------------------------
bd_size_t CFI_FlashBlockDevice::get_read_size() const
{
    LOG_2( "CFI_FlashBlockDevice::read_size --> {}\n", this->NOR_FlashChip.WriteSize() );
    return this->NOR_FlashChip.WriteSize();
}

//------------------------------------------------------------------------------
bd_size_t CFI_FlashBlockDevice::get_program_size() const
{
    LOG_2( "CFI_FlashBlockDevice::program_size --> {}\n", this->NOR_FlashChip.WriteSize() );
    return this->NOR_FlashChip.WriteSize();
}

//------------------------------------------------------------------------------
bd_size_t CFI_FlashBlockDevice::get_erase_size() const
{
    LOG_2( "CFI_FlashBlockDevice::erase_size --> {}\n", this->NOR_FlashChip.EraseSize() );
    return this->NOR_FlashChip.EraseSize();
}

//------------------------------------------------------------------------------
bd_size_t CFI_FlashBlockDevice::get_erase_size( bd_addr_t ) const
{
    LOG_2( "CFI_FlashBlockDevice::erase_size --> {}\n", this->NOR_FlashChip.EraseSize() );
    return this->NOR_FlashChip.EraseSize();
}

//------------------------------------------------------------------------------
bd_size_t CFI_FlashBlockDevice::size() const
{
    LOG_2( "CFI_FlashBlockDevice::size --> {}\n", this->NOR_FlashChip.size() );
    return this->NOR_FlashChip.size();
}

//------------------------------------------------------------------------------
const char* CFI_FlashBlockDevice::get_type() const
{
    return "NOR_FLASH_8";
}

//------------------------------------------------------------------------------
void CFI_FlashBlockDevice::lock()
{
    LOG_0( "CFI_FlashBlockDevice::lock\n" );
}

//------------------------------------------------------------------------------
void CFI_FlashBlockDevice::unlock()
{
    LOG_0( "CFI_FlashBlockDevice::unlock\n" );
}
1 Like