当前文档版本为 v0.5.0,您可以访问当前页面的 开发中 版本以获取最近可能的更新。

Driver: Flash Shell

1 功能概述

本文主要介绍PAN1080 EVB板flash shell演示,可以通过类shell命令来实现flash api的相关操作。

2 环境要求

  • PAN1080 EVB 一块

  • USB-TypeC 线一条(用于供电和查看串口打印 Log)

  • 硬件接线:

    • 使用USB线,将 PC USB 与 EVB USB-TypeC(USB->UART)相连

    • 使用杜邦线将 EVB 上的:

      • TX0 与 P00 相连

      • RX0 与 P01 相连

  • PC 软件: 串口调试助手(UartAssist)或终端工具(SecureCRT),波特率921600

3 编译和烧录

项目位置:zephyr\samples_panchip\drivers\flash_shell

使用 ZAL 工具可以对其进行编译、烧录、打开 VS Code 调试等操作。关于 ZAL 工具的详细介绍请参考:Zephyr APP Launcher 工具介绍

4 演示说明

通过串口命令输入flash_cmd命令来实现flash api操作,通过连续两个tab会自动补全命令,例如输入flash_cmd后,则shell会输出flash_cmd相关细指令。

详情参考5.1 flash_shell支持的命令5.2 flash_cmd使用以及命令解析.

5 开发说明

5.1 flash_shell支持的命令

flash_cmd - Flash related commands.
Subcommands:
  erase             :<off> <len>
                     Erase <len> bytes from device offset <off>, subject to
                     hardware page limitations.
  page_count        :Print the number of pages on the flash device.
  page_erase        :<page> [num]
                     Erase [num] pages (default 1), starting at page <page>.
  page_layout       :[start_page] [end_page]
                     Print layout of flash pages in the range [start_page,
                     end_page], which is inclusive. By default, all pages are
                     printed.
  page_read         :<page> <len> OR <page> <off> <len>
                     Read <len> bytes from given page, starting at page offset
                     <off>,or offset 0 if not given. No checks are made that
                     bytes read are all within the page.
  page_write        :<page> <off> <byte1> [... byteN]
                     Write given bytes to given page, starting at page offset
                     <off>. No checks are made that the bytes all fall within
                     the page. Pages must be erased before they can be written.
  read              :<off> <len>
                     Read <len> bytes from device offset <off>.
  set_device        :<device_name>
                     Set flash device by name. If a flash device was not found,
                     this command must be run first to bind a device to this
                     module.
  write             :<off> <byte1> [... byteN]
                     Write given bytes, starting at device offset <off>.
                     Pages must be erased before they can be written.
  write_block_size  :Print the device's write block size. This is the smallest
                     amount of data which may be written to the device, in
                     bytes.
  write_unaligned   :<off> <byte1> [... byteN]
                     Write given bytes, starting at device offset <off>.
                     Being unaligned, affected memory areas are backed up,
                     erased, protected and then overwritten.
                     This command is designed to test writing to large flash
                     pages.
  write_pattern     :<off> <len>
                     Writes a pattern of (0x00 0x01 .. 0xFF 0x00 ..) of
                     length<len> at the offset <off>.
                     Unaligned writing is used, i.e. protection and erasing are
                     automated.This command is designed to test writing to large
                     flash pages.

5.2 flash_cmd使用以及命令解析

5.2.1 使用flash_cmd read 读取地址0x1000处16个字节

uart:~$ flash_cmd read 0x1000 0x10
53 46 1d 68 | af 68 fb 68 |00 2b 04 d0 | 00 21 28 00

5.2.2 使用flash_cmd write 写入地址0xc8000处4个字节

uart:~$ flash_cmd read 0xc8000 0x4					//从800K地址处读取4个字节,因为该地址正常没写入过,默认为全ff
ff ff ff ff
uart:~$ flash_cmd write 0xc8000 0x1 0x2 0x3 0x4		//从800K地址处写入4个字节:0x1 0x2 0x3 0x4
Reading back written bytes:
01 02 03 04
uart:~$ flash_cmd read 0xc8000 0x4					//从800K地址处读取4个字节:0x1 0x2 0x3 0x4
01 02 03 04

