nRF5340: FCB(Flash Circular Buffer) on external flash

Hi Support Team,

I want to use an external flash(2M byte) to store the sensor data in a 'circular overwrite' way. I now tried it with the FCB(Flash Circular Buffer) on nRF5340-DK.

On the external flash, I use the first sector as a control block and the other 511 sectors for FCB storage.

Could I ask several questions about it:
1. When init the FCB, the user needs to set fcb.f_sector_cnt and fcb.f_scratch_cnt. The f_scratch_cnt was described as scratch space for garbage collecting when FCB fills up. If my f_sector_cnt is 511, what's the suggested value of f_scratch_cnt? Will this value affect the rotation behavior of FCB? I hope to rotate only when reaching the end of the data sectors(The oldest sector will be overwritten only when there is no free sector, in a strict FIFO way).

2. In struct fcb, both f_sector_cnt and f_scratch_cnt are uint8_t type, so the maximum capacity is 1M byte(255 sectors, 4K byte per sector). I changed the f_sector_cnt from uint8_t to uint16_t to support 511 sectors. By now, the fcb_append works well, but I don't know if the fcb_rotate is also ok.

3. The fcb.f_sectors = fcb_instance_sectors, was defined as a big array and initialized as:

    #define SPI_FLASH_SECTOR_SIZE 4096
    #define FCB_SECTOR_COUNT 510
    #define FCB_SCRATCH_SECTOR_COUNT 10
    
    memset(fcb_instance_sectors, 0, sizeof(fcb_instance_sectors));
    for (int i = 0; i < FCB_SECTOR_COUNT; i++) {
        fcb_instance_sectors[i].fs_off = i * SPI_FLASH_SECTOR_SIZE;
        fcb_instance_sectors[i].fs_size = SPI_FLASH_SECTOR_SIZE;
    }

The big array consumed a lot of stack memory, is there any other better way to define the FCB sectors?

Thank you very much.

Best regards,
Yanpeng Wu

