File write gives -5 after file size reaches cache size

So I've been working with an nRF52832 on a custom board, and it has an external flash, mx25r1635f to be exact, and that is mounted with littleFS to use for data storage. It mounts, reads, writes all fine until I start working with bigger data, then it starts giving me error -5, which is I/O error, but I know my connections should be good and it happens on multiple PCBs. I started messing around with the firmware until I realized that the problem happened at a file size of exaclty 63, but would eventually like maybe 30 or 40 trys manage to write a couple of bytes again. This problem happens even in the sample code for file handling if I put it to append the pattern data to the file. After looking through the code trying to find something that could be related I saw that the cache-size attribute was 64, on a whim I changed to 128, and.. boom the file size could get to 127. So I'm thinking it's related to the cache filling up, but I could not for the life of me find a way to solve that, and not a single soul on the internet had the same problem as far as I can tell. The best solution I could find was to split my data into small chunks of 127bytes, but that's gonna be some confusing code for other people to read and will end up a lot more complex than it should be.

.overlay:

&pinctrl {
    spi1_default: spi1_default {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 14)>,
				<NRF_PSEL(SPIM_MOSI, 0, 13)>,
				<NRF_PSEL(SPIM_MISO, 0, 16)>;
        };
    };
	
	spi1_sleep: spi1_sleep {
        group1 {
            psels = <NRF_PSEL(SPIM_SCK, 0, 14)>,
				<NRF_PSEL(SPIM_MOSI, 0, 13)>,
				<NRF_PSEL(SPIM_MISO, 0, 16)>;
				low-power-enable;
        };
    };
}

&spi1 {
	compatible = "nordic,nrf-spi";
	status = "okay";
	cs-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>;
};

/delete-node/ &storage_partition;

/ {
	fstab {
		compatible = "zephyr,fstab";
		lfs1: lfs1 {
			compatible = "zephyr,fstab,littlefs";
			mount-point = "/lfs1";
			partition = <&lfs1_part>;
			automount;
			read-size = <16>;
			prog-size = <16>;
			cache-size = <128>;
			lookahead-size = <32>;
			block-cycles = <512>;
		};
	};
};

&mx25r16 {
	partitions {
		compatible = "fixed-partitions";
		#address-cells = <1>;
		#size-cells = <1>;

		lfs1_part: partition@0 {
			label = "storage";
			reg = <0x00000000 0x1000000>;
		};
	};
};


.dts file for the custom board:
 &spi1 {
	 compatible = "nordic,nrf-spi";
	 status = "okay";
	 pinctrl-0 = <&spi1_default>;
	 pinctrl-1 = <&spi1_sleep>;
	 pinctrl-names = "default", "sleep";
	 mx25r16: mx25r1635f@0 {
		compatible = "jedec,spi-nor";
		reg = <0>;
		spi-max-frequency = <8000000>;
		jedec-id = [c2 28 15];
		size = <DT_SIZE_M(16)>;
	};
 };
 
  &flash0 { 
	 partitions {
		 compatible = "fixed-partitions";
		 #address-cells = <1>;
		 #size-cells = <1>;
 
		 boot_partition: partition@0 {
			 label = "mcuboot";
			 reg = <0x00000000 0xc000>;
		 };
		 slot0_partition: partition@c000 {
			 label = "image-0";
			 reg = <0x0000C000 0x32000>;
		 };
		 slot1_partition: partition@3e000 {
			 label = "image-1";
			 reg = <0x0003E000 0x32000>;
		 };
		 scratch_partition: partition@70000 {
			 label = "image-scratch";
			 reg = <0x00070000 0xa000>;
		 };
		 storage_partition: partition@7a000 {
			 label = "storage";
			 reg = <0x0007a000 0x00006000>;
		 };
	 };
 };

the file is first created and written a small part on startup with the following code:

bool sirros_writeFile(const char * file_name, char* file_content, size_t file_size, off_t file_pos) {
	char caminho_arquivo[MAX_FILE_PATH_LEN];
	struct fs_file_t file;
	int ret;
	size_t written_size = 0;
	snprintf(caminho_arquivo, sizeof(caminho_arquivo), "%s%s", mp->mnt_point, file_name);
	fs_file_t_init(&file);
	ret = fs_open(&file, caminho_arquivo, FS_O_CREATE | FS_O_WRITE);
	if(ret < 0)
		return false;

	if (file_pos != 0)
		fs_seek(&file, file_pos, FS_SEEK_SET);

	if(file_size == 0)
		file_size = strlen(file_content);

	written_size = fs_write(&file, file_content, file_size);
	ret = fs_close(&file);
	return written_size == file_size;
}

