PL011: Use BUSY bit

Fixes #100
pull/102/head
Andre Richter 3 years ago
parent c8e9b9713f
commit 8324b1fdac
No known key found for this signature in database
GPG Key ID: 2116C1AB102F615E

@ -188,13 +188,14 @@ diff -uNr 05_safe_globals/Makefile 06_drivers_gpio_uart/Makefile
diff -uNr 05_safe_globals/src/_arch/aarch64/cpu.rs 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
--- 05_safe_globals/src/_arch/aarch64/cpu.rs
+++ 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
@@ -17,6 +17,16 @@
@@ -17,6 +17,17 @@
// Public Code
//--------------------------------------------------------------------------------------------------
+pub use asm::nop;
+
+/// Spin for `n` cycles.
+#[cfg(feature = "bsp_rpi3")]
+#[inline(always)]
+pub fn spin_for_cycles(n: usize) {
+ for _ in 0..n {
@ -435,12 +436,17 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 06_drivers_g
diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
--- 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
+++ 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
@@ -0,0 +1,381 @@
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! PL011 UART driver.
+//!
+//! # Resources
+//!
+//! - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
+//! - https://developer.arm.com/documentation/ddi0183/latest
+
+use crate::{
+ bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization,
@ -455,50 +461,63 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri
+
+// PL011 UART registers.
+//
+// Descriptions taken from
+// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
+// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
+register_bitfields! {
+ u32,
+
+ /// Flag Register
+ /// Flag Register.
+ FR [
+ /// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
+ /// Line Control Register, UARTLCR_ LCRH.
+ /// Line Control Register, LCR_H.
+ ///
+ /// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If
+ /// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does
+ /// not indicate if there is data in the transmit shift register.
+ /// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
+ /// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
+ /// - This bit does not indicate if there is data in the transmit shift register.
+ TXFE OFFSET(7) NUMBITS(1) [],
+
+ /// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
+ /// UARTLCR_ LCRH Register.
+ /// LCR_H Register.
+ ///
+ /// If the FIFO is disabled, this bit is set when the transmit holding register is full. If
+ /// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
+ /// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
+ /// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
+ TXFF OFFSET(5) NUMBITS(1) [],
+
+ /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
+ /// UARTLCR_H Register.
+ /// LCR_H Register.
+ ///
+ /// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
+ /// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
+ RXFE OFFSET(4) NUMBITS(1) []
+
+ /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
+ /// LCR_H Register.
+ ///
+ /// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
+ /// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
+ RXFE OFFSET(4) NUMBITS(1) [],
+
+ /// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
+ /// set until the complete byte, including all the stop bits, has been sent from the shift
+ /// register.
+ ///
+ /// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
+ /// the UART is enabled or not.
+ BUSY OFFSET(3) NUMBITS(1) []
+ ],
+
+ /// Integer Baud rate divisor
+ /// Integer Baud Rate Divisor.
+ IBRD [
+ /// Integer Baud rate divisor
+ IBRD OFFSET(0) NUMBITS(16) []
+ /// The integer baud rate divisor.
+ BAUD_DIVINT OFFSET(0) NUMBITS(16) []
+ ],
+
+ /// Fractional Baud rate divisor
+ /// Fractional Baud Rate Divisor.
+ FBRD [
+ /// Fractional Baud rate divisor
+ FBRD OFFSET(0) NUMBITS(6) []
+ /// The fractional baud rate divisor.
+ BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
+ ],
+
+ /// Line Control register
+ LCRH [
+ /// Line Control Register.
+ LCR_H [
+ /// Word length. These bits indicate the number of data bits transmitted or received in a
+ /// frame.
+ WLEN OFFSET(5) NUMBITS(2) [
@ -511,34 +530,42 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri
+ /// Enable FIFOs:
+ ///
+ /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
+ /// registers
+ /// registers.
+ ///
+ /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
+ /// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
+ FEN OFFSET(4) NUMBITS(1) [
+ FifosDisabled = 0,
+ FifosEnabled = 1
+ ]
+ ],
+
+ /// Control Register
+ /// Control Register.
+ CR [
+ /// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
+ /// Data reception occurs for UART signals. When the UART is disabled in the middle of
+ /// reception, it completes the current character before stopping.
+ RXE OFFSET(9) NUMBITS(1) [
+ /// Data reception occurs for either UART signals or SIR signals depending on the setting of
+ /// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
+ /// current character before stopping.
+ RXE OFFSET(9) NUMBITS(1) [
+ Disabled = 0,
+ Enabled = 1
+ ],
+
+ /// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
+ /// Data transmission occurs for UART signals. When the UART is disabled in the middle of
+ /// transmission, it completes the current character before stopping.
+ TXE OFFSET(8) NUMBITS(1) [
+ /// Data transmission occurs for either UART signals, or SIR signals depending on the
+ /// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
+ /// completes the current character before stopping.
+ TXE OFFSET(8) NUMBITS(1) [
+ Disabled = 0,
+ Enabled = 1
+ ],
+
+ /// UART enable
+ /// UART enable:
+ ///
+ /// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
+ /// reception, it completes the current character before stopping.
+ ///
+ /// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
+ /// or SIR signals depending on the setting of the SIREN bit
+ UARTEN OFFSET(0) NUMBITS(1) [
+ /// If the UART is disabled in the middle of transmission or reception, it completes the
+ /// current character before stopping.
@ -547,9 +574,9 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri
+ ]
+ ],
+
+ /// Interrupt Clear Register
+ /// Interrupt Clear Register.
+ ICR [
+ /// Meta field for all pending interrupts
+ /// Meta field for all pending interrupts.
+ ALL OFFSET(0) NUMBITS(11) []
+ ]
+}
@ -563,7 +590,7 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri
+ (0x1c => _reserved2),
+ (0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
+ (0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
+ (0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
+ (0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
+ (0x30 => CR: WriteOnly<u32, CR::Register>),
+ (0x34 => _reserved3),
+ (0x44 => ICR: WriteOnly<u32, ICR::Register>),
@ -621,11 +648,18 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri
+ /// This results in 8N1 and 921_600 baud.
+ ///
+ /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
+ /// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
+ /// `(48_000_000 / 16) / 921_600 = 3.2552083`.
+ ///
+ /// This means the integer part is `3` and goes into the `IBRD`.
+ /// The fractional part is `0.2552083`.
+ ///
+ /// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
+ /// give the best approximation we can get. A 5modulo error margin is acceptable for UART and we're
+ /// now at 0.02modulo.
+ /// `FBRD` calculation according to the PL011 Technical Reference Manual:
+ /// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
+ ///
+ /// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
+ /// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
+ ///
+ /// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16modulo`.
+ pub fn init(&mut self) {
+ // Execution can arrive here while there are still characters queued in the TX FIFO and
+ // actively being sent out by the UART hardware. If the UART is turned off in this case,
@ -643,14 +677,18 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri
+ // Clear all pending interrupts.
+ self.registers.ICR.write(ICR::ALL::CLEAR);
+
+ // Set the baud rate.
+ self.registers.IBRD.write(IBRD::IBRD.val(3));
+ self.registers.FBRD.write(FBRD::FBRD.val(16));
+
+ // Set 8N1 + FIFO on.
+ // From the PL011 Technical Reference Manual:
+ //
+ // The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
+ // updated on a single write strobe generated by a LCR_H write. So, to internally update the
+ // contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
+ //
+ // Set the baud rate, 8N1 and FIFO enabled.
+ self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
+ self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
+ self.registers
+ .LCRH
+ .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
+ .LCR_H
+ .write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
+
+ // Turn the UART on.
+ self.registers
@ -673,25 +711,10 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri
+
+ /// Block execution until the last buffered character has been physically put on the TX wire.
+ fn flush(&self) {
+ // The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
+ // symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
+ // 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
+ //
+ // Now make an educated guess for a good delay value. According to Wikipedia, the fastest
+ // RPi4 clocks around 1.5 GHz.
+ //
+ // So lets try to be on the safe side and default to 24_000 cycles, which would equal 12_000
+ // ns would the CPU be clocked at 2 GHz.
+ const CHAR_TIME_SAFE: usize = 24_000;
+
+ // Spin until TX FIFO empty is set.
+ while !self.registers.FR.matches_all(FR::TXFE::SET) {
+ // Spin until the busy bit is cleared.
+ while self.registers.FR.matches_all(FR::BUSY::SET) {
+ cpu::nop();
+ }
+
+ // After the last character has been queued for transmission, wait for the time of one
+ // character + some extra time for safety.
+ cpu::spin_for_cycles(CHAR_TIME_SAFE);
+ }
+
+ /// Retrieve a character.
@ -1217,12 +1240,15 @@ diff -uNr 05_safe_globals/src/console.rs 06_drivers_gpio_uart/src/console.rs
diff -uNr 05_safe_globals/src/cpu.rs 06_drivers_gpio_uart/src/cpu.rs
--- 05_safe_globals/src/cpu.rs
+++ 06_drivers_gpio_uart/src/cpu.rs
@@ -15,4 +15,4 @@
@@ -15,4 +15,7 @@
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
-pub use arch_cpu::wait_forever;
+pub use arch_cpu::{nop, spin_for_cycles, wait_forever};
+pub use arch_cpu::{nop, wait_forever};
+
+#[cfg(feature = "bsp_rpi3")]
+pub use arch_cpu::spin_for_cycles;
diff -uNr 05_safe_globals/src/driver.rs 06_drivers_gpio_uart/src/driver.rs
--- 05_safe_globals/src/driver.rs

@ -20,6 +20,7 @@ use cortex_a::asm;
pub use asm::nop;
/// Spin for `n` cycles.
#[cfg(feature = "bsp_rpi3")]
#[inline(always)]
pub fn spin_for_cycles(n: usize) {
for _ in 0..n {

@ -3,6 +3,11 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! PL011 UART driver.
//!
//! # Resources
//!
//! - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
//! - https://developer.arm.com/documentation/ddi0183/latest
use crate::{
bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization,
@ -17,50 +22,63 @@ use register::{mmio::*, register_bitfields, register_structs};
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
register_bitfields! {
u32,
/// Flag Register
/// Flag Register.
FR [
/// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// Line Control Register, UARTLCR_ LCRH.
/// Line Control Register, LCR_H.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If
/// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does
/// not indicate if there is data in the transmit shift register.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
/// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
/// - This bit does not indicate if there is data in the transmit shift register.
TXFE OFFSET(7) NUMBITS(1) [],
/// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_ LCRH Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is full. If
/// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
/// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_H Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
/// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// LCR_H Register.
///
/// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
/// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) [],
/// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
/// set until the complete byte, including all the stop bits, has been sent from the shift
/// register.
///
/// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
/// the UART is enabled or not.
BUSY OFFSET(3) NUMBITS(1) []
],
/// Integer Baud rate divisor
/// Integer Baud Rate Divisor.
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
/// The integer baud rate divisor.
BAUD_DIVINT OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
/// Fractional Baud Rate Divisor.
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
/// The fractional baud rate divisor.
BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Line Control Register.
LCR_H [
/// Word length. These bits indicate the number of data bits transmitted or received in a
/// frame.
WLEN OFFSET(5) NUMBITS(2) [
@ -73,34 +91,42 @@ register_bitfields! {
/// Enable FIFOs:
///
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
/// registers
/// registers.
///
/// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
/// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
FEN OFFSET(4) NUMBITS(1) [
FifosDisabled = 0,
FifosEnabled = 1
]
],
/// Control Register
/// Control Register.
CR [
/// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
/// Data reception occurs for UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
/// Data reception occurs for either UART signals or SIR signals depending on the setting of
/// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
/// current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
/// Data transmission occurs for UART signals. When the UART is disabled in the middle of
/// transmission, it completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
/// Data transmission occurs for either UART signals, or SIR signals depending on the
/// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
/// completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
/// UART enable:
///
/// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
/// reception, it completes the current character before stopping.
///
/// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
/// or SIR signals depending on the setting of the SIREN bit
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission or reception, it completes the
/// current character before stopping.
@ -109,9 +135,9 @@ register_bitfields! {
]
],
/// Interrupt Clear Register
/// Interrupt Clear Register.
ICR [
/// Meta field for all pending interrupts
/// Meta field for all pending interrupts.
ALL OFFSET(0) NUMBITS(11) []
]
}
@ -125,7 +151,7 @@ register_structs! {
(0x1c => _reserved2),
(0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
(0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
(0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
(0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
(0x30 => CR: WriteOnly<u32, CR::Register>),
(0x34 => _reserved3),
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
@ -183,11 +209,18 @@ impl PL011UartInner {
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
///
/// This means the integer part is `3` and goes into the `IBRD`.
/// The fractional part is `0.2552083`.
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
///
/// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
pub fn init(&mut self) {
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
@ -205,14 +238,18 @@ impl PL011UartInner {
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
// From the PL011 Technical Reference Manual:
//
// The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
//
// Set the baud rate, 8N1 and FIFO enabled.
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
// Turn the UART on.
self.registers
@ -235,25 +272,10 @@ impl PL011UartInner {
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
//
// Now make an educated guess for a good delay value. According to Wikipedia, the fastest
// RPi4 clocks around 1.5 GHz.
//
// So lets try to be on the safe side and default to 24_000 cycles, which would equal 12_000
// ns would the CPU be clocked at 2 GHz.
const CHAR_TIME_SAFE: usize = 24_000;
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
// Spin until the busy bit is cleared.
while self.registers.FR.matches_all(FR::BUSY::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
cpu::spin_for_cycles(CHAR_TIME_SAFE);
}
/// Retrieve a character.

@ -15,4 +15,7 @@ pub mod smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_cpu::{nop, spin_for_cycles, wait_forever};
pub use arch_cpu::{nop, wait_forever};
#[cfg(feature = "bsp_rpi3")]
pub use arch_cpu::spin_for_cycles;

@ -204,7 +204,7 @@ diff -uNr 06_drivers_gpio_uart/src/_arch/aarch64/cpu/boot.rs 07_uart_chainloader
diff -uNr 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs 07_uart_chainloader/src/_arch/aarch64/cpu.rs
--- 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
+++ 07_uart_chainloader/src/_arch/aarch64/cpu.rs
@@ -34,3 +34,19 @@
@@ -35,3 +35,19 @@
asm::wfe()
}
}
@ -241,7 +241,7 @@ diff -uNr 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 07_uart
diff -uNr 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
--- 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
+++ 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
@@ -257,7 +257,7 @@
@@ -279,7 +279,7 @@
}
/// Retrieve a character.
@ -250,7 +250,7 @@ diff -uNr 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 0
// If RX FIFO is empty,
if self.registers.FR.matches_all(FR::RXFE::SET) {
// immediately return in non-blocking mode.
@@ -272,12 +272,7 @@
@@ -294,12 +294,7 @@
}
// Read one character.
@ -264,7 +264,7 @@ diff -uNr 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 0
// Update statistics.
self.chars_read += 1;
@@ -357,14 +352,14 @@
@@ -379,14 +374,14 @@
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner
@ -393,12 +393,15 @@ diff -uNr 06_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs 07_uart_chainloader
diff -uNr 06_drivers_gpio_uart/src/cpu.rs 07_uart_chainloader/src/cpu.rs
--- 06_drivers_gpio_uart/src/cpu.rs
+++ 07_uart_chainloader/src/cpu.rs
@@ -15,4 +15,4 @@
@@ -15,7 +15,7 @@
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
-pub use arch_cpu::{nop, spin_for_cycles, wait_forever};
+pub use arch_cpu::{branch_to_raw_addr, nop, spin_for_cycles, wait_forever};
-pub use arch_cpu::{nop, wait_forever};
+pub use arch_cpu::{branch_to_raw_addr, nop, wait_forever};
#[cfg(feature = "bsp_rpi3")]
pub use arch_cpu::spin_for_cycles;
diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs
--- 06_drivers_gpio_uart/src/main.rs

@ -20,6 +20,7 @@ use cortex_a::asm;
pub use asm::nop;
/// Spin for `n` cycles.
#[cfg(feature = "bsp_rpi3")]
#[inline(always)]
pub fn spin_for_cycles(n: usize) {
for _ in 0..n {

@ -3,6 +3,11 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! PL011 UART driver.
//!
//! # Resources
//!
//! - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
//! - https://developer.arm.com/documentation/ddi0183/latest
use crate::{
bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization,
@ -17,50 +22,63 @@ use register::{mmio::*, register_bitfields, register_structs};
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
register_bitfields! {
u32,
/// Flag Register
/// Flag Register.
FR [
/// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// Line Control Register, UARTLCR_ LCRH.
/// Line Control Register, LCR_H.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If
/// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does
/// not indicate if there is data in the transmit shift register.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
/// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
/// - This bit does not indicate if there is data in the transmit shift register.
TXFE OFFSET(7) NUMBITS(1) [],
/// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_ LCRH Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is full. If
/// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
/// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_H Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
/// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// LCR_H Register.
///
/// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
/// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) [],
/// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
/// set until the complete byte, including all the stop bits, has been sent from the shift
/// register.
///
/// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
/// the UART is enabled or not.
BUSY OFFSET(3) NUMBITS(1) []
],
/// Integer Baud rate divisor
/// Integer Baud Rate Divisor.
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
/// The integer baud rate divisor.
BAUD_DIVINT OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
/// Fractional Baud Rate Divisor.
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
/// The fractional baud rate divisor.
BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Line Control Register.
LCR_H [
/// Word length. These bits indicate the number of data bits transmitted or received in a
/// frame.
WLEN OFFSET(5) NUMBITS(2) [
@ -73,34 +91,42 @@ register_bitfields! {
/// Enable FIFOs:
///
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
/// registers
/// registers.
///
/// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
/// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
FEN OFFSET(4) NUMBITS(1) [
FifosDisabled = 0,
FifosEnabled = 1
]
],
/// Control Register
/// Control Register.
CR [
/// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
/// Data reception occurs for UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
/// Data reception occurs for either UART signals or SIR signals depending on the setting of
/// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
/// current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
/// Data transmission occurs for UART signals. When the UART is disabled in the middle of
/// transmission, it completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
/// Data transmission occurs for either UART signals, or SIR signals depending on the
/// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
/// completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
/// UART enable:
///
/// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
/// reception, it completes the current character before stopping.
///
/// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
/// or SIR signals depending on the setting of the SIREN bit
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission or reception, it completes the
/// current character before stopping.
@ -109,9 +135,9 @@ register_bitfields! {
]
],
/// Interrupt Clear Register
/// Interrupt Clear Register.
ICR [
/// Meta field for all pending interrupts
/// Meta field for all pending interrupts.
ALL OFFSET(0) NUMBITS(11) []
]
}
@ -125,7 +151,7 @@ register_structs! {
(0x1c => _reserved2),
(0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
(0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
(0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
(0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
(0x30 => CR: WriteOnly<u32, CR::Register>),
(0x34 => _reserved3),
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
@ -183,11 +209,18 @@ impl PL011UartInner {
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
///
/// This means the integer part is `3` and goes into the `IBRD`.
/// The fractional part is `0.2552083`.
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
///
/// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
pub fn init(&mut self) {
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
@ -205,14 +238,18 @@ impl PL011UartInner {
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
// From the PL011 Technical Reference Manual:
//
// The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
//
// Set the baud rate, 8N1 and FIFO enabled.
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
// Turn the UART on.
self.registers
@ -235,25 +272,10 @@ impl PL011UartInner {
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
//
// Now make an educated guess for a good delay value. According to Wikipedia, the fastest
// RPi4 clocks around 1.5 GHz.
//
// So lets try to be on the safe side and default to 24_000 cycles, which would equal 12_000
// ns would the CPU be clocked at 2 GHz.
const CHAR_TIME_SAFE: usize = 24_000;
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
// Spin until the busy bit is cleared.
while self.registers.FR.matches_all(FR::BUSY::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
cpu::spin_for_cycles(CHAR_TIME_SAFE);
}
/// Retrieve a character.

@ -15,4 +15,7 @@ pub mod smp;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
pub use arch_cpu::{branch_to_raw_addr, nop, spin_for_cycles, wait_forever};
pub use arch_cpu::{branch_to_raw_addr, nop, wait_forever};
#[cfg(feature = "bsp_rpi3")]
pub use arch_cpu::spin_for_cycles;

@ -5,7 +5,7 @@
- We add abstractions for timer hardware, and implement them for the ARM architectural timer in
`_arch/aarch64`.
- The new timer functions are used to annotate UART prints with timestamps, and to get rid of the
cycle-based delays in the `GPIO` and `UART` device drivers, which boosts accuracy.
cycle-based delays in the `GPIO` device driver, which boosts accuracy.
- A `warn!()` macro is added.
## Test it
@ -135,11 +135,12 @@ diff -uNr 07_uart_chainloader/src/_arch/aarch64/cpu/boot.rs 08_timestamps/src/_a
diff -uNr 07_uart_chainloader/src/_arch/aarch64/cpu.rs 08_timestamps/src/_arch/aarch64/cpu.rs
--- 07_uart_chainloader/src/_arch/aarch64/cpu.rs
+++ 08_timestamps/src/_arch/aarch64/cpu.rs
@@ -19,14 +19,6 @@
@@ -19,15 +19,6 @@
pub use asm::nop;
-/// Spin for `n` cycles.
-#[cfg(feature = "bsp_rpi3")]
-#[inline(always)]
-pub fn spin_for_cycles(n: usize) {
- for _ in 0..n {
@ -150,7 +151,7 @@ diff -uNr 07_uart_chainloader/src/_arch/aarch64/cpu.rs 08_timestamps/src/_arch/a
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
@@ -34,19 +26,3 @@
@@ -35,19 +26,3 @@
asm::wfe()
}
}
@ -320,33 +321,7 @@ diff -uNr 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 08_times
diff -uNr 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 08_timestamps/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
--- 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
+++ 08_timestamps/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
@@ -235,16 +235,13 @@
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
+ use crate::{time, time::interface::TimeManager};
+ use core::time::Duration;
+
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
- //
- // Now make an educated guess for a good delay value. According to Wikipedia, the fastest
- // RPi4 clocks around 1.5 GHz.
- //
- // So lets try to be on the safe side and default to 24_000 cycles, which would equal 12_000
- // ns would the CPU be clocked at 2 GHz.
- const CHAR_TIME_SAFE: usize = 24_000;
+ const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
@@ -253,11 +250,11 @@
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
- cpu::spin_for_cycles(CHAR_TIME_SAFE);
+ time::time_manager().spin_for(CHAR_TIME_SAFE);
@@ -279,7 +279,7 @@
}
/// Retrieve a character.
@ -355,7 +330,7 @@ diff -uNr 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 08
// If RX FIFO is empty,
if self.registers.FR.matches_all(FR::RXFE::SET) {
// immediately return in non-blocking mode.
@@ -272,7 +269,12 @@
@@ -294,7 +294,12 @@
}
// Read one character.
@ -369,7 +344,7 @@ diff -uNr 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 08
// Update statistics.
self.chars_read += 1;
@@ -352,14 +354,14 @@
@@ -374,14 +379,14 @@
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner
@ -498,11 +473,14 @@ diff -uNr 07_uart_chainloader/src/bsp/raspberrypi/memory.rs 08_timestamps/src/bs
diff -uNr 07_uart_chainloader/src/cpu.rs 08_timestamps/src/cpu.rs
--- 07_uart_chainloader/src/cpu.rs
+++ 08_timestamps/src/cpu.rs
@@ -15,4 +15,4 @@
@@ -15,7 +15,4 @@
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
-pub use arch_cpu::{branch_to_raw_addr, nop, spin_for_cycles, wait_forever};
-pub use arch_cpu::{branch_to_raw_addr, nop, wait_forever};
-
-#[cfg(feature = "bsp_rpi3")]
-pub use arch_cpu::spin_for_cycles;
+pub use arch_cpu::{nop, wait_forever};
diff -uNr 07_uart_chainloader/src/main.rs 08_timestamps/src/main.rs

@ -3,6 +3,11 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! PL011 UART driver.
//!
//! # Resources
//!
//! - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
//! - https://developer.arm.com/documentation/ddi0183/latest
use crate::{
bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization,
@ -17,50 +22,63 @@ use register::{mmio::*, register_bitfields, register_structs};
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
register_bitfields! {
u32,
/// Flag Register
/// Flag Register.
FR [
/// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// Line Control Register, UARTLCR_ LCRH.
/// Line Control Register, LCR_H.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If
/// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does
/// not indicate if there is data in the transmit shift register.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
/// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
/// - This bit does not indicate if there is data in the transmit shift register.
TXFE OFFSET(7) NUMBITS(1) [],
/// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_ LCRH Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is full. If
/// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
/// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_H Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
/// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// LCR_H Register.
///
/// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
/// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) [],
/// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
/// set until the complete byte, including all the stop bits, has been sent from the shift
/// register.
///
/// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
/// the UART is enabled or not.
BUSY OFFSET(3) NUMBITS(1) []
],
/// Integer Baud rate divisor
/// Integer Baud Rate Divisor.
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
/// The integer baud rate divisor.
BAUD_DIVINT OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
/// Fractional Baud Rate Divisor.
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
/// The fractional baud rate divisor.
BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Line Control Register.
LCR_H [
/// Word length. These bits indicate the number of data bits transmitted or received in a
/// frame.
WLEN OFFSET(5) NUMBITS(2) [
@ -73,34 +91,42 @@ register_bitfields! {
/// Enable FIFOs:
///
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
/// registers
/// registers.
///
/// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
/// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
FEN OFFSET(4) NUMBITS(1) [
FifosDisabled = 0,
FifosEnabled = 1
]
],
/// Control Register
/// Control Register.
CR [
/// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
/// Data reception occurs for UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
/// Data reception occurs for either UART signals or SIR signals depending on the setting of
/// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
/// current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
/// Data transmission occurs for UART signals. When the UART is disabled in the middle of
/// transmission, it completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
/// Data transmission occurs for either UART signals, or SIR signals depending on the
/// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
/// completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
/// UART enable:
///
/// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
/// reception, it completes the current character before stopping.
///
/// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
/// or SIR signals depending on the setting of the SIREN bit
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission or reception, it completes the
/// current character before stopping.
@ -109,9 +135,9 @@ register_bitfields! {
]
],
/// Interrupt Clear Register
/// Interrupt Clear Register.
ICR [
/// Meta field for all pending interrupts
/// Meta field for all pending interrupts.
ALL OFFSET(0) NUMBITS(11) []
]
}
@ -125,7 +151,7 @@ register_structs! {
(0x1c => _reserved2),
(0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
(0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
(0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
(0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
(0x30 => CR: WriteOnly<u32, CR::Register>),
(0x34 => _reserved3),
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
@ -183,11 +209,18 @@ impl PL011UartInner {
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
///
/// This means the integer part is `3` and goes into the `IBRD`.
/// The fractional part is `0.2552083`.
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
///
/// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
pub fn init(&mut self) {
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
@ -205,14 +238,18 @@ impl PL011UartInner {
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
// From the PL011 Technical Reference Manual:
//
// The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
//
// Set the baud rate, 8N1 and FIFO enabled.
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
// Turn the UART on.
self.registers
@ -235,22 +272,10 @@ impl PL011UartInner {
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
// Spin until the busy bit is cleared.
while self.registers.FR.matches_all(FR::BUSY::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.

@ -3,6 +3,11 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! PL011 UART driver.
//!
//! # Resources
//!
//! - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
//! - https://developer.arm.com/documentation/ddi0183/latest
use crate::{
bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization,
@ -17,50 +22,63 @@ use register::{mmio::*, register_bitfields, register_structs};
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
register_bitfields! {
u32,
/// Flag Register
/// Flag Register.
FR [
/// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// Line Control Register, UARTLCR_ LCRH.
/// Line Control Register, LCR_H.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If
/// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does
/// not indicate if there is data in the transmit shift register.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
/// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
/// - This bit does not indicate if there is data in the transmit shift register.
TXFE OFFSET(7) NUMBITS(1) [],
/// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_ LCRH Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is full. If
/// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
/// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_H Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
/// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// LCR_H Register.
///
/// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
/// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) [],
/// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
/// set until the complete byte, including all the stop bits, has been sent from the shift
/// register.
///
/// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
/// the UART is enabled or not.
BUSY OFFSET(3) NUMBITS(1) []
],
/// Integer Baud rate divisor
/// Integer Baud Rate Divisor.
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
/// The integer baud rate divisor.
BAUD_DIVINT OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
/// Fractional Baud Rate Divisor.
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
/// The fractional baud rate divisor.
BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Line Control Register.
LCR_H [
/// Word length. These bits indicate the number of data bits transmitted or received in a
/// frame.
WLEN OFFSET(5) NUMBITS(2) [
@ -73,34 +91,42 @@ register_bitfields! {
/// Enable FIFOs:
///
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
/// registers
/// registers.
///
/// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
/// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
FEN OFFSET(4) NUMBITS(1) [
FifosDisabled = 0,
FifosEnabled = 1
]
],
/// Control Register
/// Control Register.
CR [
/// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
/// Data reception occurs for UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
/// Data reception occurs for either UART signals or SIR signals depending on the setting of
/// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
/// current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
/// Data transmission occurs for UART signals. When the UART is disabled in the middle of
/// transmission, it completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
/// Data transmission occurs for either UART signals, or SIR signals depending on the
/// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
/// completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
/// UART enable:
///
/// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
/// reception, it completes the current character before stopping.
///
/// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
/// or SIR signals depending on the setting of the SIREN bit
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission or reception, it completes the
/// current character before stopping.
@ -109,9 +135,9 @@ register_bitfields! {
]
],
/// Interrupt Clear Register
/// Interrupt Clear Register.
ICR [
/// Meta field for all pending interrupts
/// Meta field for all pending interrupts.
ALL OFFSET(0) NUMBITS(11) []
]
}
@ -125,7 +151,7 @@ register_structs! {
(0x1c => _reserved2),
(0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
(0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
(0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
(0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
(0x30 => CR: WriteOnly<u32, CR::Register>),
(0x34 => _reserved3),
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
@ -183,11 +209,18 @@ impl PL011UartInner {
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
///
/// This means the integer part is `3` and goes into the `IBRD`.
/// The fractional part is `0.2552083`.
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
///
/// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
pub fn init(&mut self) {
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
@ -205,14 +238,18 @@ impl PL011UartInner {
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
// From the PL011 Technical Reference Manual:
//
// The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
//
// Set the baud rate, 8N1 and FIFO enabled.
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
// Turn the UART on.
self.registers
@ -235,22 +272,10 @@ impl PL011UartInner {
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
// Spin until the busy bit is cleared.
while self.registers.FR.matches_all(FR::BUSY::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.

@ -3,6 +3,11 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! PL011 UART driver.
//!
//! # Resources
//!
//! - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
//! - https://developer.arm.com/documentation/ddi0183/latest
use crate::{
bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization,
@ -17,50 +22,63 @@ use register::{mmio::*, register_bitfields, register_structs};
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
register_bitfields! {
u32,
/// Flag Register
/// Flag Register.
FR [
/// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// Line Control Register, UARTLCR_ LCRH.
/// Line Control Register, LCR_H.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If
/// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does
/// not indicate if there is data in the transmit shift register.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
/// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
/// - This bit does not indicate if there is data in the transmit shift register.
TXFE OFFSET(7) NUMBITS(1) [],
/// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_ LCRH Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is full. If
/// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
/// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_H Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
/// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// LCR_H Register.
///
/// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
/// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) [],
/// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
/// set until the complete byte, including all the stop bits, has been sent from the shift
/// register.
///
/// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
/// the UART is enabled or not.
BUSY OFFSET(3) NUMBITS(1) []
],
/// Integer Baud rate divisor
/// Integer Baud Rate Divisor.
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
/// The integer baud rate divisor.
BAUD_DIVINT OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
/// Fractional Baud Rate Divisor.
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
/// The fractional baud rate divisor.
BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Line Control Register.
LCR_H [
/// Word length. These bits indicate the number of data bits transmitted or received in a
/// frame.
WLEN OFFSET(5) NUMBITS(2) [
@ -73,34 +91,42 @@ register_bitfields! {
/// Enable FIFOs:
///
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
/// registers
/// registers.
///
/// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
/// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
FEN OFFSET(4) NUMBITS(1) [
FifosDisabled = 0,
FifosEnabled = 1
]
],
/// Control Register
/// Control Register.
CR [
/// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
/// Data reception occurs for UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
/// Data reception occurs for either UART signals or SIR signals depending on the setting of
/// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
/// current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
/// Data transmission occurs for UART signals. When the UART is disabled in the middle of
/// transmission, it completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
/// Data transmission occurs for either UART signals, or SIR signals depending on the
/// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
/// completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
/// UART enable:
///
/// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
/// reception, it completes the current character before stopping.
///
/// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
/// or SIR signals depending on the setting of the SIREN bit
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission or reception, it completes the
/// current character before stopping.
@ -109,9 +135,9 @@ register_bitfields! {
]
],
/// Interrupt Clear Register
/// Interrupt Clear Register.
ICR [
/// Meta field for all pending interrupts
/// Meta field for all pending interrupts.
ALL OFFSET(0) NUMBITS(11) []
]
}
@ -125,7 +151,7 @@ register_structs! {
(0x1c => _reserved2),
(0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
(0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
(0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
(0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
(0x30 => CR: WriteOnly<u32, CR::Register>),
(0x34 => _reserved3),
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
@ -183,11 +209,18 @@ impl PL011UartInner {
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
///
/// This means the integer part is `3` and goes into the `IBRD`.
/// The fractional part is `0.2552083`.
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
///
/// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
pub fn init(&mut self) {
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
@ -205,14 +238,18 @@ impl PL011UartInner {
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
// From the PL011 Technical Reference Manual:
//
// The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
//
// Set the baud rate, 8N1 and FIFO enabled.
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
// Turn the UART on.
self.registers
@ -235,22 +272,10 @@ impl PL011UartInner {
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
// Spin until the busy bit is cleared.
while self.registers.FR.matches_all(FR::BUSY::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.

@ -3,6 +3,11 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! PL011 UART driver.
//!
//! # Resources
//!
//! - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
//! - https://developer.arm.com/documentation/ddi0183/latest
use crate::{
bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization,
@ -17,50 +22,63 @@ use register::{mmio::*, register_bitfields, register_structs};
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
register_bitfields! {
u32,
/// Flag Register
/// Flag Register.
FR [
/// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// Line Control Register, UARTLCR_ LCRH.
/// Line Control Register, LCR_H.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If
/// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does
/// not indicate if there is data in the transmit shift register.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
/// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
/// - This bit does not indicate if there is data in the transmit shift register.
TXFE OFFSET(7) NUMBITS(1) [],
/// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_ LCRH Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is full. If
/// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
/// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_H Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
/// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// LCR_H Register.
///
/// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
/// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) [],
/// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
/// set until the complete byte, including all the stop bits, has been sent from the shift
/// register.
///
/// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
/// the UART is enabled or not.
BUSY OFFSET(3) NUMBITS(1) []
],
/// Integer Baud rate divisor
/// Integer Baud Rate Divisor.
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
/// The integer baud rate divisor.
BAUD_DIVINT OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
/// Fractional Baud Rate Divisor.
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
/// The fractional baud rate divisor.
BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Line Control Register.
LCR_H [
/// Word length. These bits indicate the number of data bits transmitted or received in a
/// frame.
WLEN OFFSET(5) NUMBITS(2) [
@ -73,34 +91,42 @@ register_bitfields! {
/// Enable FIFOs:
///
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
/// registers
/// registers.
///
/// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
/// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
FEN OFFSET(4) NUMBITS(1) [
FifosDisabled = 0,
FifosEnabled = 1
]
],
/// Control Register
/// Control Register.
CR [
/// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
/// Data reception occurs for UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
/// Data reception occurs for either UART signals or SIR signals depending on the setting of
/// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
/// current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
/// Data transmission occurs for UART signals. When the UART is disabled in the middle of
/// transmission, it completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
/// Data transmission occurs for either UART signals, or SIR signals depending on the
/// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
/// completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
/// UART enable:
///
/// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
/// reception, it completes the current character before stopping.
///
/// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
/// or SIR signals depending on the setting of the SIREN bit
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission or reception, it completes the
/// current character before stopping.
@ -109,9 +135,9 @@ register_bitfields! {
]
],
/// Interrupt Clear Register
/// Interrupt Clear Register.
ICR [
/// Meta field for all pending interrupts
/// Meta field for all pending interrupts.
ALL OFFSET(0) NUMBITS(11) []
]
}
@ -125,7 +151,7 @@ register_structs! {
(0x1c => _reserved2),
(0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
(0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
(0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
(0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
(0x30 => CR: WriteOnly<u32, CR::Register>),
(0x34 => _reserved3),
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
@ -183,11 +209,18 @@ impl PL011UartInner {
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
///
/// This means the integer part is `3` and goes into the `IBRD`.
/// The fractional part is `0.2552083`.
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
///
/// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
pub fn init(&mut self) {
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
@ -205,14 +238,18 @@ impl PL011UartInner {
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
// From the PL011 Technical Reference Manual:
//
// The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
//
// Set the baud rate, 8N1 and FIFO enabled.
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
// Turn the UART on.
self.registers
@ -235,22 +272,10 @@ impl PL011UartInner {
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
// Spin until the busy bit is cleared.
while self.registers.FR.matches_all(FR::BUSY::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.

@ -3,6 +3,11 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! PL011 UART driver.
//!
//! # Resources
//!
//! - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
//! - https://developer.arm.com/documentation/ddi0183/latest
use crate::{
bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization,
@ -17,50 +22,63 @@ use register::{mmio::*, register_bitfields, register_structs};
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
register_bitfields! {
u32,
/// Flag Register
/// Flag Register.
FR [
/// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// Line Control Register, UARTLCR_ LCRH.
/// Line Control Register, LCR_H.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If
/// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does
/// not indicate if there is data in the transmit shift register.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
/// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
/// - This bit does not indicate if there is data in the transmit shift register.
TXFE OFFSET(7) NUMBITS(1) [],
/// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_ LCRH Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is full. If
/// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
/// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_H Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
/// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// LCR_H Register.
///
/// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
/// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) [],
/// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
/// set until the complete byte, including all the stop bits, has been sent from the shift
/// register.
///
/// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
/// the UART is enabled or not.
BUSY OFFSET(3) NUMBITS(1) []
],
/// Integer Baud rate divisor
/// Integer Baud Rate Divisor.
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
/// The integer baud rate divisor.
BAUD_DIVINT OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
/// Fractional Baud Rate Divisor.
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
/// The fractional baud rate divisor.
BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Line Control Register.
LCR_H [
/// Word length. These bits indicate the number of data bits transmitted or received in a
/// frame.
WLEN OFFSET(5) NUMBITS(2) [
@ -73,34 +91,42 @@ register_bitfields! {
/// Enable FIFOs:
///
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
/// registers
/// registers.
///
/// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
/// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
FEN OFFSET(4) NUMBITS(1) [
FifosDisabled = 0,
FifosEnabled = 1
]
],
/// Control Register
/// Control Register.
CR [
/// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
/// Data reception occurs for UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
/// Data reception occurs for either UART signals or SIR signals depending on the setting of
/// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
/// current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
/// Data transmission occurs for UART signals. When the UART is disabled in the middle of
/// transmission, it completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
/// Data transmission occurs for either UART signals, or SIR signals depending on the
/// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
/// completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
/// UART enable:
///
/// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
/// reception, it completes the current character before stopping.
///
/// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
/// or SIR signals depending on the setting of the SIREN bit
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission or reception, it completes the
/// current character before stopping.
@ -109,9 +135,9 @@ register_bitfields! {
]
],
/// Interrupt Clear Register
/// Interrupt Clear Register.
ICR [
/// Meta field for all pending interrupts
/// Meta field for all pending interrupts.
ALL OFFSET(0) NUMBITS(11) []
]
}
@ -125,7 +151,7 @@ register_structs! {
(0x1c => _reserved2),
(0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
(0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
(0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
(0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
(0x30 => CR: WriteOnly<u32, CR::Register>),
(0x34 => _reserved3),
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
@ -183,11 +209,18 @@ impl PL011UartInner {
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
///
/// This means the integer part is `3` and goes into the `IBRD`.
/// The fractional part is `0.2552083`.
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
///
/// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
pub fn init(&mut self) {
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
@ -205,14 +238,18 @@ impl PL011UartInner {
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
// From the PL011 Technical Reference Manual:
//
// The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
//
// Set the baud rate, 8N1 and FIFO enabled.
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
// Turn the UART on.
self.registers
@ -235,22 +272,10 @@ impl PL011UartInner {
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
// Spin until the busy bit is cleared.
while self.registers.FR.matches_all(FR::BUSY::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.

@ -3,6 +3,11 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! PL011 UART driver.
//!
//! # Resources
//!
//! - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
//! - https://developer.arm.com/documentation/ddi0183/latest
use crate::{
bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization,
@ -17,50 +22,63 @@ use register::{mmio::*, register_bitfields, register_structs};
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
register_bitfields! {
u32,
/// Flag Register
/// Flag Register.
FR [
/// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// Line Control Register, UARTLCR_ LCRH.
/// Line Control Register, LCR_H.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If
/// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does
/// not indicate if there is data in the transmit shift register.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
/// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
/// - This bit does not indicate if there is data in the transmit shift register.
TXFE OFFSET(7) NUMBITS(1) [],
/// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_ LCRH Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is full. If
/// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
/// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_H Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
/// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// LCR_H Register.
///
/// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
/// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) [],
/// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
/// set until the complete byte, including all the stop bits, has been sent from the shift
/// register.
///
/// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
/// the UART is enabled or not.
BUSY OFFSET(3) NUMBITS(1) []
],
/// Integer Baud rate divisor
/// Integer Baud Rate Divisor.
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
/// The integer baud rate divisor.
BAUD_DIVINT OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
/// Fractional Baud Rate Divisor.
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
/// The fractional baud rate divisor.
BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Line Control Register.
LCR_H [
/// Word length. These bits indicate the number of data bits transmitted or received in a
/// frame.
WLEN OFFSET(5) NUMBITS(2) [
@ -73,34 +91,42 @@ register_bitfields! {
/// Enable FIFOs:
///
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
/// registers
/// registers.
///
/// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
/// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
FEN OFFSET(4) NUMBITS(1) [
FifosDisabled = 0,
FifosEnabled = 1
]
],
/// Control Register
/// Control Register.
CR [
/// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
/// Data reception occurs for UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
/// Data reception occurs for either UART signals or SIR signals depending on the setting of
/// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
/// current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
/// Data transmission occurs for UART signals. When the UART is disabled in the middle of
/// transmission, it completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
/// Data transmission occurs for either UART signals, or SIR signals depending on the
/// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
/// completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
/// UART enable:
///
/// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
/// reception, it completes the current character before stopping.
///
/// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
/// or SIR signals depending on the setting of the SIREN bit
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission or reception, it completes the
/// current character before stopping.
@ -109,9 +135,9 @@ register_bitfields! {
]
],
/// Interrupt Clear Register
/// Interrupt Clear Register.
ICR [
/// Meta field for all pending interrupts
/// Meta field for all pending interrupts.
ALL OFFSET(0) NUMBITS(11) []
]
}
@ -125,7 +151,7 @@ register_structs! {
(0x1c => _reserved2),
(0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
(0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
(0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
(0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
(0x30 => CR: WriteOnly<u32, CR::Register>),
(0x34 => _reserved3),
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
@ -183,11 +209,18 @@ impl PL011UartInner {
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
///
/// This means the integer part is `3` and goes into the `IBRD`.
/// The fractional part is `0.2552083`.
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
///
/// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
pub fn init(&mut self) {
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
@ -205,14 +238,18 @@ impl PL011UartInner {
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
// From the PL011 Technical Reference Manual:
//
// The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
//
// Set the baud rate, 8N1 and FIFO enabled.
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
// Turn the UART on.
self.registers
@ -235,22 +272,10 @@ impl PL011UartInner {
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
// Spin until the busy bit is cleared.
while self.registers.FR.matches_all(FR::BUSY::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.

@ -1775,8 +1775,8 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_interrupt_cont
diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
--- 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
+++ 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
@@ -5,8 +5,8 @@
//! PL011 UART driver.
@@ -10,8 +10,8 @@
//! - https://developer.arm.com/documentation/ddi0183/latest
use crate::{
- bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization,
@ -1786,11 +1786,11 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
};
use core::fmt;
use register::{mmio::*, register_bitfields, register_structs};
@@ -109,6 +109,48 @@
@@ -135,6 +135,52 @@
]
],
+ /// Interrupt FIFO Level Select Register
+ /// Interrupt FIFO Level Select Register.
+ IFLS [
+ /// Receive interrupt FIFO level select. The trigger points for the receive interrupt are as
+ /// follows.
@ -1803,25 +1803,29 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
+ ]
+ ],
+
+ /// Interrupt Mask Set Clear Register
+ /// Interrupt Mask Set/Clear Register.
+ IMSC [
+ /// Receive timeout interrupt mask. A read returns the current mask for the UARTRTINTR
+ /// interrupt. On a write of 1, the mask of the interrupt is set. A write of 0 clears the
+ /// mask.
+ /// interrupt.
+ ///
+ /// - On a write of 1, the mask of the UARTRTINTR interrupt is set.
+ /// - A write of 0 clears the mask.
+ RTIM OFFSET(6) NUMBITS(1) [
+ Disabled = 0,
+ Enabled = 1
+ ],
+
+ /// Receive interrupt mask. A read returns the current mask for the UARTRXINTR interrupt. On
+ /// a write of 1, the mask of the interrupt is set. A write of 0 clears the mask.
+ /// Receive interrupt mask. A read returns the current mask for the UARTRXINTR interrupt.
+ ///
+ /// - On a write of 1, the mask of the UARTRXINTR interrupt is set.
+ /// - A write of 0 clears the mask.
+ RXIM OFFSET(4) NUMBITS(1) [
+ Disabled = 0,
+ Enabled = 1
+ ]
+ ],
+
+ /// Masked Interrupt Status Register
+ /// Masked Interrupt Status Register.
+ MIS [
+ /// Receive timeout masked interrupt status. Returns the masked interrupt state of the
+ /// UARTRTINTR interrupt.
@ -1832,12 +1836,12 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
+ RXMIS OFFSET(4) NUMBITS(1) []
+ ],
+
/// Interrupt Clear Register
/// Interrupt Clear Register.
ICR [
/// Meta field for all pending interrupts
@@ -127,7 +169,10 @@
/// Meta field for all pending interrupts.
@@ -153,7 +199,10 @@
(0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
(0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
(0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
(0x30 => CR: WriteOnly<u32, CR::Register>),
- (0x34 => _reserved3),
+ (0x34 => IFLS: ReadWrite<u32, IFLS::Register>),
@ -1847,7 +1851,7 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
(0x48 => @END),
}
@@ -157,7 +202,8 @@
@@ -183,7 +232,8 @@
/// Representation of the UART.
pub struct PL011Uart {
@ -1857,9 +1861,9 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
}
//--------------------------------------------------------------------------------------------------
@@ -214,6 +260,14 @@
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
@@ -251,6 +301,14 @@
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
+ // Set RX FIFO fill level at 1/8.
+ self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth);
@ -1872,7 +1876,7 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
// Turn the UART on.
self.registers
.CR
@@ -308,9 +362,13 @@
@@ -333,9 +391,13 @@
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
@ -1888,7 +1892,7 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
}
}
}
@@ -330,6 +388,21 @@
@@ -355,6 +417,21 @@
Ok(())
}
@ -1910,7 +1914,7 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
}
impl console::interface::Write for PL011Uart {
@@ -376,3 +449,24 @@
@@ -401,3 +478,24 @@
self.inner.lock(|inner| inner.chars_read)
}
}

@ -3,6 +3,11 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! PL011 UART driver.
//!
//! # Resources
//!
//! - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
//! - https://developer.arm.com/documentation/ddi0183/latest
use crate::{
bsp, bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, exception,
@ -17,50 +22,63 @@ use register::{mmio::*, register_bitfields, register_structs};
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
register_bitfields! {
u32,
/// Flag Register
/// Flag Register.
FR [
/// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// Line Control Register, UARTLCR_ LCRH.
/// Line Control Register, LCR_H.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If
/// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does
/// not indicate if there is data in the transmit shift register.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
/// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
/// - This bit does not indicate if there is data in the transmit shift register.
TXFE OFFSET(7) NUMBITS(1) [],
/// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_ LCRH Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is full. If
/// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
/// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_H Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
/// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// LCR_H Register.
///
/// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
/// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) [],
/// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
/// set until the complete byte, including all the stop bits, has been sent from the shift
/// register.
///
/// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
/// the UART is enabled or not.
BUSY OFFSET(3) NUMBITS(1) []
],
/// Integer Baud rate divisor
/// Integer Baud Rate Divisor.
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
/// The integer baud rate divisor.
BAUD_DIVINT OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
/// Fractional Baud Rate Divisor.
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
/// The fractional baud rate divisor.
BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Line Control Register.
LCR_H [
/// Word length. These bits indicate the number of data bits transmitted or received in a
/// frame.
WLEN OFFSET(5) NUMBITS(2) [
@ -73,34 +91,42 @@ register_bitfields! {
/// Enable FIFOs:
///
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
/// registers
/// registers.
///
/// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
/// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
FEN OFFSET(4) NUMBITS(1) [
FifosDisabled = 0,
FifosEnabled = 1
]
],
/// Control Register
/// Control Register.
CR [
/// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
/// Data reception occurs for UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
/// Data reception occurs for either UART signals or SIR signals depending on the setting of
/// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
/// current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
/// Data transmission occurs for UART signals. When the UART is disabled in the middle of
/// transmission, it completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
/// Data transmission occurs for either UART signals, or SIR signals depending on the
/// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
/// completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
/// UART enable:
///
/// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
/// reception, it completes the current character before stopping.
///
/// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
/// or SIR signals depending on the setting of the SIREN bit
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission or reception, it completes the
/// current character before stopping.
@ -109,7 +135,7 @@ register_bitfields! {
]
],
/// Interrupt FIFO Level Select Register
/// Interrupt FIFO Level Select Register.
IFLS [
/// Receive interrupt FIFO level select. The trigger points for the receive interrupt are as
/// follows.
@ -122,25 +148,29 @@ register_bitfields! {
]
],
/// Interrupt Mask Set Clear Register
/// Interrupt Mask Set/Clear Register.
IMSC [
/// Receive timeout interrupt mask. A read returns the current mask for the UARTRTINTR
/// interrupt. On a write of 1, the mask of the interrupt is set. A write of 0 clears the
/// mask.
/// interrupt.
///
/// - On a write of 1, the mask of the UARTRTINTR interrupt is set.
/// - A write of 0 clears the mask.
RTIM OFFSET(6) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Receive interrupt mask. A read returns the current mask for the UARTRXINTR interrupt. On
/// a write of 1, the mask of the interrupt is set. A write of 0 clears the mask.
/// Receive interrupt mask. A read returns the current mask for the UARTRXINTR interrupt.
///
/// - On a write of 1, the mask of the UARTRXINTR interrupt is set.
/// - A write of 0 clears the mask.
RXIM OFFSET(4) NUMBITS(1) [
Disabled = 0,
Enabled = 1
]
],
/// Masked Interrupt Status Register
/// Masked Interrupt Status Register.
MIS [
/// Receive timeout masked interrupt status. Returns the masked interrupt state of the
/// UARTRTINTR interrupt.
@ -151,9 +181,9 @@ register_bitfields! {
RXMIS OFFSET(4) NUMBITS(1) []
],
/// Interrupt Clear Register
/// Interrupt Clear Register.
ICR [
/// Meta field for all pending interrupts
/// Meta field for all pending interrupts.
ALL OFFSET(0) NUMBITS(11) []
]
}
@ -167,7 +197,7 @@ register_structs! {
(0x1c => _reserved2),
(0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
(0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
(0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
(0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
(0x30 => CR: WriteOnly<u32, CR::Register>),
(0x34 => IFLS: ReadWrite<u32, IFLS::Register>),
(0x38 => IMSC: ReadWrite<u32, IMSC::Register>),
@ -229,11 +259,18 @@ impl PL011UartInner {
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
///
/// This means the integer part is `3` and goes into the `IBRD`.
/// The fractional part is `0.2552083`.
///
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
/// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
pub fn init(&mut self) {
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
@ -251,14 +288,18 @@ impl PL011UartInner {
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
// From the PL011 Technical Reference Manual:
//
// The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
//
// Set the baud rate, 8N1 and FIFO enabled.
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
// Set RX FIFO fill level at 1/8.
self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth);
@ -289,22 +330,10 @@ impl PL011UartInner {
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
// Spin until the busy bit is cleared.
while self.registers.FR.matches_all(FR::BUSY::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.

@ -1189,8 +1189,8 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_
diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 15_virtual_mem_part2_mmio_remap/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
--- 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
+++ 15_virtual_mem_part2_mmio_remap/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
@@ -5,10 +5,13 @@
//! PL011 UART driver.
@@ -10,10 +10,13 @@
//! - https://developer.arm.com/documentation/ddi0183/latest
use crate::{
- bsp, bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, exception,
@ -1206,7 +1206,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_
use register::{mmio::*, register_bitfields, register_structs};
//--------------------------------------------------------------------------------------------------
@@ -202,6 +205,8 @@
@@ -232,6 +235,8 @@
/// Representation of the UART.
pub struct PL011Uart {
@ -1215,10 +1215,10 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_
inner: IRQSafeNullLock<PL011UartInner>,
irq_number: bsp::device_driver::IRQNumber,
}
@@ -234,7 +239,15 @@
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5modulo error margin is acceptable for UART and we're
/// now at 0.02modulo.
@@ -271,7 +276,15 @@
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16modulo`.
- pub fn init(&mut self) {
+ ///
+ /// # Safety
@ -1232,7 +1232,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
@@ -272,6 +285,8 @@
@@ -313,6 +326,8 @@
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -1241,7 +1241,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_
}
/// Send a character.
@@ -361,13 +376,18 @@
@@ -390,13 +405,18 @@
///
/// # Safety
///
@ -1263,7 +1263,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_
irq_number,
}
}
@@ -384,7 +404,14 @@
@@ -413,7 +433,14 @@
}
unsafe fn init(&self) -> Result<(), &'static str> {
@ -1279,7 +1279,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_
Ok(())
}
@@ -403,6 +430,16 @@
@@ -432,6 +459,16 @@
Ok(())
}

@ -3,6 +3,11 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! PL011 UART driver.
//!
//! # Resources
//!
//! - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
//! - https://developer.arm.com/documentation/ddi0183/latest
use crate::{
bsp, bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, exception, memory,
@ -20,50 +25,63 @@ use register::{mmio::*, register_bitfields, register_structs};
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
register_bitfields! {
u32,
/// Flag Register
/// Flag Register.
FR [
/// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// Line Control Register, UARTLCR_ LCRH.
/// Line Control Register, LCR_H.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If
/// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does
/// not indicate if there is data in the transmit shift register.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
/// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
/// - This bit does not indicate if there is data in the transmit shift register.
TXFE OFFSET(7) NUMBITS(1) [],
/// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_ LCRH Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is full. If
/// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
/// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_H Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
/// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// LCR_H Register.
///
/// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
/// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) [],
/// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
/// set until the complete byte, including all the stop bits, has been sent from the shift
/// register.
///
/// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
/// the UART is enabled or not.
BUSY OFFSET(3) NUMBITS(1) []
],
/// Integer Baud rate divisor
/// Integer Baud Rate Divisor.
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
/// The integer baud rate divisor.
BAUD_DIVINT OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
/// Fractional Baud Rate Divisor.
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
/// The fractional baud rate divisor.
BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Line Control Register.
LCR_H [
/// Word length. These bits indicate the number of data bits transmitted or received in a
/// frame.
WLEN OFFSET(5) NUMBITS(2) [
@ -76,34 +94,42 @@ register_bitfields! {
/// Enable FIFOs:
///
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
/// registers
/// registers.
///
/// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
/// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
FEN OFFSET(4) NUMBITS(1) [
FifosDisabled = 0,
FifosEnabled = 1
]
],
/// Control Register
/// Control Register.
CR [
/// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
/// Data reception occurs for UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
/// Data reception occurs for either UART signals or SIR signals depending on the setting of
/// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
/// current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
/// Data transmission occurs for UART signals. When the UART is disabled in the middle of
/// transmission, it completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
/// Data transmission occurs for either UART signals, or SIR signals depending on the
/// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
/// completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
/// UART enable:
///
/// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
/// reception, it completes the current character before stopping.
///
/// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
/// or SIR signals depending on the setting of the SIREN bit
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission or reception, it completes the
/// current character before stopping.
@ -112,7 +138,7 @@ register_bitfields! {
]
],
/// Interrupt FIFO Level Select Register
/// Interrupt FIFO Level Select Register.
IFLS [
/// Receive interrupt FIFO level select. The trigger points for the receive interrupt are as
/// follows.
@ -125,25 +151,29 @@ register_bitfields! {
]
],
/// Interrupt Mask Set Clear Register
/// Interrupt Mask Set/Clear Register.
IMSC [
/// Receive timeout interrupt mask. A read returns the current mask for the UARTRTINTR
/// interrupt. On a write of 1, the mask of the interrupt is set. A write of 0 clears the
/// mask.
/// interrupt.
///
/// - On a write of 1, the mask of the UARTRTINTR interrupt is set.
/// - A write of 0 clears the mask.
RTIM OFFSET(6) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Receive interrupt mask. A read returns the current mask for the UARTRXINTR interrupt. On
/// a write of 1, the mask of the interrupt is set. A write of 0 clears the mask.
/// Receive interrupt mask. A read returns the current mask for the UARTRXINTR interrupt.
///
/// - On a write of 1, the mask of the UARTRXINTR interrupt is set.
/// - A write of 0 clears the mask.
RXIM OFFSET(4) NUMBITS(1) [
Disabled = 0,
Enabled = 1
]
],
/// Masked Interrupt Status Register
/// Masked Interrupt Status Register.
MIS [
/// Receive timeout masked interrupt status. Returns the masked interrupt state of the
/// UARTRTINTR interrupt.
@ -154,9 +184,9 @@ register_bitfields! {
RXMIS OFFSET(4) NUMBITS(1) []
],
/// Interrupt Clear Register
/// Interrupt Clear Register.
ICR [
/// Meta field for all pending interrupts
/// Meta field for all pending interrupts.
ALL OFFSET(0) NUMBITS(11) []
]
}
@ -170,7 +200,7 @@ register_structs! {
(0x1c => _reserved2),
(0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
(0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
(0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
(0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
(0x30 => CR: WriteOnly<u32, CR::Register>),
(0x34 => IFLS: ReadWrite<u32, IFLS::Register>),
(0x38 => IMSC: ReadWrite<u32, IMSC::Register>),
@ -234,11 +264,18 @@ impl PL011UartInner {
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
///
/// This means the integer part is `3` and goes into the `IBRD`.
/// The fractional part is `0.2552083`.
///
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
/// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
///
/// # Safety
///
@ -264,14 +301,18 @@ impl PL011UartInner {
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
// From the PL011 Technical Reference Manual:
//
// The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
//
// Set the baud rate, 8N1 and FIFO enabled.
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
// Set RX FIFO fill level at 1/8.
self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth);
@ -304,22 +345,10 @@ impl PL011UartInner {
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
// Spin until the busy bit is cleared.
while self.registers.FR.matches_all(FR::BUSY::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.

Binary file not shown.

Binary file not shown.

@ -3,6 +3,11 @@
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
//! PL011 UART driver.
//!
//! # Resources
//!
//! - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
//! - https://developer.arm.com/documentation/ddi0183/latest
use crate::{
bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization,
@ -17,50 +22,63 @@ use register::{mmio::*, register_bitfields, register_structs};
// PL011 UART registers.
//
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
register_bitfields! {
u32,
/// Flag Register
/// Flag Register.
FR [
/// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// Line Control Register, UARTLCR_ LCRH.
/// Line Control Register, LCR_H.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If
/// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does
/// not indicate if there is data in the transmit shift register.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
/// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
/// - This bit does not indicate if there is data in the transmit shift register.
TXFE OFFSET(7) NUMBITS(1) [],
/// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_ LCRH Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the transmit holding register is full. If
/// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
/// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
/// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
TXFF OFFSET(5) NUMBITS(1) [],
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// UARTLCR_H Register.
/// LCR_H Register.
///
/// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
/// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) []
/// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
/// LCR_H Register.
///
/// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
/// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
RXFE OFFSET(4) NUMBITS(1) [],
/// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
/// set until the complete byte, including all the stop bits, has been sent from the shift
/// register.
///
/// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
/// the UART is enabled or not.
BUSY OFFSET(3) NUMBITS(1) []
],
/// Integer Baud rate divisor
/// Integer Baud Rate Divisor.
IBRD [
/// Integer Baud rate divisor
IBRD OFFSET(0) NUMBITS(16) []
/// The integer baud rate divisor.
BAUD_DIVINT OFFSET(0) NUMBITS(16) []
],
/// Fractional Baud rate divisor
/// Fractional Baud Rate Divisor.
FBRD [
/// Fractional Baud rate divisor
FBRD OFFSET(0) NUMBITS(6) []
/// The fractional baud rate divisor.
BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
],
/// Line Control register
LCRH [
/// Line Control Register.
LCR_H [
/// Word length. These bits indicate the number of data bits transmitted or received in a
/// frame.
WLEN OFFSET(5) NUMBITS(2) [
@ -73,34 +91,42 @@ register_bitfields! {
/// Enable FIFOs:
///
/// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
/// registers
/// registers.
///
/// 1 = transmit and receive FIFO buffers are enabled (FIFO mode).
/// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
FEN OFFSET(4) NUMBITS(1) [
FifosDisabled = 0,
FifosEnabled = 1
]
],
/// Control Register
/// Control Register.
CR [
/// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
/// Data reception occurs for UART signals. When the UART is disabled in the middle of
/// reception, it completes the current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
/// Data reception occurs for either UART signals or SIR signals depending on the setting of
/// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
/// current character before stopping.
RXE OFFSET(9) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
/// Data transmission occurs for UART signals. When the UART is disabled in the middle of
/// transmission, it completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
/// Data transmission occurs for either UART signals, or SIR signals depending on the
/// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
/// completes the current character before stopping.
TXE OFFSET(8) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// UART enable
/// UART enable:
///
/// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
/// reception, it completes the current character before stopping.
///
/// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
/// or SIR signals depending on the setting of the SIREN bit
UARTEN OFFSET(0) NUMBITS(1) [
/// If the UART is disabled in the middle of transmission or reception, it completes the
/// current character before stopping.
@ -109,9 +135,9 @@ register_bitfields! {
]
],
/// Interrupt Clear Register
/// Interrupt Clear Register.
ICR [
/// Meta field for all pending interrupts
/// Meta field for all pending interrupts.
ALL OFFSET(0) NUMBITS(11) []
]
}
@ -125,7 +151,7 @@ register_structs! {
(0x1c => _reserved2),
(0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
(0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
(0x2c => LCRH: WriteOnly<u32, LCRH::Register>),
(0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
(0x30 => CR: WriteOnly<u32, CR::Register>),
(0x34 => _reserved3),
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
@ -183,11 +209,18 @@ impl PL011UartInner {
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
///
/// This means the integer part is `3` and goes into the `IBRD`.
/// The fractional part is `0.2552083`.
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
///
/// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
///
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
pub fn init(&mut self) {
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
@ -205,14 +238,18 @@ impl PL011UartInner {
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
// From the PL011 Technical Reference Manual:
//
// The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
//
// Set the baud rate, 8N1 and FIFO enabled.
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
.LCR_H
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
// Turn the UART on.
self.registers
@ -235,22 +272,10 @@ impl PL011UartInner {
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
// Spin until the busy bit is cleared.
while self.registers.FR.matches_all(FR::BUSY::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.

Loading…
Cancel
Save