diff options
| author | taitep <taitep@taitep.se> | 2026-01-29 19:07:43 +0100 |
|---|---|---|
| committer | taitep <taitep@taitep.se> | 2026-01-29 19:07:43 +0100 |
| commit | bbfa20befe163c04d0a99278107f2608639318d3 (patch) | |
| tree | ada64685dcefcf587fb2016a77a78a47465cd290 /src/devices/serial.rs | |
| parent | 36e6ec10069fe84aa677ab9ea4446e7fa3332886 (diff) | |
Replace custom UART with a sifive uart subset
Diffstat (limited to 'src/devices/serial.rs')
| -rw-r--r-- | src/devices/serial.rs | 140 |
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) + } + } + } + } +} |