5.2.3 使用flash_cmd erase 擦除地址0xc8000处4K字节

uart:~$ flash_cmd erase 0xc8000 0x1000				//从800K地址处擦除4K字节,默认擦写必须4K对齐,这个可以使用flash_cmd write_block_size
													//获得,该值默认在dts中定义。
uart:~$ flash_cmd read 0xc8000 0x4
ff ff ff ff

5.2.4 flash_cmd read命令代码解析

flash_cmd所有的命令均在下面,从SHELL_CMD_ARG(read, NULL, READ_HELP, cmd_read, 3, 0)我们可以知道read命令对应的实现函数,在cmd_read中。

SHELL_STATIC_SUBCMD_SET_CREATE(sub_flash,
	/* Alphabetically sorted to ensure correct Tab autocompletion. */
	SHELL_CMD_ARG(erase,	NULL,	ERASE_HELP,	cmd_erase, 3, 0),
#ifdef CONFIG_FLASH_PAGE_LAYOUT
	SHELL_CMD_ARG(page_count,  NULL, PAGE_COUNT_HELP, cmd_page_count, 1, 0),
	SHELL_CMD_ARG(page_erase, NULL, PAGE_ERASE_HELP, cmd_page_erase, 2, 1),
	SHELL_CMD_ARG(page_layout, NULL, PAGE_LAYOUT_HELP,
		      cmd_page_layout, 1, 2),
	SHELL_CMD_ARG(page_read,   NULL, PAGE_READ_HELP,  cmd_page_read, 3, 1),
	SHELL_CMD_ARG(page_write,  NULL, PAGE_WRITE_HELP,
		      cmd_page_write, 3, 255),
#endif
	SHELL_CMD_ARG(read,		NULL,	READ_HELP,	cmd_read, 3, 0),
	SHELL_CMD_ARG(set_device, NULL, SET_DEV_HELP, cmd_set_dev, 2, 0),
	SHELL_CMD_ARG(write,	  NULL,	WRITE_HELP,	cmd_write, 3, 255),
	SHELL_CMD_ARG(write_block_size,	NULL,	WRITE_BLOCK_SIZE_HELP,
						    cmd_write_block_size, 1, 0),
	SHELL_CMD_ARG(write_unaligned,  NULL,	WRITE_UNALIGNED_HELP,
							cmd_write_unaligned, 3, 255),
	SHELL_CMD_ARG(write_pattern,	NULL,	WRITE_PATTERN_HELP,
							cmd_write_pattern, 3, 255),
	SHELL_SUBCMD_SET_END /* Array terminated. */
);

SHELL_CMD_REGISTER(flash_cmd, &sub_flash, "Flash related commands.", NULL);

5.3 Flash API使用参考

5.3.1 flash_read API参考

cmd_read也是调用do_read函数,对于flash_read函数我们可以参考do_read函数:

/* Read bytes, dumping contents to console and printing on error. */
static int do_read(const struct shell *shell, off_t offset, size_t len)
{
	uint8_t buf[64];
	int ret;

	while (len > sizeof(buf)) {
		ret = flash_read(flash_device, offset, buf, sizeof(buf));
		if (ret) {
			goto err_read;
		}
		dump_buffer(shell, buf, sizeof(buf));
		len -= sizeof(buf);
		offset += sizeof(buf);
	}
	ret = flash_read(flash_device, offset, buf, len);
	if (ret) {
		goto err_read;
	}
	dump_buffer(shell, buf, len);
	return 0;

 err_read:
	PR_ERROR(shell, "flash_read error: %d\n", ret);
	return ret;
}

5.3.2 flash_write API参考

/* Write bytes and printing on error. */
static int do_write(const struct shell *shell, off_t offset, uint8_t *buf,
		    size_t len, bool read_back)
{
	int ret;

	ret = flash_write(flash_device, offset, buf, len);
	if (ret) {
		PR_ERROR(shell, "flash_write failed (err:%d).\n", ret);
		return ret;
	}

	if (read_back) {
		PR_SHELL(shell, "Reading back written bytes:\n");
		ret = do_read(shell, offset, len);
	}
	return ret;
}

