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