[SC64][SW] Optimized memory usage in the sc64deployer

This commit is contained in:
Mateusz Faderewski 2024-07-23 18:26:17 +02:00
parent e9ee025e21
commit d8976def97
5 changed files with 90 additions and 162 deletions

View File

@ -892,19 +892,15 @@ fn handle_test_command(connection: Connection) -> Result<(), sc64::Error> {
println!("{}: USB", "[SC64 Tests]".bold()); println!("{}: USB", "[SC64 Tests]".bold());
print!(" Performing USB write speed test... ");
stdout().flush().unwrap();
println!(
"{}",
format!("{:.2} MiB/s", sc64.test_usb_speed(true)?).bright_green()
);
print!(" Performing USB read speed test... "); print!(" Performing USB read speed test... ");
stdout().flush().unwrap(); stdout().flush().unwrap();
println!( let read_speed = sc64.test_usb_speed(sc64::SpeedTestDirection::Read)?;
"{}", println!("{}", format!("{read_speed:.2} MiB/s",).bright_green());
format!("{:.2} MiB/s", sc64.test_usb_speed(false)?).bright_green()
); print!(" Performing USB write speed test... ");
stdout().flush().unwrap();
let write_speed = sc64.test_usb_speed(sc64::SpeedTestDirection::Write)?;
println!("{}", format!("{write_speed:.2} MiB/s",).bright_green());
println!("{}: SDRAM (pattern)", "[SC64 Tests]".bold()); println!("{}: SDRAM (pattern)", "[SC64 Tests]".bold());

View File

@ -38,12 +38,6 @@ impl TryFrom<u32> for DataType {
} }
} }
pub struct Command {
pub id: u8,
pub args: [u32; 2],
pub data: Vec<u8>,
}
pub struct Response { pub struct Response {
pub id: u8, pub id: u8,
pub data: Vec<u8>, pub data: Vec<u8>,
@ -167,14 +161,14 @@ pub trait Backend {
} }
} }
fn send_command(&mut self, command: &Command) -> std::io::Result<()> { fn send_command(&mut self, id: u8, args: [u32; 2], data: &[u8]) -> std::io::Result<()> {
self.write_all(b"CMD")?; self.write_all(b"CMD")?;
self.write_all(&command.id.to_be_bytes())?; self.write_all(&id.to_be_bytes())?;
self.write_all(&command.args[0].to_be_bytes())?; self.write_all(&args[0].to_be_bytes())?;
self.write_all(&command.args[1].to_be_bytes())?; self.write_all(&args[1].to_be_bytes())?;
self.write_all(&command.data)?; self.write_all(data)?;
self.flush()?; self.flush()?;
@ -332,17 +326,17 @@ impl Backend for TcpBackend {
self.stream.shutdown(std::net::Shutdown::Both).ok(); self.stream.shutdown(std::net::Shutdown::Both).ok();
} }
fn send_command(&mut self, command: &Command) -> std::io::Result<()> { fn send_command(&mut self, id: u8, args: [u32; 2], data: &[u8]) -> std::io::Result<()> {
let payload_data_type: u32 = DataType::Command.into(); let payload_data_type: u32 = DataType::Command.into();
self.write_all(&payload_data_type.to_be_bytes())?; self.write_all(&payload_data_type.to_be_bytes())?;
self.write_all(&command.id.to_be_bytes())?; self.write_all(&id.to_be_bytes())?;
self.write_all(&command.args[0].to_be_bytes())?; self.write_all(&args[0].to_be_bytes())?;
self.write_all(&command.args[1].to_be_bytes())?; self.write_all(&args[1].to_be_bytes())?;
let command_data_length = command.data.len() as u32; let command_data_length = data.len() as u32;
self.write_all(&command_data_length.to_be_bytes())?; self.write_all(&command_data_length.to_be_bytes())?;
self.write_all(&command.data)?; self.write_all(data)?;
self.flush()?; self.flush()?;
@ -445,22 +439,29 @@ pub struct Link {
} }
impl Link { impl Link {
pub fn execute_command(&mut self, command: &Command) -> Result<Vec<u8>, Error> { pub fn execute_command(
self.execute_command_raw(command, false, false) &mut self,
id: u8,
args: [u32; 2],
data: &[u8],
) -> Result<Vec<u8>, Error> {
self.execute_command_raw(id, args, data, false, false)
} }
pub fn execute_command_raw( pub fn execute_command_raw(
&mut self, &mut self,
command: &Command, id: u8,
args: [u32; 2],
data: &[u8],
no_response: bool, no_response: bool,
ignore_error: bool, ignore_error: bool,
) -> Result<Vec<u8>, Error> { ) -> Result<Vec<u8>, Error> {
self.backend.send_command(command)?; self.backend.send_command(id, args, data)?;
if no_response { if no_response {
return Ok(vec![]); return Ok(vec![]);
} }
let response = self.receive_response()?; let response = self.receive_response()?;
if command.id != response.id { if id != response.id {
return Err(Error::new("Command response ID didn't match")); return Err(Error::new("Command response ID didn't match"));
} }
if !ignore_error && response.error { if !ignore_error && response.error {

View File

@ -15,13 +15,14 @@ pub use self::{
types::{ types::{
BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode, BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode,
DebugPacket, DiagnosticData, DiskPacket, DiskPacketKind, FpgaDebugData, ISViewer, DebugPacket, DiagnosticData, DiskPacket, DiskPacketKind, FpgaDebugData, ISViewer,
MemoryTestPattern, MemoryTestPatternResult, SaveType, SaveWriteback, Switch, TvType, MemoryTestPattern, MemoryTestPatternResult, SaveType, SaveWriteback, SpeedTestDirection,
Switch, TvType,
}, },
}; };
use self::{ use self::{
cic::{sign_ipl3, IPL3_LENGTH, IPL3_OFFSET}, cic::{sign_ipl3, IPL3_LENGTH, IPL3_OFFSET},
link::{Command, Link}, link::Link,
time::{convert_from_datetime, convert_to_datetime}, time::{convert_from_datetime, convert_to_datetime},
types::{ types::{
get_config, get_setting, Config, ConfigId, FirmwareStatus, Setting, SettingId, UpdateStatus, get_config, get_setting, Config, ConfigId, FirmwareStatus, Setting, SettingId, UpdateStatus,
@ -105,11 +106,7 @@ const MEMORY_CHUNK_LENGTH: usize = 8 * 1024 * 1024;
impl SC64 { impl SC64 {
fn command_identifier_get(&mut self) -> Result<[u8; 4], Error> { fn command_identifier_get(&mut self) -> Result<[u8; 4], Error> {
let data = self.link.execute_command(&Command { let data = self.link.execute_command(b'v', [0, 0], &[])?;
id: b'v',
args: [0, 0],
data: vec![],
})?;
if data.len() != 4 { if data.len() != 4 {
return Err(Error::new( return Err(Error::new(
"Invalid data length received for identifier get command", "Invalid data length received for identifier get command",
@ -119,11 +116,7 @@ impl SC64 {
} }
fn command_version_get(&mut self) -> Result<(u16, u16, u32), Error> { fn command_version_get(&mut self) -> Result<(u16, u16, u32), Error> {
let data = self.link.execute_command(&Command { let data = self.link.execute_command(b'V', [0, 0], &[])?;
id: b'V',
args: [0, 0],
data: vec![],
})?;
if data.len() != 8 { if data.len() != 8 {
return Err(Error::new( return Err(Error::new(
"Invalid data length received for version get command", "Invalid data length received for version get command",
@ -136,11 +129,7 @@ impl SC64 {
} }
fn command_state_reset(&mut self) -> Result<(), Error> { fn command_state_reset(&mut self) -> Result<(), Error> {
self.link.execute_command(&Command { self.link.execute_command(b'R', [0, 0], &[])?;
id: b'R',
args: [0, 0],
data: vec![],
})?;
Ok(()) Ok(())
} }
@ -156,20 +145,14 @@ impl SC64 {
((if disable { 1 } else { 0 }) << 24) | ((seed as u32) << 16) | checksum_high, ((if disable { 1 } else { 0 }) << 24) | ((seed as u32) << 16) | checksum_high,
checksum_low, checksum_low,
]; ];
self.link.execute_command(&Command { self.link.execute_command(b'B', args, &[])?;
id: b'B',
args,
data: vec![],
})?;
Ok(()) Ok(())
} }
fn command_config_get(&mut self, config_id: ConfigId) -> Result<Config, Error> { fn command_config_get(&mut self, config_id: ConfigId) -> Result<Config, Error> {
let data = self.link.execute_command(&Command { let data = self
id: b'c', .link
args: [config_id.into(), 0], .execute_command(b'c', [config_id.into(), 0], &[])?;
data: vec![],
})?;
if data.len() != 4 { if data.len() != 4 {
return Err(Error::new( return Err(Error::new(
"Invalid data length received for config get command", "Invalid data length received for config get command",
@ -180,20 +163,14 @@ impl SC64 {
} }
fn command_config_set(&mut self, config: Config) -> Result<(), Error> { fn command_config_set(&mut self, config: Config) -> Result<(), Error> {
self.link.execute_command(&Command { self.link.execute_command(b'C', config.into(), &[])?;
id: b'C',
args: config.into(),
data: vec![],
})?;
Ok(()) Ok(())
} }
fn command_setting_get(&mut self, setting_id: SettingId) -> Result<Setting, Error> { fn command_setting_get(&mut self, setting_id: SettingId) -> Result<Setting, Error> {
let data = self.link.execute_command(&Command { let data = self
id: b'a', .link
args: [setting_id.into(), 0], .execute_command(b'a', [setting_id.into(), 0], &[])?;
data: vec![],
})?;
if data.len() != 4 { if data.len() != 4 {
return Err(Error::new( return Err(Error::new(
"Invalid data length received for setting get command", "Invalid data length received for setting get command",
@ -204,20 +181,12 @@ impl SC64 {
} }
fn command_setting_set(&mut self, setting: Setting) -> Result<(), Error> { fn command_setting_set(&mut self, setting: Setting) -> Result<(), Error> {
self.link.execute_command(&Command { self.link.execute_command(b'A', setting.into(), &[])?;
id: b'A',
args: setting.into(),
data: vec![],
})?;
Ok(()) Ok(())
} }
fn command_time_get(&mut self) -> Result<NaiveDateTime, Error> { fn command_time_get(&mut self) -> Result<NaiveDateTime, Error> {
let data = self.link.execute_command(&Command { let data = self.link.execute_command(b't', [0, 0], &[])?;
id: b't',
args: [0, 0],
data: vec![],
})?;
if data.len() != 8 { if data.len() != 8 {
return Err(Error::new( return Err(Error::new(
"Invalid data length received for time get command", "Invalid data length received for time get command",
@ -227,20 +196,15 @@ impl SC64 {
} }
fn command_time_set(&mut self, datetime: NaiveDateTime) -> Result<(), Error> { fn command_time_set(&mut self, datetime: NaiveDateTime) -> Result<(), Error> {
self.link.execute_command(&Command { self.link
id: b'T', .execute_command(b'T', convert_from_datetime(datetime), &[])?;
args: convert_from_datetime(datetime),
data: vec![],
})?;
Ok(()) Ok(())
} }
fn command_memory_read(&mut self, address: u32, length: usize) -> Result<Vec<u8>, Error> { fn command_memory_read(&mut self, address: u32, length: usize) -> Result<Vec<u8>, Error> {
let data = self.link.execute_command(&Command { let data = self
id: b'm', .link
args: [address, length as u32], .execute_command(b'm', [address, length as u32], &[])?;
data: vec![],
})?;
if data.len() != length { if data.len() != length {
return Err(Error::new( return Err(Error::new(
"Invalid data length received for memory read command", "Invalid data length received for memory read command",
@ -250,21 +214,16 @@ impl SC64 {
} }
fn command_memory_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { fn command_memory_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
self.link.execute_command(&Command { self.link
id: b'M', .execute_command(b'M', [address, data.len() as u32], data)?;
args: [address, data.len() as u32],
data: data.to_vec(),
})?;
Ok(()) Ok(())
} }
fn command_usb_write(&mut self, datatype: u8, data: &[u8]) -> Result<(), Error> { fn command_usb_write(&mut self, datatype: u8, data: &[u8]) -> Result<(), Error> {
self.link.execute_command_raw( self.link.execute_command_raw(
&Command { b'U',
id: b'U', [datatype as u32, data.len() as u32],
args: [datatype as u32, data.len() as u32], data,
data: data.to_vec(),
},
true, true,
false, false,
)?; )?;
@ -272,29 +231,17 @@ impl SC64 {
} }
fn command_dd_set_block_ready(&mut self, error: bool) -> Result<(), Error> { fn command_dd_set_block_ready(&mut self, error: bool) -> Result<(), Error> {
self.link.execute_command(&Command { self.link.execute_command(b'D', [error as u32, 0], &[])?;
id: b'D',
args: [error as u32, 0],
data: vec![],
})?;
Ok(()) Ok(())
} }
fn command_writeback_enable(&mut self) -> Result<(), Error> { fn command_writeback_enable(&mut self) -> Result<(), Error> {
self.link.execute_command(&Command { self.link.execute_command(b'W', [0, 0], &[])?;
id: b'W',
args: [0, 0],
data: vec![],
})?;
Ok(()) Ok(())
} }
fn command_flash_wait_busy(&mut self, wait: bool) -> Result<u32, Error> { fn command_flash_wait_busy(&mut self, wait: bool) -> Result<u32, Error> {
let data = self.link.execute_command(&Command { let data = self.link.execute_command(b'p', [wait as u32, 0], &[])?;
id: b'p',
args: [wait as u32, 0],
data: vec![],
})?;
if data.len() != 4 { if data.len() != 4 {
return Err(Error::new( return Err(Error::new(
"Invalid data length received for flash wait busy command", "Invalid data length received for flash wait busy command",
@ -305,24 +252,14 @@ impl SC64 {
} }
fn command_flash_erase_block(&mut self, address: u32) -> Result<(), Error> { fn command_flash_erase_block(&mut self, address: u32) -> Result<(), Error> {
self.link.execute_command(&Command { self.link.execute_command(b'P', [address, 0], &[])?;
id: b'P',
args: [address, 0],
data: vec![],
})?;
Ok(()) Ok(())
} }
fn command_firmware_backup(&mut self, address: u32) -> Result<(FirmwareStatus, u32), Error> { fn command_firmware_backup(&mut self, address: u32) -> Result<(FirmwareStatus, u32), Error> {
let data = self.link.execute_command_raw( let data = self
&Command { .link
id: b'f', .execute_command_raw(b'f', [address, 0], &[], false, true)?;
args: [address, 0],
data: vec![],
},
false,
true,
)?;
if data.len() != 8 { if data.len() != 8 {
return Err(Error::new( return Err(Error::new(
"Invalid data length received for firmware backup command", "Invalid data length received for firmware backup command",
@ -338,15 +275,9 @@ impl SC64 {
address: u32, address: u32,
length: usize, length: usize,
) -> Result<FirmwareStatus, Error> { ) -> Result<FirmwareStatus, Error> {
let data = self.link.execute_command_raw( let data =
&Command { self.link
id: b'F', .execute_command_raw(b'F', [address, length as u32], &[], false, true)?;
args: [address, length as u32],
data: vec![],
},
false,
true,
)?;
if data.len() != 4 { if data.len() != 4 {
return Err(Error::new( return Err(Error::new(
"Invalid data length received for firmware update command", "Invalid data length received for firmware update command",
@ -356,20 +287,12 @@ impl SC64 {
} }
fn command_fpga_debug_data_get(&mut self) -> Result<FpgaDebugData, Error> { fn command_fpga_debug_data_get(&mut self) -> Result<FpgaDebugData, Error> {
let data = self.link.execute_command(&Command { let data = self.link.execute_command(b'?', [0, 0], &[])?;
id: b'?',
args: [0, 0],
data: vec![],
})?;
Ok(data.try_into()?) Ok(data.try_into()?)
} }
fn command_diagnostic_data_get(&mut self) -> Result<DiagnosticData, Error> { fn command_diagnostic_data_get(&mut self) -> Result<DiagnosticData, Error> {
let data = self.link.execute_command(&Command { let data = self.link.execute_command(b'%', [0, 0], &[])?;
id: b'%',
args: [0, 0],
data: vec![],
})?;
Ok(data.try_into()?) Ok(data.try_into()?)
} }
} }
@ -752,22 +675,27 @@ impl SC64 {
} }
} }
pub fn test_usb_speed(&mut self, write: bool) -> Result<f64, Error> { pub fn test_usb_speed(&mut self, direction: SpeedTestDirection) -> Result<f64, Error> {
const TEST_ADDRESS: u32 = SDRAM_ADDRESS; const TEST_ADDRESS: u32 = SDRAM_ADDRESS;
const TEST_LENGTH: usize = 8 * 1024 * 1024; const TEST_LENGTH: usize = 16 * 1024 * 1024;
const MIB_DIVIDER: f64 = 1024.0 * 1024.0; const MIB_DIVIDER: f64 = 1024.0 * 1024.0;
let data = vec![0x00; TEST_LENGTH]; let data = vec![0x00; TEST_LENGTH];
let time = std::time::Instant::now(); let time = std::time::Instant::now();
if write { match direction {
self.command_memory_write(TEST_ADDRESS, &data)?; SpeedTestDirection::Read => {
} else { self.command_memory_read(TEST_ADDRESS, TEST_LENGTH)?;
self.command_memory_read(TEST_ADDRESS, TEST_LENGTH)?; }
SpeedTestDirection::Write => {
self.command_memory_write(TEST_ADDRESS, &data)?;
}
} }
Ok((TEST_LENGTH as f64 / MIB_DIVIDER) / time.elapsed().as_secs_f64()) let elapsed = time.elapsed();
Ok((TEST_LENGTH as f64 / MIB_DIVIDER) / elapsed.as_secs_f64())
} }
pub fn test_sdram_pattern( pub fn test_sdram_pattern(

View File

@ -1,8 +1,6 @@
use super::{ use super::{
error::Error, error::Error,
link::{ link::{list_local_devices, new_local, AsynchronousPacket, DataType, Response, UsbPacket},
list_local_devices, new_local, AsynchronousPacket, Command, DataType, Response, UsbPacket,
},
}; };
use std::io::{Read, Write}; use std::io::{Read, Write};
@ -50,7 +48,7 @@ impl StreamHandler {
result result
} }
fn receive_command(&mut self) -> std::io::Result<Option<Command>> { fn receive_command(&mut self) -> std::io::Result<Option<(u8, [u32; 2], Vec<u8>)>> {
if let Some(header) = self.try_read_header()? { if let Some(header) = self.try_read_header()? {
if let Ok(data_type) = TryInto::<DataType>::try_into(u32::from_be_bytes(header)) { if let Ok(data_type) = TryInto::<DataType>::try_into(u32::from_be_bytes(header)) {
if !matches!(data_type, DataType::Command) { if !matches!(data_type, DataType::Command) {
@ -77,7 +75,7 @@ impl StreamHandler {
let mut data = vec![0u8; command_data_length]; let mut data = vec![0u8; command_data_length];
self.reader.read_exact(&mut data)?; self.reader.read_exact(&mut data)?;
Ok(Some(Command { id, args, data })) Ok(Some((id, args, data)))
} else { } else {
Ok(None) Ok(None)
} }
@ -121,8 +119,8 @@ fn server_accept_connection(port: String, connection: &mut StreamHandler) -> Res
loop { loop {
match connection.receive_command() { match connection.receive_command() {
Ok(Some(command)) => { Ok(Some((id, args, data))) => {
link.execute_command_raw(&command, true, true)?; link.execute_command_raw(id, args, &data, true, true)?;
} }
Ok(None) => {} Ok(None) => {}
Err(error) => match error.kind() { Err(error) => match error.kind() {

View File

@ -1018,6 +1018,11 @@ impl Display for DiagnosticData {
} }
} }
pub enum SpeedTestDirection {
Read,
Write
}
pub enum MemoryTestPattern { pub enum MemoryTestPattern {
OwnAddress(bool), OwnAddress(bool),
AllZeros, AllZeros,