Hi Daniel,
As far as i know, c40's 1 page has 32 bytes, It can write up to 128 bytes at most, and read 32 bytes at most. For example, i want to write 200 bytes(maybe more, any bytes more than write once) to fit the LittleFS‘s write API, because filesystem needs to write more than 128 bytes. Here is the code I wrote for adapting the c40 API to LittleFS (refer to <lfs_port.c>). In this code, I want to use the seconde half of DFlash as my filesystem.
<lfs_port.c>
#include "lfs_port.h"
#include "C40_Ip.h"
#include "C40_Ip_Cfg.h"
// Define buffers with aligned attribute to ensure alignment
__attribute__((aligned(8))) static uint8_t m_lfs_read_buf[LFS_READ_SIZE];
__attribute__((aligned(8))) static uint8_t m_lfs_prog_buf[LFS_PROG_SIZE];
__attribute__((aligned(8))) static uint8_t m_lfs_lookahead_buf[LFS_LOOKAHEAD_SIZE];
// Data buffer for intermediate read/write operations (extra space added for alignment and ECC)
__attribute__((aligned(8))) static uint8_t m_lfs_buf[SECTOR_SIZE + 16];
// Define global lfs objects
lfs_t lfs;
lfs_file_t file;
/*
* @brief Read data from a specific area within a block
* @Param [in] lfs_config format parameters
* @Param [in] block Logical block index, starting from 0
* @Param [in] off Offset within block, must be divisible by read_size
* @Param [out] Output buffer for read data
* @Param [in] size Number of bytes to read, must be divisible by read_size, lfs ensures no cross-block reads
* @retval 0 Success, < 0 Error code
*/
int lfs_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) {
C40_Ip_StatusType C40Status;
uint32_t addr;
uint8_t *p_src, *p_dst;
// Validate parameters
if (block >= c->block_count) {
logInfo("Read error: Invalid block number %d\n", block);
return LFS_ERR_IO;
}
if (off + size > c->block_size) {
logInfo("Read error: Out of block bounds\n");
return LFS_ERR_IO;
}
if (buffer == NULL) {
logInfo("Read error: NULL buffer\n");
return LFS_ERR_IO;
}
// Calculate block start address
addr = FILESYSTEM_START_ADDRESS + block * c->block_size;
logInfo("Reading from block %d, offset %d, size %d bytes\n", block, off, size);
// First read entire sector to intermediate buffer to ensure proper alignment handling
C40Status = C40_Ip_Read(addr, c->block_size, m_lfs_buf);
if (C40_IP_STATUS_SUCCESS != C40Status) {
logInfo("Read operation failed with status %d\n", C40Status);
return LFS_ERR_IO;
}
// Copy required portion from buffer to target buffer
p_src=(uint8_t*)m_lfs_buf + off;
p_dst = (uint8_t*)buffer;
memcpy(p_dst, p_src, size);
logInfo("Read completed successfully\n");
return LFS_ERR_OK;
}
/*
* @brief Write data to a specific area within a block. The area must be erased first, can return LFS_ERR_CORRUPT if block is corrupted
* @Param [in] lfs_config format parameters
* @Param [in] block Logical block index, starting from 0
* @Param [in] off Offset within block, must be divisible by prog_size
* @Param [in] Buffer containing data to write
* @Param [in] size Number of bytes to write, must be divisible by read_size, lfs ensures no cross-block writes
* @retval 0 Success, < 0 Error code
*/
int lfs_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) {
C40_Ip_StatusType C40Status;
uint32_t addr;
uint32_t write_offset = 0;
const uint32_t MAX_WRITE_CHUNK = 128; // Set a reasonable chunk size limit
uint8_t *verify_buf = NULL;
// Validate parameters
if (block >= c->block_count) {
logInfo("Program error: Invalid block number %d\n", block);
return LFS_ERR_IO;
}
if (off + size > c->block_size) {
logInfo("Program error: Out of block bounds\n");
return LFS_ERR_IO;
}
if (buffer == NULL) {
logInfo("Program error: NULL buffer\n");
return LFS_ERR_IO;
}
// Calculate write address
addr = FILESYSTEM_START_ADDRESS + block * c->block_size + off;
// Check address and size alignment requirements (C40 requires 8-byte alignment)
if ((addr % 8) != 0) {
logInfo("Program error: Address not aligned to 8 bytes (0x%08X)\n", addr);
return LFS_ERR_IO;
}
if ((size % 8) != 0) {
logInfo("Program error: Size not aligned to 8 bytes (%d)\n", size);
return LFS_ERR_IO;
}
logInfo("Writing to address 0x%08X, size %d bytes\n", addr, size);
// Write data in chunks to support writing more than 32 bytes of data
while (write_offset < size) {
// Calculate current chunk size
uint32_t chunk_size = (size - write_offset) > MAX_WRITE_CHUNK ? MAX_WRITE_CHUNK : (size - write_offset);
uint32_t current_addr = addr + write_offset;
const uint8_t *current_buffer = (uint8_t*)buffer + write_offset;
// Ensure chunk_size is a multiple of 8
chunk_size = (chunk_size + 7) & ~7;
logInfo("Writing chunk at offset %d, size %d bytes\n", write_offset, chunk_size);
// Copy data to intermediate buffer
memset(m_lfs_buf, 0xFF, chunk_size); // Fill with 0xFF first (Flash default state)
memcpy(m_lfs_buf, current_buffer, chunk_size);
// Call C40_Ip interface to write data
C40Status = C40_Ip_MainInterfaceWrite(current_addr, chunk_size, m_lfs_buf, DFLASH_ID);
if (C40_IP_STATUS_SUCCESS != C40Status) {
logInfo("Write command failed with status %d\n", C40Status);
return LFS_ERR_IO;
}
// Wait for write operation to complete
do {
C40Status = C40_Ip_MainInterfaceWriteStatus();
if (C40_IP_STATUS_ERROR == C40Status) {
logInfo("Write operation error\n");
return LFS_ERR_IO;
} else if (C40_IP_STATUS_ERROR_TIMEOUT == C40Status) {
logInfo("Write operation timeout\n");
return LFS_ERR_IO;
}
// Small delay to avoid excessive CPU usage
for (volatile int i = 0; i < 100; i++);
} while (C40_IP_STATUS_BUSY == C40Status);
write_offset += chunk_size;
}
// Verify written data (read back and compare)
verify_buf = (uint8_t*)malloc(size);
if (verify_buf != NULL) {
C40Status = C40_Ip_Read(addr, size, verify_buf);
if (C40_IP_STATUS_SUCCESS == C40Status) {
if (memcmp(verify_buf, buffer, size) != 0) {
logInfo("Write verification failed\n");
free(verify_buf);
return LFS_ERR_CORRUPT;
} else {
logInfo("Write verification successful\n");
}
}
free(verify_buf);
} else {
logInfo("Warning: Memory allocation for verification failed\n");
}
logInfo("Write completed successfully\n");
return LFS_ERR_OK;
}
/*
* @brief Erase specified block. Blocks must be erased before writing, erased block state is undefined
* @Param [in] lfs_config format parameters
* @Param [in] block Logical block index to erase, starting from 0
* @retval 0 Success, < 0 Error code
*/
int lfs_erase(const struct lfs_config *c, lfs_block_t block) {
C40_Ip_StatusType C40Status;
C40_Ip_VirtualSectorsType erase_sector;
// Calculate physical sector number, assuming block 0 corresponds to start of physical sectors
// Since using half of DFLASH, need to ensure block is within valid range
if (block >= c->block_count) {
logInfo("Erase error: Invalid block number %d\n", block);
return LFS_ERR_IO;
}
// Calculate physical sector number, assuming mapping starts from FILESYSTEM_START_ADDRESS
erase_sector = ((FILESYSTEM_START_ADDRESS - DFLASH_START_ADDRESS) / SECTOR_SIZE) + block;
logInfo("Erasing block %d (sector %d)...\n", block, erase_sector);
// First check if sector is locked, if so try to unlock
C40Status = C40_Ip_GetLock(erase_sector);
if (C40_IP_STATUS_SECTOR_PROTECTED == C40Status) {
logInfo("Sector %d is protected, trying to unlock...\n", erase_sector);
C40Status = C40_Ip_ClearLock(erase_sector, DFLASH_ID);
if (C40_IP_STATUS_SUCCESS != C40Status) {
logInfo("Failed to unlock sector %d\n", erase_sector);
return LFS_ERR_IO;
}
} else if (C40_IP_STATUS_ERROR == C40Status) {
logInfo("Invalid sector %d\n", erase_sector);
return LFS_ERR_IO;
}
// Call C40_Ip interface to erase sector
C40Status = C40_Ip_MainInterfaceSectorErase(erase_sector, DFLASH_ID);
if (C40_IP_STATUS_SUCCESS != C40Status) {
logInfo("Erase command failed with status %d\n", C40Status);
return LFS_ERR_IO;
}
// Wait for erase operation to complete
do {
C40Status = C40_Ip_MainInterfaceSectorEraseStatus();
if (C40_IP_STATUS_ERROR == C40Status) {
logInfo("Erase operation error\n");
return LFS_ERR_IO;
} else if (C40_IP_STATUS_ERROR_TIMEOUT == C40Status) {
logInfo("Erase operation timeout\n");
return LFS_ERR_IO;
}
// Small delay to avoid excessive CPU usage
for (volatile int i = 0; i < 100; i++);
} while (C40_IP_STATUS_BUSY == C40Status);
logInfo("Block %d erased successfully\n", block);
return LFS_ERR_OK;
}
/*
* @brief Perform sync operation on underlying block device. If block device has no sync operation, can return directly
* @Param [in] lfs_config format parameters;
* @retval 0 Success, < 0 Error code
*/
int lfs_sync(const struct lfs_config *c)
{
return LFS_ERR_OK;
}
#if 1
// configuration of the filesystem is provided by this struct
// Define lfs configuration structure
const struct lfs_config cfg = {
// Block device operation functions
.read = lfs_read,
.prog = lfs_prog,
.erase = lfs_erase,
.sync = lfs_sync,
// Block device parameters
.read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.block_size = LFS_BLOCK_SIZE,
.block_count = 4,
.cache_size = LFS_CACHE_SIZE,
.lookahead_size = LFS_LOOKAHEAD_SIZE,
.block_cycles = 500,
// Add file_max limit to prevent overflow
.file_max = 32, // Explicitly limit maximum number of files to avoid LittleFS calculating too large values
.name_max = 31, // Limit maximum filename length
.attr_max = 32, // Limit maximum attribute value length
// Buffer references (ensure variable names match exactly)
.read_buffer = m_lfs_read_buf,
.prog_buffer = m_lfs_prog_buf,
.lookahead_buffer = m_lfs_lookahead_buf,
// Block device context, can be used to pass additional parameters
.context = NULL,
};
<lfs_port.h>
#ifndef __LFS_PORT_H__
#define __LFS_PORT_H__
#include "lfs.h"
#include "log.h"
#define DFLASH_ID (0U)
#define FILESYSTEM_START_ADDRESS (0x10010000)
#define DFLASH_START_ADDRESS (0x10000000)
#define SECTOR_SIZE (8192)
#define SECTOR_COUNT_MAX (16)
// Define buffer size constants
#define LFS_READ_SIZE (C40_IP_PAGE_SIZE)
#define LFS_PROG_SIZE (C40_IP_PAGE_SIZE * 4)
#define LFS_BLOCK_SIZE (SECTOR_SIZE)
#define LFS_BLOCK_COUNT (SECTOR_COUNT_MAX)
#define LFS_CACHE_SIZE (128)
#define LFS_LOOKAHEAD_SIZE (32)
#endif
Thank you for your reply.
Best regards.
Chenyuhang