From 8324b1fdac393c4fdb4f3575e6c84bc7982464fe Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Thu, 28 Jan 2021 23:25:57 +0100 Subject: [PATCH] PL011: Use BUSY bit Fixes #100 --- 06_drivers_gpio_uart/README.md | 156 ++++++++++------- 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs | 1 + .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 144 +++++++++------- 06_drivers_gpio_uart/src/cpu.rs | 5 +- 07_uart_chainloader/README.md | 17 +- 07_uart_chainloader/demo_payload_rpi3.img | Bin 6856 -> 6808 bytes 07_uart_chainloader/demo_payload_rpi4.img | Bin 6728 -> 6680 bytes 07_uart_chainloader/src/_arch/aarch64/cpu.rs | 1 + .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 144 +++++++++------- 07_uart_chainloader/src/cpu.rs | 5 +- 08_timestamps/README.md | 46 ++--- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 141 +++++++++------- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 141 +++++++++------- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 141 +++++++++------- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 141 +++++++++------- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 141 +++++++++------- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 141 +++++++++------- 14_exceptions_part2_peripheral_IRQs/README.md | 46 ++--- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 159 +++++++++++------- 15_virtual_mem_part2_mmio_remap/README.md | 22 +-- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 159 +++++++++++------- X1_JTAG_boot/jtag_boot_rpi3.img | Bin 8240 -> 8144 bytes X1_JTAG_boot/jtag_boot_rpi4.img | Bin 7968 -> 6848 bytes .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 141 +++++++++------- 24 files changed, 1094 insertions(+), 798 deletions(-) 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 80df7c197febdb8b7a7739c51af927e30915b3c9..83fe7bffab6b25be79d8cd89f4803003add22f0b 100755 GIT binary patch delta 1161 zcmah{T}YEr7(VCw_D$#C{{PImO`UFotX3*U=BJ|NBBBv!(2q+BONy2^g@lU&5ruX- zl64ViMKQ06)rDON5hz}W{dntCg1G{V0-03nJ=+ngn-093?|Gm1=lS0A&eYtiiIm_s z#5RY?^R+5$Oe)YQM(A056%psSnXHJ2JfJS29M{L%1q-%!S;(|_dNCKtDV0^88bX#@ zyF>iIzGu_+IvNqr3PBg$5dGL0YgQb=3{k082$|`e^hyY_%NBdAtjEfri+Cn1vDKVS zQMh4_E#)_3TplYce2Ps@AIV23QUFqtEr|5uePpDmgf0~Y@k83AmWAmOdQ)A71^PtY z;w2`M1flU9Wt53`cG*ZAvJzE_=s^??B&(#T3jChs5~|m*CSC-nM-L>udkMvl4P+-a;Jy81FJ2Cnl+A;pN)>q`T6Gr)gVy=1ZUgd;6!>V&R8mq>p2C=kX?@@>74)NFS4o3mb6vdkeI!x%H?G95BE4qUWkqcq&R z&TDOTIL>(k=i4}j70#T0<9Bj?tE-!1C6BM>n5z)cx|RAZ=d4$YvpU8(ALe+5&RXoM zurbR<=vT`DRgXH0+_cGBhcUfo?eJ~+zHt3o-jCffSe*OS+z)Mq;!C)bhKf6W1HAGp AJOBUy delta 1209 zcmb7DO>7fK6n?Yob?gxEf9y3F?*?LLjS8}9NU7S!Nt9B;0kKL1NC>iusMfH6+gqRXwEMm9 z{mq*<=Q^)7Y)Z6oiN_e%+~OH=8B zeB!?27Wcr1MXzJmuX>yn2QjmReqFT9c)oM0s&TFCio4@W!7>t5u*I047_owUs`g zqdo7?vn0h9H(6wsuE+A=P@ z8Z5M&&(W$JovlTnZm?HDtbDZ_Z1N*a{nwIQ zdWPjVKOQPzwSdehx1+mt*e_9=bNqm`hWto*jE&=ekxXLE$98AvP0>kYRQyHm0sN;Yj}>c%`nqWIe5mq0{QW z*$UI+asAhL!-`AjN=0BCvnxNbqw((X9}^9)ps=Qs4ZL3AjNqe!|6E{PaaaE9KP>q5 zsz(I&3jgB*i!NkHCs{J_q56xcdt2~x0~75_QBZ4=MH-t)+JfZ4ViX^&kX=O-`w+z^ z5!u8FRV-zHP9Q#&q!pS;K_aUU5rh;`3_eIuL&b+|w+}8+A>N1+O~x}bf5gjy<;-_3 z|95Wt*}&TawNCcYd)W?2`y>Ff3}87oukprh;7u9$T>U}GV1=^IZ?Nf!Re5qF45qE5 z&6PJHHPmg$U%TIHPIn1w@-tGUfIIRy%hksMXPApHI3&HO;pf2*rHZSv^>;!Kuu!GI z`xf;2?Y3Q6n$hZ;9b=3Y>iNUhn4UYvI{<2jsF1Cbz|XN^esMH|n@1|_JdQ@p+-inj zf$0hPuxXc}d&4;?t?wTP``0sJ3n&QyuS6b=jV5@j7_eH@Lpxc=Z)LD8+9j3mz+!Z$ ze?8CFsGn95ya=5LH^wOATgi&k_e)YQ50eg8I`Kj@FU@OsJ(|hi>g0XItdZw~rwZQt zPBm!#Rt;G+w;Ih2zl-)9YZiG!PAs)Cm~C8;tR2VM-ibx|?s?$5lVI6bf!k-9^+q0e zg3@N2SsYQjT#`vO%U;1()w?}LwdX`G%g2eabxpRmoPgzg8MJy`TmoA#@&-NElo`Li z@64GYDE%UX?bW`J#YjvabeBm(a6>Lr$b0|>UAMwcdtv>wzAZ3HBg|ZBS5DjGK^mBX zPsENZgf0>~q~PV)J+X3u%ar%@YK{j;zi#GnGnUc!PTBoIbLFkT^u+U8;{N_1tc3SY zVI-~(ReHHXk$t2`SonK|!DFp1C|mww3*4#F zdC>a)9%O;0L=SE6(L=k=fM2WV+BoWWJ%7}TsRXAd&Jm$oH_#vAwS6Z^6J81jOsgS> zmo5?Z^35L3=+N*9E5Wc)H764;xCYG}UoUZETKa;ivkT1X|8=OVsu5pSeQYzFPri0`@Y f!1IFOP(>-OHtAOu_K2`g;Y@l;x|znG(~tiJ1h!Y{ delta 1313 zcmah|O>7%g5T3VdyX&OIUfa8CC$+QdxV4>Dl%z=vYG|BRIp7{r+=>f_+KLbo2qDCw z5eICkh##dwe!NHpRU$tEO-5+J4fw7Vi|4TOODp0ACs*=CnXT9)C!?w?|s`!dvq}WelfvndDJQKvRE_jE9 zewBUe{YEex6|c|uo+CaZMYCnCetXZ3D2%D~)y@-yOx8pDt`fC8Ksy0yd$F<2lR#e| z8=)6TH(PzGOr}|XAa7K==>k-X;=a69f}S<|q;PBZ7+AkeNG4zqEVEAnXF^1wt+dB% z;Y=Nvt}x%twgU-4-_Nw*a3AfZD>z>(4K6}O#F~H+_XIPH|KGlmp%QAzh_my-q)<}X ztRau=hv311g$!+qk;MRkndS$Axoz89+r=Q= zxd3$M9GKP>pw`R8{4fbLf?+cY33fzII7KGpI62SWkPo{R<%7r9>OCUB5h*24Uy0qCzvg(L(X!M1PAgtLgqX$)ZrAH%jtL9mcp-@FHObD};{u(YlD%UW0ucIug=TRK&Q+YLa?TzX!c+HPodl<5r(H zzclTs7GF~$NBg|6Bq`&}4ynUNmiiHyMLkNSpZx^R;(}_Rr>TJY#B!RJG3>xY`;rd& z9gbzE!r6{FA6$3rL+k9_@JNDR9=7g>2+#@@~ULkbjJD^bi|?cD#zUGCJVbU_86jKDh-$2=WMY5r!6yDC*n%q$SwmTs zrP$-%F*dJi$wONE6>r&WK(uIL*3Q9WLXN87L //! 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 6fe154299791b169e5b4fbfa9e85d0b734d27d9b..9f8ada21812d7d7772028c6bfed45c182479b457 100755 GIT binary patch delta 2277 zcmb7FeN0nV6uB5>*6DKj8*F?@(Us_mk-QT*tZaRp*Y)e3L2}qPAITyYv5Q6+ zCq>6kAlcXu8GFEAg#GkKUe7g_QiU(&I+nW2R92kYSW1j|De)D2CV679dy23W#YVBA@b~p94PmE&e%KYnNZQ%cnEzt#XI*qnL z7n5@U>_Imt^~r)ZIEsnKfQYvV5#xOOSfB)jf^J^&44(N2x)$>bnJxe|p$ zu-5|eWN!tTxS*2SfQeRy99Y-|$UlOhEVk1wqyBMn8tjSS-#H*?Er51lyofMn0y~hr z5Bz3(V>ki9P=`WZpek@ukajEpBXi0c1RHtiD(I-8lr(u(kw&${ z`fkH&;yuX`FZku6-0ShfUmmF4^XqIqjt}~AgsIDnsh)o`75GCr$Ij%~dFDc*=N;C{ z;ld=6bT@KPGbC9J{X$c1ue%^=z-yn%VQs#(DI}`Ra!p$5YEg}3HrE|O@T{<@Q8R~VDLWHeVKoZ9MJ1o#X|>MySJvhy&KJ7LUh$6?$Iex1?k%f=U> z`Zlw>pvu~_kQ|p}hrtRYGd-GKBz*G`T ze5Q@h00G0g5A^enBC-V-b;R6bl!0S~^hvdD8jQAQZ*_rWicBj!-zg0fgx zYL>h_?)UHlXfO6>fbDaHhCtqaW2$+5r3%f6X^7s*NTJD@o3WENW-i7D>06oKrW{!U zQQX(I(QR2JxSEozl474WvIeCk+-jq~pkTN?xHq4MvUHV?x+@sH1*&Se-O;*V(LRqe z&*&nzH)lt_=c98c9m&>-Z>C4Aoz$PbDei7^WHy~mOLLa#t8yaK^yrb>qI0Nc;kKT3 z-G(diGi3{ThtG26A#+NJJfQqrg~M_KC`!^)>zw4GTC`~ zcINs0pP6Sa+fO<=v$2WfT*MQnf(6+0nE~y^2%Yr*jEKdPM<#hhKBf7b6?=zjxOD7x zRJ*L{1p`Th^O+4D$#5uxmB9F_@oBJoA znoE$v#>_8AzG6O7M9$-4W1aVztNu19AYxQ4u; z2eFkH?;fg%@55;&OGqq2opHcERfWjbulQ^D)Km^im5k-?6$ z&4uFVArbbH8W8zaRYSLnjq+PxcwNr8qs^BZ%1O|O!s1NLxYTm7dwPf?p+>CvC(W!L z-)bo)g_gLiZI`=U(>6ig*63>v*+hEL^o)BL%Ui*@4!oQ=XWkeOmZ7loOhQ)hJl4YL zw<0pxh=?%LJbvp0CkIX-Wojc*f+u*ne^%`p5m4#uI!73wv@lC8Lbu>J=&Ih)*KEha zO-@AXV?d@L?BRT25t-^kvIY&1UJ;#kJK=}ek&j#hz<3L|l7O)Tm}|tbLvI7$G;kzs zLgZE8oPv3#gA;f|;An_qGChQ4#w+H>W^D()?Tl@JlcxuGIkv^)TGVp9JMbI|OD(6m zeGC*J6TqSNmiX>?gxsCS8x|1>=otN_wN~Oh6`K{@0tX9>LO?I|EGD5rd>z9%SgU9uue<_*l z<%l2ba#3yWm6(wWpE!5ixMRmLH-k7L*BFz_@^`t$epk=1MLD+Mh(s*B$5rVlB5@Dd zLb+F(oO`~P=&_V)cjAKkcWben3R1R5d0Z(VgP%_+YKws@V{j-nrqm^(0V%9+-iMSG zs@kQ^p#z$ocn~RN`WTxxL;r5=N_o~;d7oV=Rs~p_9Y^6DCgMF270W>$#J2MC9QPoVOx5}ScumkF*M#mjnK@iWKQ~pR%^mnG{lJ445KbDK zmf`N1QM()%mDWLiQdM>SlT_cRGd(rT;Ax5$=G-gH4r7lCy=T)d%3OY9Vyab;5jGg* zyS0LBo>kplM~vw7KEP+_reFG+16EME#7g?_*omrgvKG-^1ZgX!C^&#+F9_}997-y` z%ascDg%DvMhm_$@kg^LL5cJ6U_9Nv3=(F)*Xt%*XZF(YR?NKceyIWoFfKd~305a|2 zOr*p>mOcC|Qdo@I5|C0@?{RtOK^oYNNP8Mm*hOdqI~KE`88VR;vvP=;L1r&Q&>AHN zssIA!0|n9YBq&o2D(4#iQkelWIFlO)G$Np#JG*maffN*Gq>mChA0u4#uY|3x@}~g0 zpGH{72xE1^9-DSr86_hWfLef;CE$(_CNaYB6H>vE6_HesTMq9fCVQA2++YOnA%X9`2#u k1sh7{HtaM}Cltjb+W-In diff --git a/X1_JTAG_boot/jtag_boot_rpi4.img b/X1_JTAG_boot/jtag_boot_rpi4.img index d5573467fe56c7dfe6f5289a4c6d98bee8e3ed6c..e9d7e7ddde04a9b940c72fac1745384b4231845f 100755 GIT binary patch delta 1548 zcmb7D-D@0G6hC)nXET%7B&)kS`;q-(lkCo{B261(sct5QNRt=oQl$vm#2{!D3i}W2 z80tf@4|}n|kVP%-z@t!2=dS6e0cvpt0Hl?yf`E@(6B~Owd+g4&MseDR; z>)T3HLb207P6k2%+AWD{qXqLB+EW~VoHzFYdDd3#OTgL;;uCm&5yzIDYQFn$hKWLcK#fUIYzfQMc%vNR={g*uIOXf@Btc z$67Whcl}USPS138$*6tcF$?Dd{$sj?>qMVYg4KfZGt}(FgJhZHsD0%c%1I*cDFn_q z4LzC*rlQdWEEMHn9^}3cf7vYq<`0Q z7;wizz$$eAKj6}TMBKv>FW^h!fSn2OWjVl0hXCUcHsE`IBjCTR;}3CVe#GTUbHvG6 z(2*?faCa2&jV)Wc6^zR+Z(EFqXf zAHEOgTb9`Q(CxWw_~Gyg)wPk?&NovmUMQlL=Kdh}KbjX(S#c2@af!KcwfFv@ z>y^Vy9~u=JVfIZdPn_7;L9EWN!N(tb#vGSJVFJ0}xRb5$uAgrrqok zwZX!VxsHw2Z4)0!?O<)S+QjW6h1xbHZLF3`f3%4U*0{TfwAi$6M5bpR>+6rb$<00Y z%z1w2+%tWP4_64YX@l{!%}meQYJ~k1z$u4|C*i9=Mi?M3F=s019BR3Ip1w4-)-2s( zpqB+#oXd8F_Ci^yoo6m9^nhHsgbkNp=6>JfHv0yGGGiVMC&7A!=fu4_Qyj`D@TUO? z7fxG=(`OFGc2Q(}1w~??o1}d1`$OE_>OrUPhoJNBn!$WWiq(vv)Er^SXgftxknV9j zLs0+Jh2gnm^hA<0eu9;nF9zMs4Ig-KFc9yCfcs_G?;%R*&0~~p~x|cL<$+{9uA1X$O<4o(y%|A z3X7Hl%;0=nkafM|Ufq7&TS>8_BP^_sqFpuM_&gGu2NH)l7vT1AaJI(B21hZ`6!2pf zn9*1RMaD{KshS%&gi1mUBBCIWiV>gew~@5IH{Bogob#sReH^pq^3trMg_P9%*;Mu@0 zAO<{lA6`I48fa2)47iO9?B{|1#oYJZzMA&y6P1*C)E}i_{Xhb$;fJA${v&^?8OHgU>{p>{4`0K=8Hax*%tFhQG?1=Sp{z&;{*68{qU#BCo zWr@m*`}ZllCfenX+lwKDMBKhoJ3SZ-m#5JjjkHsz(~y2jqn&j~qu$io8VTP4>0@e# z)Ih;O`l*0hxlg6rZx_k(fiPq59mYMCRCQb7+wrZS>)dPC3Nd z^tZ)xhA&N>rNmj~5MMXGqI~`W|JEA~S*=;C!`$U}p&UR)iQ9aLaQn=^1`fypvXE5S;yvGqvfB6qO== zJFUu}M~-6ROsXqh2lgQ)Q0x(b>s%EtFuFqBhJd?y=8Td8BZOGwanS4Lu0cqVOUSaF z*q5l|#8Y_$999++6(F!osh+T$FDLbA=isD#Kxu<}vU}imWg#jI$nts=RC`2TfzzcN z;(xDYCDlmX(}W5DxrT^}3o@!R@lizP86+i5^}$pXi`^Nv>3zi{jG6wV`#7hFf00p5 zpSFrgnR&_t5!4BC6_g1g=o3V^(7#Yzm03Xdmx%3|rG91FVQ(`O**GO1a8a3HF<>yxd=vBzByVWSN#Y$`yJG(W}NPAhIE~0>coxwF^iXcitWOfKoBmv6NJ! z{;gw-6qOO671#%Omya2xXsP;mh5O^2BJ(i_Fx*eD3uXNY=TSc`$yOYkk8y57=eLs8 zu_CZl*t}>&g&G@KsgmqBN-@7lnW36<3`#*aRY7-BRwf6wiH@ABtV|gl!tTB@RY-xZ zONS2d@x-TO3o7@r2A33O*j{6zEOpDowKI#e(U`iNFc(U3DwzNW60S*+%&g4W!6Hfz zEDT9qZ?Ix)=7qfQoQFS4v$Z?+rMgyny=`7`EIXGzm@Q`J6!~?2nN^e1Tmb}iI|=iT z+TE_p=j!?!JexN+dfT_wx3n~Rk@tV?Idpv}*9}h78Ef==fw%EbTl?}AiJeyC*Qt(+qO1uN8`r% zeESy9Hq_W`epIugt&MMKYu)^S2-r1X&S~*v0;@B^dVoqP4HSD?eJquGu1OsaSYWuTcVu$JOlJIilBc zliq6+bFwQa)$_uH=N&dJt_3sZgtLrtJcr^Q2u_|XC17Vxkob&vDwAcAX 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.