diff --git a/ion/Makefile b/ion/Makefile index e123b4595..611e42916 100644 --- a/ion/Makefile +++ b/ion/Makefile @@ -32,3 +32,11 @@ tests += $(addprefix ion/test/,\ keyboard.cpp\ storage.cpp\ ) + +TEST_EXT_FLASH ?= 0 +ifeq ($(TEST_EXT_FLASH),1) +SFLAGS += -DTEST_EXT_FLASH=1 +tests += ion/test/external_flash.cpp +TEST_EXT_FLASH_REPROGRAM ?= 0 +SFLAGS += -DTEST_EXT_FLASH_REPROGRAM=$(TEST_EXT_FLASH_REPROGRAM) +endif diff --git a/ion/test/external_flash.cpp b/ion/test/external_flash.cpp new file mode 100644 index 000000000..392a058da --- /dev/null +++ b/ion/test/external_flash.cpp @@ -0,0 +1,165 @@ +#include +#include +#include +#include "ion/src/device/external_flash.h" +#include "ion/include/ion/timing.h" + +// Choose some not too uniform data to program and test the external flash memory with. + +static inline uint8_t expected_value_at(uint8_t * ptr) { + uint32_t address = reinterpret_cast(ptr) - Ion::ExternalFlash::Device::QSPIBaseAddress; + return (address / 0x10000) + (address / 0x100) + address; + // Example: the value expected at the address 0x123456 is 0x12 + 0x34 + 0x56. +} + +static inline uint16_t expected_value_at(uint16_t * ptr) { + uint8_t * ptr8 = reinterpret_cast(ptr); + return (static_cast(expected_value_at(ptr8+1)) << 8) | static_cast(expected_value_at(ptr8)); +} + +static inline uint32_t expected_value_at(uint32_t * ptr) { + uint16_t * ptr16 = reinterpret_cast(ptr); + return (static_cast(expected_value_at(ptr16+1)) << 16) + static_cast(expected_value_at(ptr16)); +} + +template +static inline void check(volatile T * p, int repeat) { + for (int i = 0; i < repeat; i++) { + quiz_assert(*p == expected_value_at(const_cast(p))); + } +} + +template +void test(int accessType, int repeat) { + uint8_t * start = reinterpret_cast(Ion::ExternalFlash::Device::QSPIBaseAddress); + uint8_t * end = reinterpret_cast(Ion::ExternalFlash::Device::QSPIBaseAddress + Ion::ExternalFlash::Device::FlashAddressSpaceSize); + + // Forward sequential access + if (accessType == 0) { + for (uint8_t * p = start; p <= end-sizeof(T); p++) { + volatile T * q = reinterpret_cast(p); + check(q, repeat); + } + } + + // Backward sequential access + if (accessType == 1) { + for (uint8_t * p = end - sizeof(T); p >= start; p--) { + volatile T * q = reinterpret_cast(p); + check(q, repeat); + } + } + + // Random access + if (accessType == 2) { + T * endT = reinterpret_cast(Ion::ExternalFlash::Device::QSPIBaseAddress + Ion::ExternalFlash::Device::FlashAddressSpaceSize); + for (size_t i=0; i> (32 - Ion::ExternalFlash::Device::NumberOfAddressBitsInChip); + volatile T * q = reinterpret_cast(randomAddr + Ion::ExternalFlash::Device::QSPIBaseAddress); + if (q <= endT - 1) { + check(q, repeat); + } + } + } +} + +static size_t uint64ToString(uint64_t n, char buffer[]) { + size_t len = 0; + do { + buffer[len++] = (n % 10) + '0'; + } while ((n /= 10) > 0); + int i = 0; + int j = len - 1; + while (i < j) { + char c = buffer[i]; + buffer[i++] = buffer[j]; + buffer[j--] = c; + } + return len; +} + +static void printElapsedTime(uint64_t startTime) { + char buffer[7+26+2] = " time: "; + size_t len = uint64ToString((Ion::Timing::millis() - startTime)/1000, buffer+7); + buffer[7+len] = 's'; + buffer[7+len+1] = 0; + quiz_print(buffer); +} + +QUIZ_CASE(ion_ext_flash_erase) { +#if TEST_EXT_FLASH_REPROGRAM + uint64_t startTime = Ion::Timing::millis(); + Ion::ExternalFlash::Device::MassErase(); + printElapsedTime(startTime); +#endif +} + +QUIZ_CASE(ion_ext_flash_program) { +#if TEST_EXT_FLASH_REPROGRAM + // Program separately each page of the flash memory + uint64_t startTime = Ion::Timing::millis(); + for (int page = 0; page < (1<<15); page++) { + uint8_t buffer[256]; + for (int byte = 0; byte < 256; byte++) { + buffer[byte] = expected_value_at(reinterpret_cast(Ion::ExternalFlash::Device::QSPIBaseAddress + page * 256 + byte)); + } + Ion::ExternalFlash::Device::WriteMemory(buffer, reinterpret_cast(page * 256), 256); + } + printElapsedTime(startTime); +#endif +} + +QUIZ_CASE(ion_extflash_read_byte_fwd) { + uint64_t startTime = Ion::Timing::millis(); + test(0, 1); + printElapsedTime(startTime); +} + +QUIZ_CASE(ion_extflash_read_byte_bck) { + uint64_t startTime = Ion::Timing::millis(); + test(1, 1); + printElapsedTime(startTime); +} + +QUIZ_CASE(ion_extflash_read_byte_rand) { + uint64_t startTime = Ion::Timing::millis(); + test(2, 1); + printElapsedTime(startTime); +} + +QUIZ_CASE(ion_extflash_read_half_fwd) { + uint64_t startTime = Ion::Timing::millis(); + test(0, 1); + printElapsedTime(startTime); +} + +QUIZ_CASE(ion_extflash_read_half_bck) { + uint64_t startTime = Ion::Timing::millis(); + test(1, 1); + printElapsedTime(startTime); +} + +QUIZ_CASE(ion_extflash_read_half_rand) { + uint64_t startTime = Ion::Timing::millis(); + test(2, 1); + printElapsedTime(startTime); +} + +QUIZ_CASE(ion_extflash_read_word_fwd) { + uint64_t startTime = Ion::Timing::millis(); + test(0, 1); + printElapsedTime(startTime); +} + +QUIZ_CASE(ion_extflash_read_word_bck) { + uint64_t startTime = Ion::Timing::millis(); + test(1, 1); + printElapsedTime(startTime); +} + +QUIZ_CASE(ion_extflash_read_word_rand) { + uint64_t startTime = Ion::Timing::millis(); + test(2, 1); + printElapsedTime(startTime); + Ion::Timing::msleep(3000); +}