Parents Reply Children
  • Update: Currently I can init FCB and append logs successfully, but can't read the correct log entries from the flash.
    Could you help me have a look at my code? Thank you very much.

    FCB Init:

    #define SPI_FLASH_SECTOR_SIZE        		4096
    #define FCB_VERSION							1
    #define FCB_SECTOR_COUNT					511
    #define FCB_SCRATCH_SECTOR_COUNT			11
    
    // FCB sectors array, need to be inited per sector
    static struct flash_sector fcb_instance_sectors[FCB_SECTOR_COUNT];
    
    int fcb_init_instance()
    {
    	struct fcb *fcb = &fcb_instance;
    	const struct flash_area *log_fa = NULL;
    	const struct flash_parameters *flash_params = NULL;
    	int rc;
    
    	memset(fcb, 0, sizeof(*fcb));
    	
    	// Init the array of fcb_instance_sectors, need to init it per sector
    	memset(fcb_instance_sectors, 0, sizeof(fcb_instance_sectors));
    	for (int i = 0; i < FCB_SECTOR_COUNT; i++) {
    		fcb_instance_sectors[i].fs_off = i * SPI_FLASH_SECTOR_SIZE;
    		fcb_instance_sectors[i].fs_size = SPI_FLASH_SECTOR_SIZE;
    	}
    
    	// 1. Open flash area log_data_parti
        rc = flash_area_open(FLASH_AREA_ID(log_data_parti), &log_fa);
        if (rc) {
            LOG_ERR("[fcb_init_instance] Failed to open log partition: %d", rc);
            return -ENODEV;
        }
    
    	flash_params = flash_get_parameters(log_fa->fa_dev);
    	if (!flash_params) {
    		LOG_ERR("[fcb_init_instance] Failed to get flash parameters\n");
    		return -ENODEV;
    	}
    
    	//2. Must erase the fcb->f_sectors before fcb_init, otherwise the f_magic checking will be failed!
    	rc = flash_area_erase(log_fa, 0, log_fa->fa_size);
    	if (rc != 0) {
    		LOG_ERR("[fcb_init_instance] Failed to erase Flash area, error %d", rc);
    		flash_area_close(log_fa);
    		return rc;
    	} 
    
    	// 3. int FCB instance on log_data_parti
    	fcb->f_magic = 0x1a1a1a1a;
        fcb->f_version = FCB_VERSION;
        fcb->f_sector_cnt = FCB_SECTOR_COUNT; 
    	fcb->f_scratch_cnt = FCB_SCRATCH_SECTOR_COUNT;
        fcb->f_sectors = fcb_instance_sectors;
    	fcb->f_align = flash_params->write_block_size;
    
        rc = fcb_init(log_fa->fa_id, fcb);
        if (rc) {
            LOG_ERR("[fcb_init_instance] FCB init failed with error %d", rc);
            return rc;
        } else {
    		LOG_WRN("[fcb_init_instance] FCB init OK! %d", rc);
    	}
    
    	flash_area_close(log_fa);
    
    	return 0;
    }

    Append entry to FCB:

    int fcb_append_log_entry(T_log_entry* log_data, size_t len)
    {
        struct fcb_entry entry_descriptor;
        int rc;
    
    	memset(&entry_descriptor, 0, sizeof(struct fcb_entry));
    	rc = fcb_append(&fcb_instance, len, &entry_descriptor);
        if (rc) {
    		// Do rotation in case of reaching the end of partition
    		if (rc == -ENOSPC) {
                LOG_ERR("[fcb_append_log_entry] No space left, rotating FCB and try again");
                rc = fcb_rotate(&fcb_instance);
                if (rc) {
                    LOG_ERR("[fcb_append_log_entry] FCB rotate failed with error %d", rc);
                    return rc;
                }
    
                // Retry to append
                rc = fcb_append(&fcb_instance, len, &entry_descriptor);
                if (rc) {
                    LOG_ERR("[fcb_append_log_entry] FCB append retry failed after rotate, error %d", rc);
                    return rc;
                }
            } else {
    			LOG_ERR("[fcb_append_log_entry] Failed to append data: %d", rc);
                return rc;
            }
        }
    
        rc = flash_area_write(fcb_instance.fap, entry_descriptor.fe_data_off, log_data, len);
        if (rc) {
            LOG_ERR("[fcb_append_log_entry] Failed to write to FCB: %d", rc);
            fcb_append_finish(&fcb_instance, &entry_descriptor);
            return rc;
        }	
    
    	rc = fcb_append_finish(&fcb_instance, &entry_descriptor);
    	if (rc) {
            LOG_ERR("[fcb_append_log_entry] FCB append finish failed with error %d", rc);
            return rc;
        }
    
    	return 0;	
    }

    Read and print entries from a sector:

    void fcb_print_Sector(uint16_t sector_no)
    {
    	struct fcb_entry entry_desc, first_entry_desc;
    	T_log_entry log_data;
    	int rc;
    
        memset(&entry_desc, 0, sizeof(entry_desc));
    	entry_desc.fe_sector = &fcb_instance_sectors[sector_no];
    	// Start with the first entry in this sector
        rc = fcb_getnext(&fcb_instance, &entry_desc);
    	memcpy(&first_entry_desc, &entry_desc, sizeof(struct fcb_entry));
    
        while(rc == 0) {
    		if(entry_desc.fe_sector == first_entry_desc.fe_sector){
    			rc = flash_area_read(fcb_instance.fap, entry_desc.fe_data_off, &log_data, entry_desc.fe_data_len);
    			if (rc) {
    				LOG_ERR("[fcb_print_Sector] Failed to read entry with error %d", rc);
    				continue;
    			} else{
    				LOG_INF("[fcb_print_Sector] type=%d, value=%4.6f, timestamp=%08u", 
    								log_data.type, log_data.value, log_data.timestamp);
    			}
    
    			// Get the next entry
    			rc = fcb_getnext(&fcb_instance, &entry_desc);
    		} else {
    			break;
    		}
        } 	
    }

Related