/* * Copyright (c) 2024 Ambiq Micro Inc. * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #define MSPI_BUS_NODE DT_ALIAS(mspi0) #define MSPI_FLASH_TEST_REGION_OFFSET 0x0 #define MSPI_FLASH_SECTOR_SIZE 4096 #define MSPI_FLASH_TEST_SIZE 3000 static const struct device *mspi_devices[] = { DT_FOREACH_CHILD_STATUS_OKAY_SEP(MSPI_BUS_NODE, DEVICE_DT_GET, (,)) }; static uint8_t expected[MSPI_FLASH_TEST_SIZE]; static uint8_t actual[MSPI_FLASH_TEST_SIZE]; static void prepare_test_pattern(uint32_t pattern_index, uint8_t *actualf, uint32_t len) { uint32_t *pui32TxPtr = (uint32_t *)actualf; uint8_t *pui8TxPtr = (uint8_t *)actualf; switch (pattern_index) { case 0: /* 0x5555AAAA */ for (uint32_t i = 0; i < len / 4; i++) { pui32TxPtr[i] = (0x5555AAAA); } break; case 1: /* 0xFFFF0000 */ for (uint32_t i = 0; i < len / 4; i++) { pui32TxPtr[i] = (0xFFFF0000); } break; case 2: /* walking */ for (uint32_t i = 0; i < len; i++) { pui8TxPtr[i] = 0x01 << (i % 8); } break; case 3: /* incremental from 1 */ for (uint32_t i = 0; i < len; i++) { pui8TxPtr[i] = ((i + 1) & 0xFF); } break; case 4: /* decremental from 0xff */ for (uint32_t i = 0; i < len; i++) { /* decrement starting from 0xff */ pui8TxPtr[i] = (0xff - i) & 0xFF; } break; default: /* incremental from 1 */ for (uint32_t i = 0; i < len; i++) { pui8TxPtr[i] = ((i + 1) & 0xFF); } break; } } static int test_multi_sector_rw(const struct device *flash_dev) { int rc = 0; const struct flash_pages_layout *layout = NULL; size_t layout_size = 0; size_t min_page_size = -1; size_t offs; TC_PRINT("\n===================================================================\n"); TC_PRINT("Perform test on multiple consequtive sectors on %s\n", flash_dev->name); TC_PRINT("\nTest 0: Get Flash page layout\n"); const struct flash_driver_api *api = flash_dev->api; api->page_layout(flash_dev, &layout, &layout_size); if (layout && layout_size) { TC_PRINT("----pages-------size----\n"); for (int i = 0; i < layout_size; ++i) { TC_PRINT("%2d: 0x%-8X 0x%-8x\n", i, layout[i].pages_count, layout[i].pages_size); min_page_size = MIN(min_page_size, layout[i].pages_size); } } else { TC_PRINT("Empty flash_pages_layout!\n"); return TC_FAIL; } TC_PRINT("\nPage size selected: %d\n", min_page_size); for (int i = 0; i < MSPI_FLASH_TEST_SIZE; i += min_page_size) { prepare_test_pattern(i % 5, expected + i, MIN(min_page_size, MSPI_FLASH_TEST_SIZE - i)); } TC_PRINT("\nTest 1: Flash erase\n"); /* Full flash erase if MSPI_FLASH_TEST_REGION_OFFSET = 0 and * MSPI_FLASH_SECTOR_SIZE = flash size * Erase 2 sectors for check for erase of consequtive sectors */ rc = flash_erase(flash_dev, MSPI_FLASH_TEST_REGION_OFFSET, MSPI_FLASH_SECTOR_SIZE * 2); if (rc != 0) { TC_PRINT("Flash erase failed! %d\n", rc); return TC_FAIL; } /* Read the content and check for erased */ memset(actual, 0, MSPI_FLASH_TEST_SIZE); offs = MSPI_FLASH_TEST_REGION_OFFSET; while (offs < MSPI_FLASH_TEST_REGION_OFFSET + 2 * MSPI_FLASH_SECTOR_SIZE) { rc = flash_read(flash_dev, offs, actual, MSPI_FLASH_TEST_SIZE); if (rc != 0) { TC_PRINT("Flash read failed! %d\n", rc); return TC_FAIL; } if (actual[0] != 0xff) { TC_PRINT("Flash erase failed at offset 0x%x got 0x%x\n", offs, actual[0]); return TC_FAIL; } offs += MSPI_FLASH_SECTOR_SIZE; } TC_PRINT("Flash erase succeeded!\n"); TC_PRINT("\nTest 2: Flash write\n"); offs = MSPI_FLASH_TEST_REGION_OFFSET; while (offs < MSPI_FLASH_TEST_REGION_OFFSET + 2 * MSPI_FLASH_SECTOR_SIZE) { TC_PRINT("\nAttempting to write %zu bytes at offset 0x%x\n", MSPI_FLASH_TEST_SIZE, offs); rc = flash_write(flash_dev, offs, expected, MSPI_FLASH_TEST_SIZE); if (rc != 0) { TC_PRINT("Flash write failed! %d\n", rc); return TC_FAIL; } memset(actual, 0, MSPI_FLASH_TEST_SIZE); rc = flash_read(flash_dev, offs, actual, MSPI_FLASH_TEST_SIZE); if (rc != 0) { TC_PRINT("Flash read failed! %d\n", rc); return TC_FAIL; } if (memcmp(expected, actual, MSPI_FLASH_TEST_SIZE) == 0) { TC_PRINT("Data read matches data written. Good!!\n"); } else { const uint8_t *wp = expected; const uint8_t *rp = actual; const uint8_t *rpe = rp + MSPI_FLASH_TEST_SIZE; int count = 0; TC_PRINT("Data read does not match data written!!\n"); while (rp < rpe) { if (*rp != *wp) { TC_PRINT("%08x wrote %02x read %02x MISMATCH\n", (uint32_t)(offs + (rp - actual)), *wp, *rp); count++; } if (count > 100) { TC_PRINT("Too many data mismatch!!\n"); break; } ++rp; ++wp; } return TC_FAIL; } offs += MSPI_FLASH_SECTOR_SIZE; } return TC_PASS; } ZTEST(mspi_flash, test_multi_sector_rw) { for (int idx = 0; idx < ARRAY_SIZE(mspi_devices); ++idx) { zassert_true(device_is_ready(mspi_devices[idx]), "flash%d is not ready", idx); zassert_true(test_multi_sector_rw(mspi_devices[idx]) == TC_PASS); } } ZTEST_SUITE(mspi_flash, NULL, NULL, NULL, NULL, NULL);