Skip to content

Commit

Permalink
tests: flash: Add page_layout test for flash controllers
Browse files Browse the repository at this point in the history
Add test suite which tests writing to and erasing all pages,
of any size defined by the page layout.

If the test is built with MCUBOOT, the test will jump into the
application, then run write/erase/boundary tests on the
bootload partition. If MCUBOOT is not selected, the test will
target the slot1 partition instead, performing the same tests.

Signed-off-by: Bjarki Arge Andreasen <bjarki@arge-andreasen.me>
  • Loading branch information
bjarki-trackunit committed Jan 23, 2024
1 parent 2d150c5 commit be18b9d
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 0 deletions.
8 changes: 8 additions & 0 deletions tests/drivers/flash/page_layout/CMakeLists.txt
@@ -0,0 +1,8 @@
# Copyright (c) 2023 Bjarki Arge Andreasen
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(flash_page_layout)

target_sources(app PRIVATE src/main.c)
5 changes: 5 additions & 0 deletions tests/drivers/flash/page_layout/prj.conf
@@ -0,0 +1,5 @@
# Copyright (c) 2023 Bjarki Arge Andreasen
# SPDX-License-Identifier: Apache-2.0

CONFIG_FLASH=y
CONFIG_ZTEST=y
218 changes: 218 additions & 0 deletions tests/drivers/flash/page_layout/src/main.c
@@ -0,0 +1,218 @@
/*
* Copyright (c) 2023 Bjarki Arge Andreasen
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <string.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/devicetree.h>
#include <zephyr/ztest.h>
#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(test_flash);

#ifdef CONFIG_BOOTLOADER_MCUBOOT

#define TEST_FLASH_PART_NODE \
DT_NODELABEL(boot_partition)

#else

#define TEST_FLASH_PART_NODE \
DT_NODELABEL(slot1_partition)

#endif

#define TEST_FLASH_PART_OFFSET \
DT_REG_ADDR(TEST_FLASH_PART_NODE)

#define TEST_FLASH_PART_SIZE \
DT_REG_SIZE(TEST_FLASH_PART_NODE)

#define TEST_FLASH_PART_END_OFFSET \
(TEST_FLASH_PART_OFFSET + TEST_FLASH_PART_SIZE)

#define TEST_FLASH_CONTROLLER_NODE \
DT_MTD_FROM_FIXED_PARTITION(TEST_FLASH_PART_NODE)

static const struct device *flash_controller = DEVICE_DT_GET(TEST_FLASH_CONTROLLER_NODE);
static uint8_t test_write_block[128];
static uint8_t test_read_block[128];

static void test_flash_fill_test_write_block(void)
{
for (uint8_t i = 0; i < sizeof(test_write_block); i++) {
test_write_block[i] = i;
}
}

static void *test_flash_setup(void)
{
test_flash_fill_test_write_block();
return NULL;
}

static bool test_flash_mem_is_set_to(const uint8_t *data, uint8_t to, size_t size)
{
for (size_t i = 0; i < size; i++) {
if (data[i] != to) {
return false;
}
}

return true;
}

static bool test_flash_is_erased(off_t offset, size_t size)
{
static uint8_t test_erase_buffer[99];
const struct flash_parameters *parameters = flash_get_parameters(flash_controller);
size_t remaining;
size_t readsize;

while (offset < size) {
remaining = size - (size_t)offset;
readsize = MIN(sizeof(test_erase_buffer), remaining);

if (flash_read(flash_controller, offset, test_erase_buffer, readsize) < 0) {
return false;
}

if (!test_flash_mem_is_set_to(test_erase_buffer, parameters->erase_value,
readsize)) {
return false;
}

offset += (off_t)readsize;
}

return true;
}

static bool test_flash_verify_partition_is_erased(void)
{
return test_flash_is_erased(TEST_FLASH_PART_OFFSET, TEST_FLASH_PART_SIZE);
}

static int test_flash_erase_partition(void)
{
LOG_INF("Erasing section of size %zu at offset %zu controlled by %s",
TEST_FLASH_PART_SIZE,
TEST_FLASH_PART_OFFSET,
flash_controller->name);

return flash_erase(flash_controller, TEST_FLASH_PART_OFFSET,
TEST_FLASH_PART_SIZE);
}

static void test_flash_before(void *f)
{
ARG_UNUSED(f);
__ASSERT(test_flash_erase_partition() == 0,
"Failed to erase partition");
__ASSERT(test_flash_verify_partition_is_erased(),
"Failed to erase partition");
}

static void test_flash_write_block_at_offset(off_t offset, size_t size)
{
zassert_ok(flash_write(flash_controller, offset, test_write_block, size),
"Failed to write block at offset %zu, of size %zu", (size_t)offset, size);
zassert_ok(flash_read(flash_controller, offset, test_read_block, size),
"Failed to read block at offset %zu, of size %zu", (size_t)offset, size);
zassert_ok(memcmp(test_write_block, test_read_block, size),
"Failed to write block at offset %zu, of size %zu to page", (size_t)offset,
size);
}

static void test_flash_write_across_page_boundary(const struct flash_pages_info *info,
size_t write_block_size)
{
off_t page_boundary = info->start_offset;
uint32_t page0_index = info->index - 1;
uint32_t page1_index = info->index;
off_t cross_write_start_offset = page_boundary - (off_t)write_block_size;
size_t cross_write_size = write_block_size * 2;

LOG_INF("Writing across page boundary at %zu, between page index %u and %u",
(size_t)page_boundary,
page0_index,
page1_index);

test_flash_write_block_at_offset(cross_write_start_offset, cross_write_size);
}

static bool test_flash_write_across_page_boundaries(const struct flash_pages_info *info,
void *data)
{
size_t write_block_size = *((size_t *)data);

if (info->start_offset <= TEST_FLASH_PART_OFFSET) {
/* Not yet reached second page within partition */
return true;
}

