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 34c3214c..47a0ae10 100755 Binary files a/07_uart_chainloader/demo_payload_rpi3.img and b/07_uart_chainloader/demo_payload_rpi3.img differ diff --git a/07_uart_chainloader/demo_payload_rpi4.img b/07_uart_chainloader/demo_payload_rpi4.img index c7919ed6..14e578c1 100755 Binary files a/07_uart_chainloader/demo_payload_rpi4.img and b/07_uart_chainloader/demo_payload_rpi4.img differ diff --git a/07_uart_chainloader/src/_arch/aarch64/cpu.rs b/07_uart_chainloader/src/_arch/aarch64/cpu.rs index 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 443facda..42480957 100755 Binary files a/X1_JTAG_boot/jtag_boot_rpi3.img and b/X1_JTAG_boot/jtag_boot_rpi3.img differ diff --git a/X1_JTAG_boot/jtag_boot_rpi4.img b/X1_JTAG_boot/jtag_boot_rpi4.img index 253652d1..0813b2ba 100755 Binary files a/X1_JTAG_boot/jtag_boot_rpi4.img and b/X1_JTAG_boot/jtag_boot_rpi4.img differ diff --git a/X1_JTAG_boot/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs b/X1_JTAG_boot/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs index 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