[SC64][SW] sc64deployer: added SDRAM tests

This commit is contained in:
Mateusz Faderewski 2024-05-08 20:24:08 +02:00
parent a0bd0ddd98
commit 7bc4e6d180
5 changed files with 218 additions and 5 deletions

37
sw/deployer/Cargo.lock generated
View File

@ -696,6 +696,12 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]] [[package]]
name = "proc-macro-hack" name = "proc-macro-hack"
version = "0.5.20+deprecated" version = "0.5.20+deprecated"
@ -729,6 +735,36 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "rayon" name = "rayon"
version = "1.9.0" version = "1.9.0"
@ -810,6 +846,7 @@ dependencies = [
"include-flate", "include-flate",
"md5", "md5",
"panic-message", "panic-message",
"rand",
"rust-ini", "rust-ini",
"serial2", "serial2",
"serialport", "serialport",

View File

@ -19,6 +19,7 @@ image = "0.24.5"
include-flate = { version = "0.2.0", features = ["stable"] } include-flate = { version = "0.2.0", features = ["stable"] }
md5 = "0.7.0" md5 = "0.7.0"
panic-message = "0.3.0" panic-message = "0.3.0"
rand = "0.8.5"
rust-ini = "0.18.0" rust-ini = "0.18.0"
serial2 = "0.2.20" serial2 = "0.2.20"
serialport = "4.3.0" serialport = "4.3.0"

View File

@ -75,6 +75,9 @@ enum Commands {
command: FirmwareCommands, command: FirmwareCommands,
}, },
/// Test SC64 hardware
Test,
/// Expose SC64 device over network /// Expose SC64 device over network
Server(ServerArgs), Server(ServerArgs),
} }
@ -333,6 +336,7 @@ fn handle_command(command: &Commands, port: Option<String>, remote: Option<Strin
Commands::Reset => handle_reset_command(connection), Commands::Reset => handle_reset_command(connection),
Commands::Set { command } => handle_set_command(connection, command), Commands::Set { command } => handle_set_command(connection, command),
Commands::Firmware { command } => handle_firmware_command(connection, command), Commands::Firmware { command } => handle_firmware_command(connection, command),
Commands::Test => handle_test_command(connection),
Commands::Server(args) => handle_server_command(connection, args), Commands::Server(args) => handle_server_command(connection, args),
}; };
match result { match result {
@ -781,7 +785,7 @@ fn handle_set_command(connection: Connection, command: &SetCommands) -> Result<(
sc64.set_datetime(datetime)?; sc64.set_datetime(datetime)?;
println!( println!(
"SC64 RTC datetime synchronized to: {}", "SC64 RTC datetime synchronized to: {}",
datetime.format("%Y-%m-%d %H:%M:%S %Z").to_string().green() datetime.format("%Y-%m-%d %H:%M:%S").to_string().green()
); );
} }
@ -877,6 +881,96 @@ fn handle_firmware_command(
} }
} }
fn handle_test_command(connection: Connection) -> Result<(), sc64::Error> {
let mut sc64 = init_sc64(connection, false)?;
println!("{}: SDRAM", "[SC64 Tests]".bold());
let sdram_tests = [
(sc64::MemoryTestType::OwnAddress, None),
(sc64::MemoryTestType::AllZeros, None),
(sc64::MemoryTestType::AllOnes, None),
(sc64::MemoryTestType::Random, None),
(sc64::MemoryTestType::Random, None),
(sc64::MemoryTestType::Random, None),
(sc64::MemoryTestType::AllZeros, Some(60)),
(sc64::MemoryTestType::AllOnes, Some(60)),
(sc64::MemoryTestType::Pattern(0x00010001), None),
(sc64::MemoryTestType::Pattern(0xFFFEFFFE), None),
(sc64::MemoryTestType::Pattern(0x00020002), None),
(sc64::MemoryTestType::Pattern(0xFFFDFFFD), None),
(sc64::MemoryTestType::Pattern(0x00040004), None),
(sc64::MemoryTestType::Pattern(0xFFFBFFFB), None),
(sc64::MemoryTestType::Pattern(0x00080008), None),
(sc64::MemoryTestType::Pattern(0xFFF7FFF7), None),
(sc64::MemoryTestType::Pattern(0x00100010), None),
(sc64::MemoryTestType::Pattern(0xFFEFFFEF), None),
(sc64::MemoryTestType::Pattern(0x00200020), None),
(sc64::MemoryTestType::Pattern(0xFFDFFFDF), None),
(sc64::MemoryTestType::Pattern(0x00400040), None),
(sc64::MemoryTestType::Pattern(0xFFBFFFBF), None),
(sc64::MemoryTestType::Pattern(0x00800080), None),
(sc64::MemoryTestType::Pattern(0xFF7FFF7F), None),
(sc64::MemoryTestType::Pattern(0x01000100), None),
(sc64::MemoryTestType::Pattern(0xFEFFFEFF), None),
(sc64::MemoryTestType::Pattern(0x02000200), None),
(sc64::MemoryTestType::Pattern(0xFDFFFDFF), None),
(sc64::MemoryTestType::Pattern(0x04000400), None),
(sc64::MemoryTestType::Pattern(0xFBFFFBFF), None),
(sc64::MemoryTestType::Pattern(0x08000800), None),
(sc64::MemoryTestType::Pattern(0xF7FFF7FF), None),
(sc64::MemoryTestType::Pattern(0x10001000), None),
(sc64::MemoryTestType::Pattern(0xEFFFEFFF), None),
(sc64::MemoryTestType::Pattern(0x20002000), None),
(sc64::MemoryTestType::Pattern(0xDFFFDFFF), None),
(sc64::MemoryTestType::Pattern(0x40004000), None),
(sc64::MemoryTestType::Pattern(0xBFFFBFFF), None),
(sc64::MemoryTestType::Pattern(0x80008000), None),
(sc64::MemoryTestType::Pattern(0x7FFF7FFF), None),
(sc64::MemoryTestType::AllZeros, Some(300)),
(sc64::MemoryTestType::AllOnes, Some(300)),
];
let sdram_tests_count = sdram_tests.len();
let mut sdram_tests_failed = false;
for (i, (test_type, fade)) in sdram_tests.into_iter().enumerate() {
let fadeout_text = if let Some(fade) = fade {
format!(", fadeout {fade} seconds")
} else {
"".to_string()
};
print!(
" ({} / {sdram_tests_count}) Testing {test_type}{fadeout_text}... ",
i + 1
);
stdout().flush().unwrap();
let result = sc64.test_sdram(test_type, fade)?;
if let Some((address, (written, read))) = result.first_error {
sdram_tests_failed = true;
println!("{}", "error!".bright_red());
println!(" Found a mismatch at address 0x{address:08X}",);
println!(" 0x{written:08X} (W) != 0x{read:08X} (R)");
println!(" Total errors found: {}", result.all_errors.len());
} else {
println!("{}", "ok".bright_green());
}
}
if sdram_tests_failed {
println!(
"{}",
"Some SDRAM tests failed, SDRAM chip might be defective".bright_red()
);
} else {
println!("{}", "All SDRAM tests passed without error".bright_green());
}
Ok(())
}
fn handle_server_command(connection: Connection, args: &ServerArgs) -> Result<(), sc64::Error> { fn handle_server_command(connection: Connection, args: &ServerArgs) -> Result<(), sc64::Error> {
let port = if let Connection::Local(port) = connection { let port = if let Connection::Local(port) = connection {
port port

View File

@ -12,8 +12,8 @@ pub use self::{
server::ServerEvent, server::ServerEvent,
types::{ types::{
BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode, BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode,
DebugPacket, DiagnosticData, DiskPacket, DiskPacketKind, FpgaDebugData, SaveType, DebugPacket, DiagnosticData, DiskPacket, DiskPacketKind, FpgaDebugData, MemoryTestResult,
SaveWriteback, Switch, TvType, MemoryTestType, SaveType, SaveWriteback, Switch, TvType,
}, },
}; };
@ -26,10 +26,12 @@ use self::{
}, },
}; };
use chrono::{DateTime, Local}; use chrono::{DateTime, Local};
use rand::Rng;
use std::{ use std::{
cmp::min,
io::{Read, Seek, Write}, io::{Read, Seek, Write},
time::Instant, thread::sleep,
{cmp::min, time::Duration}, time::{Duration, Instant},
}; };
pub struct SC64 { pub struct SC64 {
@ -748,6 +750,58 @@ impl SC64 {
} }
} }
pub fn test_sdram(
&mut self,
test_type: MemoryTestType,
fade: Option<u64>,
) -> Result<MemoryTestResult, Error> {
let item_size = std::mem::size_of::<u32>();
let mut test_data = vec![0u32; SDRAM_LENGTH / item_size];
match test_type {
MemoryTestType::OwnAddress => {
for (index, item) in test_data.iter_mut().enumerate() {
*item = (index * item_size) as u32;
}
}
MemoryTestType::AllZeros => test_data.fill(0x00000000u32),
MemoryTestType::AllOnes => test_data.fill(0xFFFFFFFFu32),
MemoryTestType::Pattern(pattern) => test_data.fill(pattern),
MemoryTestType::Random => rand::thread_rng().fill(&mut test_data[..]),
};
let raw_test_data: Vec<u8> = test_data.iter().flat_map(|v| v.to_be_bytes()).collect();
self.command_memory_write(SDRAM_ADDRESS, &raw_test_data)?;
if let Some(fade) = fade {
sleep(Duration::from_secs(fade));
}
let raw_check_data = self.command_memory_read(SDRAM_ADDRESS, SDRAM_LENGTH)?;
let check_data = raw_check_data
.chunks(4)
.map(|a| u32::from_be_bytes(a[0..4].try_into().unwrap()));
let all_errors: Vec<(usize, (u32, u32))> = test_data
.into_iter()
.zip(check_data)
.enumerate()
.filter(|(_, (a, b))| a != b)
.map(|(i, (a, b))| (i * item_size, (a, b)))
.collect();
let first_error = if all_errors.len() > 0 {
Some(all_errors.get(0).copied().unwrap())
} else {
None
};
return Ok(MemoryTestResult {
first_error,
all_errors,
});
}
fn memory_read_chunked( fn memory_read_chunked(
&mut self, &mut self,
writer: &mut dyn Write, writer: &mut dyn Write,

View File

@ -983,6 +983,33 @@ impl Display for DiagnosticData {
} }
} }
pub enum MemoryTestType {
OwnAddress,
AllZeros,
AllOnes,
Pattern(u32),
Random,
}
pub struct MemoryTestResult {
pub first_error: Option<(usize, (u32, u32))>,
pub all_errors: Vec<(usize, (u32, u32))>,
}
impl Display for MemoryTestType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MemoryTestType::OwnAddress => f.write_str("Own address"),
MemoryTestType::AllZeros => f.write_str("All zeros"),
MemoryTestType::AllOnes => f.write_str("All ones"),
MemoryTestType::Random => f.write_str("Random"),
MemoryTestType::Pattern(pattern) => {
f.write_fmt(format_args!("Pattern 0x{pattern:08X}"))
}
}
}
}
macro_rules! get_config { macro_rules! get_config {
($sc64:ident, $config:ident) => {{ ($sc64:ident, $config:ident) => {{
if let Config::$config(value) = $sc64.command_config_get(ConfigId::$config)? { if let Config::$config(value) = $sc64.command_config_get(ConfigId::$config)? {