-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Raw SD block write/read test for manual use
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
Showing
3 changed files
with
559 additions
and
0 deletions.
There are no files selected for viewing
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
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
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(""); | ||
} | ||
} |
Oops, something went wrong.