.c code for writing the file

bool sirros_appendFile(const char * file_name, char* file_content, size_t file_size) {
	char caminho_arquivo[MAX_FILE_PATH_LEN];
	struct fs_file_t file;
	int ret;
	size_t written_size = 0;
	snprintf(caminho_arquivo, sizeof(caminho_arquivo), "%s%s", mp->mnt_point, file_name);
	fs_file_t_init(&file);
	ret = fs_open(&file, caminho_arquivo, FS_O_CREATE | FS_O_APPEND);
	if(ret < 0)
		return false;

	if(file_size == 0)
		file_size = strlen(file_content);

	ret = fs_sync(&file);
	if(ret < 0)
		return false;
	written_size = fs_write(&file, file_content, file_size);
	ret = fs_close(&file);
	return written_size == file_size;
}

If anyone knows how to solve the problem, I'd be very grateful.

Parents
  • Hi,

    Where do you call you write function from? Is it from main context or interrupt context?

    Can you provide a full minimal sample that can be used to reproduce this behavior?

    Which nRF Connect SDK version are you using?

    Best regards,
    Jørgen

  • The NCS version is v2.2.0, the sample you can use is this:

    /*
     * Copyright (c) 2022 Lukasz Majewski, DENX Software Engineering GmbH
     * Copyright (c) 2019 Peter Bigot Consulting, LLC
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    /* Sample which uses the filesystem API with littlefs */
    
    #include <stdio.h>
    
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/fs/fs.h>
    #include <zephyr/fs/littlefs.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/storage/flash_map.h>
    
    LOG_MODULE_REGISTER(main);
    
    /* Matches LFS_NAME_MAX */
    #define MAX_PATH_LEN 255
    #define TEST_FILE_SIZE 547
    
    static uint8_t file_test_pattern[TEST_FILE_SIZE];
    static int lsdir(const char *path)
    {
    	int res;
    	struct fs_dir_t dirp;
    	static struct fs_dirent entry;
    
    	fs_dir_t_init(&dirp);
    
    	/* Verify fs_opendir() */
    	res = fs_opendir(&dirp, path);
    	if (res) {
    		LOG_ERR("Error opening dir %s [%d]\n", path, res);
    		return res;
    	}
    
    	LOG_PRINTK("\nListing dir %s ...\n", path);
    	for (;;) {
    		/* Verify fs_readdir() */
    		res = fs_readdir(&dirp, &entry);
    
    		/* entry.name[0] == 0 means end-of-dir */
    		if (res || entry.name[0] == 0) {
    			if (res < 0) {
    				LOG_ERR("Error reading dir [%d]\n", res);
    			}
    			break;
    		}
    
    		if (entry.type == FS_DIR_ENTRY_DIR) {
    			LOG_PRINTK("[DIR ] %s\n", entry.name);
    		} else {
    			LOG_PRINTK("[FILE] %s (size = %zu)\n",
    				   entry.name, entry.size);
    		}
    	}
    
    	/* Verify fs_closedir() */
    	fs_closedir(&dirp);
    
    	return res;
    }
    
    static int littlefs_increase_infile_value(char *fname)
    {
    	uint8_t boot_count = 0;
    	struct fs_file_t file;
    	int rc, ret;
    	fs_file_t_init(&file);
    	rc = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR);
    	if (rc < 0) {
    		LOG_ERR("FAIL: open %s: %d", fname, rc);
    		return rc;
    	}
    
    	rc = fs_read(&file, &boot_count, sizeof(boot_count));
    	if (rc < 0) {
    		LOG_ERR("FAIL: read %s: [rd:%d]", fname, rc);
    		goto out;
    	}
    	LOG_PRINTK("%s read count:%u (bytes: %d)\n", fname, boot_count, rc);
    
    	rc = fs_seek(&file, 0, FS_SEEK_SET);
    	if (rc < 0) {
    		LOG_ERR("FAIL: seek %s: %d", fname, rc);
    		goto out;
    	}
    
    	boot_count += 1;
    	rc = fs_write(&file, &boot_count, sizeof(boot_count));
    	if (rc < 0) {
    		LOG_ERR("FAIL: write %s: %d", fname, rc);
    		goto out;
    	}
    
    	LOG_PRINTK("%s write new boot count %u: [wr:%d]\n", fname,
    		   boot_count, rc);
    
     out:
    	ret = fs_close(&file);
    	if (ret < 0) {
    		LOG_ERR("FAIL: close %s: %d", fname, ret);
    		return ret;
    	}
    
    	return (rc < 0 ? rc : 0);
    }
    
    static void incr_pattern(uint8_t *p, uint16_t size, uint8_t inc)
    {
    	uint8_t fill = 0x55;
    
    	if (p[0] % 2 == 0) {
    		fill = 0xAA;
    	}
    
    	for (int i = 0; i < (size - 1); i++) {
    		if (i % 8 == 0) {
    			p[i] += inc;
    		} else {
    			p[i] = fill;
    		}
    	}
    
    	p[size - 1] += inc;
    }
    
    static void init_pattern(uint8_t *p, uint16_t size)
    {
    	uint8_t v = 0x1;
    
    	memset(p, 0x55, size);
    
    	for (int i = 0; i < size; i += 8) {
    		p[i] = v++;
    	}
    
    	p[size - 1] = 0xAA;
    }
    
    static void print_pattern(uint8_t *p, uint16_t size){
    	int i, j = size / 16, k;
    
    	for (k = 0, i = 0; k < j; i += 16, k++) {
    		LOG_PRINTK("%02x %02x %02x %02x %02x %02x %02x %02x ",
    			   p[i], p[i+1], p[i+2], p[i+3],
    			   p[i+4], p[i+5], p[i+6], p[i+7]);
    		LOG_PRINTK("%02x %02x %02x %02x %02x %02x %02x %02x\n",
    			   p[i+8], p[i+9], p[i+10], p[i+11],
    			   p[i+12], p[i+13], p[i+14], p[i+15]);
    
    		/* Mark 512B (sector) chunks of the test file */
    		if ((k + 1) % 32 == 0) {
    			LOG_PRINTK("\n");
    		}
    	}
    
    	for (; i < size; i++) {
    		LOG_PRINTK("%02x ", p[i]);
    	}
    
    	LOG_PRINTK("\n");
    }
    
    static int littlefs_binary_file_adj(char *fname)
    {
    	struct fs_dirent dirent;
    	struct fs_file_t file;
    	int rc, ret;
    
    	/*
    	 * Uncomment below line to force re-creation of the test pattern
    	 * file on the littlefs FS.
    	 */
    	/* fs_unlink(fname); */
    	fs_file_t_init(&file);
    	printf("iniciou 1\n");
    	rc = fs_open(&file, fname, FS_O_CREATE | FS_O_WRITE);
    	if (rc < 0) {
    		LOG_ERR("FAIL: open %s: %d", fname, rc);
    		return rc;
    	}
    	rc = fs_stat(fname, &dirent);
    	if (rc < 0) {
    		LOG_ERR("FAIL: stat %s: %d", fname, rc);
    		goto out;
    	}
    	LOG_PRINTK("%i ", dirent.size);
    
    	/* Check if the file exists - if not just write the pattern */
    	if (rc == 0 && dirent.type == FS_DIR_ENTRY_FILE && dirent.size == 0) {
    		LOG_INF("Test file: %s not found, create one!",
    			fname);
    		init_pattern(file_test_pattern, sizeof(file_test_pattern));
    	} else {
    		rc = fs_read(&file, file_test_pattern,
    			     sizeof(file_test_pattern));
    		if (rc < 0) {
    			LOG_ERR("FAIL: read %s: [rd:%d]",
    				fname, rc);
    			goto out;
    		}
    		incr_pattern(file_test_pattern, sizeof(file_test_pattern), 0x1);
    	}
    	printf("leu dados");
    
    	LOG_PRINTK("------ FILE: %s ------\n", fname);
    	/*print_pattern(file_test_pattern, sizeof(file_test_pattern));
    
    	rc = fs_seek(&file, 0, FS_SEEK_SET);
    	if (rc < 0) {
    		LOG_ERR("FAIL: seek %s: %d", fname, rc);
    		goto out;
    	}
    */
    	rc = fs_write(&file, file_test_pattern, sizeof(file_test_pattern));
    	if (rc < 0) {
    		LOG_ERR("FAIL: write %s: %d", fname, rc);
    	}
    	
    
     out:
    	ret = fs_close(&file);
    	if (ret < 0) {
    		LOG_ERR("FAIL: close %s: %d", fname, ret);
    		return ret;
    	}
    
    	return (rc < 0 ? rc : 0);
    }
    
    #ifdef CONFIG_APP_LITTLEFS_STORAGE_FLASH
    static int littlefs_flash_erase(unsigned int id)
    {
    	const struct flash_area *pfa;
    	int rc;
    
    	rc = flash_area_open(id, &pfa);
    	if (rc < 0) {
    		LOG_ERR("FAIL: unable to find flash area %u: %d\n",
    			id, rc);
    		return rc;
    	}
    
    	LOG_PRINTK("Area %u at 0x%x on %s for %u bytes\n",
    		   id, (unsigned int)pfa->fa_off, pfa->fa_dev->name,
    		   (unsigned int)pfa->fa_size);
    
    	/* Optional wipe flash contents */
    	if (IS_ENABLED(CONFIG_APP_WIPE_STORAGE)) {
    		rc = flash_area_erase(pfa, 0, pfa->fa_size);
    		LOG_ERR("Erasing flash area ... %d", rc);
    	}
    
    	flash_area_close(pfa);
    	return rc;
    }
    #define PARTITION_NODE DT_NODELABEL(lfs1)
    
    #if DT_NODE_EXISTS(PARTITION_NODE)
    FS_FSTAB_DECLARE_ENTRY(PARTITION_NODE);
    #else /* PARTITION_NODE */
    FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage);
    static struct fs_mount_t lfs_storage_mnt = {
    	.type = FS_LITTLEFS,
    	.fs_data = &storage,
    	.storage_dev = (void *)FIXED_PARTITION_ID(storage_partition),
    	.mnt_point = "/lfs",
    };
    #endif /* PARTITION_NODE */
    
    	struct fs_mount_t *mp =
    #if DT_NODE_EXISTS(PARTITION_NODE)
    		&FS_FSTAB_ENTRY(PARTITION_NODE)
    #else
    		&lfs_storage_mnt
    #endif
    		;
    
    static int littlefs_mount(struct fs_mount_t *mp)
    {
    	int rc;
    
    	rc = littlefs_flash_erase((uintptr_t)mp->storage_dev);
    	if (rc < 0) {
    		return rc;
    	}
    
    	/* Do not mount if auto-mount has been enabled */
    #if !DT_NODE_EXISTS(PARTITION_NODE) ||						\
    	!(FSTAB_ENTRY_DT_MOUNT_FLAGS(PARTITION_NODE) & FS_MOUNT_FLAG_AUTOMOUNT)
    	rc = fs_mount(mp);
    	if (rc < 0) {
    		LOG_PRINTK("FAIL: mount id %" PRIuPTR " at %s: %d\n",
    		       (uintptr_t)mp->storage_dev, mp->mnt_point, rc);
    		return rc;
    	}
    	LOG_PRINTK("%s mount: %d\n", mp->mnt_point, rc);
    #else
    	LOG_PRINTK("%s automounted\n", mp->mnt_point);
    #endif
    
    	return 0;
    }
    #endif /* CONFIG_APP_LITTLEFS_STORAGE_FLASH */
    
    #ifdef CONFIG_APP_LITTLEFS_STORAGE_BLK_SDMMC
    struct fs_littlefs lfsfs;
    static struct fs_mount_t __mp = {
    	.type = FS_LITTLEFS,
    	.fs_data = &lfsfs,
    	.flags = FS_MOUNT_FLAG_USE_DISK_ACCESS,
    };
    struct fs_mount_t *mp = &__mp;
    
    static int littlefs_mount(struct fs_mount_t *mp)
    {
    	static const char *disk_mount_pt = "/"CONFIG_SDMMC_VOLUME_NAME":";
    	static const char *disk_pdrv = CONFIG_SDMMC_VOLUME_NAME;
    
    	mp->storage_dev = (void *)disk_pdrv;
    	mp->mnt_point = disk_mount_pt;
    
    	return fs_mount(mp);
    }
    #endif /* CONFIG_APP_LITTLEFS_STORAGE_BLK_SDMMC */
    
    void main(void)
    {
    	char fname1[MAX_PATH_LEN];
    	char fname2[MAX_PATH_LEN];
    	struct fs_statvfs sbuf;
    	int rc;
    
    	LOG_PRINTK("Sample program to r/w files on littlefs\n");
    
    	rc = littlefs_mount(mp);
    	if (rc < 0) {
    		return;
    	}
    
    char* file_content;
    size_t file_size;
    	char parametros_arq[MAX_PATH_LEN];
    	struct fs_file_t file;
    	int ret;
      ssize_t read_size;
    	snprintf(parametros_arq, sizeof(parametros_arq), "%s/pattern.bin", mp->mnt_point);
    	fs_unlink(parametros_arq);
      fs_file_t_init(&file);
    
    	ret = fs_open(&file, &parametros_arq, FS_O_CREATE | FS_O_RDWR);
    	if (ret)
    		return 0;
    	read_size = fs_read(&file, file_content, file_size);
    	ret = fs_close(&file);
    
    
    	snprintf(fname1, sizeof(fname1), "%s/boot_count", mp->mnt_point);
    	snprintf(fname2, sizeof(fname2), "%s/pattern.bin", mp->mnt_point);
    
    	rc = fs_statvfs(mp->mnt_point, &sbuf);
    	if (rc < 0) {
    		LOG_PRINTK("FAIL: statvfs: %d\n", rc);
    		goto out;
    	}
    
    /*
    	LOG_PRINTK("%s: bsize = %lu ; frsize = %lu ;"
    		   " blocks = %lu ; bfree = %lu\n",
    		   mp->mnt_point,
    		   sbuf.f_bsize, sbuf.f_frsize,
    		   sbuf.f_blocks, sbuf.f_bfree);
    
    	rc = lsdir(mp->mnt_point);
    	if (rc < 0) {
    		LOG_PRINTK("FAIL: lsdir %s: %d\n", mp->mnt_point, rc);
    		goto out;
    	}
    */
    	rc = littlefs_increase_infile_value(fname1);
    	if (rc) {
    		goto out;
    	}
    
    	rc = littlefs_binary_file_adj(fname2);
    	if (rc) {
    		goto out;
    	}
    
    out:
    	rc = fs_unmount(mp);
    	LOG_PRINTK("%s unmount: %d\n", mp->mnt_point, rc);
    }
    


    prj.conf:
    CONFIG_JSON_LIBRARY=y
    CONFIG_CBPRINTF_FP_SUPPORT=y
    CONFIG_MAIN_STACK_SIZE=5000
    CONFIG_DEBUG=y
    CONFIG_CPLUSPLUS=y
    CONFIG_LIB_CPLUSPLUS=y
    CONFIG_NEWLIB_LIBC=y
    CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y
    
    CONFIG_CONSOLE=y
    CONFIG_USE_SEGGER_RTT=y
    CONFIG_RTT_CONSOLE=y
    
    
    CONFIG_LOG=y
    CONFIG_LOG_DEFAULT_LEVEL=4
    CONFIG_LOG_MODE_IMMEDIATE=y
    
    CONFIG_I2C=y
    CONFIG_SENSOR=y
    CONFIG_W1=y
    CONFIG_SPI=y
    
    CONFIG_FLASH=y
    CONFIG_FLASH_MAP=y
    CONFIG_FLASH_PAGE_LAYOUT=y
    CONFIG_MPU_ALLOW_FLASH_WRITE=y
    
    CONFIG_FILE_SYSTEM=y
    CONFIG_FILE_SYSTEM_LITTLEFS=y
    
    CONFIG_BT=y
    CONFIG_BT_SMP=y
    CONFIG_BT_SIGNING=y
    CONFIG_BT_PERIPHERAL=y
    CONFIG_BT_PRIVACY=n
    CONFIG_BT_ATT_PREPARE_COUNT=10
    CONFIG_BT_DEVICE_APPEARANCE=833
    CONFIG_BT_DEVICE_NAME_DYNAMIC=y
    CONFIG_BT_DEVICE_NAME_MAX=65
    

  • Hi,

    Looks like you got some suggestions on Discord and GitHub. Did you manage to resolve the issue with these pointers?

    Best regards,
    Jørgen

Reply Children
Related