From c35a30cd0bce2725c3a2586309b7edd3875fb117 Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Mon, 4 Jan 2021 14:37:17 +0100 Subject: [PATCH] Bump UART to 921_600 baud + other bugfixes Fixes #95 Fixes #98 Co-authored-by: Takumasa Sakao --- 06_drivers_gpio_uart/README.md | 158 +++++++++++++----- 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs | 1 - .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 130 ++++++++++---- 06_drivers_gpio_uart/src/console.rs | 7 + 06_drivers_gpio_uart/src/main.rs | 3 + 07_uart_chainloader/README.md | 158 ++++++++---------- 07_uart_chainloader/demo_payload_rpi3.img | Bin 6720 -> 6856 bytes 07_uart_chainloader/demo_payload_rpi4.img | Bin 6600 -> 6728 bytes 07_uart_chainloader/src/_arch/aarch64/cpu.rs | 1 - .../src/bsp/device_driver/bcm/bcm2xxx_gpio.rs | 2 +- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 126 ++++++++++---- 07_uart_chainloader/src/console.rs | 6 +- 07_uart_chainloader/src/main.rs | 5 +- 08_timestamps/README.md | 91 +++++++--- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 135 ++++++++++----- 08_timestamps/src/console.rs | 6 +- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 135 ++++++++++----- 09_hw_debug_JTAG/src/console.rs | 6 +- 10_privilege_level/README.md | 10 +- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 135 ++++++++++----- 10_privilege_level/src/console.rs | 6 +- 10_privilege_level/src/main.rs | 4 + .../README.md | 8 +- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 135 ++++++++++----- .../src/console.rs | 6 +- .../src/main.rs | 4 + 12_exceptions_part1_groundwork/README.md | 6 +- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 135 ++++++++++----- 12_exceptions_part1_groundwork/src/console.rs | 6 +- 12_exceptions_part1_groundwork/src/main.rs | 4 + 13_integrated_testing/README.md | 10 +- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 135 ++++++++++----- 13_integrated_testing/src/console.rs | 6 +- 13_integrated_testing/src/main.rs | 4 + 14_exceptions_part2_peripheral_IRQs/README.md | 118 +++---------- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 85 +++++++--- .../src/console.rs | 6 +- 15_virtual_mem_part2_mmio_remap/README.md | 20 +-- .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 85 +++++++--- .../src/console.rs | 6 +- X1_JTAG_boot/jtag_boot_rpi3.img | Bin 8080 -> 8240 bytes X1_JTAG_boot/jtag_boot_rpi4.img | Bin 6800 -> 7968 bytes .../device_driver/bcm/bcm2xxx_pl011_uart.rs | 135 ++++++++++----- X1_JTAG_boot/src/console.rs | 6 +- utils/minipush.rb | 21 ++- utils/miniterm.rb | 4 +- 46 files changed, 1356 insertions(+), 714 deletions(-) diff --git a/06_drivers_gpio_uart/README.md b/06_drivers_gpio_uart/README.md index 7e3da3ba..6ca14cfe 100644 --- a/06_drivers_gpio_uart/README.md +++ b/06_drivers_gpio_uart/README.md @@ -185,14 +185,13 @@ 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 -@@ -37,6 +37,17 @@ +@@ -37,6 +37,16 @@ // 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 { @@ -433,7 +432,7 @@ 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,307 @@ +@@ -0,0 +1,381 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2021 Andre Richter @@ -572,6 +571,12 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri +/// Abstraction for the associated MMIO registers. +type Registers = MMIODerefWrapper; + ++#[derive(PartialEq)] ++enum BlockingMode { ++ Blocking, ++ NonBlocking, ++} ++ +//-------------------------------------------------------------------------------------------------- +// Public Definitions +//-------------------------------------------------------------------------------------------------- @@ -610,24 +615,41 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri + + /// Set up baud rate and characteristics. + /// -+ /// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt). ++ /// This results in 8N1 and 921_600 baud. + /// -+ /// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is: -+ /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). ++ /// 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). + /// -+ /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will -+ /// give the best approximation we can get. A 5 modulo error margin is acceptable for UART and we're -+ /// now at 0.01 modulo. ++ /// 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. + pub fn init(&mut self) { -+ // Turn it off temporarily. ++ // 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. ++ // ++ // For example, this can happen during runtime on a call to panic!(), because panic!() ++ // initializes its own UART instance and calls init(). ++ // ++ // Hence, flush first to ensure all pending characters are transmitted. ++ self.flush(); ++ ++ // Turn the UART off temporarily. + self.registers.CR.set(0); + ++ // Clear all pending interrupts. + self.registers.ICR.write(ICR::ALL::CLEAR); -+ self.registers.IBRD.write(IBRD::IBRD.val(5)); -+ self.registers.FBRD.write(FBRD::FBRD.val(13)); ++ ++ // Set the baud rate. ++ self.registers.IBRD.write(IBRD::IBRD.val(3)); ++ self.registers.FBRD.write(FBRD::FBRD.val(16)); ++ ++ // Set 8N1 + FIFO on. + self.registers + .LCRH -+ .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on ++ .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); ++ ++ // Turn the UART on. + self.registers + .CR + .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -645,6 +667,58 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri + + self.chars_written += 1; + } ++ ++ /// 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) { ++ 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. ++ fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { ++ // If RX FIFO is empty, ++ if self.registers.FR.matches_all(FR::RXFE::SET) { ++ // immediately return in non-blocking mode. ++ if blocking_mode == BlockingMode::NonBlocking { ++ return None; ++ } ++ ++ // Otherwise, wait until a char was received. ++ while self.registers.FR.matches_all(FR::RXFE::SET) { ++ cpu::nop(); ++ } ++ } ++ ++ // Read one character. ++ let mut ret = self.registers.DR.get() as u8 as char; ++ ++ // Convert carrige return to newline. ++ if ret == '\r' { ++ ret = '\n' ++ } ++ ++ // Update statistics. ++ self.chars_read += 1; ++ ++ Some(ret) ++ } +} + +/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are @@ -667,6 +741,8 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri +} + +impl PL011Uart { ++ /// Create an instance. ++ /// + /// # Safety + /// + /// - The user must ensure to provide a correct MMIO start address. @@ -706,29 +782,26 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri + // readability. + self.inner.lock(|inner| fmt::Write::write_fmt(inner, args)) + } ++ ++ fn flush(&self) { ++ // Spin until TX FIFO empty is set. ++ self.inner.lock(|inner| inner.flush()); ++ } +} + +impl console::interface::Read for PL011Uart { + fn read_char(&self) -> char { -+ self.inner.lock(|inner| { -+ // Spin while RX FIFO empty is set. -+ while inner.registers.FR.matches_all(FR::RXFE::SET) { -+ cpu::nop(); -+ } -+ -+ // Read one character. -+ let mut ret = inner.registers.DR.get() as u8 as char; -+ -+ // Convert carrige return to newline. -+ if ret == '\r' { -+ ret = '\n' -+ } -+ -+ // Update statistics. -+ inner.chars_read += 1; ++ self.inner ++ .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) ++ } + -+ ret -+ }) ++ fn clear_rx(&self) { ++ // Read from the RX FIFO until it is indicating empty. ++ while self ++ .inner ++ .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) ++ .is_some() ++ {} + } +} + @@ -1096,7 +1169,7 @@ diff -uNr 05_safe_globals/src/bsp.rs 06_drivers_gpio_uart/src/bsp.rs diff -uNr 05_safe_globals/src/console.rs 06_drivers_gpio_uart/src/console.rs --- 05_safe_globals/src/console.rs +++ 06_drivers_gpio_uart/src/console.rs -@@ -14,18 +14,34 @@ +@@ -14,8 +14,26 @@ /// Console write functions. pub trait Write { @@ -1105,19 +1178,25 @@ diff -uNr 05_safe_globals/src/console.rs 06_drivers_gpio_uart/src/console.rs + /// Write a Rust format string. fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; - } - ++ ++ /// Block execution until the last buffered character has been physically put on the TX ++ /// wire. ++ fn flush(&self); ++ } ++ + /// Console read functions. + pub trait Read { + /// Read a single character. + fn read_char(&self) -> char { + ' ' + } -+ } + ++ /// Clear RX buffers, if any. ++ fn clear_rx(&self); + } + /// Console statistics. - pub trait Statistics { - /// Return the number of characters written. +@@ -24,8 +42,13 @@ fn chars_written(&self) -> usize { 0 } @@ -1216,7 +1295,7 @@ diff -uNr 05_safe_globals/src/main.rs 06_drivers_gpio_uart/src/main.rs mod memory; mod panic_wait; mod print; -@@ -115,16 +125,46 @@ +@@ -115,16 +125,49 @@ /// # Safety /// /// - Only a single core must be active and running this function. @@ -1240,6 +1319,7 @@ diff -uNr 05_safe_globals/src/main.rs 06_drivers_gpio_uart/src/main.rs - println!("[0] Hello from pure Rust!"); +/// The main function running after the early init. +fn kernel_main() -> ! { ++ use bsp::console::console; + use console::interface::All; + use driver::interface::DriverManager; + @@ -1263,6 +1343,8 @@ diff -uNr 05_safe_globals/src/main.rs 06_drivers_gpio_uart/src/main.rs - println!("[2] Stopping here."); - cpu::wait_forever() ++ // Discard any spurious received characters before going into echo mode. ++ console().clear_rx(); + loop { + let c = bsp::console::console().read_char(); + bsp::console::console().write_char(c); diff --git a/06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs b/06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs index dfc6be13..2d5d0b04 100644 --- a/06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs +++ b/06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs @@ -40,7 +40,6 @@ pub unsafe fn _start() -> ! { 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 fdd92f65..39834723 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 @@ -136,6 +136,12 @@ register_structs! { /// Abstraction for the associated MMIO registers. type Registers = MMIODerefWrapper; +#[derive(PartialEq)] +enum BlockingMode { + Blocking, + NonBlocking, +} + //-------------------------------------------------------------------------------------------------- // Public Definitions //-------------------------------------------------------------------------------------------------- @@ -174,24 +180,41 @@ impl PL011UartInner { /// Set up baud rate and characteristics. /// - /// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt). + /// This results in 8N1 and 921_600 baud. /// - /// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is: - /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). + /// 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). /// - /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will - /// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're - /// now at 0.01 %. + /// 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%. pub fn init(&mut self) { - // Turn it off temporarily. + // 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. + // + // For example, this can happen during runtime on a call to panic!(), because panic!() + // initializes its own UART instance and calls init(). + // + // Hence, flush first to ensure all pending characters are transmitted. + self.flush(); + + // Turn the UART off temporarily. self.registers.CR.set(0); + // Clear all pending interrupts. self.registers.ICR.write(ICR::ALL::CLEAR); - self.registers.IBRD.write(IBRD::IBRD.val(5)); - self.registers.FBRD.write(FBRD::FBRD.val(13)); + + // Set the baud rate. + self.registers.IBRD.write(IBRD::IBRD.val(3)); + self.registers.FBRD.write(FBRD::FBRD.val(16)); + + // Set 8N1 + FIFO on. self.registers .LCRH - .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); + + // Turn the UART on. self.registers .CR .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -209,6 +232,58 @@ impl PL011UartInner { self.chars_written += 1; } + + /// 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) { + 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. + fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { + // If RX FIFO is empty, + if self.registers.FR.matches_all(FR::RXFE::SET) { + // immediately return in non-blocking mode. + if blocking_mode == BlockingMode::NonBlocking { + return None; + } + + // Otherwise, wait until a char was received. + while self.registers.FR.matches_all(FR::RXFE::SET) { + cpu::nop(); + } + } + + // Read one character. + let mut ret = self.registers.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + // Update statistics. + self.chars_read += 1; + + Some(ret) + } } /// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are @@ -231,6 +306,8 @@ impl fmt::Write for PL011UartInner { } impl PL011Uart { + /// Create an instance. + /// /// # Safety /// /// - The user must ensure to provide a correct MMIO start address. @@ -270,29 +347,26 @@ impl console::interface::Write for PL011Uart { // readability. self.inner.lock(|inner| fmt::Write::write_fmt(inner, args)) } + + fn flush(&self) { + // Spin until TX FIFO empty is set. + self.inner.lock(|inner| inner.flush()); + } } impl console::interface::Read for PL011Uart { fn read_char(&self) -> char { - self.inner.lock(|inner| { - // Spin while RX FIFO empty is set. - while inner.registers.FR.matches_all(FR::RXFE::SET) { - cpu::nop(); - } - - // Read one character. - let mut ret = inner.registers.DR.get() as u8 as char; - - // Convert carrige return to newline. - if ret == '\r' { - ret = '\n' - } - - // Update statistics. - inner.chars_read += 1; + self.inner + .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) + } - ret - }) + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner + .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) + .is_some() + {} } } diff --git a/06_drivers_gpio_uart/src/console.rs b/06_drivers_gpio_uart/src/console.rs index 0a238594..3552823c 100644 --- a/06_drivers_gpio_uart/src/console.rs +++ b/06_drivers_gpio_uart/src/console.rs @@ -19,6 +19,10 @@ pub mod interface { /// Write a Rust format string. fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; + + /// Block execution until the last buffered character has been physically put on the TX + /// wire. + fn flush(&self); } /// Console read functions. @@ -27,6 +31,9 @@ pub mod interface { fn read_char(&self) -> char { ' ' } + + /// Clear RX buffers, if any. + fn clear_rx(&self); } /// Console statistics. diff --git a/06_drivers_gpio_uart/src/main.rs b/06_drivers_gpio_uart/src/main.rs index e0bf47a9..1674b9e4 100644 --- a/06_drivers_gpio_uart/src/main.rs +++ b/06_drivers_gpio_uart/src/main.rs @@ -143,6 +143,7 @@ unsafe fn kernel_init() -> ! { /// The main function running after the early init. fn kernel_main() -> ! { + use bsp::console::console; use console::interface::All; use driver::interface::DriverManager; @@ -163,6 +164,8 @@ fn kernel_main() -> ! { ); println!("[3] Echoing input now"); + // Discard any spurious received characters before going into echo mode. + console().clear_rx(); loop { let c = bsp::console::console().read_char(); bsp::console::console().write_char(c); diff --git a/07_uart_chainloader/README.md b/07_uart_chainloader/README.md index ef90e02e..fe625110 100644 --- a/07_uart_chainloader/README.md +++ b/07_uart_chainloader/README.md @@ -197,7 +197,7 @@ diff -uNr 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs 07_uart_chainloader/src/ } else { // If not core0, infinitely wait for events. wait_forever() -@@ -55,3 +55,19 @@ +@@ -54,3 +54,19 @@ asm::wfe() } } @@ -218,55 +218,62 @@ diff -uNr 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs 07_uart_chainloader/src/ + core::intrinsics::unreachable() +} +diff -uNr 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs +--- 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs ++++ 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs +@@ -144,7 +144,7 @@ + // Make an educated guess for a good delay value (Sequence described in the BCM2837 + // peripherals PDF). + // +- // - According to Wikipedia, the fastest Pi3 clocks around 1.4 GHz. ++ // - According to Wikipedia, the fastest RPi4 clocks around 1.5 GHz. + // - The Linux 2837 GPIO driver waits 1 µs between the steps. + // + // So lets try to be on the safe side and default to 2000 cycles, which would equal 1 µs + 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 -@@ -270,6 +270,15 @@ - // readability. - self.inner.lock(|inner| fmt::Write::write_fmt(inner, args)) +@@ -257,7 +257,7 @@ } -+ -+ fn flush(&self) { -+ // Spin until TX FIFO empty is set. -+ self.inner.lock(|inner| { -+ while !inner.registers.FR.matches_all(FR::TXFE::SET) { -+ cpu::nop(); -+ } -+ }); -+ } - } - impl console::interface::Read for PL011Uart { -@@ -280,18 +289,20 @@ - cpu::nop(); - } + /// Retrieve a character. +- fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { ++ fn read_char(&mut self, blocking_mode: BlockingMode) -> Option { + // If RX FIFO is empty, + if self.registers.FR.matches_all(FR::RXFE::SET) { + // immediately return in non-blocking mode. +@@ -272,12 +272,7 @@ + } -- // Read one character. -- let mut ret = inner.registers.DR.get() as u8 as char; -- -- // Convert carrige return to newline. -- if ret == '\r' { -- ret = '\n' -- } + // Read one character. +- let mut ret = self.registers.DR.get() as u8 as char; - - // Update statistics. - inner.chars_read += 1; +- // Convert carrige return to newline. +- if ret == '\r' { +- ret = '\n' +- } ++ let ret = self.registers.DR.get() as u8 as char; + + // Update statistics. + self.chars_read += 1; +@@ -357,14 +352,14 @@ + impl console::interface::Read for PL011Uart { + fn read_char(&self) -> char { + self.inner +- .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) ++ .lock(|inner| inner.read_char(BlockingMode::Blocking).unwrap()) + } -- ret -+ // Read one character. -+ inner.registers.DR.get() as u8 as char -+ }) -+ } -+ -+ fn clear(&self) { -+ self.inner.lock(|inner| { -+ // Read from the RX FIFO until it is indicating empty. -+ while !inner.registers.FR.matches_all(FR::RXFE::SET) { -+ inner.registers.DR.get(); -+ } - }) + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner +- .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) ++ .lock(|inner| inner.read_char(BlockingMode::NonBlocking)) + .is_some() + {} } - } diff -uNr 06_drivers_gpio_uart/src/bsp/raspberrypi/link.ld 07_uart_chainloader/src/bsp/raspberrypi/link.ld --- 06_drivers_gpio_uart/src/bsp/raspberrypi/link.ld @@ -376,31 +383,6 @@ diff -uNr 06_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs 07_uart_chainloader unsafe { range = RangeInclusive::new(__bss_start.get(), __bss_end_inclusive.get()); -diff -uNr 06_drivers_gpio_uart/src/console.rs 07_uart_chainloader/src/console.rs ---- 06_drivers_gpio_uart/src/console.rs -+++ 07_uart_chainloader/src/console.rs -@@ -19,6 +19,10 @@ - - /// Write a Rust format string. - fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; -+ -+ /// Block execution until the last character has been physically put on the TX wire -+ /// (draining TX buffers/FIFOs, if any). -+ fn flush(&self); - } - - /// Console read functions. -@@ -27,6 +31,9 @@ - fn read_char(&self) -> char { - ' ' - } -+ -+ /// Clear RX buffers, if any. -+ fn clear(&self); - } - - /// Console statistics. - diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs --- 06_drivers_gpio_uart/src/main.rs +++ 07_uart_chainloader/src/main.rs @@ -432,23 +414,13 @@ diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs mod runtime_init; mod synchronization; -@@ -143,28 +147,52 @@ - - /// The main function running after the early init. +@@ -145,29 +149,49 @@ fn kernel_main() -> ! { -+ use bsp::console::console; + use bsp::console::console; use console::interface::All; - use driver::interface::DriverManager; -- -- println!("[0] Booting on: {}", bsp::board_name()); -- println!("[1] Drivers loaded:"); -- for (i, driver) in bsp::driver::driver_manager() -- .all_device_drivers() -- .iter() -- .enumerate() -- { -- println!(" {}. {}", i + 1, driver.compatible()); +- println!("[0] Booting on: {}", bsp::board_name()); + println!(" __ __ _ _ _ _ "); + println!("| \\/ (_)_ _ (_) | ___ __ _ __| |"); + println!("| |\\/| | | ' \\| | |__/ _ \\/ _` / _` |"); @@ -458,22 +430,30 @@ diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs + println!(); + println!("[ML] Requesting binary"); + console().flush(); -+ -+ // Clear the RX FIFOs, if any, of spurious received characters before starting with the loader -+ // protocol. -+ console().clear(); -+ -+ // Notify `Minipush` to send the binary. -+ for _ in 0..3 { -+ console().write_char(3 as char); - } + +- println!("[1] Drivers loaded:"); +- for (i, driver) in bsp::driver::driver_manager() +- .all_device_drivers() +- .iter() +- .enumerate() +- { +- println!(" {}. {}", i + 1, driver.compatible()); +- } ++ // Discard any spurious received characters before starting with the loader protocol. ++ console().clear_rx(); - println!( - "[2] Chars written: {}", - bsp::console::console().chars_written() - ); - println!("[3] Echoing input now"); -- ++ // Notify `Minipush` to send the binary. ++ for _ in 0..3 { ++ console().write_char(3 as char); ++ } + +- // Discard any spurious received characters before going into echo mode. +- console().clear_rx(); - loop { - let c = bsp::console::console().read_char(); - bsp::console::console().write_char(c); diff --git a/07_uart_chainloader/demo_payload_rpi3.img b/07_uart_chainloader/demo_payload_rpi3.img index 34c3214cfbe8d37cc863e86b50c5d3630ce0304e..47a0ae1037b18fa72ffdd9262c664c64bfc89289 100755 GIT binary patch delta 1724 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 diff --git a/07_uart_chainloader/demo_payload_rpi4.img b/07_uart_chainloader/demo_payload_rpi4.img index c7919ed6517de38049e1f9fd7eb05966c58a4063..14e578c1784483707bb257c94ea90a0cb9d7cb97 100755 GIT binary patch delta 1870 zcmb_cTWlLe6uq-+JG*%~#;-W3V>=HV+msN}#JohDRzYeCYOpH!=tmL?f;K``-H(8T zWJ5uara~D_qzWOCKBS@zA5aA(tX7aTd^CV)DEigs^J_z-DXzmiw1rg}mlU+iH^m^$u$t0)5 zzBvgJs_aW-hvB|>ASiyz`?FeShy3wKl8+-+xH7G2I|8kc<=8;CVRu6&DK{B?SzvF9 z9ZjW}D_0^a`5B-QboYcBX%2JFi^7DKCQ3dGN`a~3{lq)jX4y_iBRgP?6AwFOJ*EHs zlVi3Ah_jJK2D~&M{zS=@5QRthX>z{gH<5TI&y{T^!hZXtv*HBt?7Nqi0gRSo+QJr~ zgO5K%50P4Su5y4JWUcnDa4kKCG{lO8+7E#_ZHsWB_#mi%ZI=|lH82&VBm?vZ_LluI zhYJ&0lsqQGZH)FDtz}F0fPDkaVdTP!b~p&ZZ{BB3Rgp*ooyFSm&2Y2_(Q^y9`g=if zVt@acW-6hTLVh+>)sS8-J)tW_?Qo_F+OLYB>WHI$y-aRIe^lS7=hxC!w>bRX@4v|RYjK%0A2R< zfubG6}xUIJ4+gr4T< zC5S&~OQ?$&`KlLG87V~O{j`TudRJ5mnnlsRwkkTsIH;>z$sr5Ah4(NOlyp>l%>cvIuBVH=ht2c?EmWv;e%{oBbJ{BuD~?{S_j%nv~I{o`q3gL zzYAy{g`wYp=_b0B3n$FZIIFn^juW~t1JuV$X@mAYJQ58{e=FO9Mie{@^?xUu{x7yA zCfoT2{l@0QqN20M2Tn`ltAV$6P38?KV%)$l{LNU_Hr=L~T%)0x{w5v6HS@Ho8N3=3 z?6gaA4i~x566%m{QE(7u-??6{xmkiKZ)up&Xc9}gzv_{Ex4XN%#u(+Jx9(JGZwh-c za~|ITNwfw=V5Pb8Iv5Kbm8c zt^ng~tM}QO<{E=WubJ*1bNoUz``lYs+Uqvl{ciTB_YwIeyD_}Tx~k*kBzv*?$=21m eBjyGiWG$Lc5{B55#L@xs$nVL6MqA{Zas?t delta 1698 zcmZ8hZ%kWN6hF7GrGFT-(3VpE?De%_T~V?iW6g|lWx^~Z&ZHrZWQjobL8dcAO#Ech z;-8E;SbDugK4~^JhR3o5tr{AQ1M$Q7AYvRCW0n;7QjtX+VdD+qdG33!#Br1MoO{o| z-~F9)->sv!n$k7o>(6VQChw#fMhU>A)Z?MKRiG;Z&<`>#CW$=mr86Xzm=?O1Y!Fk# zM#yTiO_wsH681VnC+rL1ShEfD9KavXXOrsad2=eUHVgDIbRCw0RGO}%In1vniJT7$ z^4bxQ*I9}AIPqqlH9t>Ckd0ZQ#KV5Fe53u>X2y%2B<>)M^m=JN{GFV$5}9YXo!PYB z6^JLZT6~h2R!cMClFvwm^aL#iNOK5l&j5XeJVl3zpRJbml780aY(f2^Q?wqWBn5Pd z-EclvR*q@SavoDUFskF4pB1}AXBEw1{MwE>7J$HQnH_gUA`+cLHu7Q6yeqKI(h5fZ zZj#-ozyAFpiq}uuq-u8A6-pM!By?w29h-JS^M(M5=5ei>MvxFMdVduRYW3BmMczyn z$w*w9H)0#{YzagoYX#*x=D1n{&4)2A?*_$#exn~BLc3>T17I|F(Ih`m)xRH0RBFKF_iE5VBzbtZ`>rqe&h4l#_bO8$n91uN=_H8Vf z6WkMTvca;3h=<-^1!GbO{gwl>LbR_arrgJx8$M9%2rjZ*&3-KtISq({U*S4>rYsQM zqoKD(qk+@E|N6YcJmw>eL7mT4x*f^K7NxZY|JJIE_+!}-2pkgw!XnpyRKw8`63gDG zh4P!t@LB^IsXCopZdK+jKu<55l`H6U1rBQgT%T8uU;tz<+-X&Y(dWV)vvR;l{OV3P zdRJ9_fCV8CqyoxwsQ@*jd-odK873=(OVHItyTiHGDe8*7bqvn|keQz?{+6D&t2T;Fmoa2{6 zTl@X4RxpR}>Ewq#J=|ruiGF=wsCvn;UTMu{YqF zKxA{im#W5%vI~ZJ(%5hLSV!eS$AU*kmGiKH%2W0}r@rlCGnG-&$dt-6jfJy-5m{yw Z7zk***UY{{esD4Q&7b diff --git a/07_uart_chainloader/src/_arch/aarch64/cpu.rs b/07_uart_chainloader/src/_arch/aarch64/cpu.rs index 1895b6f1..184347d7 100644 --- a/07_uart_chainloader/src/_arch/aarch64/cpu.rs +++ b/07_uart_chainloader/src/_arch/aarch64/cpu.rs @@ -40,7 +40,6 @@ pub unsafe fn _start() -> ! { pub use asm::nop; /// Spin for `n` cycles. -#[cfg(feature = "bsp_rpi3")] #[inline(always)] pub fn spin_for_cycles(n: usize) { for _ in 0..n { diff --git a/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs b/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs index 771151e7..9d41900d 100644 --- a/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs +++ b/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs @@ -144,7 +144,7 @@ impl GPIOInner { // Make an educated guess for a good delay value (Sequence described in the BCM2837 // peripherals PDF). // - // - According to Wikipedia, the fastest Pi3 clocks around 1.4 GHz. + // - According to Wikipedia, the fastest RPi4 clocks around 1.5 GHz. // - The Linux 2837 GPIO driver waits 1 µs between the steps. // // So lets try to be on the safe side and default to 2000 cycles, which would equal 1 µs diff --git a/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index 1678cf00..9a92c9d2 100644 --- a/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ b/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs @@ -136,6 +136,12 @@ register_structs! { /// Abstraction for the associated MMIO registers. type Registers = MMIODerefWrapper; +#[derive(PartialEq)] +enum BlockingMode { + Blocking, + NonBlocking, +} + //-------------------------------------------------------------------------------------------------- // Public Definitions //-------------------------------------------------------------------------------------------------- @@ -174,24 +180,41 @@ impl PL011UartInner { /// Set up baud rate and characteristics. /// - /// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt). + /// This results in 8N1 and 921_600 baud. /// - /// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is: - /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). + /// 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). /// - /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will - /// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're - /// now at 0.01 %. + /// 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%. pub fn init(&mut self) { - // Turn it off temporarily. + // 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. + // + // For example, this can happen during runtime on a call to panic!(), because panic!() + // initializes its own UART instance and calls init(). + // + // Hence, flush first to ensure all pending characters are transmitted. + self.flush(); + + // Turn the UART off temporarily. self.registers.CR.set(0); + // Clear all pending interrupts. self.registers.ICR.write(ICR::ALL::CLEAR); - self.registers.IBRD.write(IBRD::IBRD.val(5)); - self.registers.FBRD.write(FBRD::FBRD.val(13)); + + // Set the baud rate. + self.registers.IBRD.write(IBRD::IBRD.val(3)); + self.registers.FBRD.write(FBRD::FBRD.val(16)); + + // Set 8N1 + FIFO on. self.registers .LCRH - .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); + + // Turn the UART on. self.registers .CR .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -209,6 +232,53 @@ impl PL011UartInner { self.chars_written += 1; } + + /// 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) { + 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. + fn read_char(&mut self, blocking_mode: BlockingMode) -> Option { + // If RX FIFO is empty, + if self.registers.FR.matches_all(FR::RXFE::SET) { + // immediately return in non-blocking mode. + if blocking_mode == BlockingMode::NonBlocking { + return None; + } + + // Otherwise, wait until a char was received. + while self.registers.FR.matches_all(FR::RXFE::SET) { + cpu::nop(); + } + } + + // Read one character. + let ret = self.registers.DR.get() as u8 as char; + + // Update statistics. + self.chars_read += 1; + + Some(ret) + } } /// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are @@ -231,6 +301,8 @@ impl fmt::Write for PL011UartInner { } impl PL011Uart { + /// Create an instance. + /// /// # Safety /// /// - The user must ensure to provide a correct MMIO start address. @@ -273,37 +345,23 @@ impl console::interface::Write for PL011Uart { fn flush(&self) { // Spin until TX FIFO empty is set. - self.inner.lock(|inner| { - while !inner.registers.FR.matches_all(FR::TXFE::SET) { - cpu::nop(); - } - }); + self.inner.lock(|inner| inner.flush()); } } impl console::interface::Read for PL011Uart { fn read_char(&self) -> char { - self.inner.lock(|inner| { - // Spin while RX FIFO empty is set. - while inner.registers.FR.matches_all(FR::RXFE::SET) { - cpu::nop(); - } - - // Update statistics. - inner.chars_read += 1; - - // Read one character. - inner.registers.DR.get() as u8 as char - }) + self.inner + .lock(|inner| inner.read_char(BlockingMode::Blocking).unwrap()) } - fn clear(&self) { - self.inner.lock(|inner| { - // Read from the RX FIFO until it is indicating empty. - while !inner.registers.FR.matches_all(FR::RXFE::SET) { - inner.registers.DR.get(); - } - }) + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner + .lock(|inner| inner.read_char(BlockingMode::NonBlocking)) + .is_some() + {} } } diff --git a/07_uart_chainloader/src/console.rs b/07_uart_chainloader/src/console.rs index 2d38cc1d..3552823c 100644 --- a/07_uart_chainloader/src/console.rs +++ b/07_uart_chainloader/src/console.rs @@ -20,8 +20,8 @@ pub mod interface { /// Write a Rust format string. fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; - /// Block execution until the last character has been physically put on the TX wire - /// (draining TX buffers/FIFOs, if any). + /// Block execution until the last buffered character has been physically put on the TX + /// wire. fn flush(&self); } @@ -33,7 +33,7 @@ pub mod interface { } /// Clear RX buffers, if any. - fn clear(&self); + fn clear_rx(&self); } /// Console statistics. diff --git a/07_uart_chainloader/src/main.rs b/07_uart_chainloader/src/main.rs index 2e436374..c8f2a91f 100644 --- a/07_uart_chainloader/src/main.rs +++ b/07_uart_chainloader/src/main.rs @@ -160,9 +160,8 @@ fn kernel_main() -> ! { println!("[ML] Requesting binary"); console().flush(); - // Clear the RX FIFOs, if any, of spurious received characters before starting with the loader - // protocol. - console().clear(); + // Discard any spurious received characters before starting with the loader protocol. + console().clear_rx(); // Notify `Minipush` to send the binary. for _ in 0..3 { diff --git a/08_timestamps/README.md b/08_timestamps/README.md index 45a5c270..111b1168 100644 --- a/08_timestamps/README.md +++ b/08_timestamps/README.md @@ -129,12 +129,11 @@ diff -uNr 07_uart_chainloader/src/_arch/aarch64/cpu.rs 08_timestamps/src/_arch/a } else { // If not core0, infinitely wait for events. wait_forever() -@@ -39,15 +39,6 @@ +@@ -39,14 +39,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 { @@ -145,7 +144,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() -> ! { -@@ -55,19 +46,3 @@ +@@ -54,19 +46,3 @@ asm::wfe() } } @@ -283,7 +282,7 @@ diff -uNr 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 08_times - // Make an educated guess for a good delay value (Sequence described in the BCM2837 - // peripherals PDF). - // -- // - According to Wikipedia, the fastest Pi3 clocks around 1.4 GHz. +- // - According to Wikipedia, the fastest RPi4 clocks around 1.5 GHz. - // - The Linux 2837 GPIO driver waits 1 µs between the steps. - // - // So lets try to be on the safe side and default to 2000 cycles, which would equal 1 µs @@ -308,27 +307,72 @@ 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 -@@ -289,11 +289,18 @@ - cpu::nop(); - } +@@ -235,16 +235,13 @@ -+ // Read one character. -+ let mut ret = inner.registers.DR.get() as u8 as char; -+ -+ // Convert carrige return to newline. -+ if ret == '\r' { -+ ret = '\n' -+ } + /// 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; + - // Update statistics. - inner.chars_read += 1; + // 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); + } -- // Read one character. -- inner.registers.DR.get() as u8 as char -+ ret - }) + /// Retrieve a character. +- fn read_char(&mut self, blocking_mode: BlockingMode) -> Option { ++ fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { + // If RX FIFO is empty, + if self.registers.FR.matches_all(FR::RXFE::SET) { + // immediately return in non-blocking mode. +@@ -272,7 +269,12 @@ + } + + // Read one character. +- let ret = self.registers.DR.get() as u8 as char; ++ let mut ret = self.registers.DR.get() as u8 as char; ++ ++ // Convert carrige return to newline. ++ if ret == '\r' { ++ ret = '\n' ++ } + + // Update statistics. + self.chars_read += 1; +@@ -352,14 +354,14 @@ + impl console::interface::Read for PL011Uart { + fn read_char(&self) -> char { + self.inner +- .lock(|inner| inner.read_char(BlockingMode::Blocking).unwrap()) ++ .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) } + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner +- .lock(|inner| inner.read_char(BlockingMode::NonBlocking)) ++ .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) + .is_some() + {} + } diff -uNr 07_uart_chainloader/src/bsp/raspberrypi/link.ld 08_timestamps/src/bsp/raspberrypi/link.ld --- 07_uart_chainloader/src/bsp/raspberrypi/link.ld @@ -484,7 +528,7 @@ diff -uNr 07_uart_chainloader/src/main.rs 08_timestamps/src/main.rs /// Early init code. /// -@@ -147,52 +146,31 @@ +@@ -147,51 +146,31 @@ /// The main function running after the early init. fn kernel_main() -> ! { @@ -504,9 +548,8 @@ diff -uNr 07_uart_chainloader/src/main.rs 08_timestamps/src/main.rs - println!("[ML] Requesting binary"); - console().flush(); - -- // Clear the RX FIFOs, if any, of spurious received characters before starting with the loader -- // protocol. -- console().clear(); +- // Discard any spurious received characters before starting with the loader protocol. +- console().clear_rx(); - - // Notify `Minipush` to send the binary. - for _ in 0..3 { 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 46cceff9..876aa461 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 @@ -136,6 +136,12 @@ register_structs! { /// Abstraction for the associated MMIO registers. type Registers = MMIODerefWrapper; +#[derive(PartialEq)] +enum BlockingMode { + Blocking, + NonBlocking, +} + //-------------------------------------------------------------------------------------------------- // Public Definitions //-------------------------------------------------------------------------------------------------- @@ -174,24 +180,41 @@ impl PL011UartInner { /// Set up baud rate and characteristics. /// - /// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt). + /// This results in 8N1 and 921_600 baud. /// - /// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is: - /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). + /// 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). /// - /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will - /// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're - /// now at 0.01 %. + /// 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%. pub fn init(&mut self) { - // Turn it off temporarily. + // 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. + // + // For example, this can happen during runtime on a call to panic!(), because panic!() + // initializes its own UART instance and calls init(). + // + // Hence, flush first to ensure all pending characters are transmitted. + self.flush(); + + // Turn the UART off temporarily. self.registers.CR.set(0); + // Clear all pending interrupts. self.registers.ICR.write(ICR::ALL::CLEAR); - self.registers.IBRD.write(IBRD::IBRD.val(5)); - self.registers.FBRD.write(FBRD::FBRD.val(13)); + + // Set the baud rate. + self.registers.IBRD.write(IBRD::IBRD.val(3)); + self.registers.FBRD.write(FBRD::FBRD.val(16)); + + // Set 8N1 + FIFO on. self.registers .LCRH - .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); + + // Turn the UART on. self.registers .CR .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -209,6 +232,55 @@ impl PL011UartInner { self.chars_written += 1; } + + /// 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) { + 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. + fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { + // If RX FIFO is empty, + if self.registers.FR.matches_all(FR::RXFE::SET) { + // immediately return in non-blocking mode. + if blocking_mode == BlockingMode::NonBlocking { + return None; + } + + // Otherwise, wait until a char was received. + while self.registers.FR.matches_all(FR::RXFE::SET) { + cpu::nop(); + } + } + + // Read one character. + let mut ret = self.registers.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + // Update statistics. + self.chars_read += 1; + + Some(ret) + } } /// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are @@ -231,6 +303,8 @@ impl fmt::Write for PL011UartInner { } impl PL011Uart { + /// Create an instance. + /// /// # Safety /// /// - The user must ensure to provide a correct MMIO start address. @@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart { fn flush(&self) { // Spin until TX FIFO empty is set. - self.inner.lock(|inner| { - while !inner.registers.FR.matches_all(FR::TXFE::SET) { - cpu::nop(); - } - }); + self.inner.lock(|inner| inner.flush()); } } impl console::interface::Read for PL011Uart { fn read_char(&self) -> char { - self.inner.lock(|inner| { - // Spin while RX FIFO empty is set. - while inner.registers.FR.matches_all(FR::RXFE::SET) { - cpu::nop(); - } - - // Read one character. - let mut ret = inner.registers.DR.get() as u8 as char; - - // Convert carrige return to newline. - if ret == '\r' { - ret = '\n' - } - - // Update statistics. - inner.chars_read += 1; - - ret - }) + self.inner + .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) } - fn clear(&self) { - self.inner.lock(|inner| { - // Read from the RX FIFO until it is indicating empty. - while !inner.registers.FR.matches_all(FR::RXFE::SET) { - inner.registers.DR.get(); - } - }) + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner + .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) + .is_some() + {} } } diff --git a/08_timestamps/src/console.rs b/08_timestamps/src/console.rs index 2d38cc1d..3552823c 100644 --- a/08_timestamps/src/console.rs +++ b/08_timestamps/src/console.rs @@ -20,8 +20,8 @@ pub mod interface { /// Write a Rust format string. fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; - /// Block execution until the last character has been physically put on the TX wire - /// (draining TX buffers/FIFOs, if any). + /// Block execution until the last buffered character has been physically put on the TX + /// wire. fn flush(&self); } @@ -33,7 +33,7 @@ pub mod interface { } /// Clear RX buffers, if any. - fn clear(&self); + fn clear_rx(&self); } /// Console statistics. 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 46cceff9..876aa461 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 @@ -136,6 +136,12 @@ register_structs! { /// Abstraction for the associated MMIO registers. type Registers = MMIODerefWrapper; +#[derive(PartialEq)] +enum BlockingMode { + Blocking, + NonBlocking, +} + //-------------------------------------------------------------------------------------------------- // Public Definitions //-------------------------------------------------------------------------------------------------- @@ -174,24 +180,41 @@ impl PL011UartInner { /// Set up baud rate and characteristics. /// - /// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt). + /// This results in 8N1 and 921_600 baud. /// - /// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is: - /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). + /// 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). /// - /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will - /// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're - /// now at 0.01 %. + /// 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%. pub fn init(&mut self) { - // Turn it off temporarily. + // 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. + // + // For example, this can happen during runtime on a call to panic!(), because panic!() + // initializes its own UART instance and calls init(). + // + // Hence, flush first to ensure all pending characters are transmitted. + self.flush(); + + // Turn the UART off temporarily. self.registers.CR.set(0); + // Clear all pending interrupts. self.registers.ICR.write(ICR::ALL::CLEAR); - self.registers.IBRD.write(IBRD::IBRD.val(5)); - self.registers.FBRD.write(FBRD::FBRD.val(13)); + + // Set the baud rate. + self.registers.IBRD.write(IBRD::IBRD.val(3)); + self.registers.FBRD.write(FBRD::FBRD.val(16)); + + // Set 8N1 + FIFO on. self.registers .LCRH - .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); + + // Turn the UART on. self.registers .CR .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -209,6 +232,55 @@ impl PL011UartInner { self.chars_written += 1; } + + /// 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) { + 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. + fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { + // If RX FIFO is empty, + if self.registers.FR.matches_all(FR::RXFE::SET) { + // immediately return in non-blocking mode. + if blocking_mode == BlockingMode::NonBlocking { + return None; + } + + // Otherwise, wait until a char was received. + while self.registers.FR.matches_all(FR::RXFE::SET) { + cpu::nop(); + } + } + + // Read one character. + let mut ret = self.registers.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + // Update statistics. + self.chars_read += 1; + + Some(ret) + } } /// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are @@ -231,6 +303,8 @@ impl fmt::Write for PL011UartInner { } impl PL011Uart { + /// Create an instance. + /// /// # Safety /// /// - The user must ensure to provide a correct MMIO start address. @@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart { fn flush(&self) { // Spin until TX FIFO empty is set. - self.inner.lock(|inner| { - while !inner.registers.FR.matches_all(FR::TXFE::SET) { - cpu::nop(); - } - }); + self.inner.lock(|inner| inner.flush()); } } impl console::interface::Read for PL011Uart { fn read_char(&self) -> char { - self.inner.lock(|inner| { - // Spin while RX FIFO empty is set. - while inner.registers.FR.matches_all(FR::RXFE::SET) { - cpu::nop(); - } - - // Read one character. - let mut ret = inner.registers.DR.get() as u8 as char; - - // Convert carrige return to newline. - if ret == '\r' { - ret = '\n' - } - - // Update statistics. - inner.chars_read += 1; - - ret - }) + self.inner + .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) } - fn clear(&self) { - self.inner.lock(|inner| { - // Read from the RX FIFO until it is indicating empty. - while !inner.registers.FR.matches_all(FR::RXFE::SET) { - inner.registers.DR.get(); - } - }) + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner + .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) + .is_some() + {} } } diff --git a/09_hw_debug_JTAG/src/console.rs b/09_hw_debug_JTAG/src/console.rs index 2d38cc1d..3552823c 100644 --- a/09_hw_debug_JTAG/src/console.rs +++ b/09_hw_debug_JTAG/src/console.rs @@ -20,8 +20,8 @@ pub mod interface { /// Write a Rust format string. fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; - /// Block execution until the last character has been physically put on the TX wire - /// (draining TX buffers/FIFOs, if any). + /// Block execution until the last buffered character has been physically put on the TX + /// wire. fn flush(&self); } @@ -33,7 +33,7 @@ pub mod interface { } /// Clear RX buffers, if any. - fn clear(&self); + fn clear_rx(&self); } /// Console statistics. diff --git a/10_privilege_level/README.md b/10_privilege_level/README.md index 3ffa1bbe..8cd31381 100644 --- a/10_privilege_level/README.md +++ b/10_privilege_level/README.md @@ -464,10 +464,11 @@ diff -uNr 09_hw_debug_JTAG/src/main.rs 10_privilege_level/src/main.rs mod memory; mod panic_wait; mod print; -@@ -146,12 +147,19 @@ +@@ -146,12 +147,20 @@ /// The main function running after the early init. fn kernel_main() -> ! { ++ use bsp::console::console; + use console::interface::All; use core::time::Duration; use driver::interface::DriverManager; @@ -484,7 +485,7 @@ diff -uNr 09_hw_debug_JTAG/src/main.rs 10_privilege_level/src/main.rs info!( "Architectural timer resolution: {} ns", time::time_manager().resolution().as_nanos() -@@ -166,11 +174,12 @@ +@@ -166,11 +175,15 @@ info!(" {}. {}", i + 1, driver.compatible()); } @@ -492,8 +493,11 @@ diff -uNr 09_hw_debug_JTAG/src/main.rs 10_privilege_level/src/main.rs - time::time_manager().spin_for(Duration::from_nanos(1)); + info!("Timer test, spinning for 1 second"); + time::time_manager().spin_for(Duration::from_secs(1)); - ++ + info!("Echoing input now"); + ++ // Discard any spurious received characters before going into echo mode. ++ console().clear_rx(); loop { - info!("Spinning for 1 second"); - time::time_manager().spin_for(Duration::from_secs(1)); 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 46cceff9..876aa461 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 @@ -136,6 +136,12 @@ register_structs! { /// Abstraction for the associated MMIO registers. type Registers = MMIODerefWrapper; +#[derive(PartialEq)] +enum BlockingMode { + Blocking, + NonBlocking, +} + //-------------------------------------------------------------------------------------------------- // Public Definitions //-------------------------------------------------------------------------------------------------- @@ -174,24 +180,41 @@ impl PL011UartInner { /// Set up baud rate and characteristics. /// - /// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt). + /// This results in 8N1 and 921_600 baud. /// - /// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is: - /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). + /// 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). /// - /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will - /// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're - /// now at 0.01 %. + /// 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%. pub fn init(&mut self) { - // Turn it off temporarily. + // 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. + // + // For example, this can happen during runtime on a call to panic!(), because panic!() + // initializes its own UART instance and calls init(). + // + // Hence, flush first to ensure all pending characters are transmitted. + self.flush(); + + // Turn the UART off temporarily. self.registers.CR.set(0); + // Clear all pending interrupts. self.registers.ICR.write(ICR::ALL::CLEAR); - self.registers.IBRD.write(IBRD::IBRD.val(5)); - self.registers.FBRD.write(FBRD::FBRD.val(13)); + + // Set the baud rate. + self.registers.IBRD.write(IBRD::IBRD.val(3)); + self.registers.FBRD.write(FBRD::FBRD.val(16)); + + // Set 8N1 + FIFO on. self.registers .LCRH - .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); + + // Turn the UART on. self.registers .CR .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -209,6 +232,55 @@ impl PL011UartInner { self.chars_written += 1; } + + /// 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) { + 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. + fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { + // If RX FIFO is empty, + if self.registers.FR.matches_all(FR::RXFE::SET) { + // immediately return in non-blocking mode. + if blocking_mode == BlockingMode::NonBlocking { + return None; + } + + // Otherwise, wait until a char was received. + while self.registers.FR.matches_all(FR::RXFE::SET) { + cpu::nop(); + } + } + + // Read one character. + let mut ret = self.registers.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + // Update statistics. + self.chars_read += 1; + + Some(ret) + } } /// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are @@ -231,6 +303,8 @@ impl fmt::Write for PL011UartInner { } impl PL011Uart { + /// Create an instance. + /// /// # Safety /// /// - The user must ensure to provide a correct MMIO start address. @@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart { fn flush(&self) { // Spin until TX FIFO empty is set. - self.inner.lock(|inner| { - while !inner.registers.FR.matches_all(FR::TXFE::SET) { - cpu::nop(); - } - }); + self.inner.lock(|inner| inner.flush()); } } impl console::interface::Read for PL011Uart { fn read_char(&self) -> char { - self.inner.lock(|inner| { - // Spin while RX FIFO empty is set. - while inner.registers.FR.matches_all(FR::RXFE::SET) { - cpu::nop(); - } - - // Read one character. - let mut ret = inner.registers.DR.get() as u8 as char; - - // Convert carrige return to newline. - if ret == '\r' { - ret = '\n' - } - - // Update statistics. - inner.chars_read += 1; - - ret - }) + self.inner + .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) } - fn clear(&self) { - self.inner.lock(|inner| { - // Read from the RX FIFO until it is indicating empty. - while !inner.registers.FR.matches_all(FR::RXFE::SET) { - inner.registers.DR.get(); - } - }) + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner + .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) + .is_some() + {} } } diff --git a/10_privilege_level/src/console.rs b/10_privilege_level/src/console.rs index 2d38cc1d..3552823c 100644 --- a/10_privilege_level/src/console.rs +++ b/10_privilege_level/src/console.rs @@ -20,8 +20,8 @@ pub mod interface { /// Write a Rust format string. fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; - /// Block execution until the last character has been physically put on the TX wire - /// (draining TX buffers/FIFOs, if any). + /// Block execution until the last buffered character has been physically put on the TX + /// wire. fn flush(&self); } @@ -33,7 +33,7 @@ pub mod interface { } /// Clear RX buffers, if any. - fn clear(&self); + fn clear_rx(&self); } /// Console statistics. diff --git a/10_privilege_level/src/main.rs b/10_privilege_level/src/main.rs index 0c730ff9..fc2bd691 100644 --- a/10_privilege_level/src/main.rs +++ b/10_privilege_level/src/main.rs @@ -147,6 +147,7 @@ unsafe fn kernel_init() -> ! { /// The main function running after the early init. fn kernel_main() -> ! { + use bsp::console::console; use console::interface::All; use core::time::Duration; use driver::interface::DriverManager; @@ -178,6 +179,9 @@ fn kernel_main() -> ! { time::time_manager().spin_for(Duration::from_secs(1)); info!("Echoing input now"); + + // Discard any spurious received characters before going into echo mode. + console().clear_rx(); loop { let c = bsp::console::console().read_char(); bsp::console::console().write_char(c); diff --git a/11_virtual_mem_part1_identity_mapping/README.md b/11_virtual_mem_part1_identity_mapping/README.md index 6216fd65..c6444296 100644 --- a/11_virtual_mem_part1_identity_mapping/README.md +++ b/11_virtual_mem_part1_identity_mapping/README.md @@ -914,7 +914,7 @@ diff -uNr 10_privilege_level/src/main.rs 11_virtual_mem_part1_identity_mapping/s for i in bsp::driver::driver_manager().all_device_drivers().iter() { if let Err(x) = i.init() { -@@ -154,6 +168,9 @@ +@@ -155,6 +169,9 @@ info!("Booting on: {}", bsp::board_name()); @@ -924,7 +924,7 @@ diff -uNr 10_privilege_level/src/main.rs 11_virtual_mem_part1_identity_mapping/s let (_, privilege_level) = exception::current_privilege_level(); info!("Current privilege level: {}", privilege_level); -@@ -177,6 +194,13 @@ +@@ -178,6 +195,13 @@ info!("Timer test, spinning for 1 second"); time::time_manager().spin_for(Duration::from_secs(1)); @@ -936,8 +936,8 @@ diff -uNr 10_privilege_level/src/main.rs 11_virtual_mem_part1_identity_mapping/s + .unwrap(); + info!("Echoing input now"); - loop { - let c = bsp::console::console().read_char(); + + // Discard any spurious received characters before going into echo mode. diff -uNr 10_privilege_level/src/memory/mmu.rs 11_virtual_mem_part1_identity_mapping/src/memory/mmu.rs --- 10_privilege_level/src/memory/mmu.rs 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 46cceff9..876aa461 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 @@ -136,6 +136,12 @@ register_structs! { /// Abstraction for the associated MMIO registers. type Registers = MMIODerefWrapper; +#[derive(PartialEq)] +enum BlockingMode { + Blocking, + NonBlocking, +} + //-------------------------------------------------------------------------------------------------- // Public Definitions //-------------------------------------------------------------------------------------------------- @@ -174,24 +180,41 @@ impl PL011UartInner { /// Set up baud rate and characteristics. /// - /// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt). + /// This results in 8N1 and 921_600 baud. /// - /// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is: - /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). + /// 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). /// - /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will - /// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're - /// now at 0.01 %. + /// 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%. pub fn init(&mut self) { - // Turn it off temporarily. + // 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. + // + // For example, this can happen during runtime on a call to panic!(), because panic!() + // initializes its own UART instance and calls init(). + // + // Hence, flush first to ensure all pending characters are transmitted. + self.flush(); + + // Turn the UART off temporarily. self.registers.CR.set(0); + // Clear all pending interrupts. self.registers.ICR.write(ICR::ALL::CLEAR); - self.registers.IBRD.write(IBRD::IBRD.val(5)); - self.registers.FBRD.write(FBRD::FBRD.val(13)); + + // Set the baud rate. + self.registers.IBRD.write(IBRD::IBRD.val(3)); + self.registers.FBRD.write(FBRD::FBRD.val(16)); + + // Set 8N1 + FIFO on. self.registers .LCRH - .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); + + // Turn the UART on. self.registers .CR .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -209,6 +232,55 @@ impl PL011UartInner { self.chars_written += 1; } + + /// 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) { + 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. + fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { + // If RX FIFO is empty, + if self.registers.FR.matches_all(FR::RXFE::SET) { + // immediately return in non-blocking mode. + if blocking_mode == BlockingMode::NonBlocking { + return None; + } + + // Otherwise, wait until a char was received. + while self.registers.FR.matches_all(FR::RXFE::SET) { + cpu::nop(); + } + } + + // Read one character. + let mut ret = self.registers.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + // Update statistics. + self.chars_read += 1; + + Some(ret) + } } /// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are @@ -231,6 +303,8 @@ impl fmt::Write for PL011UartInner { } impl PL011Uart { + /// Create an instance. + /// /// # Safety /// /// - The user must ensure to provide a correct MMIO start address. @@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart { fn flush(&self) { // Spin until TX FIFO empty is set. - self.inner.lock(|inner| { - while !inner.registers.FR.matches_all(FR::TXFE::SET) { - cpu::nop(); - } - }); + self.inner.lock(|inner| inner.flush()); } } impl console::interface::Read for PL011Uart { fn read_char(&self) -> char { - self.inner.lock(|inner| { - // Spin while RX FIFO empty is set. - while inner.registers.FR.matches_all(FR::RXFE::SET) { - cpu::nop(); - } - - // Read one character. - let mut ret = inner.registers.DR.get() as u8 as char; - - // Convert carrige return to newline. - if ret == '\r' { - ret = '\n' - } - - // Update statistics. - inner.chars_read += 1; - - ret - }) + self.inner + .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) } - fn clear(&self) { - self.inner.lock(|inner| { - // Read from the RX FIFO until it is indicating empty. - while !inner.registers.FR.matches_all(FR::RXFE::SET) { - inner.registers.DR.get(); - } - }) + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner + .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) + .is_some() + {} } } diff --git a/11_virtual_mem_part1_identity_mapping/src/console.rs b/11_virtual_mem_part1_identity_mapping/src/console.rs index 2d38cc1d..3552823c 100644 --- a/11_virtual_mem_part1_identity_mapping/src/console.rs +++ b/11_virtual_mem_part1_identity_mapping/src/console.rs @@ -20,8 +20,8 @@ pub mod interface { /// Write a Rust format string. fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; - /// Block execution until the last character has been physically put on the TX wire - /// (draining TX buffers/FIFOs, if any). + /// Block execution until the last buffered character has been physically put on the TX + /// wire. fn flush(&self); } @@ -33,7 +33,7 @@ pub mod interface { } /// Clear RX buffers, if any. - fn clear(&self); + fn clear_rx(&self); } /// Console statistics. diff --git a/11_virtual_mem_part1_identity_mapping/src/main.rs b/11_virtual_mem_part1_identity_mapping/src/main.rs index 41ca22e3..25971535 100644 --- a/11_virtual_mem_part1_identity_mapping/src/main.rs +++ b/11_virtual_mem_part1_identity_mapping/src/main.rs @@ -161,6 +161,7 @@ unsafe fn kernel_init() -> ! { /// The main function running after the early init. fn kernel_main() -> ! { + use bsp::console::console; use console::interface::All; use core::time::Duration; use driver::interface::DriverManager; @@ -202,6 +203,9 @@ fn kernel_main() -> ! { .unwrap(); info!("Echoing input now"); + + // Discard any spurious received characters before going into echo mode. + console().clear_rx(); loop { let c = bsp::console::console().read_char(); bsp::console::console().write_char(c); diff --git a/12_exceptions_part1_groundwork/README.md b/12_exceptions_part1_groundwork/README.md index c6264088..be16673f 100644 --- a/12_exceptions_part1_groundwork/README.md +++ b/12_exceptions_part1_groundwork/README.md @@ -973,7 +973,7 @@ diff -uNr 11_virtual_mem_part1_identity_mapping/src/main.rs 12_exceptions_part1_ if let Err(string) = memory::mmu::mmu().init() { panic!("MMU: {}", string); } -@@ -194,13 +197,28 @@ +@@ -195,13 +198,28 @@ info!("Timer test, spinning for 1 second"); time::time_manager().spin_for(Duration::from_secs(1)); @@ -1006,8 +1006,8 @@ diff -uNr 11_virtual_mem_part1_identity_mapping/src/main.rs 12_exceptions_part1_ + // Will never reach here in this tutorial. info!("Echoing input now"); - loop { - let c = bsp::console::console().read_char(); + + // Discard any spurious received characters before going into echo mode. diff -uNr 11_virtual_mem_part1_identity_mapping/src/memory/mmu.rs 12_exceptions_part1_groundwork/src/memory/mmu.rs --- 11_virtual_mem_part1_identity_mapping/src/memory/mmu.rs 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 46cceff9..876aa461 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 @@ -136,6 +136,12 @@ register_structs! { /// Abstraction for the associated MMIO registers. type Registers = MMIODerefWrapper; +#[derive(PartialEq)] +enum BlockingMode { + Blocking, + NonBlocking, +} + //-------------------------------------------------------------------------------------------------- // Public Definitions //-------------------------------------------------------------------------------------------------- @@ -174,24 +180,41 @@ impl PL011UartInner { /// Set up baud rate and characteristics. /// - /// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt). + /// This results in 8N1 and 921_600 baud. /// - /// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is: - /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). + /// 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). /// - /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will - /// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're - /// now at 0.01 %. + /// 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%. pub fn init(&mut self) { - // Turn it off temporarily. + // 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. + // + // For example, this can happen during runtime on a call to panic!(), because panic!() + // initializes its own UART instance and calls init(). + // + // Hence, flush first to ensure all pending characters are transmitted. + self.flush(); + + // Turn the UART off temporarily. self.registers.CR.set(0); + // Clear all pending interrupts. self.registers.ICR.write(ICR::ALL::CLEAR); - self.registers.IBRD.write(IBRD::IBRD.val(5)); - self.registers.FBRD.write(FBRD::FBRD.val(13)); + + // Set the baud rate. + self.registers.IBRD.write(IBRD::IBRD.val(3)); + self.registers.FBRD.write(FBRD::FBRD.val(16)); + + // Set 8N1 + FIFO on. self.registers .LCRH - .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); + + // Turn the UART on. self.registers .CR .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -209,6 +232,55 @@ impl PL011UartInner { self.chars_written += 1; } + + /// 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) { + 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. + fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { + // If RX FIFO is empty, + if self.registers.FR.matches_all(FR::RXFE::SET) { + // immediately return in non-blocking mode. + if blocking_mode == BlockingMode::NonBlocking { + return None; + } + + // Otherwise, wait until a char was received. + while self.registers.FR.matches_all(FR::RXFE::SET) { + cpu::nop(); + } + } + + // Read one character. + let mut ret = self.registers.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + // Update statistics. + self.chars_read += 1; + + Some(ret) + } } /// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are @@ -231,6 +303,8 @@ impl fmt::Write for PL011UartInner { } impl PL011Uart { + /// Create an instance. + /// /// # Safety /// /// - The user must ensure to provide a correct MMIO start address. @@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart { fn flush(&self) { // Spin until TX FIFO empty is set. - self.inner.lock(|inner| { - while !inner.registers.FR.matches_all(FR::TXFE::SET) { - cpu::nop(); - } - }); + self.inner.lock(|inner| inner.flush()); } } impl console::interface::Read for PL011Uart { fn read_char(&self) -> char { - self.inner.lock(|inner| { - // Spin while RX FIFO empty is set. - while inner.registers.FR.matches_all(FR::RXFE::SET) { - cpu::nop(); - } - - // Read one character. - let mut ret = inner.registers.DR.get() as u8 as char; - - // Convert carrige return to newline. - if ret == '\r' { - ret = '\n' - } - - // Update statistics. - inner.chars_read += 1; - - ret - }) + self.inner + .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) } - fn clear(&self) { - self.inner.lock(|inner| { - // Read from the RX FIFO until it is indicating empty. - while !inner.registers.FR.matches_all(FR::RXFE::SET) { - inner.registers.DR.get(); - } - }) + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner + .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) + .is_some() + {} } } diff --git a/12_exceptions_part1_groundwork/src/console.rs b/12_exceptions_part1_groundwork/src/console.rs index 2d38cc1d..3552823c 100644 --- a/12_exceptions_part1_groundwork/src/console.rs +++ b/12_exceptions_part1_groundwork/src/console.rs @@ -20,8 +20,8 @@ pub mod interface { /// Write a Rust format string. fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; - /// Block execution until the last character has been physically put on the TX wire - /// (draining TX buffers/FIFOs, if any). + /// Block execution until the last buffered character has been physically put on the TX + /// wire. fn flush(&self); } @@ -33,7 +33,7 @@ pub mod interface { } /// Clear RX buffers, if any. - fn clear(&self); + fn clear_rx(&self); } /// Console statistics. diff --git a/12_exceptions_part1_groundwork/src/main.rs b/12_exceptions_part1_groundwork/src/main.rs index 495840e8..a2cf7752 100644 --- a/12_exceptions_part1_groundwork/src/main.rs +++ b/12_exceptions_part1_groundwork/src/main.rs @@ -164,6 +164,7 @@ unsafe fn kernel_init() -> ! { /// The main function running after the early init. fn kernel_main() -> ! { + use bsp::console::console; use console::interface::All; use core::time::Duration; use driver::interface::DriverManager; @@ -220,6 +221,9 @@ fn kernel_main() -> ! { // Will never reach here in this tutorial. info!("Echoing input now"); + + // Discard any spurious received characters before going into echo mode. + console().clear_rx(); loop { let c = bsp::console::console().read_char(); bsp::console::console().write_char(c); diff --git a/13_integrated_testing/README.md b/13_integrated_testing/README.md index a282e659..849cbbfb 100644 --- a/13_integrated_testing/README.md +++ b/13_integrated_testing/README.md @@ -1444,9 +1444,9 @@ diff -uNr 12_exceptions_part1_groundwork/src/main.rs 13_integrated_testing/src/m unsafe fn kernel_init() -> ! { use driver::interface::DriverManager; use memory::mmu::interface::MMU; -@@ -165,9 +49,7 @@ - /// The main function running after the early init. +@@ -166,9 +50,7 @@ fn kernel_main() -> ! { + use bsp::console::console; use console::interface::All; - use core::time::Duration; use driver::interface::DriverManager; @@ -1454,7 +1454,7 @@ diff -uNr 12_exceptions_part1_groundwork/src/main.rs 13_integrated_testing/src/m info!("Booting on: {}", bsp::board_name()); -@@ -194,31 +76,6 @@ +@@ -195,31 +77,6 @@ info!(" {}. {}", i + 1, driver.compatible()); } @@ -1484,8 +1484,8 @@ diff -uNr 12_exceptions_part1_groundwork/src/main.rs 13_integrated_testing/src/m - - // Will never reach here in this tutorial. info!("Echoing input now"); - loop { - let c = bsp::console::console().read_char(); + + // Discard any spurious received characters before going into echo mode. diff -uNr 12_exceptions_part1_groundwork/src/memory/mmu.rs 13_integrated_testing/src/memory/mmu.rs --- 12_exceptions_part1_groundwork/src/memory/mmu.rs 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 46cceff9..876aa461 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 @@ -136,6 +136,12 @@ register_structs! { /// Abstraction for the associated MMIO registers. type Registers = MMIODerefWrapper; +#[derive(PartialEq)] +enum BlockingMode { + Blocking, + NonBlocking, +} + //-------------------------------------------------------------------------------------------------- // Public Definitions //-------------------------------------------------------------------------------------------------- @@ -174,24 +180,41 @@ impl PL011UartInner { /// Set up baud rate and characteristics. /// - /// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt). + /// This results in 8N1 and 921_600 baud. /// - /// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is: - /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). + /// 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). /// - /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will - /// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're - /// now at 0.01 %. + /// 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%. pub fn init(&mut self) { - // Turn it off temporarily. + // 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. + // + // For example, this can happen during runtime on a call to panic!(), because panic!() + // initializes its own UART instance and calls init(). + // + // Hence, flush first to ensure all pending characters are transmitted. + self.flush(); + + // Turn the UART off temporarily. self.registers.CR.set(0); + // Clear all pending interrupts. self.registers.ICR.write(ICR::ALL::CLEAR); - self.registers.IBRD.write(IBRD::IBRD.val(5)); - self.registers.FBRD.write(FBRD::FBRD.val(13)); + + // Set the baud rate. + self.registers.IBRD.write(IBRD::IBRD.val(3)); + self.registers.FBRD.write(FBRD::FBRD.val(16)); + + // Set 8N1 + FIFO on. self.registers .LCRH - .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); + + // Turn the UART on. self.registers .CR .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -209,6 +232,55 @@ impl PL011UartInner { self.chars_written += 1; } + + /// 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) { + 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. + fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { + // If RX FIFO is empty, + if self.registers.FR.matches_all(FR::RXFE::SET) { + // immediately return in non-blocking mode. + if blocking_mode == BlockingMode::NonBlocking { + return None; + } + + // Otherwise, wait until a char was received. + while self.registers.FR.matches_all(FR::RXFE::SET) { + cpu::nop(); + } + } + + // Read one character. + let mut ret = self.registers.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + // Update statistics. + self.chars_read += 1; + + Some(ret) + } } /// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are @@ -231,6 +303,8 @@ impl fmt::Write for PL011UartInner { } impl PL011Uart { + /// Create an instance. + /// /// # Safety /// /// - The user must ensure to provide a correct MMIO start address. @@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart { fn flush(&self) { // Spin until TX FIFO empty is set. - self.inner.lock(|inner| { - while !inner.registers.FR.matches_all(FR::TXFE::SET) { - cpu::nop(); - } - }); + self.inner.lock(|inner| inner.flush()); } } impl console::interface::Read for PL011Uart { fn read_char(&self) -> char { - self.inner.lock(|inner| { - // Spin while RX FIFO empty is set. - while inner.registers.FR.matches_all(FR::RXFE::SET) { - cpu::nop(); - } - - // Read one character. - let mut ret = inner.registers.DR.get() as u8 as char; - - // Convert carrige return to newline. - if ret == '\r' { - ret = '\n' - } - - // Update statistics. - inner.chars_read += 1; - - ret - }) + self.inner + .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) } - fn clear(&self) { - self.inner.lock(|inner| { - // Read from the RX FIFO until it is indicating empty. - while !inner.registers.FR.matches_all(FR::RXFE::SET) { - inner.registers.DR.get(); - } - }) + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner + .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) + .is_some() + {} } } diff --git a/13_integrated_testing/src/console.rs b/13_integrated_testing/src/console.rs index 2d38cc1d..3552823c 100644 --- a/13_integrated_testing/src/console.rs +++ b/13_integrated_testing/src/console.rs @@ -20,8 +20,8 @@ pub mod interface { /// Write a Rust format string. fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; - /// Block execution until the last character has been physically put on the TX wire - /// (draining TX buffers/FIFOs, if any). + /// Block execution until the last buffered character has been physically put on the TX + /// wire. fn flush(&self); } @@ -33,7 +33,7 @@ pub mod interface { } /// Clear RX buffers, if any. - fn clear(&self); + fn clear_rx(&self); } /// Console statistics. diff --git a/13_integrated_testing/src/main.rs b/13_integrated_testing/src/main.rs index 989bd045..769f9e8f 100644 --- a/13_integrated_testing/src/main.rs +++ b/13_integrated_testing/src/main.rs @@ -48,6 +48,7 @@ unsafe fn kernel_init() -> ! { /// The main function running after the early init. fn kernel_main() -> ! { + use bsp::console::console; use console::interface::All; use driver::interface::DriverManager; @@ -77,6 +78,9 @@ fn kernel_main() -> ! { } info!("Echoing input now"); + + // Discard any spurious received characters before going into echo mode. + console().clear_rx(); loop { let c = bsp::console::console().read_char(); bsp::console::console().write_char(c); diff --git a/14_exceptions_part2_peripheral_IRQs/README.md b/14_exceptions_part2_peripheral_IRQs/README.md index dc18b75f..8e68edcb 100644 --- a/14_exceptions_part2_peripheral_IRQs/README.md +++ b/14_exceptions_part2_peripheral_IRQs/README.md @@ -1862,20 +1862,7 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs (0x44 => ICR: WriteOnly), (0x48 => @END), } -@@ -136,6 +181,12 @@ - /// Abstraction for the associated MMIO registers. - type Registers = MMIODerefWrapper; - -+#[derive(PartialEq)] -+enum BlockingMode { -+ Blocking, -+ NonBlocking, -+} -+ - //-------------------------------------------------------------------------------------------------- - // Public Definitions - //-------------------------------------------------------------------------------------------------- -@@ -151,7 +202,8 @@ +@@ -157,7 +202,8 @@ /// Representation of the UART. pub struct PL011Uart { @@ -1885,59 +1872,22 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs } //-------------------------------------------------------------------------------------------------- -@@ -192,6 +244,10 @@ - self.registers +@@ -214,6 +260,14 @@ .LCRH - .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on -+ self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth); // RX FIFO fill level at 1/8 + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); + ++ // Set RX FIFO fill level at 1/8. ++ self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth); ++ ++ // Enable RX IRQ + RX timeout IRQ. + self.registers + .IMSC -+ .write(IMSC::RXIM::Enabled + IMSC::RTIM::Enabled); // RX IRQ + RX timeout IRQ ++ .write(IMSC::RXIM::Enabled + IMSC::RTIM::Enabled); ++ + // Turn the UART on. self.registers .CR - .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); -@@ -209,6 +265,35 @@ - - self.chars_written += 1; - } -+ -+ /// Retrieve a character. -+ fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { -+ // If RX FIFO is empty, -+ if self.registers.FR.matches_all(FR::RXFE::SET) { -+ // immediately return in non-blocking mode. -+ if blocking_mode == BlockingMode::NonBlocking { -+ return None; -+ } -+ -+ // Otherwise, wait until a char was received. -+ while self.registers.FR.matches_all(FR::RXFE::SET) { -+ cpu::nop(); -+ } -+ } -+ -+ // Read one character. -+ let mut ret = self.registers.DR.get() as u8 as char; -+ -+ // Convert carrige return to newline. -+ if ret == '\r' { -+ ret = '\n' -+ } -+ -+ // Update statistics. -+ self.chars_read += 1; -+ -+ Some(ret) -+ } - } - - /// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are -@@ -231,12 +316,18 @@ - } - - impl PL011Uart { -+ /// Create an instance. -+ /// +@@ -308,9 +362,13 @@ /// # Safety /// /// - The user must ensure to provide a correct MMIO start address. @@ -1953,7 +1903,7 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs } } } -@@ -256,6 +347,21 @@ +@@ -330,6 +388,21 @@ Ok(()) } @@ -1975,35 +1925,7 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs } impl console::interface::Write for PL011Uart { -@@ -283,25 +389,8 @@ - - impl console::interface::Read for PL011Uart { - fn read_char(&self) -> char { -- self.inner.lock(|inner| { -- // Spin while RX FIFO empty is set. -- while inner.registers.FR.matches_all(FR::RXFE::SET) { -- cpu::nop(); -- } -- -- // Read one character. -- let mut ret = inner.registers.DR.get() as u8 as char; -- -- // Convert carrige return to newline. -- if ret == '\r' { -- ret = '\n' -- } -- -- // Update statistics. -- inner.chars_read += 1; -- -- ret -- }) -+ self.inner -+ .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) - } - - fn clear(&self) { -@@ -323,3 +412,24 @@ +@@ -376,3 +449,24 @@ self.inner.lock(|inner| inner.chars_read) } } @@ -2439,7 +2361,7 @@ diff -uNr 13_integrated_testing/src/main.rs 14_exceptions_part2_peripheral_IRQs/ #[no_mangle] unsafe fn kernel_init() -> ! { use driver::interface::DriverManager; -@@ -42,14 +42,27 @@ +@@ -42,15 +42,27 @@ bsp::driver::driver_manager().post_device_driver_init(); // println! is usable from here on. @@ -2462,24 +2384,28 @@ diff -uNr 13_integrated_testing/src/main.rs 14_exceptions_part2_peripheral_IRQs/ /// The main function running after the early init. fn kernel_main() -> ! { +- use bsp::console::console; - use console::interface::All; use driver::interface::DriverManager; + use exception::asynchronous::interface::IRQManager; info!("Booting on: {}", bsp::board_name()); -@@ -76,9 +89,9 @@ +@@ -77,12 +89,9 @@ info!(" {}. {}", i + 1, driver.compatible()); } +- info!("Echoing input now"); + info!("Registered IRQ handlers:"); + bsp::exception::asynchronous::irq_manager().print_handler(); -+ - info!("Echoing input now"); + +- // Discard any spurious received characters before going into echo mode. +- console().clear_rx(); - loop { - let c = bsp::console::console().read_char(); - bsp::console::console().write_char(c); - } ++ info!("Echoing input now"); + cpu::wait_forever(); } 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 9504cfc3..7b3a85cf 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 @@ -226,28 +226,49 @@ impl PL011UartInner { /// Set up baud rate and characteristics. /// - /// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt). + /// This results in 8N1 and 921_600 baud. /// - /// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is: - /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). + /// 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). /// - /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will - /// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're - /// now at 0.01 %. + /// 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%. pub fn init(&mut self) { - // Turn it off temporarily. + // 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. + // + // For example, this can happen during runtime on a call to panic!(), because panic!() + // initializes its own UART instance and calls init(). + // + // Hence, flush first to ensure all pending characters are transmitted. + self.flush(); + + // Turn the UART off temporarily. self.registers.CR.set(0); + // Clear all pending interrupts. self.registers.ICR.write(ICR::ALL::CLEAR); - self.registers.IBRD.write(IBRD::IBRD.val(5)); - self.registers.FBRD.write(FBRD::FBRD.val(13)); + + // Set the baud rate. + self.registers.IBRD.write(IBRD::IBRD.val(3)); + self.registers.FBRD.write(FBRD::FBRD.val(16)); + + // Set 8N1 + FIFO on. self.registers .LCRH - .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on - self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth); // RX FIFO fill level at 1/8 + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); + + // Set RX FIFO fill level at 1/8. + self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth); + + // Enable RX IRQ + RX timeout IRQ. self.registers .IMSC - .write(IMSC::RXIM::Enabled + IMSC::RTIM::Enabled); // RX IRQ + RX timeout IRQ + .write(IMSC::RXIM::Enabled + IMSC::RTIM::Enabled); + + // Turn the UART on. self.registers .CR .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -266,6 +287,26 @@ impl PL011UartInner { self.chars_written += 1; } + /// 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) { + 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. fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { // If RX FIFO is empty, @@ -379,11 +420,7 @@ impl console::interface::Write for PL011Uart { fn flush(&self) { // Spin until TX FIFO empty is set. - self.inner.lock(|inner| { - while !inner.registers.FR.matches_all(FR::TXFE::SET) { - cpu::nop(); - } - }); + self.inner.lock(|inner| inner.flush()); } } @@ -393,13 +430,13 @@ impl console::interface::Read for PL011Uart { .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) } - fn clear(&self) { - self.inner.lock(|inner| { - // Read from the RX FIFO until it is indicating empty. - while !inner.registers.FR.matches_all(FR::RXFE::SET) { - inner.registers.DR.get(); - } - }) + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner + .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) + .is_some() + {} } } diff --git a/14_exceptions_part2_peripheral_IRQs/src/console.rs b/14_exceptions_part2_peripheral_IRQs/src/console.rs index 2d38cc1d..3552823c 100644 --- a/14_exceptions_part2_peripheral_IRQs/src/console.rs +++ b/14_exceptions_part2_peripheral_IRQs/src/console.rs @@ -20,8 +20,8 @@ pub mod interface { /// Write a Rust format string. fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; - /// Block execution until the last character has been physically put on the TX wire - /// (draining TX buffers/FIFOs, if any). + /// Block execution until the last buffered character has been physically put on the TX + /// wire. fn flush(&self); } @@ -33,7 +33,7 @@ pub mod interface { } /// Clear RX buffers, if any. - fn clear(&self); + fn clear_rx(&self); } /// Console statistics. diff --git a/15_virtual_mem_part2_mmio_remap/README.md b/15_virtual_mem_part2_mmio_remap/README.md index fb574abf..acb48ded 100644 --- a/15_virtual_mem_part2_mmio_remap/README.md +++ b/15_virtual_mem_part2_mmio_remap/README.md @@ -1305,9 +1305,9 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_ irq_number: bsp::device_driver::IRQNumber, } @@ -234,7 +239,15 @@ - /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will - /// give the best approximation we can get. A 5 modulo error margin is acceptable for UART and we're - /// now at 0.01 modulo. + /// 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. - pub fn init(&mut self) { + /// + /// # Safety @@ -1318,10 +1318,10 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_ + self.registers = Registers::new(addr); + } + - // Turn it off temporarily. - self.registers.CR.set(0); - -@@ -251,6 +264,8 @@ + // 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 @@ self.registers .CR .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -1330,7 +1330,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_ } /// Send a character. -@@ -320,13 +335,18 @@ +@@ -361,13 +376,18 @@ /// /// # Safety /// @@ -1352,7 +1352,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_ irq_number, } } -@@ -343,7 +363,14 @@ +@@ -384,7 +404,14 @@ } unsafe fn init(&self) -> Result<(), &'static str> { @@ -1368,7 +1368,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_ Ok(()) } -@@ -362,6 +389,16 @@ +@@ -403,6 +430,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 b2fc254b..e404f11b 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 @@ -231,14 +231,14 @@ impl PL011UartInner { /// Set up baud rate and characteristics. /// - /// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt). + /// This results in 8N1 and 921_600 baud. /// - /// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is: - /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). + /// 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). /// - /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will - /// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're - /// now at 0.01 %. + /// 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%. /// /// # Safety /// @@ -248,19 +248,40 @@ impl PL011UartInner { self.registers = Registers::new(addr); } - // Turn it off temporarily. + // 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. + // + // For example, this can happen during runtime on a call to panic!(), because panic!() + // initializes its own UART instance and calls init(). + // + // Hence, flush first to ensure all pending characters are transmitted. + self.flush(); + + // Turn the UART off temporarily. self.registers.CR.set(0); + // Clear all pending interrupts. self.registers.ICR.write(ICR::ALL::CLEAR); - self.registers.IBRD.write(IBRD::IBRD.val(5)); - self.registers.FBRD.write(FBRD::FBRD.val(13)); + + // Set the baud rate. + self.registers.IBRD.write(IBRD::IBRD.val(3)); + self.registers.FBRD.write(FBRD::FBRD.val(16)); + + // Set 8N1 + FIFO on. self.registers .LCRH - .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on - self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth); // RX FIFO fill level at 1/8 + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); + + // Set RX FIFO fill level at 1/8. + self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth); + + // Enable RX IRQ + RX timeout IRQ. self.registers .IMSC - .write(IMSC::RXIM::Enabled + IMSC::RTIM::Enabled); // RX IRQ + RX timeout IRQ + .write(IMSC::RXIM::Enabled + IMSC::RTIM::Enabled); + + // Turn the UART on. self.registers .CR .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -281,6 +302,26 @@ impl PL011UartInner { self.chars_written += 1; } + /// 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) { + 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. fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { // If RX FIFO is empty, @@ -416,11 +457,7 @@ impl console::interface::Write for PL011Uart { fn flush(&self) { // Spin until TX FIFO empty is set. - self.inner.lock(|inner| { - while !inner.registers.FR.matches_all(FR::TXFE::SET) { - cpu::nop(); - } - }); + self.inner.lock(|inner| inner.flush()); } } @@ -430,13 +467,13 @@ impl console::interface::Read for PL011Uart { .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) } - fn clear(&self) { - self.inner.lock(|inner| { - // Read from the RX FIFO until it is indicating empty. - while !inner.registers.FR.matches_all(FR::RXFE::SET) { - inner.registers.DR.get(); - } - }) + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner + .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) + .is_some() + {} } } diff --git a/15_virtual_mem_part2_mmio_remap/src/console.rs b/15_virtual_mem_part2_mmio_remap/src/console.rs index 2d38cc1d..3552823c 100644 --- a/15_virtual_mem_part2_mmio_remap/src/console.rs +++ b/15_virtual_mem_part2_mmio_remap/src/console.rs @@ -20,8 +20,8 @@ pub mod interface { /// Write a Rust format string. fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; - /// Block execution until the last character has been physically put on the TX wire - /// (draining TX buffers/FIFOs, if any). + /// Block execution until the last buffered character has been physically put on the TX + /// wire. fn flush(&self); } @@ -33,7 +33,7 @@ pub mod interface { } /// Clear RX buffers, if any. - fn clear(&self); + fn clear_rx(&self); } /// Console statistics. diff --git a/X1_JTAG_boot/jtag_boot_rpi3.img b/X1_JTAG_boot/jtag_boot_rpi3.img index 443facda4297a662552100554a1bd6de4427e137..4248095748295740449cb7c013902e8a4ea778fb 100755 GIT binary patch delta 2340 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 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 46cceff9..876aa461 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 @@ -136,6 +136,12 @@ register_structs! { /// Abstraction for the associated MMIO registers. type Registers = MMIODerefWrapper; +#[derive(PartialEq)] +enum BlockingMode { + Blocking, + NonBlocking, +} + //-------------------------------------------------------------------------------------------------- // Public Definitions //-------------------------------------------------------------------------------------------------- @@ -174,24 +180,41 @@ impl PL011UartInner { /// Set up baud rate and characteristics. /// - /// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt). + /// This results in 8N1 and 921_600 baud. /// - /// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is: - /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). + /// 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). /// - /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will - /// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're - /// now at 0.01 %. + /// 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%. pub fn init(&mut self) { - // Turn it off temporarily. + // 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. + // + // For example, this can happen during runtime on a call to panic!(), because panic!() + // initializes its own UART instance and calls init(). + // + // Hence, flush first to ensure all pending characters are transmitted. + self.flush(); + + // Turn the UART off temporarily. self.registers.CR.set(0); + // Clear all pending interrupts. self.registers.ICR.write(ICR::ALL::CLEAR); - self.registers.IBRD.write(IBRD::IBRD.val(5)); - self.registers.FBRD.write(FBRD::FBRD.val(13)); + + // Set the baud rate. + self.registers.IBRD.write(IBRD::IBRD.val(3)); + self.registers.FBRD.write(FBRD::FBRD.val(16)); + + // Set 8N1 + FIFO on. self.registers .LCRH - .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); + + // Turn the UART on. self.registers .CR .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -209,6 +232,55 @@ impl PL011UartInner { self.chars_written += 1; } + + /// 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) { + 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. + fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option { + // If RX FIFO is empty, + if self.registers.FR.matches_all(FR::RXFE::SET) { + // immediately return in non-blocking mode. + if blocking_mode == BlockingMode::NonBlocking { + return None; + } + + // Otherwise, wait until a char was received. + while self.registers.FR.matches_all(FR::RXFE::SET) { + cpu::nop(); + } + } + + // Read one character. + let mut ret = self.registers.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + // Update statistics. + self.chars_read += 1; + + Some(ret) + } } /// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are @@ -231,6 +303,8 @@ impl fmt::Write for PL011UartInner { } impl PL011Uart { + /// Create an instance. + /// /// # Safety /// /// - The user must ensure to provide a correct MMIO start address. @@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart { fn flush(&self) { // Spin until TX FIFO empty is set. - self.inner.lock(|inner| { - while !inner.registers.FR.matches_all(FR::TXFE::SET) { - cpu::nop(); - } - }); + self.inner.lock(|inner| inner.flush()); } } impl console::interface::Read for PL011Uart { fn read_char(&self) -> char { - self.inner.lock(|inner| { - // Spin while RX FIFO empty is set. - while inner.registers.FR.matches_all(FR::RXFE::SET) { - cpu::nop(); - } - - // Read one character. - let mut ret = inner.registers.DR.get() as u8 as char; - - // Convert carrige return to newline. - if ret == '\r' { - ret = '\n' - } - - // Update statistics. - inner.chars_read += 1; - - ret - }) + self.inner + .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) } - fn clear(&self) { - self.inner.lock(|inner| { - // Read from the RX FIFO until it is indicating empty. - while !inner.registers.FR.matches_all(FR::RXFE::SET) { - inner.registers.DR.get(); - } - }) + fn clear_rx(&self) { + // Read from the RX FIFO until it is indicating empty. + while self + .inner + .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking)) + .is_some() + {} } } diff --git a/X1_JTAG_boot/src/console.rs b/X1_JTAG_boot/src/console.rs index 2d38cc1d..3552823c 100644 --- a/X1_JTAG_boot/src/console.rs +++ b/X1_JTAG_boot/src/console.rs @@ -20,8 +20,8 @@ pub mod interface { /// Write a Rust format string. fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; - /// Block execution until the last character has been physically put on the TX wire - /// (draining TX buffers/FIFOs, if any). + /// Block execution until the last buffered character has been physically put on the TX + /// wire. fn flush(&self); } @@ -33,7 +33,7 @@ pub mod interface { } /// Clear RX buffers, if any. - fn clear(&self); + fn clear_rx(&self); } /// Console statistics. diff --git a/utils/minipush.rb b/utils/minipush.rb index e06433a8..bda97bb1 100755 --- a/utils/minipush.rb +++ b/utils/minipush.rb @@ -25,24 +25,29 @@ class MiniPush < MiniTerm private - # The three characters signaling the request token are expected to arrive as the last three - # characters _at the end_ of a character stream (e.g. after a header print from Miniload). + # The three characters signaling the request token form the consecutive sequence "\x03\x03\x03". def wait_for_binary_request puts "[#{@name_short}] 🔌 Please power the target now" # Timeout for the request token starts after the first sign of life was received. received = @target_serial.readpartial(4096) Timeout.timeout(10) do + count = 0 + loop do raise ProtocolError if received.nil? - if received.chars.last(3) == ["\u{3}", "\u{3}", "\u{3}"] - # Print the last chunk minus the request token. - print received[0..-4] - return - end + received.chars.each do |c| + if c == "\u{3}" + count += 1 + return true if count == 3 + else + # A normal character resets token counting. + count = 0 - print received + print c + end + end received = @target_serial.readpartial(4096) end diff --git a/utils/miniterm.rb b/utils/miniterm.rb index 010761cd..09156669 100755 --- a/utils/miniterm.rb +++ b/utils/miniterm.rb @@ -11,6 +11,8 @@ require 'io/console' require 'colorize' require 'serialport' +SERIAL_BAUD = 921_600 + class ConnectionError < StandardError; end # The main class @@ -41,7 +43,7 @@ class MiniTerm def open_serial wait_for_serial - @target_serial = SerialPort.new(@target_serial_name, 576_000, 8, 1, SerialPort::NONE) + @target_serial = SerialPort.new(@target_serial_name, SERIAL_BAUD, 8, 1, SerialPort::NONE) # Ensure all output is immediately flushed to the device. @target_serial.sync = true