Bump UART to 921_600 baud + other bugfixes

Fixes #95
Fixes #98

Co-authored-by: Takumasa Sakao <sakataku7@gmail.com>
pull/102/head
Andre Richter 3 years ago committed by Andre Richter
parent ff382c3faf
commit c35a30cd0b

@ -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 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 --- 05_safe_globals/src/_arch/aarch64/cpu.rs
+++ 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs +++ 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
@@ -37,6 +37,17 @@ @@ -37,6 +37,16 @@
// Public Code // Public Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
+pub use asm::nop; +pub use asm::nop;
+ +
+/// Spin for `n` cycles. +/// Spin for `n` cycles.
+#[cfg(feature = "bsp_rpi3")]
+#[inline(always)] +#[inline(always)]
+pub fn spin_for_cycles(n: usize) { +pub fn spin_for_cycles(n: usize) {
+ for _ in 0..n { + 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 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 --- 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 +++ 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 +// SPDX-License-Identifier: MIT OR Apache-2.0
+// +//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com> +// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
@ -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. +/// Abstraction for the associated MMIO registers.
+type Registers = MMIODerefWrapper<RegisterBlock>; +type Registers = MMIODerefWrapper<RegisterBlock>;
+ +
+#[derive(PartialEq)]
+enum BlockingMode {
+ Blocking,
+ NonBlocking,
+}
+
+//-------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------
+// Public Definitions +// 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. + /// 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: + /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
+ /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). + /// `(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 + /// 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 modulo error margin is acceptable for UART and we're + /// give the best approximation we can get. A 5modulo error margin is acceptable for UART and we're
+ /// now at 0.01 modulo. + /// now at 0.02modulo.
+ pub fn init(&mut self) { + 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); + self.registers.CR.set(0);
+ +
+ // Clear all pending interrupts.
+ self.registers.ICR.write(ICR::ALL::CLEAR); + 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 + self.registers
+ .LCRH + .LCRH
+ .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
+
+ // Turn the UART on.
+ self.registers + self.registers
+ .CR + .CR
+ .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); + .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; + 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<char> {
+ // 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 +/// 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 { +impl PL011Uart {
+ /// Create an instance.
+ ///
+ /// # Safety + /// # Safety
+ /// + ///
+ /// - The user must ensure to provide a correct MMIO start address. + /// - 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. + // readability.
+ self.inner.lock(|inner| fmt::Write::write_fmt(inner, args)) + 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 { +impl console::interface::Read for PL011Uart {
+ fn read_char(&self) -> char { + fn read_char(&self) -> char {
+ self.inner.lock(|inner| { + self.inner
+ // Spin while RX FIFO empty is set. + .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
+ 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 + 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 diff -uNr 05_safe_globals/src/console.rs 06_drivers_gpio_uart/src/console.rs
--- 05_safe_globals/src/console.rs --- 05_safe_globals/src/console.rs
+++ 06_drivers_gpio_uart/src/console.rs +++ 06_drivers_gpio_uart/src/console.rs
@@ -14,18 +14,34 @@ @@ -14,8 +14,26 @@
/// Console write functions. /// Console write functions.
pub trait Write { 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. /// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; 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. + /// Console read functions.
+ pub trait Read { + pub trait Read {
+ /// Read a single character. + /// Read a single character.
+ fn read_char(&self) -> char { + fn read_char(&self) -> char {
+ ' ' + ' '
+ } + }
+ }
+ +
+ /// Clear RX buffers, if any.
+ fn clear_rx(&self);
}
/// Console statistics. /// Console statistics.
pub trait Statistics { @@ -24,8 +42,13 @@
/// Return the number of characters written.
fn chars_written(&self) -> usize { fn chars_written(&self) -> usize {
0 0
} }
@ -1216,7 +1295,7 @@ diff -uNr 05_safe_globals/src/main.rs 06_drivers_gpio_uart/src/main.rs
mod memory; mod memory;
mod panic_wait; mod panic_wait;
mod print; mod print;
@@ -115,16 +125,46 @@ @@ -115,16 +125,49 @@
/// # Safety /// # Safety
/// ///
/// - Only a single core must be active and running this function. /// - 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!"); - println!("[0] Hello from pure Rust!");
+/// The main function running after the early init. +/// The main function running after the early init.
+fn kernel_main() -> ! { +fn kernel_main() -> ! {
+ use bsp::console::console;
+ use console::interface::All; + use console::interface::All;
+ use driver::interface::DriverManager; + 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."); - println!("[2] Stopping here.");
- cpu::wait_forever() - cpu::wait_forever()
+ // Discard any spurious received characters before going into echo mode.
+ console().clear_rx();
+ loop { + loop {
+ let c = bsp::console::console().read_char(); + let c = bsp::console::console().read_char();
+ bsp::console::console().write_char(c); + bsp::console::console().write_char(c);

@ -40,7 +40,6 @@ pub unsafe fn _start() -> ! {
pub use asm::nop; pub use asm::nop;
/// Spin for `n` cycles. /// Spin for `n` cycles.
#[cfg(feature = "bsp_rpi3")]
#[inline(always)] #[inline(always)]
pub fn spin_for_cycles(n: usize) { pub fn spin_for_cycles(n: usize) {
for _ in 0..n { for _ in 0..n {

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers. /// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>; type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics. /// 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: /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). /// `(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 /// 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 /// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.01 %. /// now at 0.02%.
pub fn init(&mut self) { 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); self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR); 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 self.registers
.LCRH .LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers self.registers
.CR .CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,58 @@ impl PL011UartInner {
self.chars_written += 1; 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<char> {
// 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 /// 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 { impl PL011Uart {
/// Create an instance.
///
/// # Safety /// # Safety
/// ///
/// - The user must ensure to provide a correct MMIO start address. /// - The user must ensure to provide a correct MMIO start address.
@ -270,29 +347,26 @@ impl console::interface::Write for PL011Uart {
// readability. // readability.
self.inner.lock(|inner| fmt::Write::write_fmt(inner, args)) 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 { impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char { fn read_char(&self) -> char {
self.inner.lock(|inner| { self.inner
// Spin while RX FIFO empty is set. .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
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 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()
{}
} }
} }

@ -19,6 +19,10 @@ pub mod interface {
/// Write a Rust format string. /// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; 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. /// Console read functions.
@ -27,6 +31,9 @@ pub mod interface {
fn read_char(&self) -> char { fn read_char(&self) -> char {
' ' ' '
} }
/// Clear RX buffers, if any.
fn clear_rx(&self);
} }
/// Console statistics. /// Console statistics.

@ -143,6 +143,7 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init. /// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
use bsp::console::console;
use console::interface::All; use console::interface::All;
use driver::interface::DriverManager; use driver::interface::DriverManager;
@ -163,6 +164,8 @@ fn kernel_main() -> ! {
); );
println!("[3] Echoing input now"); println!("[3] Echoing input now");
// Discard any spurious received characters before going into echo mode.
console().clear_rx();
loop { loop {
let c = bsp::console::console().read_char(); let c = bsp::console::console().read_char();
bsp::console::console().write_char(c); bsp::console::console().write_char(c);

@ -197,7 +197,7 @@ diff -uNr 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs 07_uart_chainloader/src/
} else { } else {
// If not core0, infinitely wait for events. // If not core0, infinitely wait for events.
wait_forever() wait_forever()
@@ -55,3 +55,19 @@ @@ -54,3 +54,19 @@
asm::wfe() asm::wfe()
} }
} }
@ -218,55 +218,62 @@ diff -uNr 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs 07_uart_chainloader/src/
+ core::intrinsics::unreachable() + 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 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 --- 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 +++ 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
@@ -270,6 +270,15 @@ @@ -257,7 +257,7 @@
// 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| {
+ while !inner.registers.FR.matches_all(FR::TXFE::SET) {
+ cpu::nop();
+ }
+ });
+ }
}
impl console::interface::Read for PL011Uart { /// Retrieve a character.
@@ -280,18 +289,20 @@ - fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<char> {
cpu::nop(); + fn read_char(&mut self, blocking_mode: BlockingMode) -> Option<char> {
} // 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. // Read one character.
- let mut ret = inner.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. - // Convert carrige return to newline.
inner.chars_read += 1; - 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 fn clear_rx(&self) {
+ // Read one character. // Read from the RX FIFO until it is indicating empty.
+ inner.registers.DR.get() as u8 as char while self
+ }) .inner
+ } - .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
+ + .lock(|inner| inner.read_char(BlockingMode::NonBlocking))
+ fn clear(&self) { .is_some()
+ 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();
+ }
})
} }
}
diff -uNr 06_drivers_gpio_uart/src/bsp/raspberrypi/link.ld 07_uart_chainloader/src/bsp/raspberrypi/link.ld 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 --- 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 { unsafe {
range = RangeInclusive::new(__bss_start.get(), __bss_end_inclusive.get()); 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 diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs
--- 06_drivers_gpio_uart/src/main.rs --- 06_drivers_gpio_uart/src/main.rs
+++ 07_uart_chainloader/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 runtime_init;
mod synchronization; mod synchronization;
@@ -143,28 +147,52 @@ @@ -145,29 +149,49 @@
/// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
+ use bsp::console::console; use bsp::console::console;
use console::interface::All; use console::interface::All;
- use driver::interface::DriverManager; - use driver::interface::DriverManager;
-
- println!("[0] Booting on: {}", bsp::board_name());
- println!("[1] Drivers loaded:"); - println!("[0] Booting on: {}", bsp::board_name());
- for (i, driver) in bsp::driver::driver_manager()
- .all_device_drivers()
- .iter()
- .enumerate()
- {
- println!(" {}. {}", i + 1, driver.compatible());
+ println!(" __ __ _ _ _ _ "); + println!(" __ __ _ _ _ _ ");
+ println!("| \\/ (_)_ _ (_) | ___ __ _ __| |"); + println!("| \\/ (_)_ _ (_) | ___ __ _ __| |");
+ println!("| |\\/| | | ' \\| | |__/ _ \\/ _` / _` |"); + println!("| |\\/| | | ' \\| | |__/ _ \\/ _` / _` |");
@ -458,22 +430,30 @@ diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs
+ println!(); + println!();
+ println!("[ML] Requesting binary"); + println!("[ML] Requesting binary");
+ console().flush(); + console().flush();
+
+ // Clear the RX FIFOs, if any, of spurious received characters before starting with the loader - println!("[1] Drivers loaded:");
+ // protocol. - for (i, driver) in bsp::driver::driver_manager()
+ console().clear(); - .all_device_drivers()
+ - .iter()
+ // Notify `Minipush` to send the binary. - .enumerate()
+ for _ in 0..3 { - {
+ console().write_char(3 as char); - println!(" {}. {}", i + 1, driver.compatible());
} - }
+ // Discard any spurious received characters before starting with the loader protocol.
+ console().clear_rx();
- println!( - println!(
- "[2] Chars written: {}", - "[2] Chars written: {}",
- bsp::console::console().chars_written() - bsp::console::console().chars_written()
- ); - );
- println!("[3] Echoing input now"); - 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 { - loop {
- let c = bsp::console::console().read_char(); - let c = bsp::console::console().read_char();
- bsp::console::console().write_char(c); - bsp::console::console().write_char(c);

@ -40,7 +40,6 @@ pub unsafe fn _start() -> ! {
pub use asm::nop; pub use asm::nop;
/// Spin for `n` cycles. /// Spin for `n` cycles.
#[cfg(feature = "bsp_rpi3")]
#[inline(always)] #[inline(always)]
pub fn spin_for_cycles(n: usize) { pub fn spin_for_cycles(n: usize) {
for _ in 0..n { for _ in 0..n {

@ -144,7 +144,7 @@ impl GPIOInner {
// Make an educated guess for a good delay value (Sequence described in the BCM2837 // Make an educated guess for a good delay value (Sequence described in the BCM2837
// peripherals PDF). // 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. // - 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 // So lets try to be on the safe side and default to 2000 cycles, which would equal 1 µs

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers. /// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>; type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics. /// 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: /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). /// `(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 /// 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 /// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.01 %. /// now at 0.02%.
pub fn init(&mut self) { 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); self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR); 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 self.registers
.LCRH .LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers self.registers
.CR .CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,53 @@ impl PL011UartInner {
self.chars_written += 1; 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<char> {
// 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 /// 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 { impl PL011Uart {
/// Create an instance.
///
/// # Safety /// # Safety
/// ///
/// - The user must ensure to provide a correct MMIO start address. /// - The user must ensure to provide a correct MMIO start address.
@ -273,37 +345,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) { fn flush(&self) {
// Spin until TX FIFO empty is set. // Spin until TX FIFO empty is set.
self.inner.lock(|inner| { self.inner.lock(|inner| inner.flush());
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
} }
} }
impl console::interface::Read for PL011Uart { impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char { fn read_char(&self) -> char {
self.inner.lock(|inner| { self.inner
// Spin while RX FIFO empty is set. .lock(|inner| inner.read_char(BlockingMode::Blocking).unwrap())
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
})
} }
fn clear(&self) { fn clear_rx(&self) {
self.inner.lock(|inner| { // Read from the RX FIFO until it is indicating empty.
// Read from the RX FIFO until it is indicating empty. while self
while !inner.registers.FR.matches_all(FR::RXFE::SET) { .inner
inner.registers.DR.get(); .lock(|inner| inner.read_char(BlockingMode::NonBlocking))
} .is_some()
}) {}
} }
} }

@ -20,8 +20,8 @@ pub mod interface {
/// Write a Rust format string. /// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last character has been physically put on the TX wire /// Block execution until the last buffered character has been physically put on the TX
/// (draining TX buffers/FIFOs, if any). /// wire.
fn flush(&self); fn flush(&self);
} }
@ -33,7 +33,7 @@ pub mod interface {
} }
/// Clear RX buffers, if any. /// Clear RX buffers, if any.
fn clear(&self); fn clear_rx(&self);
} }
/// Console statistics. /// Console statistics.

@ -160,9 +160,8 @@ fn kernel_main() -> ! {
println!("[ML] Requesting binary"); println!("[ML] Requesting binary");
console().flush(); console().flush();
// Clear the RX FIFOs, if any, of spurious received characters before starting with the loader // Discard any spurious received characters before starting with the loader protocol.
// protocol. console().clear_rx();
console().clear();
// Notify `Minipush` to send the binary. // Notify `Minipush` to send the binary.
for _ in 0..3 { for _ in 0..3 {

@ -129,12 +129,11 @@ diff -uNr 07_uart_chainloader/src/_arch/aarch64/cpu.rs 08_timestamps/src/_arch/a
} else { } else {
// If not core0, infinitely wait for events. // If not core0, infinitely wait for events.
wait_forever() wait_forever()
@@ -39,15 +39,6 @@ @@ -39,14 +39,6 @@
pub use asm::nop; pub use asm::nop;
-/// Spin for `n` cycles. -/// Spin for `n` cycles.
-#[cfg(feature = "bsp_rpi3")]
-#[inline(always)] -#[inline(always)]
-pub fn spin_for_cycles(n: usize) { -pub fn spin_for_cycles(n: usize) {
- for _ in 0..n { - 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. /// Pause execution on the core.
#[inline(always)] #[inline(always)]
pub fn wait_forever() -> ! { pub fn wait_forever() -> ! {
@@ -55,19 +46,3 @@ @@ -54,19 +46,3 @@
asm::wfe() 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 - // Make an educated guess for a good delay value (Sequence described in the BCM2837
- // peripherals PDF). - // 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. - // - 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 - // 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 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 --- 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
+++ 08_timestamps/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs +++ 08_timestamps/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
@@ -289,11 +289,18 @@ @@ -235,16 +235,13 @@
cpu::nop();
}
+ // Read one character. /// Block execution until the last buffered character has been physically put on the TX wire.
+ let mut ret = inner.registers.DR.get() as u8 as char; fn flush(&self) {
+ + use crate::{time, time::interface::TimeManager};
+ // Convert carrige return to newline. + use core::time::Duration;
+ if ret == '\r' {
+ ret = '\n'
+ }
+ +
// Update statistics. // The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
inner.chars_read += 1; // 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. /// Retrieve a character.
- inner.registers.DR.get() as u8 as char - fn read_char(&mut self, blocking_mode: BlockingMode) -> Option<char> {
+ ret + fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<char> {
}) // 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 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 --- 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. /// Early init code.
/// ///
@@ -147,52 +146,31 @@ @@ -147,51 +146,31 @@
/// The main function running after the early init. /// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
@ -504,9 +548,8 @@ diff -uNr 07_uart_chainloader/src/main.rs 08_timestamps/src/main.rs
- println!("[ML] Requesting binary"); - println!("[ML] Requesting binary");
- console().flush(); - console().flush();
- -
- // Clear the RX FIFOs, if any, of spurious received characters before starting with the loader - // Discard any spurious received characters before starting with the loader protocol.
- // protocol. - console().clear_rx();
- console().clear();
- -
- // Notify `Minipush` to send the binary. - // Notify `Minipush` to send the binary.
- for _ in 0..3 { - for _ in 0..3 {

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers. /// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>; type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics. /// 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: /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). /// `(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 /// 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 /// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.01 %. /// now at 0.02%.
pub fn init(&mut self) { 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); self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR); 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 self.registers
.LCRH .LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers self.registers
.CR .CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1; 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<char> {
// 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 /// 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 { impl PL011Uart {
/// Create an instance.
///
/// # Safety /// # Safety
/// ///
/// - The user must ensure to provide a correct MMIO start address. /// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) { fn flush(&self) {
// Spin until TX FIFO empty is set. // Spin until TX FIFO empty is set.
self.inner.lock(|inner| { self.inner.lock(|inner| inner.flush());
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
} }
} }
impl console::interface::Read for PL011Uart { impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char { fn read_char(&self) -> char {
self.inner.lock(|inner| { self.inner
// Spin while RX FIFO empty is set. .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
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
})
} }
fn clear(&self) { fn clear_rx(&self) {
self.inner.lock(|inner| { // Read from the RX FIFO until it is indicating empty.
// Read from the RX FIFO until it is indicating empty. while self
while !inner.registers.FR.matches_all(FR::RXFE::SET) { .inner
inner.registers.DR.get(); .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
} .is_some()
}) {}
} }
} }

@ -20,8 +20,8 @@ pub mod interface {
/// Write a Rust format string. /// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last character has been physically put on the TX wire /// Block execution until the last buffered character has been physically put on the TX
/// (draining TX buffers/FIFOs, if any). /// wire.
fn flush(&self); fn flush(&self);
} }
@ -33,7 +33,7 @@ pub mod interface {
} }
/// Clear RX buffers, if any. /// Clear RX buffers, if any.
fn clear(&self); fn clear_rx(&self);
} }
/// Console statistics. /// Console statistics.

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers. /// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>; type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics. /// 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: /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). /// `(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 /// 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 /// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.01 %. /// now at 0.02%.
pub fn init(&mut self) { 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); self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR); 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 self.registers
.LCRH .LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers self.registers
.CR .CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1; 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<char> {
// 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 /// 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 { impl PL011Uart {
/// Create an instance.
///
/// # Safety /// # Safety
/// ///
/// - The user must ensure to provide a correct MMIO start address. /// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) { fn flush(&self) {
// Spin until TX FIFO empty is set. // Spin until TX FIFO empty is set.
self.inner.lock(|inner| { self.inner.lock(|inner| inner.flush());
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
} }
} }
impl console::interface::Read for PL011Uart { impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char { fn read_char(&self) -> char {
self.inner.lock(|inner| { self.inner
// Spin while RX FIFO empty is set. .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
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
})
} }
fn clear(&self) { fn clear_rx(&self) {
self.inner.lock(|inner| { // Read from the RX FIFO until it is indicating empty.
// Read from the RX FIFO until it is indicating empty. while self
while !inner.registers.FR.matches_all(FR::RXFE::SET) { .inner
inner.registers.DR.get(); .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
} .is_some()
}) {}
} }
} }

@ -20,8 +20,8 @@ pub mod interface {
/// Write a Rust format string. /// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last character has been physically put on the TX wire /// Block execution until the last buffered character has been physically put on the TX
/// (draining TX buffers/FIFOs, if any). /// wire.
fn flush(&self); fn flush(&self);
} }
@ -33,7 +33,7 @@ pub mod interface {
} }
/// Clear RX buffers, if any. /// Clear RX buffers, if any.
fn clear(&self); fn clear_rx(&self);
} }
/// Console statistics. /// Console statistics.

@ -464,10 +464,11 @@ diff -uNr 09_hw_debug_JTAG/src/main.rs 10_privilege_level/src/main.rs
mod memory; mod memory;
mod panic_wait; mod panic_wait;
mod print; mod print;
@@ -146,12 +147,19 @@ @@ -146,12 +147,20 @@
/// The main function running after the early init. /// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
+ use bsp::console::console;
+ use console::interface::All; + use console::interface::All;
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager; use driver::interface::DriverManager;
@ -484,7 +485,7 @@ diff -uNr 09_hw_debug_JTAG/src/main.rs 10_privilege_level/src/main.rs
info!( info!(
"Architectural timer resolution: {} ns", "Architectural timer resolution: {} ns",
time::time_manager().resolution().as_nanos() time::time_manager().resolution().as_nanos()
@@ -166,11 +174,12 @@ @@ -166,11 +175,15 @@
info!(" {}. {}", i + 1, driver.compatible()); 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)); - time::time_manager().spin_for(Duration::from_nanos(1));
+ info!("Timer test, spinning for 1 second"); + info!("Timer test, spinning for 1 second");
+ time::time_manager().spin_for(Duration::from_secs(1)); + time::time_manager().spin_for(Duration::from_secs(1));
+
+ info!("Echoing input now"); + info!("Echoing input now");
+ // Discard any spurious received characters before going into echo mode.
+ console().clear_rx();
loop { loop {
- info!("Spinning for 1 second"); - info!("Spinning for 1 second");
- time::time_manager().spin_for(Duration::from_secs(1)); - time::time_manager().spin_for(Duration::from_secs(1));

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers. /// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>; type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics. /// 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: /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). /// `(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 /// 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 /// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.01 %. /// now at 0.02%.
pub fn init(&mut self) { 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); self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR); 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 self.registers
.LCRH .LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers self.registers
.CR .CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1; 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<char> {
// 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 /// 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 { impl PL011Uart {
/// Create an instance.
///
/// # Safety /// # Safety
/// ///
/// - The user must ensure to provide a correct MMIO start address. /// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) { fn flush(&self) {
// Spin until TX FIFO empty is set. // Spin until TX FIFO empty is set.
self.inner.lock(|inner| { self.inner.lock(|inner| inner.flush());
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
} }
} }
impl console::interface::Read for PL011Uart { impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char { fn read_char(&self) -> char {
self.inner.lock(|inner| { self.inner
// Spin while RX FIFO empty is set. .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
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
})
} }
fn clear(&self) { fn clear_rx(&self) {
self.inner.lock(|inner| { // Read from the RX FIFO until it is indicating empty.
// Read from the RX FIFO until it is indicating empty. while self
while !inner.registers.FR.matches_all(FR::RXFE::SET) { .inner
inner.registers.DR.get(); .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
} .is_some()
}) {}
} }
} }

@ -20,8 +20,8 @@ pub mod interface {
/// Write a Rust format string. /// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last character has been physically put on the TX wire /// Block execution until the last buffered character has been physically put on the TX
/// (draining TX buffers/FIFOs, if any). /// wire.
fn flush(&self); fn flush(&self);
} }
@ -33,7 +33,7 @@ pub mod interface {
} }
/// Clear RX buffers, if any. /// Clear RX buffers, if any.
fn clear(&self); fn clear_rx(&self);
} }
/// Console statistics. /// Console statistics.

@ -147,6 +147,7 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init. /// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
use bsp::console::console;
use console::interface::All; use console::interface::All;
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager; use driver::interface::DriverManager;
@ -178,6 +179,9 @@ fn kernel_main() -> ! {
time::time_manager().spin_for(Duration::from_secs(1)); time::time_manager().spin_for(Duration::from_secs(1));
info!("Echoing input now"); info!("Echoing input now");
// Discard any spurious received characters before going into echo mode.
console().clear_rx();
loop { loop {
let c = bsp::console::console().read_char(); let c = bsp::console::console().read_char();
bsp::console::console().write_char(c); bsp::console::console().write_char(c);

@ -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() { for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() { if let Err(x) = i.init() {
@@ -154,6 +168,9 @@ @@ -155,6 +169,9 @@
info!("Booting on: {}", bsp::board_name()); 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(); let (_, privilege_level) = exception::current_privilege_level();
info!("Current privilege level: {}", privilege_level); info!("Current privilege level: {}", privilege_level);
@@ -177,6 +194,13 @@ @@ -178,6 +195,13 @@
info!("Timer test, spinning for 1 second"); info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1)); 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(); + .unwrap();
+ +
info!("Echoing input now"); 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 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 --- 10_privilege_level/src/memory/mmu.rs

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers. /// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>; type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics. /// 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: /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). /// `(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 /// 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 /// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.01 %. /// now at 0.02%.
pub fn init(&mut self) { 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); self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR); 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 self.registers
.LCRH .LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers self.registers
.CR .CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1; 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<char> {
// 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 /// 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 { impl PL011Uart {
/// Create an instance.
///
/// # Safety /// # Safety
/// ///
/// - The user must ensure to provide a correct MMIO start address. /// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) { fn flush(&self) {
// Spin until TX FIFO empty is set. // Spin until TX FIFO empty is set.
self.inner.lock(|inner| { self.inner.lock(|inner| inner.flush());
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
} }
} }
impl console::interface::Read for PL011Uart { impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char { fn read_char(&self) -> char {
self.inner.lock(|inner| { self.inner
// Spin while RX FIFO empty is set. .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
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
})
} }
fn clear(&self) { fn clear_rx(&self) {
self.inner.lock(|inner| { // Read from the RX FIFO until it is indicating empty.
// Read from the RX FIFO until it is indicating empty. while self
while !inner.registers.FR.matches_all(FR::RXFE::SET) { .inner
inner.registers.DR.get(); .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
} .is_some()
}) {}
} }
} }

@ -20,8 +20,8 @@ pub mod interface {
/// Write a Rust format string. /// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last character has been physically put on the TX wire /// Block execution until the last buffered character has been physically put on the TX
/// (draining TX buffers/FIFOs, if any). /// wire.
fn flush(&self); fn flush(&self);
} }
@ -33,7 +33,7 @@ pub mod interface {
} }
/// Clear RX buffers, if any. /// Clear RX buffers, if any.
fn clear(&self); fn clear_rx(&self);
} }
/// Console statistics. /// Console statistics.

@ -161,6 +161,7 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init. /// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
use bsp::console::console;
use console::interface::All; use console::interface::All;
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager; use driver::interface::DriverManager;
@ -202,6 +203,9 @@ fn kernel_main() -> ! {
.unwrap(); .unwrap();
info!("Echoing input now"); info!("Echoing input now");
// Discard any spurious received characters before going into echo mode.
console().clear_rx();
loop { loop {
let c = bsp::console::console().read_char(); let c = bsp::console::console().read_char();
bsp::console::console().write_char(c); bsp::console::console().write_char(c);

@ -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() { if let Err(string) = memory::mmu::mmu().init() {
panic!("MMU: {}", string); panic!("MMU: {}", string);
} }
@@ -194,13 +197,28 @@ @@ -195,13 +198,28 @@
info!("Timer test, spinning for 1 second"); info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1)); 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. + // Will never reach here in this tutorial.
info!("Echoing input now"); 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 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 --- 11_virtual_mem_part1_identity_mapping/src/memory/mmu.rs

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers. /// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>; type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics. /// 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: /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). /// `(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 /// 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 /// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.01 %. /// now at 0.02%.
pub fn init(&mut self) { 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); self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR); 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 self.registers
.LCRH .LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers self.registers
.CR .CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1; 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<char> {
// 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 /// 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 { impl PL011Uart {
/// Create an instance.
///
/// # Safety /// # Safety
/// ///
/// - The user must ensure to provide a correct MMIO start address. /// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) { fn flush(&self) {
// Spin until TX FIFO empty is set. // Spin until TX FIFO empty is set.
self.inner.lock(|inner| { self.inner.lock(|inner| inner.flush());
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
} }
} }
impl console::interface::Read for PL011Uart { impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char { fn read_char(&self) -> char {
self.inner.lock(|inner| { self.inner
// Spin while RX FIFO empty is set. .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
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
})
} }
fn clear(&self) { fn clear_rx(&self) {
self.inner.lock(|inner| { // Read from the RX FIFO until it is indicating empty.
// Read from the RX FIFO until it is indicating empty. while self
while !inner.registers.FR.matches_all(FR::RXFE::SET) { .inner
inner.registers.DR.get(); .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
} .is_some()
}) {}
} }
} }

@ -20,8 +20,8 @@ pub mod interface {
/// Write a Rust format string. /// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last character has been physically put on the TX wire /// Block execution until the last buffered character has been physically put on the TX
/// (draining TX buffers/FIFOs, if any). /// wire.
fn flush(&self); fn flush(&self);
} }
@ -33,7 +33,7 @@ pub mod interface {
} }
/// Clear RX buffers, if any. /// Clear RX buffers, if any.
fn clear(&self); fn clear_rx(&self);
} }
/// Console statistics. /// Console statistics.

@ -164,6 +164,7 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init. /// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
use bsp::console::console;
use console::interface::All; use console::interface::All;
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager; use driver::interface::DriverManager;
@ -220,6 +221,9 @@ fn kernel_main() -> ! {
// Will never reach here in this tutorial. // Will never reach here in this tutorial.
info!("Echoing input now"); info!("Echoing input now");
// Discard any spurious received characters before going into echo mode.
console().clear_rx();
loop { loop {
let c = bsp::console::console().read_char(); let c = bsp::console::console().read_char();
bsp::console::console().write_char(c); bsp::console::console().write_char(c);

@ -1444,9 +1444,9 @@ diff -uNr 12_exceptions_part1_groundwork/src/main.rs 13_integrated_testing/src/m
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager; use driver::interface::DriverManager;
use memory::mmu::interface::MMU; use memory::mmu::interface::MMU;
@@ -165,9 +49,7 @@ @@ -166,9 +50,7 @@
/// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
use bsp::console::console;
use console::interface::All; use console::interface::All;
- use core::time::Duration; - use core::time::Duration;
use driver::interface::DriverManager; 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()); info!("Booting on: {}", bsp::board_name());
@@ -194,31 +76,6 @@ @@ -195,31 +77,6 @@
info!(" {}. {}", i + 1, driver.compatible()); 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. - // Will never reach here in this tutorial.
info!("Echoing input now"); 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 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 --- 12_exceptions_part1_groundwork/src/memory/mmu.rs

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers. /// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>; type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics. /// 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: /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). /// `(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 /// 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 /// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.01 %. /// now at 0.02%.
pub fn init(&mut self) { 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); self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR); 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 self.registers
.LCRH .LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers self.registers
.CR .CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1; 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<char> {
// 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 /// 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 { impl PL011Uart {
/// Create an instance.
///
/// # Safety /// # Safety
/// ///
/// - The user must ensure to provide a correct MMIO start address. /// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) { fn flush(&self) {
// Spin until TX FIFO empty is set. // Spin until TX FIFO empty is set.
self.inner.lock(|inner| { self.inner.lock(|inner| inner.flush());
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
} }
} }
impl console::interface::Read for PL011Uart { impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char { fn read_char(&self) -> char {
self.inner.lock(|inner| { self.inner
// Spin while RX FIFO empty is set. .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
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
})
} }
fn clear(&self) { fn clear_rx(&self) {
self.inner.lock(|inner| { // Read from the RX FIFO until it is indicating empty.
// Read from the RX FIFO until it is indicating empty. while self
while !inner.registers.FR.matches_all(FR::RXFE::SET) { .inner
inner.registers.DR.get(); .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
} .is_some()
}) {}
} }
} }

@ -20,8 +20,8 @@ pub mod interface {
/// Write a Rust format string. /// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last character has been physically put on the TX wire /// Block execution until the last buffered character has been physically put on the TX
/// (draining TX buffers/FIFOs, if any). /// wire.
fn flush(&self); fn flush(&self);
} }
@ -33,7 +33,7 @@ pub mod interface {
} }
/// Clear RX buffers, if any. /// Clear RX buffers, if any.
fn clear(&self); fn clear_rx(&self);
} }
/// Console statistics. /// Console statistics.

@ -48,6 +48,7 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init. /// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
use bsp::console::console;
use console::interface::All; use console::interface::All;
use driver::interface::DriverManager; use driver::interface::DriverManager;
@ -77,6 +78,9 @@ fn kernel_main() -> ! {
} }
info!("Echoing input now"); info!("Echoing input now");
// Discard any spurious received characters before going into echo mode.
console().clear_rx();
loop { loop {
let c = bsp::console::console().read_char(); let c = bsp::console::console().read_char();
bsp::console::console().write_char(c); bsp::console::console().write_char(c);

@ -1862,20 +1862,7 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
(0x44 => ICR: WriteOnly<u32, ICR::Register>), (0x44 => ICR: WriteOnly<u32, ICR::Register>),
(0x48 => @END), (0x48 => @END),
} }
@@ -136,6 +181,12 @@ @@ -157,7 +202,8 @@
/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;
+#[derive(PartialEq)]
+enum BlockingMode {
+ Blocking,
+ NonBlocking,
+}
+
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
@@ -151,7 +202,8 @@
/// Representation of the UART. /// Representation of the UART.
pub struct PL011Uart { 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 @@ @@ -214,6 +260,14 @@
self.registers
.LCRH .LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
+ self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth); // RX FIFO fill level at 1/8
+ // Set RX FIFO fill level at 1/8.
+ self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth);
+
+ // Enable RX IRQ + RX timeout IRQ.
+ self.registers + self.registers
+ .IMSC + .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 self.registers
.CR .CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); @@ -308,9 +362,13 @@
@@ -209,6 +265,35 @@
self.chars_written += 1;
}
+
+ /// Retrieve a character.
+ fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<char> {
+ // 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.
+ ///
/// # Safety /// # Safety
/// ///
/// - The user must ensure to provide a correct MMIO start address. /// - 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(()) 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 { impl console::interface::Write for PL011Uart {
@@ -283,25 +389,8 @@ @@ -376,3 +449,24 @@
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 @@
self.inner.lock(|inner| inner.chars_read) 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] #[no_mangle]
unsafe fn kernel_init() -> ! { unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager; use driver::interface::DriverManager;
@@ -42,14 +42,27 @@ @@ -42,15 +42,27 @@
bsp::driver::driver_manager().post_device_driver_init(); bsp::driver::driver_manager().post_device_driver_init();
// println! is usable from here on. // 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. /// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
- use bsp::console::console;
- use console::interface::All; - use console::interface::All;
use driver::interface::DriverManager; use driver::interface::DriverManager;
+ use exception::asynchronous::interface::IRQManager; + use exception::asynchronous::interface::IRQManager;
info!("Booting on: {}", bsp::board_name()); info!("Booting on: {}", bsp::board_name());
@@ -76,9 +89,9 @@ @@ -77,12 +89,9 @@
info!(" {}. {}", i + 1, driver.compatible()); info!(" {}. {}", i + 1, driver.compatible());
} }
- info!("Echoing input now");
+ info!("Registered IRQ handlers:"); + info!("Registered IRQ handlers:");
+ bsp::exception::asynchronous::irq_manager().print_handler(); + 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 { - loop {
- let c = bsp::console::console().read_char(); - let c = bsp::console::console().read_char();
- bsp::console::console().write_char(c); - bsp::console::console().write_char(c);
- } - }
+ info!("Echoing input now");
+ cpu::wait_forever(); + cpu::wait_forever();
} }

@ -226,28 +226,49 @@ impl PL011UartInner {
/// Set up baud rate and characteristics. /// 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: /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). /// `(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 /// 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 /// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.01 %. /// now at 0.02%.
pub fn init(&mut self) { 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); self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR); 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 self.registers
.LCRH .LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth); // RX FIFO fill level at 1/8
// Set RX FIFO fill level at 1/8.
self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth);
// Enable RX IRQ + RX timeout IRQ.
self.registers self.registers
.IMSC .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 self.registers
.CR .CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -266,6 +287,26 @@ impl PL011UartInner {
self.chars_written += 1; 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. /// Retrieve a character.
fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<char> { fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<char> {
// If RX FIFO is empty, // If RX FIFO is empty,
@ -379,11 +420,7 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) { fn flush(&self) {
// Spin until TX FIFO empty is set. // Spin until TX FIFO empty is set.
self.inner.lock(|inner| { self.inner.lock(|inner| inner.flush());
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
} }
} }
@ -393,13 +430,13 @@ impl console::interface::Read for PL011Uart {
.lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
} }
fn clear(&self) { fn clear_rx(&self) {
self.inner.lock(|inner| { // Read from the RX FIFO until it is indicating empty.
// Read from the RX FIFO until it is indicating empty. while self
while !inner.registers.FR.matches_all(FR::RXFE::SET) { .inner
inner.registers.DR.get(); .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
} .is_some()
}) {}
} }
} }

