Skip to content

Commit

Permalink
Raw SD block write/read test for manual use
Browse files Browse the repository at this point in the history
Note that this manual test will corrupt the contents of
the card so it can only be run manually and will intentionally
insist that you wilfully insert an SD card before it performs
any writes to the card. This is to avoid inadvertent data loss.
  • Loading branch information
alees24 committed Nov 28, 2024
1 parent aaf4ef8 commit e8fcd16
Show file tree
Hide file tree
Showing 3 changed files with 559 additions and 0 deletions.
1 change: 1 addition & 0 deletions sw/cheri/checks/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set(CHECKS
gpio_check.cc
lcd_check.cc
uart_check.cc
sdraw_check.cc
spi_test.cc
system_info_check.cc
revocation_test.cc
Expand Down
169 changes: 169 additions & 0 deletions sw/cheri/checks/sdraw_check.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/**
* Copyright lowRISC contributors.
* Licensed under the Apache License, Version 2.0, see LICENSE for details.
* SPDX-License-Identifier: Apache-2.0
*/

/**
* SD card raw write/read block test; this is a MANUAL check that should be performed only
* on a card THAT DOES NOT CONTAIN ANY DATA OF VALUE.
*
* It does not require formatting or any particular file system to be present.
*
* If there is a card inserted when the test starts up, you will be asked to remove the
* card and then insert it after the warning message is displayed; this is to decrease the
* likelihood of inadvertent data loss.
*/

#define CHERIOT_NO_AMBIENT_MALLOC
#define CHERIOT_NO_NEW_DELETE
#define CHERIOT_PLATFORM_CUSTOM_UART

#include "../common/sonata-devices.hh"
// clang-format off
#include <ds/xoroshiro.h>
#include <cheri.hh>
// clang-format on
#include "../common/console.hh"
#include "../common/sdcard-utils.hh"
#include "../common/uart-utils.hh"

using namespace CHERI;

static constexpr unsigned kBlockLen = 0x200u;

static uint8_t write_data[kBlockLen];
static uint8_t read_data[kBlockLen];

// Dump out a sequence of bytes as hexadecimal and ASCII text.
static void dump_bytes(const uint8_t *buf, size_t blkBytes, Log &log) {
for (size_t off = 0u; off < blkBytes; ++off) {
log.print("{:02x}", buf[off]);
if ((off & 0xfu) == 0xfu) {
log.print(" : ");
for (size_t aoff = (off & ~0xfu); aoff <= off; aoff++) {
char text[2];
text[0] = buf[aoff];
if (!isprint(text[0])) text[0] = '.';
text[1] = '\0';
log.print(text);
}
log.println("");
} else {
log.print(" ");
}
}
}

// Compare a sequence of bytes against a reference, returning the number of mismatches.
static int compare_bytes(const uint8_t *ref, const uint8_t *data, size_t len, Log &log) {
unsigned mismatches = 0u;
while (len-- > 0u) {
mismatches += *ref++ != *data++;
}
return mismatches;
}

[[noreturn]] extern "C" void entry_point(void *rwRoot) {
CapRoot root{rwRoot};

auto uart0 = uart_ptr(root);
uart0->init(BAUD_RATE);
WriteUart uart{uart0};
Log log(uart);

// The SPI controller talkes to the microSD card in SPI mode.
Capability<volatile SonataSpi> spi = root.cast<volatile SonataSpi>();
spi.address() = SPI_ADDRESS + 2 * SPI_RANGE;
spi.bounds() = SPI_BOUNDS;

// We need to use the pinmux to select the microSD card for SPI controller 2 reads (CIPO),
// as well as preventing outbound traffic to the microSD card also reaching the application
// flash (for safety; it _should_ ignore traffic not accompanied by Chip Select assertion).
auto pin_output = pin_sinks_ptr(root);
SonataPinmux::Sink appspi_cs = pin_output->get(SonataPinmux::PinSink::appspi_cs);
SonataPinmux::Sink appspi_clk = pin_output->get(SonataPinmux::PinSink::appspi_cs);
SonataPinmux::Sink appspi_d0 = pin_output->get(SonataPinmux::PinSink::appspi_d0);
SonataPinmux::Sink microsd_dat3 = pin_output->get(SonataPinmux::PinSink::microsd_dat3);
SonataPinmux::Sink microsd_clk = pin_output->get(SonataPinmux::PinSink::microsd_clk);
SonataPinmux::Sink microsd_cmd = pin_output->get(SonataPinmux::PinSink::microsd_cmd);

auto block_input = block_sinks_ptr(root);
SonataPinmux::Sink spi_0_cipo = block_input->get(SonataPinmux::BlockSink::spi_0_cipo);

// Suppress traffic to the application flash.
appspi_cs.disable();
appspi_clk.disable();
appspi_d0.disable();
// Direct SPI controller 2 to drive the microSD pins.
microsd_dat3.default_selection();
microsd_clk.default_selection();
microsd_cmd.default_selection();
// Select microSD CIPO as SPI controller input.
constexpr uint8_t PmuxSpi0CipoToSdDat0 = 2;
spi_0_cipo.select(PmuxSpi0CipoToSdDat0);

// We need to use the GPIO to detect card presence.
Capability<volatile SonataGpioBoard> gpio = root.cast<volatile SonataGpioBoard>();
gpio.address() = GPIO_ADDRESS;
gpio.bounds() = GPIO_BOUNDS;

// microSD card is on Chip Select 1 (0 goes to the application flash).
constexpr unsigned csBit = 1u;
// microSD card detection bit is on input 16.
constexpr unsigned detBit = 16u;

log.println("SD card raw write/read test.");

SdCard sd(spi, gpio, csBit, detBit, true, &log);

// Wait until there is no card in the slot before issuing a warning message.
if (sd.present()) {
log.println("Please remove the microSD card from the slot.");
while (sd.present()) {
// Prevent erroneous optimisation to infinite loop (.l: j l)
asm("");
}
}
// Wait until a card is detected.
if (!sd.present()) {
log.println("Please insert a microSD card that does not contain any valued data.");
log.println("*** DATA BLOCKS WILL BE OVERWRITTEN ***");
while (!sd.present()) {
// Prevent erroneous optimisation to infinite loop (.l: j l)
asm("");
}
}

log.println("Starting write/read test.... ");

sd.init();

// Initialise random number generation.
ds::xoroshiro::P64R32 prng;
prng.set_state(0xDEADBEEF, 0xBAADCAFE);

const unsigned numBlocks = 16u;
int failures = 0;
for (unsigned b = 0u; b < numBlocks; b++) {
// Choose a random block.
uint32_t blk = (uint16_t)prng();
// Randomise the data that we're going to write.
for (unsigned idx = 0u; idx < kBlockLen; idx++) {
write_data[idx] = (uint8_t)prng();
}
// Write out the data to the chosen block.
sd.write_blocks(blk, write_data);
dump_bytes(write_data, kBlockLen, log);
// Read it back.
sd.read_blocks(blk, read_data);
dump_bytes(read_data, kBlockLen, log);
// Check each of the read bytes against what we tried to write.
failures += compare_bytes(write_data, read_data, kBlockLen, log);
}
write_test_result(log, failures);

while (true) {
asm("");
}
}
Loading

0 comments on commit e8fcd16

Please sign in to comment.