serial refactor

This commit is contained in:
Mateusz Faderewski 2024-06-30 21:44:32 +02:00
parent 6d460e83bd
commit db9e9fa515
7 changed files with 285 additions and 128 deletions

View File

@ -45,12 +45,26 @@ module memory_dma (
logic dma_start;
logic dma_stop;
logic dma_stop_requested;
always_comb begin
dma_start = dma_scb.start && !dma_scb.stop && !dma_scb.busy;
dma_stop = dma_scb.stop;
end
always_ff @(posedge clk) begin
if (reset) begin
dma_stop_requested <= 1'b0;
end else begin
if (dma_stop) begin
dma_stop_requested <= 1'b1;
end
if (dma_start) begin
dma_stop_requested <= 1'b0;
end
end
end
// Remaining counter and FIFO enable
@ -197,7 +211,7 @@ module memory_dma (
remaining <= dma_scb.transfer_length;
end
if ((mem_bus.write && rx_rdata_pop) || (!mem_bus.write && tx_wdata_push)) begin
if (!dma_stop_requested && ((mem_bus.write && rx_rdata_pop) || (!mem_bus.write && tx_wdata_push))) begin
remaining <= remaining - 1'd1;
end
end

36
sw/deployer/Cargo.lock generated
View File

@ -218,9 +218,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "cc"
version = "1.0.101"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d"
checksum = "2755ff20a1d93490d26ba33a6f092a38a508398a5320df5d4b3014fcccce9410"
dependencies = [
"jobserver",
"libc",
@ -265,9 +265,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.7"
version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
dependencies = [
"clap_builder",
"clap_derive",
@ -284,9 +284,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.7"
version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
dependencies = [
"anstream",
"anstyle",
@ -296,9 +296,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.5"
version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
dependencies = [
"heck",
"proc-macro2",
@ -799,9 +799,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.21"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "loop9"
@ -906,9 +906,9 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "num-bigint"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
@ -1196,9 +1196,9 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "rgb"
version = "0.8.37"
version = "0.8.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8"
checksum = "a7439be6844e40133eda024efd85bf07f59d0dd2f59b10c00dd6cfb92cc5c741"
dependencies = [
"bytemuck",
]
@ -1291,9 +1291,9 @@ dependencies = [
[[package]]
name = "serialport"
version = "4.3.0"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f5a15d0be940df84846264b09b51b10b931fb2f275becb80934e3568a016828"
checksum = "de7c4f0cce25b9b3518eea99618112f9ee4549f974480c8f43d3c06f03c131a0"
dependencies = [
"bitflags 2.6.0",
"cfg-if",
@ -1452,9 +1452,9 @@ dependencies = [
[[package]]
name = "unescaper"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0adf6ad32eb5b3cadff915f7b770faaac8f7ff0476633aa29eb0d9584d889d34"
checksum = "c878a167baa8afd137494101a688ef8c67125089ff2249284bd2b5f9bfedb815"
dependencies = [
"thiserror",
]

View File

@ -8,7 +8,7 @@ documentation = "https://github.com/Polprzewodnikowy/SummerCart64"
[dependencies]
chrono = "0.4.38"
clap = { version = "4.5.7", features = ["derive"] }
clap = { version = "4.5.8", features = ["derive"] }
clap-num = "1.1.1"
colored = "2.1.0"
crc32fast = "1.4.2"
@ -24,7 +24,7 @@ panic-message = "0.3.0"
rand = "0.8.5"
rust-ini = "0.18.0"
serial2 = "0.2.26"
serialport = "4.3.0"
serialport = "4.4.0"
[profile.release]
lto = true

View File

@ -36,24 +36,24 @@ struct Wrapper {
impl Wrapper {
const DEFAULT_POLL_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(16);
const DEFAULT_RW_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
const DEFAULT_CHUNKSIZE: usize = 4096;
const WRITE_CHUNK_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(100);
fn new(
read_timeout: Option<std::time::Duration>,
write_timeout: Option<std::time::Duration>,
) -> std::io::Result<Wrapper> {
) -> std::io::Result<Self> {
let context = unsafe { libftdi1_sys::ftdi_new() };
if context.is_null() {
return Err(std::io::ErrorKind::OutOfMemory.into());
}
let mut wrapper = Wrapper {
let mut wrapper = Self {
context,
unclog_buffer: std::collections::VecDeque::new(),
write_buffer: vec![],
read_timeout: Wrapper::DEFAULT_RW_TIMEOUT,
write_timeout: Wrapper::DEFAULT_RW_TIMEOUT,
read_chunksize: Wrapper::DEFAULT_CHUNKSIZE,
write_chunksize: Wrapper::DEFAULT_CHUNKSIZE,
read_timeout: Self::DEFAULT_RW_TIMEOUT,
write_timeout: Self::DEFAULT_RW_TIMEOUT,
read_chunksize: 4096,
write_chunksize: 4096,
};
wrapper.set_timeouts(read_timeout, write_timeout)?;
wrapper.read_data_set_chunksize(wrapper.read_chunksize)?;
@ -62,7 +62,7 @@ impl Wrapper {
}
fn list_devices(vendor: u16, product: u16) -> std::io::Result<Vec<DeviceInfo>> {
let wrapper = Wrapper::new(None, None)?;
let wrapper = Self::new(None, None)?;
let mut device_list: *mut libftdi1_sys::ftdi_device_list = std::ptr::null_mut();
let devices = unsafe {
@ -152,8 +152,8 @@ impl Wrapper {
read_timeout: Option<std::time::Duration>,
write_timeout: Option<std::time::Duration>,
) -> std::io::Result<()> {
let read_timeout = read_timeout.unwrap_or(Wrapper::DEFAULT_RW_TIMEOUT);
let write_timeout = write_timeout.unwrap_or(Wrapper::DEFAULT_RW_TIMEOUT);
let read_timeout = read_timeout.unwrap_or(Self::DEFAULT_RW_TIMEOUT);
let write_timeout = write_timeout.unwrap_or(Self::DEFAULT_RW_TIMEOUT);
unsafe {
(*self.context).usb_read_timeout = i32::try_from(read_timeout.as_millis())
.map_err(|_| std::io::ErrorKind::InvalidInput)?;
@ -236,7 +236,7 @@ impl Wrapper {
}
fn set_latency_timer(&mut self, latency: Option<std::time::Duration>) -> std::io::Result<()> {
let latency = u8::try_from(latency.unwrap_or(Wrapper::DEFAULT_POLL_TIMEOUT).as_millis())
let latency = u8::try_from(latency.unwrap_or(Self::DEFAULT_POLL_TIMEOUT).as_millis())
.map_err(|_| std::io::ErrorKind::InvalidInput)?;
match unsafe { libftdi1_sys::ftdi_set_latency_timer(self.context, latency) } {
0 => Ok(()),
@ -320,7 +320,9 @@ impl Wrapper {
match self.read(&mut vec![0u8; self.read_chunksize]) {
Ok(_) => {}
Err(error) => match error.kind() {
std::io::ErrorKind::Interrupted | std::io::ErrorKind::WouldBlock => {
std::io::ErrorKind::Interrupted
| std::io::ErrorKind::TimedOut
| std::io::ErrorKind::WouldBlock => {
return Ok(());
}
_ => return Err(error),
@ -369,7 +371,7 @@ impl Wrapper {
Vec::from(buffer).as_mut_ptr(),
buffer.len() as i32,
&mut transferred,
100,
Self::WRITE_CHUNK_TIMEOUT.as_millis() as u32,
)
};
*written = transferred as usize;
@ -469,7 +471,7 @@ impl FtdiDevice {
wrapper.set_module_detach_mode(ModuleDetachMode::AutoDetachReattach);
wrapper.set_interface(InterfaceIndex::A)?;
const CHUNK_SIZE: usize = 64 * 1024;
const CHUNK_SIZE: usize = 16 * 1024;
wrapper.read_data_set_chunksize(CHUNK_SIZE)?;
wrapper.write_data_set_chunksize(CHUNK_SIZE)?;

View File

@ -1,4 +1,4 @@
use super::{error::Error, ftdi::FtdiDevice};
use super::{error::Error, ftdi::FtdiDevice, serial::SerialDevice};
use std::{
collections::VecDeque,
fmt::Display,
@ -75,34 +75,58 @@ pub trait Backend {
fn flush(&mut self) -> std::io::Result<()>;
fn reset(&mut self) -> std::io::Result<()> {
fn discard_input(&mut self) -> std::io::Result<()> {
Ok(())
}
fn discard_output(&mut self) -> std::io::Result<()> {
Ok(())
}
fn set_dtr(&mut self, _value: bool) -> std::io::Result<()> {
Ok(())
}
fn read_dsr(&mut self) -> std::io::Result<bool> {
Ok(false)
}
fn close(&mut self) {}
fn purge_incoming_data(&mut self) -> std::io::Result<()> {
fn reset(&mut self) -> std::io::Result<()> {
self.discard_output()?;
let timeout = Instant::now();
self.set_dtr(true)?;
loop {
match self.read(&mut vec![0; 1]) {
Ok(length) => match length {
0 => return Ok(()),
_ => {}
},
Err(error) => match error.kind() {
std::io::ErrorKind::Interrupted
| std::io::ErrorKind::TimedOut
| std::io::ErrorKind::WouldBlock => return Ok(()),
_ => return Err(error),
},
if self.read_dsr()? {
break;
}
if timeout.elapsed() >= RESET_TIMEOUT {
if timeout.elapsed() > RESET_TIMEOUT {
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"SC64 read buffer flush took too long",
"Couldn't reset SC64 device (on)",
));
}
}
self.discard_input()?;
let timeout = Instant::now();
self.set_dtr(false)?;
loop {
if !self.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Couldn't reset SC64 device (off)",
));
}
}
Ok(())
}
fn try_read_exact(&mut self, buffer: &mut [u8], block: bool) -> std::io::Result<Option<()>> {
@ -196,7 +220,7 @@ pub trait Backend {
}
pub struct SerialBackend {
device: serial2::SerialPort,
device: SerialDevice,
}
impl Backend for SerialBackend {
@ -212,47 +236,32 @@ impl Backend for SerialBackend {
self.device.flush()
}
fn reset(&mut self) -> std::io::Result<()> {
self.device.set_dtr(true)?;
let timeout = Instant::now();
loop {
self.device.discard_buffers()?;
if self.device.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Couldn't reset SC64 device (on)",
));
}
fn discard_input(&mut self) -> std::io::Result<()> {
self.device.discard_input()
}
self.purge_incoming_data()?;
self.device.set_dtr(false)?;
let timeout = Instant::now();
loop {
if !self.device.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Couldn't reset SC64 device (off)",
));
}
fn discard_output(&mut self) -> std::io::Result<()> {
self.device.discard_output()
}
Ok(())
fn set_dtr(&mut self, value: bool) -> std::io::Result<()> {
self.device.set_dtr(value)
}
fn read_dsr(&mut self) -> std::io::Result<bool> {
self.device.read_dsr()
}
}
fn new_serial_backend(port: &str) -> std::io::Result<SerialBackend> {
let mut serial = serial2::SerialPort::open(port, 115_200)?;
serial.set_read_timeout(POLL_TIMEOUT)?;
serial.set_write_timeout(WRITE_TIMEOUT)?;
Ok(SerialBackend { device: serial })
Ok(SerialBackend {
device: SerialDevice::new(
port,
Some(POLL_TIMEOUT),
Some(READ_TIMEOUT),
Some(WRITE_TIMEOUT),
)?,
})
}
struct FtdiBackend {
@ -272,40 +281,20 @@ impl Backend for FtdiBackend {
self.device.flush()
}
fn reset(&mut self) -> std::io::Result<()> {
self.device.discard_output()?;
let timeout = Instant::now();
self.device.set_dtr(true)?;
loop {
if self.device.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Couldn't reset SC64 device (on)",
));
}
fn discard_input(&mut self) -> std::io::Result<()> {
self.device.discard_input()
}
self.device.discard_input()?;
let timeout = Instant::now();
self.device.set_dtr(false)?;
loop {
if !self.device.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Couldn't reset SC64 device (off)",
));
}
fn discard_output(&mut self) -> std::io::Result<()> {
self.device.discard_output()
}
Ok(())
fn set_dtr(&mut self, value: bool) -> std::io::Result<()> {
self.device.set_dtr(value)
}
fn read_dsr(&mut self) -> std::io::Result<bool> {
self.device.read_dsr()
}
}
@ -580,21 +569,17 @@ pub fn list_local_devices() -> Result<Vec<DeviceInfo>, Error> {
}
}
if let Ok(list) = serialport::available_ports() {
if let Ok(list) = SerialDevice::list(SC64_VID, SC64_PID) {
for device in list.into_iter() {
if let serialport::SerialPortType::UsbPort(i) = device.port_type {
if let Some(serial) = i.serial_number {
if i.vid == SC64_VID && i.pid == SC64_PID && serial.starts_with(SC64_SID) {
if device.serial.starts_with(SC64_SID) {
devices.push(DeviceInfo {
backend: BackendType::Serial,
port: format!("{SERIAL_PREFIX}{}", device.port_name),
serial,
port: format!("{SERIAL_PREFIX}{}", device.port),
serial: device.serial,
});
}
}
}
}
}
if devices.len() == 0 {
return Err(Error::new("No SC64 devices found"));

View File

@ -3,6 +3,7 @@ mod error;
pub mod firmware;
mod ftdi;
mod link;
mod serial;
pub mod server;
mod time;
mod types;

View File

@ -0,0 +1,155 @@
pub struct DeviceInfo {
pub port: String,
pub serial: String,
}
pub struct SerialDevice {
serial: serial2::SerialPort,
unclog_buffer: std::collections::VecDeque<u8>,
poll_timeout: std::time::Duration,
read_timeout: std::time::Duration,
write_timeout: std::time::Duration,
}
impl SerialDevice {
const DEFAULT_POLL_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(16);
const DEFAULT_RW_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
const WRITE_CHUNK_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(100);
const BUFFER_SIZE: usize = 16 * 1024;
pub fn new(
port: &str,
poll_timeout: Option<std::time::Duration>,
read_timeout: Option<std::time::Duration>,
write_timeout: Option<std::time::Duration>,
) -> std::io::Result<Self> {
let mut device = Self {
serial: serial2::SerialPort::open(port, 115_200)?,
unclog_buffer: std::collections::VecDeque::new(),
poll_timeout: poll_timeout.unwrap_or(Self::DEFAULT_POLL_TIMEOUT),
read_timeout: read_timeout.unwrap_or(Self::DEFAULT_RW_TIMEOUT),
write_timeout: write_timeout.unwrap_or(Self::DEFAULT_RW_TIMEOUT),
};
device.serial.set_read_timeout(device.poll_timeout)?;
device.serial.set_write_timeout(Self::WRITE_CHUNK_TIMEOUT)?;
Ok(device)
}
pub fn list(vendor: u16, product: u16) -> std::io::Result<Vec<DeviceInfo>> {
let mut devices = vec![];
for port in serialport::available_ports()? {
if let serialport::SerialPortType::UsbPort(info) = port.port_type {
if info.vid == vendor && info.pid == product && info.serial_number.is_some() {
devices.push(DeviceInfo {
port: port.port_name,
serial: info.serial_number.unwrap(),
})
}
}
}
Ok(devices)
}
pub fn set_dtr(&mut self, value: bool) -> std::io::Result<()> {
self.serial.set_dtr(value)
}
pub fn read_dsr(&mut self) -> std::io::Result<bool> {
self.serial.read_dsr()
}
pub fn discard_input(&mut self) -> std::io::Result<()> {
let timeout = std::time::Instant::now();
self.serial.discard_input_buffer()?;
loop {
match self.serial.read(&mut vec![0u8; Self::BUFFER_SIZE]) {
Ok(_) => {}
Err(error) => match error.kind() {
std::io::ErrorKind::Interrupted
| std::io::ErrorKind::TimedOut
| std::io::ErrorKind::WouldBlock => {
return Ok(());
}
_ => return Err(error),
},
};
if timeout.elapsed() > self.read_timeout {
return Err(std::io::ErrorKind::TimedOut.into());
}
}
}
pub fn discard_output(&mut self) -> std::io::Result<()> {
self.serial.discard_output_buffer()
}
fn unclog_pipe(&mut self) -> std::io::Result<()> {
let mut buffer = vec![0u8; Self::BUFFER_SIZE];
let read = match self.serial.read(&mut buffer) {
Ok(read) => read,
Err(error) => match error.kind() {
std::io::ErrorKind::Interrupted
| std::io::ErrorKind::TimedOut
| std::io::ErrorKind::WouldBlock => 0,
_ => return Err(error),
},
};
self.unclog_buffer.extend(buffer[0..read].iter());
Ok(())
}
}
impl std::io::Read for SerialDevice {
fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
if buffer.is_empty() {
Err(std::io::ErrorKind::InvalidInput.into())
} else if self.unclog_buffer.is_empty() {
self.serial.read(buffer)
} else {
for (index, item) in buffer.iter_mut().enumerate() {
if let Some(byte) = self.unclog_buffer.pop_front() {
*item = byte;
} else {
return Ok(index);
}
}
Ok(buffer.len())
}
}
}
impl std::io::Write for SerialDevice {
fn write(&mut self, buffer: &[u8]) -> std::io::Result<usize> {
let timeout = std::time::Instant::now();
loop {
match self.serial.write(buffer) {
Ok(bytes) => return Ok(bytes),
Err(error) => match error.kind() {
std::io::ErrorKind::TimedOut => self.unclog_pipe()?,
_ => return Err(error),
},
};
if timeout.elapsed() > self.write_timeout {
return Err(std::io::ErrorKind::TimedOut.into());
}
}
}
fn flush(&mut self) -> std::io::Result<()> {
let timeout = std::time::Instant::now();
loop {
match self.serial.flush() {
Ok(()) => return Ok(()),
Err(error) => match error.kind() {
std::io::ErrorKind::TimedOut => self.unclog_pipe()?,
_ => return Err(error),
},
};
if timeout.elapsed() > self.write_timeout {
return Err(std::io::ErrorKind::TimedOut.into());
}
}
}
}