@ -20,8 +20,8 @@ pub mod interface {
/// Write a Rust format string. /// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last character has been physically put on the TX wire /// Block execution until the last buffered character has been physically put on the TX
/// (draining TX buffers/FIFOs, if any). /// wire.
fn flush(&self); fn flush(&self);
} }
@ -33,7 +33,7 @@ pub mod interface {
} }
/// Clear RX buffers, if any. /// Clear RX buffers, if any.
fn clear(&self); fn clear_rx(&self);
} }
/// Console statistics. /// Console statistics.

@ -1305,9 +1305,9 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_
irq_number: bsp::device_driver::IRQNumber, irq_number: bsp::device_driver::IRQNumber,
} }
@@ -234,7 +239,15 @@ @@ -234,7 +239,15 @@
/// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will /// 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 modulo error margin is acceptable for UART and we're /// give the best approximation we can get. A 5modulo error margin is acceptable for UART and we're
/// now at 0.01 modulo. /// now at 0.02modulo.
- pub fn init(&mut self) { - pub fn init(&mut self) {
+ /// + ///
+ /// # Safety + /// # Safety
@ -1318,10 +1318,10 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_
+ self.registers = Registers::new(addr); + self.registers = Registers::new(addr);
+ } + }
+ +
// Turn it off temporarily. // Execution can arrive here while there are still characters queued in the TX FIFO and
self.registers.CR.set(0); // actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
@@ -251,6 +264,8 @@ @@ -272,6 +285,8 @@
self.registers self.registers
.CR .CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); .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. /// Send a character.
@@ -320,13 +335,18 @@ @@ -361,13 +376,18 @@
/// ///
/// # Safety /// # Safety
/// ///
@ -1352,7 +1352,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/device_driver/bcm/bcm2xxx_
irq_number, irq_number,
} }
} }
@@ -343,7 +363,14 @@ @@ -384,7 +404,14 @@
} }
unsafe fn init(&self) -> Result<(), &'static str> { 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(()) Ok(())
} }
@@ -362,6 +389,16 @@ @@ -403,6 +430,16 @@
Ok(()) Ok(())
} }

