Created
July 7, 2020 21:27
-
-
Save mzero/2a2f669160dc5135d296ba610cfe66e1 to your computer and use it in GitHub Desktop.
Feather Express flash tester
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Adafruit_SPIFlash.h> | |
namespace { | |
// === | |
// === Test Data Generation and Checking | |
// === | |
const size_t BLOCKSIZE = 512; | |
const uint32_t MAGIC1 = 0x6f6c6568; | |
const uint32_t MAGIC2 = 0x61746164; | |
static const size_t WORDCOUNT = BLOCKSIZE/sizeof(uint32_t) - 3; | |
inline uint32_t sequenceStep(uint32_t x) { | |
uint64_t x_ = static_cast<uint64_t>(x); | |
x_ = (x_ * 1815976680ULL) % 4294967291ULL; | |
// from "Tables of Linear Congruential Generators of Different Sizes and | |
// Good Lattice Structure" - Peter L'Ecuyer, Mathematics of Computation, | |
// Volume 68, Number 225, January 1999 | |
return static_cast<uint32_t>(x_); | |
} | |
// A "Helo" block is a block of data that contains a verifiable sequence of | |
// pseudo-random data. It can generated and written by this sketch, or you | |
// can write a file using a pyhon program and write that to a flash chip | |
// formatted with an SDFat file system. | |
union Helo { | |
struct { | |
uint32_t magic1; | |
uint32_t magic2; | |
uint32_t blockNumber; | |
uint32_t words[WORDCOUNT]; | |
} data; | |
uint8_t bytes[BLOCKSIZE]; | |
}; | |
void makeblock(Helo& helo, uint32_t n) { | |
helo.data.magic1 = MAGIC1; | |
helo.data.magic2 = MAGIC2; | |
helo.data.blockNumber = n; | |
uint32_t x = helo.data.blockNumber; | |
for (size_t i = 0; i < WORDCOUNT; ++i) { | |
x = sequenceStep(x); | |
helo.data.words[i] = x; | |
} | |
} | |
enum class CheckResult { | |
absent, | |
good, | |
bad | |
}; | |
CheckResult checkblock(const Helo& helo) { | |
if (helo.data.magic1 != MAGIC1 || helo.data.magic2 != MAGIC2) | |
return CheckResult::absent; | |
int errorCount = 0; | |
uint32_t x = helo.data.blockNumber; | |
for (size_t i = 0; i < WORDCOUNT; ++i) { | |
x = sequenceStep(x); | |
if (helo.data.words[i] != x) { | |
errorCount += 1; | |
if (errorCount == 1) | |
Serial.printf( | |
"helo error: block %5d, word %3d, expected %08x, actual %08x\n", | |
helo.data.blockNumber, i, x, helo.data.words[i]); | |
// break; | |
} | |
} | |
if (errorCount > 1) | |
Serial.printf( | |
" and %d errors\n", errorCount); | |
return errorCount == 0 ? CheckResult::good : CheckResult::bad; | |
} | |
} | |
namespace { | |
// === | |
// === Utilities | |
// === | |
void fatal(const char* msg) { | |
Serial.print("\n[FATAL] "); | |
Serial.println(msg); | |
while (true) ; | |
} | |
// === | |
// === Flash routines | |
// === | |
// On-board external flash (QSPI or SPI) macros should already | |
// defined in your board variant if supported | |
// - EXTERNAL_FLASH_USE_QSPI | |
// - EXTERNAL_FLASH_USE_CS/EXTERNAL_FLASH_USE_SPI | |
#if defined(EXTERNAL_FLASH_USE_QSPI) | |
Adafruit_FlashTransport_QSPI flashTransport; | |
#elif defined(EXTERNAL_FLASH_USE_SPI) | |
Adafruit_FlashTransport_SPI flashTransport( | |
EXTERNAL_FLASH_USE_CS, EXTERNAL_FLASH_USE_SPI); | |
#else | |
#error No QSPI/SPI flash are defined on your board variant.h ! | |
#endif | |
Adafruit_SPIFlash flash(&flashTransport); | |
void eraseFlash() { | |
if (!flash.eraseChip()) | |
fatal("flash erase failed"); | |
flash.waitUntilReady(); | |
Serial.println("flash erased"); | |
} | |
void writeTestData() { | |
Helo helo; | |
int count = 0; | |
uint32_t flashsize = flash.size(); | |
for (uint32_t addr = 0; addr < flashsize; addr += BLOCKSIZE) { | |
makeblock(helo, addr / BLOCKSIZE); | |
flash.writeBuffer(addr, helo.bytes, BLOCKSIZE); | |
count += 1; | |
yield(); | |
} | |
Serial.printf("test data written to %d blocks\n", count); | |
} | |
void verifyTestData() { | |
Helo helo; | |
int count = 0; | |
int heloCount = 0; | |
int heloBad = 0; | |
uint32_t flashsize = flash.size(); | |
for (uint32_t addr = 0; addr < flashsize; addr += BLOCKSIZE) { | |
flash.readBuffer(addr, helo.bytes, BLOCKSIZE); | |
switch (checkblock(helo)) { | |
case CheckResult::bad: heloBad += 1; // fall through | |
case CheckResult::good: heloCount += 1; // fall through | |
default: count += 1; | |
} | |
yield(); | |
} | |
Serial.printf("tested %d blocks, %d were Helo data blocks, %d failed\n", | |
count, heloCount, heloBad); | |
} | |
} | |
void setup() { | |
Serial.begin(115200); | |
while (!Serial) delay(10); | |
if (!flash.begin()) { | |
fatal("Failed to initialize flash chip."); | |
} | |
Serial.print("JEDEC ID: "); Serial.println(flash.getJEDECID(), HEX); | |
Serial.print("Flash size: "); Serial.println(flash.size()); | |
Serial.print( | |
"\n" | |
"Enter a command:\n" | |
" e(rase) - erase the whole flash chip\n" | |
" w(rite) - write test data in every block\n" | |
" v(erify) - read all blocks, and verify those with Helo data\n" | |
); | |
} | |
void loop() { | |
Serial.print("\n> "); | |
char cmd[32]; | |
while (Serial.readBytesUntil('\r', cmd, sizeof(cmd)) < 1) | |
delay(100); | |
Serial.println(cmd); | |
switch (cmd[0]) { | |
case 'e': eraseFlash(); break; | |
case 'w': writeTestData(); break; | |
case 'v': verifyTestData(); break; | |
default: | |
Serial.println("what?"); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/env python | |
import struct | |
blocksize = 512 | |
def makeBlock(n): | |
buf = 'helodata' + struct.pack('<I', n); | |
# from "Tables of Linear Congruential Generators of Different Sizes and | |
# Good Lattice Structure" - Peter L'Ecuyer, Mathematics of Computation, | |
# Volume 68, Number 225, January 1999 | |
m = (1 << 32) - 5; | |
a = 1815976680; | |
x = n | |
for j in xrange(0, (blocksize - len(buf))/4): | |
x = (x * a) % m | |
buf += struct.pack('<I', x); | |
return buf | |
def writeFile(): | |
with open('data.dat', 'wb') as f: | |
for i in xrange(0, 400): | |
f.write(makeBlock(i)) | |
if __name__ == "__main__": | |
writeFile() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment