#![no_std]
#![doc = include_str!("../README.md")]
#![deny(clippy::undocumented_unsafe_blocks)]
#![deny(unsafe_op_in_unsafe_fn)]
#[cfg(feature = "embedded-hal-nb")]
mod embedded_hal_nb;
#[cfg(feature = "embedded-io")]
mod embedded_io;
use bitflags::bitflags;
use core::fmt;
pub use safe_mmio::UniqueMmioPointer;
use safe_mmio::{
field, field_shared,
fields::{ReadPure, ReadPureWrite, ReadWrite, WriteOnly},
};
use thiserror::Error;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
struct DataRegister(u32);
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
struct ReceiveStatusRegister(u32);
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
struct FlagsRegister(u32);
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
struct LineControlRegister(u32);
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
struct ControlRegister(u32);
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
pub struct Interrupts(u32);
bitflags! {
impl DataRegister: u32 {
const OE = 1 << 11;
const BE = 1 << 10;
const PE = 1 << 9;
const FE = 1 << 8;
}
impl ReceiveStatusRegister: u32 {
const OE = 1 << 3;
const BE = 1 << 2;
const PE = 1 << 1;
const FE = 1 << 0;
}
impl FlagsRegister: u32 {
const RI = 1 << 8;
const TXFE = 1 << 7;
const RXFF = 1 << 6;
const TXFF = 1 << 5;
const RXFE = 1 << 4;
const BUSY = 1 << 3;
const DCD = 1 << 2;
const DSR = 1 << 1;
const CTS = 1 << 0;
}
impl LineControlRegister: u32 {
const SPS = 1 << 7;
const WLEN_5BITS = 0b00 << 5;
const WLEN_6BITS = 0b01 << 5;
const WLEN_7BITS = 0b10 << 5;
const WLEN_8BITS = 0b11 << 5;
const FEN = 1 << 4;
const STP2 = 1 << 3;
const EPS = 1 << 2;
const PEN = 1 << 1;
const BRK = 1 << 0;
}
impl ControlRegister: u32 {
const CTSEn = 1 << 15;
const RTSEn = 1 << 14;
const Out2 = 1 << 13;
const Out1 = 1 << 12;
const RTS = 1 << 11;
const DTR = 1 << 10;
const RXE = 1 << 9;
const TXE = 1 << 8;
const LBE = 1 << 7;
const SIRLP = 1 << 2;
const SIREN = 1 << 1;
const UARTEN = 1 << 0;
}
impl Interrupts: u32 {
const OEI = 1 << 10;
const BEI = 1 << 9;
const PEI = 1 << 8;
const FEI = 1 << 7;
const RTI = 1 << 6;
const TXI = 1 << 5;
const RXI = 1 << 4;
const DSRMI = 1 << 3;
const DCDMI = 1 << 2;
const CTSMI = 1 << 1;
const RIMI = 1 << 0;
}
}
#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
#[repr(C, align(4))]
pub struct PL011Registers {
uartdr: ReadWrite<u32>,
uartrsr_ecr: ReadPureWrite<u32>,
reserved_08: [u32; 4],
uartfr: ReadPure<FlagsRegister>,
reserved_1c: u32,
uartilpr: ReadPureWrite<u32>,
uartibrd: ReadPureWrite<u32>,
uartfbrd: ReadPureWrite<u32>,
uartlcr_h: ReadPureWrite<LineControlRegister>,
uartcr: ReadPureWrite<ControlRegister>,
uartifls: ReadPureWrite<u32>,
uartimsc: ReadPureWrite<Interrupts>,
uartris: ReadPure<Interrupts>,
uartmis: ReadPure<Interrupts>,
uarticr: WriteOnly<Interrupts>,
uartdmacr: ReadPureWrite<u32>,
reserved_4c: [u32; 997],
uartperiphid0: ReadPure<u32>,
uartperiphid1: ReadPure<u32>,
uartperiphid2: ReadPure<u32>,
uartperiphid3: ReadPure<u32>,
uartpcellid0: ReadPure<u32>,
uartpcellid1: ReadPure<u32>,
uartpcellid2: ReadPure<u32>,
uartpcellid3: ReadPure<u32>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DataBits {
Bits5,
Bits6,
Bits7,
Bits8,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Parity {
None,
Even,
Odd,
One,
Zero,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum StopBits {
One,
Two,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LineConfig {
pub data_bits: DataBits,
pub parity: Parity,
pub stop_bits: StopBits,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FifoLevel {
Bytes4 = 0b000,
Bytes8 = 0b001,
Bytes16 = 0b010,
Bytes24 = 0b011,
Bytes28 = 0b100,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Identification {
pub part_number: u16,
pub designer: u8,
pub revision_number: u8,
pub configuration: u8,
}
impl Identification {
const PART_NUMBER: u16 = 0x11;
const DESIGNER_ARM: u8 = b'A';
const REVISION_MAX: u8 = 0x03;
const CONFIGURATION: u8 = 0x00;
pub fn is_valid(&self) -> bool {
self.part_number == Self::PART_NUMBER
&& self.designer == Self::DESIGNER_ARM
&& self.revision_number <= Self::REVISION_MAX
&& self.configuration == Self::CONFIGURATION
}
}
#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
pub enum Error {
#[error("Invalid parameter")]
InvalidParameter,
#[error("Overrun")]
Overrun,
#[error("Break")]
Break,
#[error("Parity")]
Parity,
#[error("Framing")]
Framing,
}
pub struct Uart<'a> {
regs: UniqueMmioPointer<'a, PL011Registers>,
}
impl<'a> Uart<'a> {
pub fn new(regs: UniqueMmioPointer<'a, PL011Registers>) -> Self {
Self { regs }
}
pub fn enable(&mut self, config: LineConfig, baud_rate: u32, sysclk: u32) -> Result<(), Error> {
let (uartibrd, uartfbrd) = Self::calculate_baud_rate_divisor(baud_rate, sysclk)?;
let line_control = match config.data_bits {
DataBits::Bits5 => LineControlRegister::WLEN_5BITS,
DataBits::Bits6 => LineControlRegister::WLEN_6BITS,
DataBits::Bits7 => LineControlRegister::WLEN_7BITS,
DataBits::Bits8 => LineControlRegister::WLEN_8BITS,
} | match config.parity {
Parity::None => LineControlRegister::empty(),
Parity::Even => LineControlRegister::PEN | LineControlRegister::EPS,
Parity::Odd => LineControlRegister::PEN,
Parity::One => LineControlRegister::PEN | LineControlRegister::SPS,
Parity::Zero => {
LineControlRegister::PEN | LineControlRegister::EPS | LineControlRegister::SPS
}
} | match config.stop_bits {
StopBits::One => LineControlRegister::empty(),
StopBits::Two => LineControlRegister::STP2,
} | LineControlRegister::FEN;
field!(self.regs, uartrsr_ecr).write(0);
field!(self.regs, uartcr).write(ControlRegister::empty());
field!(self.regs, uartibrd).write(uartibrd);
field!(self.regs, uartfbrd).write(uartfbrd);
field!(self.regs, uartlcr_h).write(line_control);
field!(self.regs, uartcr)
.write(ControlRegister::RXE | ControlRegister::TXE | ControlRegister::UARTEN);
Ok(())
}
pub fn disable(&mut self) {
field!(self.regs, uartcr).write(ControlRegister::empty());
}
pub fn is_rx_fifo_empty(&self) -> bool {
self.flags().contains(FlagsRegister::RXFE)
}
pub fn is_rx_fifo_full(&self) -> bool {
self.flags().contains(FlagsRegister::RXFF)
}
pub fn is_tx_fifo_empty(&self) -> bool {
self.flags().contains(FlagsRegister::TXFE)
}
pub fn is_tx_fifo_full(&self) -> bool {
self.flags().contains(FlagsRegister::TXFF)
}
pub fn is_busy(&self) -> bool {
self.flags().contains(FlagsRegister::BUSY)
}
fn flags(&self) -> FlagsRegister {
field_shared!(self.regs, uartfr).read()
}
pub fn read_word(&mut self) -> Result<Option<u8>, Error> {
if self.is_rx_fifo_empty() {
return Ok(None);
}
let dr = field!(self.regs, uartdr).read();
let flags = DataRegister::from_bits_truncate(dr);
if flags.contains(DataRegister::OE) {
return Err(Error::Overrun);
} else if flags.contains(DataRegister::BE) {
return Err(Error::Break);
} else if flags.contains(DataRegister::PE) {
return Err(Error::Parity);
} else if flags.contains(DataRegister::FE) {
return Err(Error::Framing);
}
Ok(Some(dr as u8))
}
pub fn write_word(&mut self, word: u8) {
field!(self.regs, uartdr).write(word as u32);
}
pub fn read_identification(&self) -> Identification {
let id: [u32; 4] = {
[
field_shared!(self.regs, uartperiphid0).read(),
field_shared!(self.regs, uartperiphid1).read(),
field_shared!(self.regs, uartperiphid2).read(),
field_shared!(self.regs, uartperiphid3).read(),
]
};
Identification {
part_number: (id[0] & 0xff) as u16 | ((id[1] & 0x0f) << 8) as u16,
designer: ((id[1] & 0xf0) >> 4) as u8 | ((id[2] & 0x0f) << 4) as u8,
revision_number: ((id[2] & 0xf0) >> 4) as u8,
configuration: (id[3] & 0xff) as u8,
}
}
fn calculate_baud_rate_divisor(baud_rate: u32, sysclk: u32) -> Result<(u32, u32), Error> {
let baud_div = sysclk
.checked_mul(8)
.and_then(|clk| clk.checked_div(baud_rate))
.ok_or(Error::InvalidParameter)?;
let baud_div_bits = baud_div
.checked_add(1)
.map(|div| div >> 1)
.ok_or(Error::InvalidParameter)?;
let ibrd = baud_div_bits >> 6;
let fbrd = baud_div_bits & 0x3F;
if ibrd == 0 || (ibrd == 0xffff && fbrd != 0) || ibrd > 0xffff {
return Err(Error::InvalidParameter);
}
Ok((ibrd, fbrd))
}
pub fn set_interrupt_fifo_levels(&mut self, rx_level: FifoLevel, tx_level: FifoLevel) {
let fifo_levels = ((rx_level as u32) << 3) | tx_level as u32;
field!(self.regs, uartifls).write(fifo_levels);
}
pub fn raw_interrupt_status(&self) -> Interrupts {
field_shared!(self.regs, uartris).read()
}
pub fn masked_interrupt_status(&self) -> Interrupts {
field_shared!(self.regs, uartmis).read()
}
pub fn interrupt_masks(&self) -> Interrupts {
field_shared!(self.regs, uartimsc).read()
}
pub fn set_interrupt_masks(&mut self, masks: Interrupts) {
field!(self.regs, uartimsc).write(masks)
}
pub fn clear_interrupts(&mut self, interrupts: Interrupts) {
field!(self.regs, uarticr).write(interrupts)
}
}
unsafe impl Sync for Uart<'_> {}
impl fmt::Write for Uart<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for byte in s.as_bytes() {
while self.is_tx_fifo_full() {}
self.write_word(*byte);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use zerocopy::transmute_mut;
pub struct FakePL011Registers {
regs: [u32; 1024],
}
impl FakePL011Registers {
pub fn new() -> Self {
Self { regs: [0u32; 1024] }
}
pub fn clear(&mut self) {
self.regs.fill(0);
}
pub fn reg_write(&mut self, offset: usize, value: u32) {
self.regs[offset / 4] = value;
}
pub fn reg_read(&self, offset: usize) -> u32 {
self.regs[offset / 4]
}
fn get(&mut self) -> UniqueMmioPointer<'_, PL011Registers> {
UniqueMmioPointer::from(transmute_mut!(&mut self.regs))
}
pub fn uart_for_test(&mut self) -> Uart<'_> {
Uart::new(self.get())
}
}
#[test]
fn regs_size() {
assert_eq!(core::mem::size_of::<PL011Registers>(), 0x1000);
}
#[test]
fn enable_230400_8n1() {
let mut regs = FakePL011Registers::new();
let mut uart = regs.uart_for_test();
let config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
assert_eq!(Ok(()), uart.enable(config, 230400, 4_000_000));
assert_eq!(0x00, regs.reg_read(0x004)); assert_eq!(1, regs.reg_read(0x024)); assert_eq!(5, regs.reg_read(0x028)); assert_eq!(0b01110000, regs.reg_read(0x02c)); assert_eq!(0x0301, regs.reg_read(0x030)); }
#[test]
fn enable_example_baudrates() {
let mut regs = FakePL011Registers::new();
{
let mut uart = regs.uart_for_test();
let config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
assert_eq!(Ok(()), uart.enable(config, 230400, 4_000_000));
assert_eq!(0x1, regs.reg_read(0x024)); assert_eq!(0x5, regs.reg_read(0x028)); }
regs.clear();
{
let mut uart = regs.uart_for_test();
let config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
assert_eq!(Ok(()), uart.enable(config, 115200, 4_000_000));
assert_eq!(0x2, regs.reg_read(0x024)); assert_eq!(0xb, regs.reg_read(0x028)); }
regs.clear();
{
let mut uart = regs.uart_for_test();
let config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
assert_eq!(Ok(()), uart.enable(config, 76800, 4_000_000));
assert_eq!(0x3, regs.reg_read(0x024)); assert_eq!(0x10, regs.reg_read(0x028)); }
regs.clear();
{
let mut uart = regs.uart_for_test();
let config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
assert_eq!(Ok(()), uart.enable(config, 38400, 4_000_000));
assert_eq!(0x6, regs.reg_read(0x024)); assert_eq!(0x21, regs.reg_read(0x028)); }
regs.clear();
{
let mut uart = regs.uart_for_test();
let config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
assert_eq!(Ok(()), uart.enable(config, 14400, 4_000_000));
assert_eq!(0x11, regs.reg_read(0x024)); assert_eq!(0x17, regs.reg_read(0x028)); }
regs.clear();
{
let mut uart = regs.uart_for_test();
let config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
assert_eq!(Ok(()), uart.enable(config, 2400, 4_000_000));
assert_eq!(0x68, regs.reg_read(0x024)); assert_eq!(0xb, regs.reg_read(0x028)); }
regs.clear();
{
let mut uart = regs.uart_for_test();
let config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
assert_eq!(Ok(()), uart.enable(config, 110, 4_000_000));
assert_eq!(0x8e0, regs.reg_read(0x024)); assert_eq!(0x2f, regs.reg_read(0x028)); }
}
#[test]
fn enable_invalid_baudrates() {
let mut regs = FakePL011Registers::new();
let mut uart = regs.uart_for_test();
{
let config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
assert_eq!(
Err(Error::InvalidParameter),
uart.enable(config, 0, 4_000_000)
);
}
{
let config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
assert_eq!(
Err(Error::InvalidParameter),
uart.enable(config, 1, 1048561)
);
}
{
let config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
assert_eq!(
Err(Error::InvalidParameter),
uart.enable(config, 1, 100_000_000)
);
}
{
let config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
assert_eq!(Err(Error::InvalidParameter), uart.enable(config, 1, 1));
}
}
#[test]
fn enable_lineconfigs() {
let mut regs = FakePL011Registers::new();
{
let mut uart = regs.uart_for_test();
let config = LineConfig {
data_bits: DataBits::Bits7,
parity: Parity::Even,
stop_bits: StopBits::Two,
};
assert_eq!(Ok(()), uart.enable(config, 230400, 4_000_000));
assert_eq!(0b01011110, regs.reg_read(0x02c)); }
regs.clear();
{
let mut regs = FakePL011Registers::new();
let mut uart = regs.uart_for_test();
let config = LineConfig {
data_bits: DataBits::Bits6,
parity: Parity::Odd,
stop_bits: StopBits::One,
};
assert_eq!(Ok(()), uart.enable(config, 230400, 4_000_000));
assert_eq!(0b00110010, regs.reg_read(0x02c)); }
regs.clear();
{
let mut regs = FakePL011Registers::new();
let mut uart = regs.uart_for_test();
let config = LineConfig {
data_bits: DataBits::Bits5,
parity: Parity::One,
stop_bits: StopBits::One,
};
assert_eq!(Ok(()), uart.enable(config, 230400, 4_000_000));
assert_eq!(0b10010010, regs.reg_read(0x02c)); }
{
let mut regs = FakePL011Registers::new();
let mut uart = regs.uart_for_test();
let config = LineConfig {
data_bits: DataBits::Bits5,
parity: Parity::Zero,
stop_bits: StopBits::Two,
};
assert_eq!(Ok(()), uart.enable(config, 230400, 4_000_000));
assert_eq!(0b10011110, regs.reg_read(0x02c)); }
}
#[test]
fn disable() {
let mut regs = FakePL011Registers::new();
let mut uart = regs.uart_for_test();
let config = LineConfig {
data_bits: DataBits::Bits8,
parity: Parity::None,
stop_bits: StopBits::One,
};
assert_eq!(Ok(()), uart.enable(config, 230400, 4_000_000));
uart.disable();
assert_eq!(0, regs.reg_read(0x030)); }
#[test]
fn rx_fifo_empty() {
let mut regs = FakePL011Registers::new();
{
let uart = regs.uart_for_test();
assert!(!uart.is_rx_fifo_empty());
}
{
regs.reg_write(0x018, 1 << 4);
let uart = regs.uart_for_test();
assert!(uart.is_rx_fifo_empty());
}
}
#[test]
fn rx_fifo_full() {
let mut regs = FakePL011Registers::new();
{
let uart = regs.uart_for_test();
assert!(!uart.is_rx_fifo_full());
}
{
regs.reg_write(0x018, 1 << 6);
let uart = regs.uart_for_test();
assert!(uart.is_rx_fifo_full());
}
}
#[test]
fn tx_fifo_empty() {
let mut regs = FakePL011Registers::new();
{
let uart = regs.uart_for_test();
assert!(!uart.is_tx_fifo_empty());
}
{
regs.reg_write(0x018, 1 << 7);
let uart = regs.uart_for_test();
assert!(uart.is_tx_fifo_empty());
}
}
#[test]
fn tx_fifo_full() {
let mut regs = FakePL011Registers::new();
{
let uart = regs.uart_for_test();
assert!(!uart.is_tx_fifo_full());
}
{
regs.reg_write(0x018, 1 << 5);
let uart = regs.uart_for_test();
assert!(uart.is_tx_fifo_full());
}
}
#[test]
fn busy() {
let mut regs = FakePL011Registers::new();
{
let uart = regs.uart_for_test();
assert!(!uart.is_busy());
}
{
regs.reg_write(0x018, 1 << 3);
let uart = regs.uart_for_test();
assert!(uart.is_busy());
}
}
#[test]
fn read_word() {
let mut regs = FakePL011Registers::new();
{
regs.reg_write(0x000, 1 << 11);
let mut uart = regs.uart_for_test();
assert_eq!(Err(Error::Overrun), uart.read_word());
}
{
regs.reg_write(0x000, 1 << 10);
let mut uart = regs.uart_for_test();
assert_eq!(Err(Error::Break), uart.read_word());
}
{
regs.reg_write(0x000, 1 << 9);
let mut uart = regs.uart_for_test();
assert_eq!(Err(Error::Parity), uart.read_word());
}
{
regs.reg_write(0x000, 1 << 8);
let mut uart = regs.uart_for_test();
assert_eq!(Err(Error::Framing), uart.read_word());
}
{
regs.reg_write(0x000, 0x41);
let mut uart = regs.uart_for_test();
assert_eq!(Ok(Some(0x41)), uart.read_word());
}
{
regs.reg_write(0x018, 0x10);
let mut uart = regs.uart_for_test();
assert_eq!(Ok(None), uart.read_word());
}
}
#[test]
fn write_word() {
let mut regs = FakePL011Registers::new();
let mut uart = regs.uart_for_test();
uart.write_word(0x41);
assert_eq!(0x41, regs.reg_read(0x000));
}
#[test]
fn read_identification() {
let mut regs = FakePL011Registers::new();
regs.reg_write(0xfe0, 0x11);
regs.reg_write(0xfe4, 0x10);
regs.reg_write(0xfe8, 0x34);
regs.reg_write(0xfec, 0x00);
let uart = regs.uart_for_test();
let identification = uart.read_identification();
assert_eq!(0x0011, identification.part_number);
assert_eq!(0x41, identification.designer);
assert_eq!(0x03, identification.revision_number);
assert_eq!(0x00, identification.configuration);
assert!(identification.is_valid());
}
#[test]
fn fifo_level() {
let mut regs = FakePL011Registers::new();
{
let mut uart = regs.uart_for_test();
uart.set_interrupt_fifo_levels(FifoLevel::Bytes4, FifoLevel::Bytes8);
}
assert_eq!(regs.reg_read(0x34), 0x01);
{
let mut uart = regs.uart_for_test();
uart.set_interrupt_fifo_levels(FifoLevel::Bytes8, FifoLevel::Bytes16);
}
assert_eq!(regs.reg_read(0x34), 0x0a);
{
let mut uart = regs.uart_for_test();
uart.set_interrupt_fifo_levels(FifoLevel::Bytes16, FifoLevel::Bytes24);
}
assert_eq!(regs.reg_read(0x34), 0x13);
{
let mut uart = regs.uart_for_test();
uart.set_interrupt_fifo_levels(FifoLevel::Bytes24, FifoLevel::Bytes28);
}
assert_eq!(regs.reg_read(0x34), 0x1c);
{
let mut uart = regs.uart_for_test();
uart.set_interrupt_fifo_levels(FifoLevel::Bytes28, FifoLevel::Bytes4);
}
assert_eq!(regs.reg_read(0x34), 0x20);
}
#[test]
fn interrupt_status() {
let mut regs = FakePL011Registers::new();
{
let uart = regs.uart_for_test();
assert_eq!(uart.raw_interrupt_status(), Interrupts::empty());
assert_eq!(uart.masked_interrupt_status(), Interrupts::empty());
}
{
regs.reg_write(0x3c, 0b0000_0110_0000_1001);
regs.reg_write(0x40, 0b0000_0100_0000_0001);
let uart = regs.uart_for_test();
assert_eq!(
uart.raw_interrupt_status(),
Interrupts::OEI | Interrupts::BEI | Interrupts::DSRMI | Interrupts::RIMI
);
assert_eq!(
uart.masked_interrupt_status(),
Interrupts::OEI | Interrupts::RIMI
);
}
}
#[test]
fn interrupt_mask() {
let mut regs = FakePL011Registers::new();
{
let mut uart = regs.uart_for_test();
assert_eq!(uart.interrupt_masks(), Interrupts::empty());
uart.set_interrupt_masks(Interrupts::BEI | Interrupts::RTI);
assert_eq!(uart.interrupt_masks(), Interrupts::BEI | Interrupts::RTI);
}
assert_eq!(regs.reg_read(0x38), 0b0000_0010_0100_0000);
}
#[test]
fn interrupt_clear() {
let mut regs = FakePL011Registers::new();
{
let mut uart = regs.uart_for_test();
uart.clear_interrupts(Interrupts::OEI | Interrupts::RIMI | Interrupts::RTI);
}
assert_eq!(regs.reg_read(0x44), 0b0000_0100_0100_0001);
}
#[test]
fn core_write() {
let mut regs = FakePL011Registers::new();
let mut uart = regs.uart_for_test();
assert_eq!(Ok(()), core::fmt::Write::write_str(&mut uart, "hello"));
}
}