Skip to content

Commit 97c8fba

Browse files
BootloaderTest: Refactor write testing
This introduces a new configuration value that can be used to limit flash writing (to reduce wear). At the same time, the newly introduced return value for FINALIZE_FLASH is used to verify that no flash is written when not needed (and some is written when it is needed).
1 parent 939c689 commit 97c8fba

File tree

1 file changed

+59
-23
lines changed

1 file changed

+59
-23
lines changed

BootloaderTest/BootloaderTest.ino

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3636

3737
PrintingSoftWire bus(SDA, SCL);
3838

39-
struct {
39+
struct Cfg {
4040
// A single test is done using these values, then they are changed
4141
// into various variations
4242

@@ -56,6 +56,14 @@ struct {
5656
// These are not changed, so set them here
5757
bool printRawData = false;
5858
bool displayAttached = true;
59+
60+
enum {
61+
FLASH_ONCE, // Flash random data in the first run
62+
FLASH_ONE_BYTE_PER_RUN, // Additionallly change one byte on each run
63+
FLASH_EVERY_TIME, // Change every byte on every run. This will wear out the flash!
64+
};
65+
uint8_t flashWear = FLASH_ONCE;
66+
5967
// When non-zero, only write this much bytes to flash on each test, to
6068
// speed up testing
6169
uint16_t maxWriteSize = 0;
@@ -413,7 +421,7 @@ bool write_flash_cmd(uint16_t address, uint8_t *data, uint8_t len, uint8_t *stat
413421
return true;
414422
}
415423

416-
bool write_flash(uint8_t *data, uint16_t len, uint8_t writelen) {
424+
bool write_flash(uint8_t *data, uint16_t len, uint8_t writelen, uint8_t *erase_count) {
417425
uint16_t offset = 0;
418426
auto on_failure = [&offset]() {
419427
Serial.print("offset = 0x");
@@ -428,11 +436,11 @@ bool write_flash(uint8_t *data, uint16_t len, uint8_t writelen) {
428436
assertOk(status);
429437
offset += nextlen;
430438
}
431-
assertTrue(run_transaction_ok(Commands::FINALIZE_FLASH));
439+
assertTrue(run_transaction_ok(Commands::FINALIZE_FLASH, nullptr, 0, erase_count, 1, 1));
432440
return true;
433441
}
434442

435-
bool write_and_verify_flash(uint8_t *data, uint16_t len, uint8_t writelen, uint8_t readlen) {
443+
bool write_and_verify_flash(uint8_t *data, uint16_t len, uint8_t writelen, uint8_t readlen, uint8_t *erase_count) {
436444
auto on_failure = [readlen, writelen]() {
437445
Serial.print("writelen = ");
438446
Serial.println(writelen);
@@ -443,7 +451,7 @@ bool write_and_verify_flash(uint8_t *data, uint16_t len, uint8_t writelen, uint8
443451
#ifdef TIME_WRITE
444452
unsigned long start = millis();
445453
#endif
446-
assertTrue(write_flash(data, len, writelen));
454+
assertTrue(write_flash(data, len, writelen, erase_count));
447455
#ifdef TIME_WRITE
448456
unsigned long now = millis();
449457
Serial.print("Write took ");
@@ -478,30 +486,57 @@ test(120_write_flash) {
478486
if (cfg.maxWriteSize && cfg.maxWriteSize < flash_size)
479487
flash_size = cfg.maxWriteSize;
480488

481-
uint8_t *data = (uint8_t*)malloc(flash_size);
482-
auto on_failure = [data]() { free(data); };
483-
assertTrue(data != nullptr);
489+
static uint8_t *data = NULL;
490+
static size_t data_len = 0;
491+
// The first time, we always generate fully random data, flash it, and
492+
// then change one byte. Since the previous testing session will likely have
493+
// generated the same contents (since the random seed is not random),
494+
// this ensures that at leaste one byte will really be different.
495+
bool full_random = (data == NULL) || (cfg.flashWear == Cfg::FLASH_EVERY_TIME);
496+
bool change_one = (data == NULL) || (cfg.flashWear == Cfg::FLASH_ONE_BYTE_PER_RUN);
497+
if (data_len == 0) {
498+
data = (uint8_t*)malloc(flash_size);
499+
assertTrue(data != nullptr);
500+
data_len = flash_size;
501+
} else {
502+
assertEqual(data_len, flash_size);
503+
}
504+
484505
assertMoreOrEqual(flash_size, 2U);
485-
// The bootloader requires a RCALL or RJMP instruction at address 0,
486-
// so give it one
487-
data[0] = 0x00;
488-
data[1] = 0xC0;
489-
for (uint16_t i = 2; i < flash_size; ++i)
490-
data[i] = random();
506+
if (full_random) {
507+
// The bootloader requires a RCALL or RJMP instruction at address 0,
508+
// so give it one
509+
data[0] = 0x00;
510+
data[1] = 0xC0;
511+
for (uint16_t i = 2; i < flash_size; ++i)
512+
data[i] = random();
513+
}
514+
515+
// Write flash using various messages sizes
516+
uint8_t erase_count;
517+
assertTrue(write_and_verify_flash(data, flash_size, 1, 1, &erase_count));
518+
// If we didn't randomize, nothing should have changed (but if we did
519+
// randomize, we can't be sure that something changed).
520+
if (!full_random)
521+
assertEqual(erase_count, 0);
491522

492-
assertTrue(write_and_verify_flash(data, flash_size, 1, 1));
493-
assertTrue(write_and_verify_flash(data, flash_size, 7, 16));
494-
assertTrue(write_and_verify_flash(data, flash_size, 16, 3));
523+
if (change_one) {
524+
data[random(2, flash_size)] += random(256);
525+
}
526+
assertTrue(write_and_verify_flash(data, flash_size, 7, 16, &erase_count));
527+
assertEqual(erase_count, change_one ? 1 : 0);
528+
assertTrue(write_and_verify_flash(data, flash_size, 16, 3, &erase_count));
529+
assertEqual(erase_count, 0);
495530
// Max write and read size
496531
uint8_t writelen = MAX_MSG_LEN - 4; // cmd, 2xaddr, crc
497-
uint8_t readlen = MAX_MSG_LEN - 2; // cmd, 2xaddr
498-
assertTrue(write_and_verify_flash(data, flash_size, writelen, readlen));
532+
uint8_t readlen = MAX_MSG_LEN - 3; // status, len, 2xaddr
533+
assertTrue(write_and_verify_flash(data, flash_size, writelen, readlen, &erase_count));
534+
assertEqual(erase_count, 0);
499535
// Random sizes
500536
writelen = random(1, writelen);
501537
readlen = random(1, readlen);
502-
assertTrue(write_and_verify_flash(data, flash_size, writelen, readlen));
503-
504-
free(data);
538+
assertTrue(write_and_verify_flash(data, flash_size, writelen, readlen, &erase_count));
539+
assertEqual(erase_count, 0);
505540
}
506541

507542
test(130_invalid_writes) {
@@ -543,9 +578,10 @@ test(130_invalid_writes) {
543578
assertEqual(status, Status::COMMAND_FAILED);
544579
assertEqual(reason, 2);
545580

581+
// FINALIZE_FLASH should also fail with an invalid address 0/1
546582
assertTrue(write_flash_cmd(0, data, 2, &status, &reason));
547583
assertOk(status);
548-
assertTrue(run_transaction(Commands::FINALIZE_FLASH, nullptr, 0, &status, &reason, 0, 1));
584+
assertTrue(run_transaction(Commands::FINALIZE_FLASH, nullptr, 0, &status, &reason, 1, 1));
549585
assertEqual(status, Status::COMMAND_FAILED);
550586
assertEqual(reason, 2);
551587
}

0 commit comments

Comments
 (0)