Skip to content

Instantly share code, notes, and snippets.

@mzero
Created July 7, 2020 21:27
Show Gist options
  • Save mzero/2a2f669160dc5135d296ba610cfe66e1 to your computer and use it in GitHub Desktop.
Save mzero/2a2f669160dc5135d296ba610cfe66e1 to your computer and use it in GitHub Desktop.
Feather Express flash tester
#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?");
}
}
#!/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