diff --git a/Cargo.lock b/Cargo.lock index 5ff54c3..6232b09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "bitflags" @@ -49,6 +49,14 @@ dependencies = [ "volatile-register", ] +[[package]] +name = "uart_sifive" +version = "0.0.0" +dependencies = [ + "bitflags 2.6.0", + "volatile-register", +] + [[package]] name = "uart_xilinx" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 7693d02..944051b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["uart8250", "uart_xilinx"] +members = ["uart8250", "uart_xilinx", "uart_sifive"] diff --git a/uart_sifive/Cargo.lock b/uart_sifive/Cargo.lock new file mode 100644 index 0000000..4440e5f --- /dev/null +++ b/uart_sifive/Cargo.lock @@ -0,0 +1,32 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "uart_sifive" +version = "0.0.0" +dependencies = [ + "bitflags", + "volatile-register", +] + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] diff --git a/uart_sifive/Cargo.toml b/uart_sifive/Cargo.toml new file mode 100644 index 0000000..2382406 --- /dev/null +++ b/uart_sifive/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "uart_sifive" +version = "0.0.0" +edition = "2024" +authors = ["Woshiluo Luo"] +license = "MIT" +keywords = ["uart"] +categories = ["embedded"] +description = "This crate provide a struct with many methods to operate uarts in Sifive UART" +homepage = "https://github.com/duskmoon314/uart-rs" +repository = "https://github.com/duskmoon314/uart-rs" +readme = "README.md" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bitflags = "2" +volatile-register = "0.2" + +[features] +default = [] diff --git a/uart_sifive/README.md b/uart_sifive/README.md new file mode 100644 index 0000000..2ecb5ae --- /dev/null +++ b/uart_sifive/README.md @@ -0,0 +1,17 @@ +# uart-sifive + +**Work In Progress** + +A simple struct and helper function for Sifive UART (`sifive,uart0`). + +## REF + +- + +## Intro + +**Noticed:** This crate may have problems. Any help would be welcomed, even if your help will bring about **breaking change**. Please feel free to start an Issue or a PR. + +Currently I **cannot guarantee** the stability of this crate, and it is likely to introduce destructive updates (including but not limited to renaming of structs, renaming of functions and methods, code restructuring). So fixing the dependency version should be a good way to go. + +Besides, this crate currently is not following [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/). Please feel free to start an Issue or a PR to help me fix this. diff --git a/uart_sifive/src/lib.rs b/uart_sifive/src/lib.rs new file mode 100644 index 0000000..010e48d --- /dev/null +++ b/uart_sifive/src/lib.rs @@ -0,0 +1,19 @@ +/*! +# uart_sifive + +A simple struct and helper function for Sifive UART (`sifive,uart0`). + +## REF + +- +*/ + +#![no_std] + +#[macro_use] +extern crate bitflags; + +pub mod registers; +pub mod uart; + +pub use uart::MmioUartSifive; diff --git a/uart_sifive/src/registers.rs b/uart_sifive/src/registers.rs new file mode 100644 index 0000000..08d2a33 --- /dev/null +++ b/uart_sifive/src/registers.rs @@ -0,0 +1,13 @@ +use volatile_register::{RO, RW, WO}; + +/// # UART Registers +#[repr(C)] +pub struct Registers { + pub tx: RW, + pub rx: RO, + pub txctrl: RW, + pub rxctrl: RW, + pub ie: RW, + pub ip: RO, + pub div: RW, +} diff --git a/uart_sifive/src/uart.rs b/uart_sifive/src/uart.rs new file mode 100644 index 0000000..22bf8b9 --- /dev/null +++ b/uart_sifive/src/uart.rs @@ -0,0 +1,209 @@ +use super::registers::Registers; + +bitflags! { + /// TxData Register + pub struct TxData: u32 { + const FULL = 1 << 31; + // const DATA = 0b1111_1111; + } + + /// RxData Register + pub struct RxData: u32 { + const EMPTY = 1 << 31; + // const DATA = 0b1111_1111; + } + + /// TxControl Register + pub struct TxControl: u32 { + const ENABLE = 0b01; + const NSTOP = 0b10; + // const COUNT = 0b111 << 15; + } + + /// RxControl Register + pub struct RxControl: u32 { + const ENABLE = 0b01; + const NSTOP = 0b10; + // const COUNT = 0b111 << 15; + } + + /// This sturct use for `ie` and `ip` register + pub struct InterruptRegister: u32 { + const RXWM = 0b10; + const TXWM = 0b01; + } + + // struct DivRegister: u32 { + // const div = 1 << 16 - 1; + // } +} + +/// # MMIO version of Sifive UART +/// +/// **Noticed** This hasn't been tested. +pub struct MmioUartSifive { + reg_pointer: *mut Registers, +} + +impl MmioUartSifive { + /// New a uart + pub const fn new(base_address: usize) -> Self { + Self { + reg_pointer: base_address as _, + } + } + + #[allow(clippy::mut_from_ref)] + fn reg(&self) -> &mut Registers { + unsafe { &mut *self.reg_pointer } + } + + /// Set a new base_address + pub fn set_base_address(&mut self, base_address: usize) { + self.reg_pointer = base_address as _; + } + + /// Read a byte + pub fn read_byte(&self) -> Option { + let rx = self.read_rx(); + let rx_empty = RxData::from_bits_truncate(rx).contains(RxData::EMPTY); + if !rx_empty { + Some(self.read_rx() as u8) + } else { + None + } + } + + /// Write a byte + pub fn write_byte(&self, value: u8) { + self.write_tx(value as u32) + } + + /// Read Rx FIFO + #[inline] + pub fn read_rx(&self) -> u32 { + self.reg().rx.read() + } + + #[inline] + pub fn read_tx(&self) -> u32 { + self.reg().tx.read() + } + + #[inline] + pub fn write_tx(&self, value: u32) { + unsafe { self.reg().tx.write(value) } + } + + #[inline] + pub fn read_rxctrl(&self) -> u32 { + self.reg().rxctrl.read() + } + + #[inline] + pub fn write_rxctrl(&self, value: u32) { + unsafe { self.reg().rxctrl.write(value) } + } + + #[inline] + pub fn read_txctrl(&self) -> u32 { + self.reg().txctrl.read() + } + + #[inline] + pub fn write_txctrl(&self, value: u32) { + unsafe { self.reg().txctrl.write(value) } + } + + #[inline] + pub fn read_ip(&self) -> InterruptRegister { + InterruptRegister::from_bits_truncate(self.reg().ip.read()) + } + + #[inline] + pub fn read_ie(&self) -> InterruptRegister { + InterruptRegister::from_bits_truncate(self.reg().ie.read()) + } + + #[inline] + pub fn write_ie(&self, value: u32) { + unsafe { self.reg().ie.write(value) } + } + + #[inline] + pub fn read_div(&self) -> u32 { + self.reg().div.read() + } + + #[inline] + pub fn write_div(&self, value: u32) { + unsafe { self.reg().div.write(value) } + } + + pub fn is_tx_fifo_full(&self) -> bool { + TxData::from_bits_truncate(self.read_tx()).contains(TxData::FULL) + } + + pub fn is_read_interrupt_enabled(&self) -> bool { + self.read_ie().contains(InterruptRegister::RXWM) + } + + pub fn is_write_interrupt_enabled(&self) -> bool { + self.read_ie().contains(InterruptRegister::TXWM) + } + + pub fn enable_write(&self) { + self.write_txctrl(self.read_txctrl() | TxControl::ENABLE.bits()) + } + + pub fn enable_read(&self) { + self.write_rxctrl(self.read_rxctrl() | RxControl::ENABLE.bits()) + } + + pub fn disable_write(&self) { + self.write_txctrl(self.read_txctrl() & !TxControl::ENABLE.bits()) + } + + pub fn disable_read(&self) { + self.write_rxctrl(self.read_rxctrl() & !RxControl::ENABLE.bits()) + } + + pub fn disable_interrupt(&self) { + self.write_ie(0) + } + + pub fn enable_read_interrupt(&self) { + self.write_ie((self.read_ie() | InterruptRegister::RXWM).bits() as u32) + } + + pub fn enable_write_interrupt(&self) { + self.write_ie((self.read_ie() | InterruptRegister::TXWM).bits() as u32) + } + + /// Read a slice + pub fn read(&self, buf: &mut [u8]) -> usize { + let mut count = 0; + for current in buf { + if let Some(ch) = self.read_byte() { + count += 1; + *current = ch; + } else { + break; + } + } + count + } + + /// Write a slice + pub fn write(&self, buf: &[u8]) -> usize { + let mut count = 0; + for current in buf { + if self.is_tx_fifo_full() { + break; + } + count += 1; + self.write_byte(*current); + } + count + } +}