@ -231,14 +231,14 @@ impl PL011UartInner {
/// Set up baud rate and characteristics. /// 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: /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). /// `(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 /// 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 /// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.01 %. /// now at 0.02%.
/// ///
/// # Safety /// # Safety
/// ///
@ -248,19 +248,40 @@ impl PL011UartInner {
self.registers = Registers::new(addr); 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); self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR); 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 self.registers
.LCRH .LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth); // RX FIFO fill level at 1/8
// Set RX FIFO fill level at 1/8.
self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth);
// Enable RX IRQ + RX timeout IRQ.
self.registers self.registers
.IMSC .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 self.registers
.CR .CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -281,6 +302,26 @@ impl PL011UartInner {
self.chars_written += 1; 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. /// Retrieve a character.
fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<char> { fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<char> {
// If RX FIFO is empty, // If RX FIFO is empty,
@ -416,11 +457,7 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) { fn flush(&self) {
// Spin until TX FIFO empty is set. // Spin until TX FIFO empty is set.
self.inner.lock(|inner| { self.inner.lock(|inner| inner.flush());
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
} }
} }
@ -430,13 +467,13 @@ impl console::interface::Read for PL011Uart {
.lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap()) .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
} }
fn clear(&self) { fn clear_rx(&self) {
self.inner.lock(|inner| { // Read from the RX FIFO until it is indicating empty.
// Read from the RX FIFO until it is indicating empty. while self
while !inner.registers.FR.matches_all(FR::RXFE::SET) { .inner
inner.registers.DR.get(); .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
} .is_some()
}) {}
} }
} }

