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" );
}