if (info->start_offset >= TEST_FLASH_PART_END_OFFSET) {
/* Reached first page after partition end */
return false;
}

test_flash_write_across_page_boundary(info, write_block_size);
return true;
}

ZTEST(flash_page_layout, test_write_across_page_boundaries_in_part)
{
const struct flash_parameters *parameters = flash_get_parameters(flash_controller);
size_t write_block_size = parameters->write_block_size;

flash_page_foreach(flash_controller, test_flash_write_across_page_boundaries,
&write_block_size);
}

static void test_flash_erase_page(const struct flash_pages_info *info)
{
off_t page_offset = info->start_offset;
size_t page_size = info->size;
size_t page_index = info->index;

LOG_INF("Erasing page at %zu of size %zu with index %zu",
(size_t)page_offset, page_size, page_index);

zassert_ok(flash_erase(flash_controller, page_offset, page_size),
"Failed to erase page");

zassert_true(test_flash_is_erased(page_offset, page_size),
"Failed to erase page");
}

static bool test_flash_erase_pages(const struct flash_pages_info *info, void *data)
{
if (info->start_offset < TEST_FLASH_PART_OFFSET) {
/* Not yet reached first page within partition */
return true;
}

if (info->start_offset >= TEST_FLASH_PART_END_OFFSET) {
/* Reached first page after partition end */
return false;
}

test_flash_erase_page(info);
return true;
}

ZTEST(flash_page_layout, test_erase_single_pages_in_part)
{
const struct flash_parameters *parameters = flash_get_parameters(flash_controller);
size_t write_block_size = parameters->write_block_size;

flash_page_foreach(flash_controller, test_flash_write_across_page_boundaries,
&write_block_size);

flash_page_foreach(flash_controller, test_flash_erase_pages, NULL);
}

ZTEST_SUITE(flash_page_layout, NULL, test_flash_setup, test_flash_before, NULL, NULL);
4 changes: 4 additions & 0 deletions tests/drivers/flash/page_layout/sysbuild.conf
@@ -0,0 +1,4 @@
# Copyright (c) 2023 Bjarki Arge Andreasen
# SPDX-License-Identifier: Apache-2.0

SB_CONFIG_BOOTLOADER_MCUBOOT=y
17 changes: 17 additions & 0 deletions tests/drivers/flash/page_layout/testcase.yaml
@@ -0,0 +1,17 @@
# Copyright (c) 2023 Bjarki Arge Andreasen
# SPDX-License-Identifier: Apache-2.0

common:
sysbuild: true
tags:
- drivers
- flash
tests:
flash.page_layout.build_only:
harness: ztest
build_only: true
platform_allow:
- sam_v71_xult
- b_u585i_iot02a
- nrf9160dk_nrf9160
- nrf5340dk_nrf5340_cpuapp

0 comments on commit be18b9d

Please sign in to comment.