diff --git a/06_drivers_gpio_uart/README.md b/06_drivers_gpio_uart/README.md index 2f23d1b5..9b526c33 100644 --- a/06_drivers_gpio_uart/README.md +++ b/06_drivers_gpio_uart/README.md @@ -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 + +//! 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), + (0x28 => FBRD: WriteOnly), -+ (0x2c => LCRH: WriteOnly), ++ (0x2c => LCR_H: WriteOnly), + (0x30 => CR: WriteOnly), + (0x34 => _reserved3), + (0x44 => ICR: WriteOnly), @@ -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 diff --git a/06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs b/06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs index e16fdecc..ade53a3c 100644 --- a/06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs +++ b/06_drivers_gpio_uart/src/_arch/aarch64/cpu.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 { diff --git a/06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index 39834723..ce39752d 100644 --- a/06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ b/06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -3,6 +3,11 @@ // Copyright (c) 2018-2021 Andre Richter //! 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), (0x28 => FBRD: WriteOnly), - (0x2c => LCRH: WriteOnly), + (0x2c => LCR_H: WriteOnly), (0x30 => CR: WriteOnly), (0x34 => _reserved3), (0x44 => ICR: WriteOnly), @@ -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. diff --git a/06_drivers_gpio_uart/src/cpu.rs b/06_drivers_gpio_uart/src/cpu.rs index 103f00db..f0df4791 100644 --- a/06_drivers_gpio_uart/src/cpu.rs +++ b/06_drivers_gpio_uart/src/cpu.rs @@ -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; diff --git a/07_uart_chainloader/README.md b/07_uart_chainloader/README.md index 0bd4bce2..1cca2b12 100644 --- a/07_uart_chainloader/README.md +++ b/07_uart_chainloader/README.md @@ -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 diff --git a/07_uart_chainloader/demo_payload_rpi3.img b/07_uart_chainloader/demo_payload_rpi3.img index 80df7c19..83fe7bff 100755 Binary files a/07_uart_chainloader/demo_payload_rpi3.img and b/07_uart_chainloader/demo_payload_rpi3.img differ diff --git a/07_uart_chainloader/demo_payload_rpi4.img b/07_uart_chainloader/demo_payload_rpi4.img index 54fe2b81..f2dec5cb 100755 Binary files a/07_uart_chainloader/demo_payload_rpi4.img and b/07_uart_chainloader/demo_payload_rpi4.img differ diff --git a/07_uart_chainloader/src/_arch/aarch64/cpu.rs b/07_uart_chainloader/src/_arch/aarch64/cpu.rs index b53dc269..893c7580 100644 --- a/07_uart_chainloader/src/_arch/aarch64/cpu.rs +++ b/07_uart_chainloader/src/_arch/aarch64/cpu.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 { diff --git a/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index 9a92c9d2..f21395d3 100644 --- a/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ b/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -3,6 +3,11 @@ // Copyright (c) 2018-2021 Andre Richter //! 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), (0x28 => FBRD: WriteOnly), - (0x2c => LCRH: WriteOnly), + (0x2c => LCR_H: WriteOnly), (0x30 => CR: WriteOnly), (0x34 => _reserved3), (0x44 => ICR: WriteOnly), @@ -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. diff --git a/07_uart_chainloader/src/cpu.rs b/07_uart_chainloader/src/cpu.rs index c543bd98..7de5c29c 100644 --- a/07_uart_chainloader/src/cpu.rs +++ b/07_uart_chainloader/src/cpu.rs @@ -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; diff --git a/08_timestamps/README.md b/08_timestamps/README.md index 432ac091..83e38ca5 100644 --- a/08_timestamps/README.md +++ b/08_timestamps/README.md @@ -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 diff --git a/08_timestamps/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/08_timestamps/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index 876aa461..ce39752d 100644 --- a/08_timestamps/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ b/08_timestamps/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -3,6 +3,11 @@ // Copyright (c) 2018-2021 Andre Richter //! 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), (0x28 => FBRD: WriteOnly), - (0x2c => LCRH: WriteOnly), + (0x2c => LCR_H: WriteOnly), (0x30 => CR: WriteOnly), (0x34 => _reserved3), (0x44 => ICR: WriteOnly), @@ -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. diff --git a/09_hw_debug_JTAG/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/09_hw_debug_JTAG/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index 876aa461..ce39752d 100644 --- a/09_hw_debug_JTAG/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ b/09_hw_debug_JTAG/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -3,6 +3,11 @@ // Copyright (c) 2018-2021 Andre Richter //! 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), (0x28 => FBRD: WriteOnly), - (0x2c => LCRH: WriteOnly), + (0x2c => LCR_H: WriteOnly), (0x30 => CR: WriteOnly), (0x34 => _reserved3), (0x44 => ICR: WriteOnly), @@ -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. diff --git a/10_privilege_level/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/10_privilege_level/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index 876aa461..ce39752d 100644 --- a/10_privilege_level/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ b/10_privilege_level/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -3,6 +3,11 @@ // Copyright (c) 2018-2021 Andre Richter //! 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), (0x28 => FBRD: WriteOnly), - (0x2c => LCRH: WriteOnly), + (0x2c => LCR_H: WriteOnly), (0x30 => CR: WriteOnly), (0x34 => _reserved3), (0x44 => ICR: WriteOnly), @@ -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. diff --git a/11_virtual_mem_part1_identity_mapping/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/11_virtual_mem_part1_identity_mapping/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index 876aa461..ce39752d 100644 --- a/11_virtual_mem_part1_identity_mapping/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ b/11_virtual_mem_part1_identity_mapping/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -3,6 +3,11 @@ // Copyright (c) 2018-2021 Andre Richter //! 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), (0x28 => FBRD: WriteOnly), - (0x2c => LCRH: WriteOnly), + (0x2c => LCR_H: WriteOnly), (0x30 => CR: WriteOnly), (0x34 => _reserved3), (0x44 => ICR: WriteOnly), @@ -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. diff --git a/12_exceptions_part1_groundwork/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/12_exceptions_part1_groundwork/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index 876aa461..ce39752d 100644 --- a/12_exceptions_part1_groundwork/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ b/12_exceptions_part1_groundwork/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -3,6 +3,11 @@ // Copyright (c) 2018-2021 Andre Richter //! 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), (0x28 => FBRD: WriteOnly), - (0x2c => LCRH: WriteOnly), + (0x2c => LCR_H: WriteOnly), (0x30 => CR: WriteOnly), (0x34 => _reserved3), (0x44 => ICR: WriteOnly), @@ -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. diff --git a/13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index 876aa461..ce39752d 100644 --- a/13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ b/13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -3,6 +3,11 @@ // Copyright (c) 2018-2021 Andre Richter //! 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), (0x28 => FBRD: WriteOnly), - (0x2c => LCRH: WriteOnly), + (0x2c => LCR_H: WriteOnly), (0x30 => CR: WriteOnly), (0x34 => _reserved3), (0x44 => ICR: WriteOnly), @@ -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. diff --git a/14_exceptions_part2_peripheral_IRQs/README.md b/14_exceptions_part2_peripheral_IRQs/README.md index 2c74961d..e6dd762e 100644 --- a/14_exceptions_part2_peripheral_IRQs/README.md +++ b/14_exceptions_part2_peripheral_IRQs/README.md @@ -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), - (0x2c => LCRH: WriteOnly), + (0x2c => LCR_H: WriteOnly), (0x30 => CR: WriteOnly), - (0x34 => _reserved3), + (0x34 => IFLS: ReadWrite), @@ -1847,7 +1851,7 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs (0x44 => ICR: WriteOnly), (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) } } diff --git a/14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index 7b3a85cf..1dd0a14c 100644 --- a/14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ b/14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -3,6 +3,11 @@ // Copyright (c) 2018-2021 Andre Richter //! 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), (0x28 => FBRD: WriteOnly), - (0x2c => LCRH: WriteOnly), + (0x2c => LCR_H: WriteOnly), (0x30 => CR: WriteOnly), (0x34 => IFLS: ReadWrite), (0x38 => IMSC: ReadWrite), @@ -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. diff --git a/15_virtual_mem_part2_mmio_remap/README.md b/15_virtual_mem_part2_mmio_remap/README.md index beb2cffc..b326d589 100644 --- a/15_virtual_mem_part2_mmio_remap/README.md +++ b/15_virtual_mem_part2_mmio_remap/README.md @@ -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, 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(()) } diff --git a/15_virtual_mem_part2_mmio_remap/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/15_virtual_mem_part2_mmio_remap/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index d7d83840..e279c206 100644 --- a/15_virtual_mem_part2_mmio_remap/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ b/15_virtual_mem_part2_mmio_remap/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -3,6 +3,11 @@ // Copyright (c) 2018-2021 Andre Richter //! 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), (0x28 => FBRD: WriteOnly), - (0x2c => LCRH: WriteOnly), + (0x2c => LCR_H: WriteOnly), (0x30 => CR: WriteOnly), (0x34 => IFLS: ReadWrite), (0x38 => IMSC: ReadWrite), @@ -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. diff --git a/X1_JTAG_boot/jtag_boot_rpi3.img b/X1_JTAG_boot/jtag_boot_rpi3.img index 6fe15429..9f8ada21 100755 Binary files a/X1_JTAG_boot/jtag_boot_rpi3.img and b/X1_JTAG_boot/jtag_boot_rpi3.img differ diff --git a/X1_JTAG_boot/jtag_boot_rpi4.img b/X1_JTAG_boot/jtag_boot_rpi4.img index d5573467..e9d7e7dd 100755 Binary files a/X1_JTAG_boot/jtag_boot_rpi4.img and b/X1_JTAG_boot/jtag_boot_rpi4.img differ diff --git a/X1_JTAG_boot/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/X1_JTAG_boot/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index 876aa461..ce39752d 100644 --- a/X1_JTAG_boot/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ b/X1_JTAG_boot/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -3,6 +3,11 @@ // Copyright (c) 2018-2021 Andre Richter //! 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), (0x28 => FBRD: WriteOnly), - (0x2c => LCRH: WriteOnly), + (0x2c => LCR_H: WriteOnly), (0x30 => CR: WriteOnly), (0x34 => _reserved3), (0x44 => ICR: WriteOnly), @@ -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.