@ -20,8 +20,8 @@ pub mod interface {
/// Write a Rust format string. /// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last character has been physically put on the TX wire /// Block execution until the last buffered character has been physically put on the TX
/// (draining TX buffers/FIFOs, if any). /// wire.
fn flush(&self); fn flush(&self);
} }
@ -33,7 +33,7 @@ pub mod interface {
} }
/// Clear RX buffers, if any. /// Clear RX buffers, if any.
fn clear(&self); fn clear_rx(&self);
} }
/// Console statistics. /// Console statistics.

Binary file not shown.

Binary file not shown.

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers. /// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>; type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Public Definitions // Public Definitions
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics. /// 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: /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field). /// `(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 /// 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 /// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.01 %. /// now at 0.02%.
pub fn init(&mut self) { 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); self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR); 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 self.registers
.LCRH .LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers self.registers
.CR .CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1; 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<char> {
// 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 /// 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 { impl PL011Uart {
/// Create an instance.
///
/// # Safety /// # Safety
/// ///
/// - The user must ensure to provide a correct MMIO start address. /// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) { fn flush(&self) {
// Spin until TX FIFO empty is set. // Spin until TX FIFO empty is set.
self.inner.lock(|inner| { self.inner.lock(|inner| inner.flush());
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
} }
} }
impl console::interface::Read for PL011Uart { impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char { fn read_char(&self) -> char {
self.inner.lock(|inner| { self.inner
// Spin while RX FIFO empty is set. .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
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
})
} }
fn clear(&self) { fn clear_rx(&self) {
self.inner.lock(|inner| { // Read from the RX FIFO until it is indicating empty.
// Read from the RX FIFO until it is indicating empty. while self
while !inner.registers.FR.matches_all(FR::RXFE::SET) { .inner
inner.registers.DR.get(); .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
} .is_some()
}) {}
} }
} }