5.3.3 flash_erase API参考

/* Erase area and printing on error. */
static int do_erase(const struct shell *shell, off_t offset, size_t size)
{
	int ret;

	ret = flash_erase(flash_device, offset, size);
	if (ret) {
		PR_ERROR(shell, "flash_erase failed (err:%d).\n", ret);
		return ret;
	}

	return ret;
}

5.3.4 do_write_unaligned 非对齐地址写参考实现

static int do_write_unaligned(const struct shell *shell, off_t offset, uint8_t *buf,
		    size_t len, bool read_back)
{
	int ret = 0;
	size_t page_size = flash_get_write_block_size(flash_device);
	size_t size_before = offset % page_size;
	size_t size_after = page_size - ((size_before + len) % page_size);
	size_t aligned_size = size_before + len + size_after;
	off_t  start_page = offset - size_before;
	off_t  last_page = start_page + aligned_size - page_size;
	bool single_page_write = (size_before + len < page_size);

	char *before_data;
	char *after_data;

	if (0 == size_before && 0 == size_after) {
		/* Aligned write */
		flash_erase(flash_device, offset, len);
		flash_write(flash_device, offset, buf, len);

		return 0;
	}

	before_data = k_malloc(page_size);
	after_data = k_malloc(page_size);

	if (!before_data || !after_data) {
		PR_ERROR(shell, "No heap memory for flash manipulation\n");
		ret = -ENOMEM;
		goto free_buffers;
	}

	/* Stash useful data from the pages that will be affected. */
	if (single_page_write) {
		/* Read old data before new data is written. */
		if (size_before) {
			flash_read(flash_device, start_page, before_data, size_before);
		}
		/* Fill the with new data. */
		memcpy(before_data + size_before, buf, len);
		/* Fill the last part of old data. */
		if (size_after) {
			flash_read(flash_device, offset + len,
					before_data + size_before + len,
					size_after);
		}
	} else {
		/* Multipage write, different start and end pages. */
		if (size_before) {
			flash_read(flash_device, start_page, before_data,
					size_before);
			/* Fill the rest with new data. */
			memcpy(before_data + size_before, buf,
			       page_size - size_before);
		}
		if (size_after) {
			/* Copy ending part of new data. */
			memcpy((void *)after_data,
			       (void *)(buf + len -
				   ((len + size_before) % page_size)),
			       page_size - size_after);
			/* Copy ending part of flash page. */
			flash_read(flash_device, offset + len,
					after_data + (page_size - size_after),
					size_after);
		}
	}

	/* Erase all the pages that overlap with new data. */
	flash_erase(flash_device, start_page, aligned_size);

	/* Write stashed and new data. */
	if (single_page_write || size_before > 0) {
		/* Write first page if available. */
		flash_write(flash_device, start_page, before_data,
				 page_size);
	}
	if (!single_page_write) {
		size_t middle_data_len = aligned_size;
		off_t middle_page_start = start_page;
		off_t data_offset = (off_t)buf;

		/* Write the middle bit if available */
		if (size_before > 0) {
			middle_page_start += page_size;
			middle_data_len -= page_size;
			data_offset += (page_size - size_before);
		}
		if (size_after > 0) {
			middle_data_len -= page_size;
		}
		if (middle_data_len > 0) {
			flash_write(flash_device, middle_page_start,
					 (const void *)data_offset,
					 middle_data_len);
		}

		/* Write the last page if needed. */
		if (size_after > 0) {
			flash_write(flash_device, last_page, after_data,
					 page_size);
		}
	}

	if (read_back) {
		PR_SHELL(shell, "Reading back written bytes:\n");
		ret = do_read(shell, offset, len);
	}

free_buffers:
	k_free(before_data);
	k_free(after_data);

	return ret;
}

6 RAM/Flash资源使用情况

Memory region         Used Size  Region Size  %age Used
FLASH:       69208 B      1020 KB      6.63%
SRAM:       36384 B        64 KB     55.52%
IDT_LIST:          0 GB         2 KB      0.00%