summaryrefslogtreecommitdiff
path: root/src/devices/serial.rs
diff options
context:
space:
mode:
authortaitep <taitep@taitep.se>2026-01-29 19:07:43 +0100
committertaitep <taitep@taitep.se>2026-01-29 19:07:43 +0100
commitbbfa20befe163c04d0a99278107f2608639318d3 (patch)
treeada64685dcefcf587fb2016a77a78a47465cd290 /src/devices/serial.rs
parent36e6ec10069fe84aa677ab9ea4446e7fa3332886 (diff)
Replace custom UART with a sifive uart subset
Diffstat (limited to 'src/devices/serial.rs')
-rw-r--r--src/devices/serial.rs140
1 files changed, 140 insertions, 0 deletions
diff --git a/src/devices/serial.rs b/src/devices/serial.rs
new file mode 100644
index 0000000..7e90748
--- /dev/null
+++ b/src/devices/serial.rs
@@ -0,0 +1,140 @@
+// Copyright (c) 2026 taitep
+// SPDX-License-Identifier: BSD-2-Clause
+//
+// This file is part of TRVE (https://gitea.taitep.se/taitep/trve)
+// See LICENSE file in the project root for full license text.
+
+use std::{
+ io::{Read, Write},
+ sync::{Arc, Mutex},
+ time::Duration,
+};
+
+mod fifo;
+use fifo::UartFifo;
+
+use crate::{exceptions::MemoryExceptionType, mem::MemDeviceInterface};
+
+pub struct SifiveUart {
+ rx: Mutex<(UartFifo<2048>, bool)>,
+ tx: Mutex<(UartFifo<2048>, bool)>,
+}
+
+impl SifiveUart {
+ pub fn new_arc() -> Arc<Self> {
+ Arc::new(Self {
+ rx: Mutex::new((UartFifo::default(), false)),
+ tx: Mutex::new((UartFifo::default(), false)),
+ })
+ }
+
+ pub fn spawn_io_thread<R: Read + Send + 'static, T: Write + Send + Sync + 'static>(
+ self: Arc<Self>,
+ mut rx_backend: R,
+ mut tx_backend: T,
+ interval: Duration,
+ ) {
+ std::thread::spawn(move || {
+ loop {
+ {
+ // Read data
+ let mut rx_guard = self.rx.lock().expect("could not lock uart RX half");
+ let (rx_buf, rx_en) = &mut *rx_guard;
+
+ if *rx_en {
+ let _ = rx_buf.read_from(&mut rx_backend);
+ }
+ }
+ {
+ // Write data
+ let mut tx_guard = self.tx.lock().expect("could not lock uart RX half");
+ let (tx_buf, tx_en) = &mut *tx_guard;
+
+ if *tx_en {
+ let _ = tx_buf.write_to(&mut tx_backend);
+ let _ = tx_backend.flush();
+ }
+ }
+
+ std::thread::sleep(interval);
+ }
+ });
+ }
+}
+
+impl MemDeviceInterface for SifiveUart {
+ fn write_word(&self, addr: u64, value: u32) -> Result<(), MemoryExceptionType> {
+ // dbg!(addr, value);
+ match addr {
+ 0x00 => {
+ // TXDATA
+ let (ref mut tx_buf, _) = *self.tx.lock().expect("could not lock uart TX half");
+ tx_buf.push_single_byte(value as u8);
+ Ok(())
+ }
+ 0x08 => {
+ // TXCTRL
+ let (_, ref mut tx_en) = *self.tx.lock().expect("could not lock uart TX half");
+ *tx_en = value & 1 != 0;
+ Ok(())
+ }
+ 0x04 => Ok(()), // RXDATA
+ 0x0c => {
+ // RXCTRL
+ let (_, ref mut rx_en) = *self.rx.lock().expect("could not lock uart RX half");
+ *rx_en = value & 1 != 0;
+ Ok(())
+ }
+ 0x10 => Ok(()), // IE
+ 0x14 => Ok(()), // IP
+ 0x18 => Ok(()), // DIV
+ _ => {
+ if addr < 0x1c {
+ Err(MemoryExceptionType::AddressMisaligned)
+ } else {
+ Err(MemoryExceptionType::AccessFault)
+ }
+ }
+ }
+ }
+
+ fn read_word(&self, addr: u64) -> Result<u32, MemoryExceptionType> {
+ // dbg!(addr);
+ match addr {
+ 0x00 => {
+ // TXDATA
+ let (ref tx_buf, _) = *self.tx.lock().expect("could not lock uart TX half");
+ Ok(if tx_buf.is_full() { 0x80000000 } else { 0 })
+ }
+ 0x08 => {
+ // TXCTRL
+ let (_, tx_en) = *self.tx.lock().expect("could not lock uart TX half");
+ Ok(if tx_en { 1 } else { 0 })
+ }
+ 0x04 => {
+ // RXDATA
+ let (ref mut rx_buf, _) = *self.rx.lock().expect("could not lock uart RX half");
+ Ok(match rx_buf.pop_single_byte() {
+ None => 0x80000000,
+ Some(b) => b as u32,
+ })
+ }
+ 0x0c => {
+ // RXCTRL
+ let (_, rx_en) = *self.rx.lock().expect("could not lock uart RX half");
+ Ok(if rx_en { 1 } else { 0 })
+ }
+ 0x10 => Ok(0), // IE
+ 0x14 => Ok(0), // IP
+ 0x18 => Ok(1), // DIV
+
+ _ => {
+ if addr < 0x1c {
+ Err(MemoryExceptionType::AddressMisaligned)
+ } else {
+ Err(MemoryExceptionType::AccessFault)
+ }
+ }
+ }
+ }
+}