@ -20,8 +20,8 @@ pub mod interface {
/// Write a Rust format string. /// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last character has been physically put on the TX wire /// Block execution until the last buffered character has been physically put on the TX
/// (draining TX buffers/FIFOs, if any). /// wire.
fn flush(&self); fn flush(&self);
} }
@ -33,7 +33,7 @@ pub mod interface {
} }
/// Clear RX buffers, if any. /// Clear RX buffers, if any.
fn clear(&self); fn clear_rx(&self);
} }
/// Console statistics. /// Console statistics.

@ -25,24 +25,29 @@ class MiniPush < MiniTerm
private private
# The three characters signaling the request token are expected to arrive as the last three # The three characters signaling the request token form the consecutive sequence "\x03\x03\x03".
# characters _at the end_ of a character stream (e.g. after a header print from Miniload).
def wait_for_binary_request def wait_for_binary_request
puts "[#{@name_short}] 🔌 Please power the target now" puts "[#{@name_short}] 🔌 Please power the target now"
# Timeout for the request token starts after the first sign of life was received. # Timeout for the request token starts after the first sign of life was received.
received = @target_serial.readpartial(4096) received = @target_serial.readpartial(4096)
Timeout.timeout(10) do Timeout.timeout(10) do
count = 0
loop do loop do
raise ProtocolError if received.nil? raise ProtocolError if received.nil?
if received.chars.last(3) == ["\u{3}", "\u{3}", "\u{3}"] received.chars.each do |c|
# Print the last chunk minus the request token. if c == "\u{3}"
print received[0..-4] count += 1
return return true if count == 3
end else
# A normal character resets token counting.
count = 0
print received print c
end
end
received = @target_serial.readpartial(4096) received = @target_serial.readpartial(4096)
end end

@ -11,6 +11,8 @@ require 'io/console'
require 'colorize' require 'colorize'
require 'serialport' require 'serialport'
SERIAL_BAUD = 921_600
class ConnectionError < StandardError; end class ConnectionError < StandardError; end
# The main class # The main class
@ -41,7 +43,7 @@ class MiniTerm
def open_serial def open_serial
wait_for_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. # Ensure all output is immediately flushed to the device.
@target_serial.sync = true @target_serial.sync = true

Loading…
Cancel
Save