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
--- 05_safe_globals/src/_arch/aarch64/cpu.rs
+++ 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
@@ -37,6 +37,17 @@
@@ -37,6 +37,16 @@
// Public Code
//--------------------------------------------------------------------------------------------------
+pub use asm::nop;
+
+/// Spin for `n` cycles.
+#[cfg(feature = "bsp_rpi3")]
+#[inline(always)]
+pub fn spin_for_cycles(n: usize) {
+ for _ in 0..n {
@ -433,7 +432,7 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 06_drivers_g
diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
--- 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
+++ 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
@@ -0,0 +1,307 @@
@@ -0,0 +1,381 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <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.
+type Registers = MMIODerefWrapper<RegisterBlock>;
+
+#[derive(PartialEq)]
+enum BlockingMode {
+ Blocking,
+ NonBlocking,
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
@ -610,24 +615,41 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri
+
+ /// Set up baud rate and characteristics.
+ ///
+ /// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt).
+ /// This results in 8N1 and 921_600 baud.
+ ///
+ /// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is:
+ /// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field).
+ /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
+ /// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
+ ///
+ /// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will
+ /// give the best approximation we can get. A 5 modulo error margin is acceptable for UART and we're
+ /// now at 0.01 modulo.
+ /// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
+ /// give the best approximation we can get. A 5modulo error margin is acceptable for UART and we're
+ /// now at 0.02modulo.
+ pub fn init(&mut self) {
+ // Turn it off temporarily.
+ // Execution can arrive here while there are still characters queued in the TX FIFO and
+ // actively being sent out by the UART hardware. If the UART is turned off in this case,
+ // those queued characters would be lost.
+ //
+ // For example, this can happen during runtime on a call to panic!(), because panic!()
+ // initializes its own UART instance and calls init().
+ //
+ // Hence, flush first to ensure all pending characters are transmitted.
+ self.flush();
+
+ // Turn the UART off temporarily.
+ self.registers.CR.set(0);
+
+ // Clear all pending interrupts.
+ self.registers.ICR.write(ICR::ALL::CLEAR);
+ self.registers.IBRD.write(IBRD::IBRD.val(5));
+ self.registers.FBRD.write(FBRD::FBRD.val(13));
+
+ // Set the baud rate.
+ self.registers.IBRD.write(IBRD::IBRD.val(3));
+ self.registers.FBRD.write(FBRD::FBRD.val(16));
+
+ // Set 8N1 + FIFO on.
+ self.registers
+ .LCRH
+ .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
+ .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
+
+ // Turn the UART on.
+ self.registers
+ .CR
+ .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -645,6 +667,58 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri
+
+ self.chars_written += 1;
+ }
+
+ /// Block execution until the last buffered character has been physically put on the TX wire.
+ fn flush(&self) {
+ // The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
+ // symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
+ // 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
+ //
+ // Now make an educated guess for a good delay value. According to Wikipedia, the fastest
+ // RPi4 clocks around 1.5 GHz.
+ //
+ // So lets try to be on the safe side and default to 24_000 cycles, which would equal 12_000
+ // ns would the CPU be clocked at 2 GHz.
+ const CHAR_TIME_SAFE: usize = 24_000;
+
+ // Spin until TX FIFO empty is set.
+ while !self.registers.FR.matches_all(FR::TXFE::SET) {
+ cpu::nop();
+ }
+
+ // After the last character has been queued for transmission, wait for the time of one
+ // character + some extra time for safety.
+ cpu::spin_for_cycles(CHAR_TIME_SAFE);
+ }
+
+ /// Retrieve a character.
+ fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<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
@ -667,6 +741,8 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri
+}
+
+impl PL011Uart {
+ /// Create an instance.
+ ///
+ /// # Safety
+ ///
+ /// - The user must ensure to provide a correct MMIO start address.
@ -706,29 +782,26 @@ diff -uNr 05_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 06_dri
+ // readability.
+ self.inner.lock(|inner| fmt::Write::write_fmt(inner, args))
+ }
+
+ fn flush(&self) {
+ // Spin until TX FIFO empty is set.
+ self.inner.lock(|inner| inner.flush());
+ }
+}
+
+impl console::interface::Read for PL011Uart {
+ fn read_char(&self) -> char {
+ self.inner.lock(|inner| {
+ // Spin while RX FIFO empty is set.
+ while inner.registers.FR.matches_all(FR::RXFE::SET) {
+ cpu::nop();
+ }
+
+ // Read one character.
+ let mut ret = inner.registers.DR.get() as u8 as char;
+
+ // Convert carrige return to newline.
+ if ret == '\r' {
+ ret = '\n'
+ }
+
+ // Update statistics.
+ inner.chars_read += 1;
+ self.inner
+ .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
+ }
+
+ ret
+ })
+ fn clear_rx(&self) {
+ // Read from the RX FIFO until it is indicating empty.
+ while self
+ .inner
+ .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
+ .is_some()
+ {}
+ }
+}
+
@ -1096,7 +1169,7 @@ diff -uNr 05_safe_globals/src/bsp.rs 06_drivers_gpio_uart/src/bsp.rs
diff -uNr 05_safe_globals/src/console.rs 06_drivers_gpio_uart/src/console.rs
--- 05_safe_globals/src/console.rs
+++ 06_drivers_gpio_uart/src/console.rs
@@ -14,18 +14,34 @@
@@ -14,8 +14,26 @@
/// Console write functions.
pub trait Write {
@ -1105,19 +1178,25 @@ diff -uNr 05_safe_globals/src/console.rs 06_drivers_gpio_uart/src/console.rs
+
/// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
}
+
+ /// Block execution until the last buffered character has been physically put on the TX
+ /// wire.
+ fn flush(&self);
+ }
+
+ /// Console read functions.
+ pub trait Read {
+ /// Read a single character.
+ fn read_char(&self) -> char {
+ ' '
+ }
+ }
+
+ /// Clear RX buffers, if any.
+ fn clear_rx(&self);
}
/// Console statistics.
pub trait Statistics {
/// Return the number of characters written.
@@ -24,8 +42,13 @@
fn chars_written(&self) -> usize {
0
}
@ -1216,7 +1295,7 @@ diff -uNr 05_safe_globals/src/main.rs 06_drivers_gpio_uart/src/main.rs
mod memory;
mod panic_wait;
mod print;
@@ -115,16 +125,46 @@
@@ -115,16 +125,49 @@
/// # Safety
///
/// - Only a single core must be active and running this function.
@ -1240,6 +1319,7 @@ diff -uNr 05_safe_globals/src/main.rs 06_drivers_gpio_uart/src/main.rs
- println!("[0] Hello from pure Rust!");
+/// The main function running after the early init.
+fn kernel_main() -> ! {
+ use bsp::console::console;
+ use console::interface::All;
+ use driver::interface::DriverManager;
+
@ -1263,6 +1343,8 @@ diff -uNr 05_safe_globals/src/main.rs 06_drivers_gpio_uart/src/main.rs
- println!("[2] Stopping here.");
- cpu::wait_forever()
+ // Discard any spurious received characters before going into echo mode.
+ console().clear_rx();
+ loop {
+ let c = bsp::console::console().read_char();
+ bsp::console::console().write_char(c);

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

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics.
///
/// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt).
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is:
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field).
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will
/// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're
/// now at 0.01 %.
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
pub fn init(&mut self) {
// Turn it off temporarily.
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
//
// For example, this can happen during runtime on a call to panic!(), because panic!()
// initializes its own UART instance and calls init().
//
// Hence, flush first to ensure all pending characters are transmitted.
self.flush();
// Turn the UART off temporarily.
self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
self.registers.IBRD.write(IBRD::IBRD.val(5));
self.registers.FBRD.write(FBRD::FBRD.val(13));
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,58 @@ impl PL011UartInner {
self.chars_written += 1;
}
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
//
// Now make an educated guess for a good delay value. According to Wikipedia, the fastest
// RPi4 clocks around 1.5 GHz.
//
// So lets try to be on the safe side and default to 24_000 cycles, which would equal 12_000
// ns would the CPU be clocked at 2 GHz.
const CHAR_TIME_SAFE: usize = 24_000;
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
cpu::spin_for_cycles(CHAR_TIME_SAFE);
}
/// Retrieve a character.
fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<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,6 +306,8 @@ impl fmt::Write for PL011UartInner {
}
impl PL011Uart {
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
@ -270,29 +347,26 @@ impl console::interface::Write for PL011Uart {
// readability.
self.inner.lock(|inner| fmt::Write::write_fmt(inner, args))
}
fn flush(&self) {
// Spin until TX FIFO empty is set.
self.inner.lock(|inner| inner.flush());
}
}
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner.lock(|inner| {
// Spin while RX FIFO empty is set.
while inner.registers.FR.matches_all(FR::RXFE::SET) {
cpu::nop();
}
// Read one character.
let mut ret = inner.registers.DR.get() as u8 as char;
// Convert carrige return to newline.
if ret == '\r' {
ret = '\n'
}
// Update statistics.
inner.chars_read += 1;
self.inner
.lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
}
ret
})
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
.lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
.is_some()
{}
}
}

@ -19,6 +19,10 @@ pub mod interface {
/// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
/// Block execution until the last buffered character has been physically put on the TX
/// wire.
fn flush(&self);
}
/// Console read functions.
@ -27,6 +31,9 @@ pub mod interface {
fn read_char(&self) -> char {
' '
}
/// Clear RX buffers, if any.
fn clear_rx(&self);
}
/// Console statistics.

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

@ -197,7 +197,7 @@ diff -uNr 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs 07_uart_chainloader/src/
} else {
// If not core0, infinitely wait for events.
wait_forever()
@@ -55,3 +55,19 @@
@@ -54,3 +54,19 @@
asm::wfe()
}
}
@ -218,55 +218,62 @@ diff -uNr 06_drivers_gpio_uart/src/_arch/aarch64/cpu.rs 07_uart_chainloader/src/
+ core::intrinsics::unreachable()
+}
diff -uNr 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
--- 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
+++ 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
@@ -144,7 +144,7 @@
// Make an educated guess for a good delay value (Sequence described in the BCM2837
// peripherals PDF).
//
- // - According to Wikipedia, the fastest Pi3 clocks around 1.4 GHz.
+ // - According to Wikipedia, the fastest RPi4 clocks around 1.5 GHz.
// - The Linux 2837 GPIO driver waits 1 µs between the steps.
//
// So lets try to be on the safe side and default to 2000 cycles, which would equal 1 µs
diff -uNr 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
--- 06_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
+++ 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
@@ -270,6 +270,15 @@
// readability.
self.inner.lock(|inner| fmt::Write::write_fmt(inner, args))
@@ -257,7 +257,7 @@
}
+
+ fn flush(&self) {
+ // Spin until TX FIFO empty is set.
+ self.inner.lock(|inner| {
+ while !inner.registers.FR.matches_all(FR::TXFE::SET) {
+ cpu::nop();
+ }
+ });
+ }
}
impl console::interface::Read for PL011Uart {
@@ -280,18 +289,20 @@
cpu::nop();
}
/// Retrieve a character.
- fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<char> {
+ 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.
- let mut ret = inner.registers.DR.get() as u8 as char;
-
- // Convert carrige return to newline.
- if ret == '\r' {
- ret = '\n'
- }
// Read one character.
- let mut ret = self.registers.DR.get() as u8 as char;
-
// Update statistics.
inner.chars_read += 1;
- // Convert carrige return to newline.
- if ret == '\r' {
- ret = '\n'
- }
+ let ret = self.registers.DR.get() as u8 as char;
// Update statistics.
self.chars_read += 1;
@@ -357,14 +352,14 @@
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner
- .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
+ .lock(|inner| inner.read_char(BlockingMode::Blocking).unwrap())
}
- ret
+ // Read one character.
+ inner.registers.DR.get() as u8 as char
+ })
+ }
+
+ fn clear(&self) {
+ self.inner.lock(|inner| {
+ // Read from the RX FIFO until it is indicating empty.
+ while !inner.registers.FR.matches_all(FR::RXFE::SET) {
+ inner.registers.DR.get();
+ }
})
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
- .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
+ .lock(|inner| inner.read_char(BlockingMode::NonBlocking))
.is_some()
{}
}
}
diff -uNr 06_drivers_gpio_uart/src/bsp/raspberrypi/link.ld 07_uart_chainloader/src/bsp/raspberrypi/link.ld
--- 06_drivers_gpio_uart/src/bsp/raspberrypi/link.ld
@ -376,31 +383,6 @@ diff -uNr 06_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs 07_uart_chainloader
unsafe {
range = RangeInclusive::new(__bss_start.get(), __bss_end_inclusive.get());
diff -uNr 06_drivers_gpio_uart/src/console.rs 07_uart_chainloader/src/console.rs
--- 06_drivers_gpio_uart/src/console.rs
+++ 07_uart_chainloader/src/console.rs
@@ -19,6 +19,10 @@
/// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
+
+ /// Block execution until the last character has been physically put on the TX wire
+ /// (draining TX buffers/FIFOs, if any).
+ fn flush(&self);
}
/// Console read functions.
@@ -27,6 +31,9 @@
fn read_char(&self) -> char {
' '
}
+
+ /// Clear RX buffers, if any.
+ fn clear(&self);
}
/// Console statistics.
diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs
--- 06_drivers_gpio_uart/src/main.rs
+++ 07_uart_chainloader/src/main.rs
@ -432,23 +414,13 @@ diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs
mod runtime_init;
mod synchronization;
@@ -143,28 +147,52 @@
/// The main function running after the early init.
@@ -145,29 +149,49 @@
fn kernel_main() -> ! {
+ use bsp::console::console;
use bsp::console::console;
use console::interface::All;
- use driver::interface::DriverManager;
-
- println!("[0] Booting on: {}", bsp::board_name());
- println!("[1] Drivers loaded:");
- for (i, driver) in bsp::driver::driver_manager()
- .all_device_drivers()
- .iter()
- .enumerate()
- {
- println!(" {}. {}", i + 1, driver.compatible());
- println!("[0] Booting on: {}", bsp::board_name());
+ println!(" __ __ _ _ _ _ ");
+ println!("| \\/ (_)_ _ (_) | ___ __ _ __| |");
+ println!("| |\\/| | | ' \\| | |__/ _ \\/ _` / _` |");
@ -458,22 +430,30 @@ diff -uNr 06_drivers_gpio_uart/src/main.rs 07_uart_chainloader/src/main.rs
+ println!();
+ println!("[ML] Requesting binary");
+ console().flush();
+
+ // Clear the RX FIFOs, if any, of spurious received characters before starting with the loader
+ // protocol.
+ console().clear();
+
+ // Notify `Minipush` to send the binary.
+ for _ in 0..3 {
+ console().write_char(3 as char);
}
- println!("[1] Drivers loaded:");
- for (i, driver) in bsp::driver::driver_manager()
- .all_device_drivers()
- .iter()
- .enumerate()
- {
- println!(" {}. {}", i + 1, driver.compatible());
- }
+ // Discard any spurious received characters before starting with the loader protocol.
+ console().clear_rx();
- println!(
- "[2] Chars written: {}",
- bsp::console::console().chars_written()
- );
- println!("[3] Echoing input now");
-
+ // Notify `Minipush` to send the binary.
+ for _ in 0..3 {
+ console().write_char(3 as char);
+ }
- // Discard any spurious received characters before going into echo mode.
- console().clear_rx();
- loop {
- let c = bsp::console::console().read_char();
- bsp::console::console().write_char(c);

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

@ -144,7 +144,7 @@ impl GPIOInner {
// Make an educated guess for a good delay value (Sequence described in the BCM2837
// peripherals PDF).
//
// - According to Wikipedia, the fastest Pi3 clocks around 1.4 GHz.
// - According to Wikipedia, the fastest RPi4 clocks around 1.5 GHz.
// - The Linux 2837 GPIO driver waits 1 µs between the steps.
//
// So lets try to be on the safe side and default to 2000 cycles, which would equal 1 µs

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics.
///
/// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt).
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is:
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field).
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will
/// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're
/// now at 0.01 %.
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
pub fn init(&mut self) {
// Turn it off temporarily.
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
//
// For example, this can happen during runtime on a call to panic!(), because panic!()
// initializes its own UART instance and calls init().
//
// Hence, flush first to ensure all pending characters are transmitted.
self.flush();
// Turn the UART off temporarily.
self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
self.registers.IBRD.write(IBRD::IBRD.val(5));
self.registers.FBRD.write(FBRD::FBRD.val(13));
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,53 @@ impl PL011UartInner {
self.chars_written += 1;
}
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
//
// Now make an educated guess for a good delay value. According to Wikipedia, the fastest
// RPi4 clocks around 1.5 GHz.
//
// So lets try to be on the safe side and default to 24_000 cycles, which would equal 12_000
// ns would the CPU be clocked at 2 GHz.
const CHAR_TIME_SAFE: usize = 24_000;
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
cpu::spin_for_cycles(CHAR_TIME_SAFE);
}
/// Retrieve a character.
fn read_char(&mut self, blocking_mode: BlockingMode) -> Option<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
@ -231,6 +301,8 @@ impl fmt::Write for PL011UartInner {
}
impl PL011Uart {
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
@ -273,37 +345,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) {
// Spin until TX FIFO empty is set.
self.inner.lock(|inner| {
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
self.inner.lock(|inner| inner.flush());
}
}
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner.lock(|inner| {
// Spin while RX FIFO empty is set.
while inner.registers.FR.matches_all(FR::RXFE::SET) {
cpu::nop();
}
// Update statistics.
inner.chars_read += 1;
// Read one character.
inner.registers.DR.get() as u8 as char
})
self.inner
.lock(|inner| inner.read_char(BlockingMode::Blocking).unwrap())
}
fn clear(&self) {
self.inner.lock(|inner| {
// Read from the RX FIFO until it is indicating empty.
while !inner.registers.FR.matches_all(FR::RXFE::SET) {
inner.registers.DR.get();
}
})
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
.lock(|inner| inner.read_char(BlockingMode::NonBlocking))
.is_some()
{}
}
}

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

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

@ -129,12 +129,11 @@ diff -uNr 07_uart_chainloader/src/_arch/aarch64/cpu.rs 08_timestamps/src/_arch/a
} else {
// If not core0, infinitely wait for events.
wait_forever()
@@ -39,15 +39,6 @@
@@ -39,14 +39,6 @@
pub use asm::nop;
-/// Spin for `n` cycles.
-#[cfg(feature = "bsp_rpi3")]
-#[inline(always)]
-pub fn spin_for_cycles(n: usize) {
- for _ in 0..n {
@ -145,7 +144,7 @@ diff -uNr 07_uart_chainloader/src/_arch/aarch64/cpu.rs 08_timestamps/src/_arch/a
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
@@ -55,19 +46,3 @@
@@ -54,19 +46,3 @@
asm::wfe()
}
}
@ -283,7 +282,7 @@ diff -uNr 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 08_times
- // Make an educated guess for a good delay value (Sequence described in the BCM2837
- // peripherals PDF).
- //
- // - According to Wikipedia, the fastest Pi3 clocks around 1.4 GHz.
- // - According to Wikipedia, the fastest RPi4 clocks around 1.5 GHz.
- // - The Linux 2837 GPIO driver waits 1 µs between the steps.
- //
- // So lets try to be on the safe side and default to 2000 cycles, which would equal 1 µs
@ -308,27 +307,72 @@ diff -uNr 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 08_times
diff -uNr 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 08_timestamps/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
--- 07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
+++ 08_timestamps/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
@@ -289,11 +289,18 @@
cpu::nop();
}
@@ -235,16 +235,13 @@
+ // Read one character.
+ let mut ret = inner.registers.DR.get() as u8 as char;
+
+ // Convert carrige return to newline.
+ if ret == '\r' {
+ ret = '\n'
+ }
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
+ use crate::{time, time::interface::TimeManager};
+ use core::time::Duration;
+
// Update statistics.
inner.chars_read += 1;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
- //
- // Now make an educated guess for a good delay value. According to Wikipedia, the fastest
- // RPi4 clocks around 1.5 GHz.
- //
- // So lets try to be on the safe side and default to 24_000 cycles, which would equal 12_000
- // ns would the CPU be clocked at 2 GHz.
- const CHAR_TIME_SAFE: usize = 24_000;
+ const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
@@ -253,11 +250,11 @@
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
- cpu::spin_for_cycles(CHAR_TIME_SAFE);
+ time::time_manager().spin_for(CHAR_TIME_SAFE);
}
- // Read one character.
- inner.registers.DR.get() as u8 as char
+ ret
})
/// Retrieve a character.
- fn read_char(&mut self, blocking_mode: BlockingMode) -> Option<char> {
+ 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
--- 07_uart_chainloader/src/bsp/raspberrypi/link.ld
@ -484,7 +528,7 @@ diff -uNr 07_uart_chainloader/src/main.rs 08_timestamps/src/main.rs
/// Early init code.
///
@@ -147,52 +146,31 @@
@@ -147,51 +146,31 @@
/// The main function running after the early init.
fn kernel_main() -> ! {
@ -504,9 +548,8 @@ diff -uNr 07_uart_chainloader/src/main.rs 08_timestamps/src/main.rs
- println!("[ML] Requesting binary");
- console().flush();
-
- // Clear the RX FIFOs, if any, of spurious received characters before starting with the loader
- // protocol.
- console().clear();
- // Discard any spurious received characters before starting with the loader protocol.
- console().clear_rx();
-
- // Notify `Minipush` to send the binary.
- for _ in 0..3 {

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics.
///
/// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt).
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is:
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field).
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will
/// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're
/// now at 0.01 %.
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
pub fn init(&mut self) {
// Turn it off temporarily.
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
//
// For example, this can happen during runtime on a call to panic!(), because panic!()
// initializes its own UART instance and calls init().
//
// Hence, flush first to ensure all pending characters are transmitted.
self.flush();
// Turn the UART off temporarily.
self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
self.registers.IBRD.write(IBRD::IBRD.val(5));
self.registers.FBRD.write(FBRD::FBRD.val(13));
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1;
}
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.
fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<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,6 +303,8 @@ impl fmt::Write for PL011UartInner {
}
impl PL011Uart {
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) {
// Spin until TX FIFO empty is set.
self.inner.lock(|inner| {
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
self.inner.lock(|inner| inner.flush());
}
}
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner.lock(|inner| {
// Spin while RX FIFO empty is set.
while inner.registers.FR.matches_all(FR::RXFE::SET) {
cpu::nop();
}
// Read one character.
let mut ret = inner.registers.DR.get() as u8 as char;
// Convert carrige return to newline.
if ret == '\r' {
ret = '\n'
}
// Update statistics.
inner.chars_read += 1;
ret
})
self.inner
.lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
}
fn clear(&self) {
self.inner.lock(|inner| {
// Read from the RX FIFO until it is indicating empty.
while !inner.registers.FR.matches_all(FR::RXFE::SET) {
inner.registers.DR.get();
}
})
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
.lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
.is_some()
{}
}
}

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

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics.
///
/// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt).
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is:
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field).
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will
/// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're
/// now at 0.01 %.
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
pub fn init(&mut self) {
// Turn it off temporarily.
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
//
// For example, this can happen during runtime on a call to panic!(), because panic!()
// initializes its own UART instance and calls init().
//
// Hence, flush first to ensure all pending characters are transmitted.
self.flush();
// Turn the UART off temporarily.
self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
self.registers.IBRD.write(IBRD::IBRD.val(5));
self.registers.FBRD.write(FBRD::FBRD.val(13));
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1;
}
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.
fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<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,6 +303,8 @@ impl fmt::Write for PL011UartInner {
}
impl PL011Uart {
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) {
// Spin until TX FIFO empty is set.
self.inner.lock(|inner| {
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
self.inner.lock(|inner| inner.flush());
}
}
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner.lock(|inner| {
// Spin while RX FIFO empty is set.
while inner.registers.FR.matches_all(FR::RXFE::SET) {
cpu::nop();
}
// Read one character.
let mut ret = inner.registers.DR.get() as u8 as char;
// Convert carrige return to newline.
if ret == '\r' {
ret = '\n'
}
// Update statistics.
inner.chars_read += 1;
ret
})
self.inner
.lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
}
fn clear(&self) {
self.inner.lock(|inner| {
// Read from the RX FIFO until it is indicating empty.
while !inner.registers.FR.matches_all(FR::RXFE::SET) {
inner.registers.DR.get();
}
})
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
.lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
.is_some()
{}
}
}

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

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

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics.
///
/// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt).
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is:
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field).
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will
/// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're
/// now at 0.01 %.
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
pub fn init(&mut self) {
// Turn it off temporarily.
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
//
// For example, this can happen during runtime on a call to panic!(), because panic!()
// initializes its own UART instance and calls init().
//
// Hence, flush first to ensure all pending characters are transmitted.
self.flush();
// Turn the UART off temporarily.
self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
self.registers.IBRD.write(IBRD::IBRD.val(5));
self.registers.FBRD.write(FBRD::FBRD.val(13));
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1;
}
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.
fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<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,6 +303,8 @@ impl fmt::Write for PL011UartInner {
}
impl PL011Uart {
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) {
// Spin until TX FIFO empty is set.
self.inner.lock(|inner| {
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
self.inner.lock(|inner| inner.flush());
}
}
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner.lock(|inner| {
// Spin while RX FIFO empty is set.
while inner.registers.FR.matches_all(FR::RXFE::SET) {
cpu::nop();
}
// Read one character.
let mut ret = inner.registers.DR.get() as u8 as char;
// Convert carrige return to newline.
if ret == '\r' {
ret = '\n'
}
// Update statistics.
inner.chars_read += 1;
ret
})
self.inner
.lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
}
fn clear(&self) {
self.inner.lock(|inner| {
// Read from the RX FIFO until it is indicating empty.
while !inner.registers.FR.matches_all(FR::RXFE::SET) {
inner.registers.DR.get();
}
})
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
.lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
.is_some()
{}
}
}

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

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

@ -914,7 +914,7 @@ diff -uNr 10_privilege_level/src/main.rs 11_virtual_mem_part1_identity_mapping/s
for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
@@ -154,6 +168,9 @@
@@ -155,6 +169,9 @@
info!("Booting on: {}", bsp::board_name());
@ -924,7 +924,7 @@ diff -uNr 10_privilege_level/src/main.rs 11_virtual_mem_part1_identity_mapping/s
let (_, privilege_level) = exception::current_privilege_level();
info!("Current privilege level: {}", privilege_level);
@@ -177,6 +194,13 @@
@@ -178,6 +195,13 @@
info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1));
@ -936,8 +936,8 @@ diff -uNr 10_privilege_level/src/main.rs 11_virtual_mem_part1_identity_mapping/s
+ .unwrap();
+
info!("Echoing input now");
loop {
let c = bsp::console::console().read_char();
// Discard any spurious received characters before going into echo mode.
diff -uNr 10_privilege_level/src/memory/mmu.rs 11_virtual_mem_part1_identity_mapping/src/memory/mmu.rs
--- 10_privilege_level/src/memory/mmu.rs

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics.
///
/// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt).
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is:
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field).
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will
/// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're
/// now at 0.01 %.
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
pub fn init(&mut self) {
// Turn it off temporarily.
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
//
// For example, this can happen during runtime on a call to panic!(), because panic!()
// initializes its own UART instance and calls init().
//
// Hence, flush first to ensure all pending characters are transmitted.
self.flush();
// Turn the UART off temporarily.
self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
self.registers.IBRD.write(IBRD::IBRD.val(5));
self.registers.FBRD.write(FBRD::FBRD.val(13));
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1;
}
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.
fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<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,6 +303,8 @@ impl fmt::Write for PL011UartInner {
}
impl PL011Uart {
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) {
// Spin until TX FIFO empty is set.
self.inner.lock(|inner| {
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
self.inner.lock(|inner| inner.flush());
}
}
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner.lock(|inner| {
// Spin while RX FIFO empty is set.
while inner.registers.FR.matches_all(FR::RXFE::SET) {
cpu::nop();
}
// Read one character.
let mut ret = inner.registers.DR.get() as u8 as char;
// Convert carrige return to newline.
if ret == '\r' {
ret = '\n'
}
// Update statistics.
inner.chars_read += 1;
ret
})
self.inner
.lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
}
fn clear(&self) {
self.inner.lock(|inner| {
// Read from the RX FIFO until it is indicating empty.
while !inner.registers.FR.matches_all(FR::RXFE::SET) {
inner.registers.DR.get();
}
})
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
.lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
.is_some()
{}
}
}

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

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

@ -973,7 +973,7 @@ diff -uNr 11_virtual_mem_part1_identity_mapping/src/main.rs 12_exceptions_part1_
if let Err(string) = memory::mmu::mmu().init() {
panic!("MMU: {}", string);
}
@@ -194,13 +197,28 @@
@@ -195,13 +198,28 @@
info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1));
@ -1006,8 +1006,8 @@ diff -uNr 11_virtual_mem_part1_identity_mapping/src/main.rs 12_exceptions_part1_
+ // Will never reach here in this tutorial.
info!("Echoing input now");
loop {
let c = bsp::console::console().read_char();
// Discard any spurious received characters before going into echo mode.
diff -uNr 11_virtual_mem_part1_identity_mapping/src/memory/mmu.rs 12_exceptions_part1_groundwork/src/memory/mmu.rs
--- 11_virtual_mem_part1_identity_mapping/src/memory/mmu.rs

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics.
///
/// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt).
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is:
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field).
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will
/// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're
/// now at 0.01 %.
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
pub fn init(&mut self) {
// Turn it off temporarily.
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
//
// For example, this can happen during runtime on a call to panic!(), because panic!()
// initializes its own UART instance and calls init().
//
// Hence, flush first to ensure all pending characters are transmitted.
self.flush();
// Turn the UART off temporarily.
self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
self.registers.IBRD.write(IBRD::IBRD.val(5));
self.registers.FBRD.write(FBRD::FBRD.val(13));
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1;
}
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.
fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<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,6 +303,8 @@ impl fmt::Write for PL011UartInner {
}
impl PL011Uart {
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) {
// Spin until TX FIFO empty is set.
self.inner.lock(|inner| {
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
self.inner.lock(|inner| inner.flush());
}
}
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner.lock(|inner| {
// Spin while RX FIFO empty is set.
while inner.registers.FR.matches_all(FR::RXFE::SET) {
cpu::nop();
}
// Read one character.
let mut ret = inner.registers.DR.get() as u8 as char;
// Convert carrige return to newline.
if ret == '\r' {
ret = '\n'
}
// Update statistics.
inner.chars_read += 1;
ret
})
self.inner
.lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
}
fn clear(&self) {
self.inner.lock(|inner| {
// Read from the RX FIFO until it is indicating empty.
while !inner.registers.FR.matches_all(FR::RXFE::SET) {
inner.registers.DR.get();
}
})
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
.lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
.is_some()
{}
}
}

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

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

@ -1444,9 +1444,9 @@ diff -uNr 12_exceptions_part1_groundwork/src/main.rs 13_integrated_testing/src/m
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
use memory::mmu::interface::MMU;
@@ -165,9 +49,7 @@
/// The main function running after the early init.
@@ -166,9 +50,7 @@
fn kernel_main() -> ! {
use bsp::console::console;
use console::interface::All;
- use core::time::Duration;
use driver::interface::DriverManager;
@ -1454,7 +1454,7 @@ diff -uNr 12_exceptions_part1_groundwork/src/main.rs 13_integrated_testing/src/m
info!("Booting on: {}", bsp::board_name());
@@ -194,31 +76,6 @@
@@ -195,31 +77,6 @@
info!(" {}. {}", i + 1, driver.compatible());
}
@ -1484,8 +1484,8 @@ diff -uNr 12_exceptions_part1_groundwork/src/main.rs 13_integrated_testing/src/m
-
- // Will never reach here in this tutorial.
info!("Echoing input now");
loop {
let c = bsp::console::console().read_char();
// Discard any spurious received characters before going into echo mode.
diff -uNr 12_exceptions_part1_groundwork/src/memory/mmu.rs 13_integrated_testing/src/memory/mmu.rs
--- 12_exceptions_part1_groundwork/src/memory/mmu.rs

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics.
///
/// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt).
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is:
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field).
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will
/// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're
/// now at 0.01 %.
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
pub fn init(&mut self) {
// Turn it off temporarily.
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
//
// For example, this can happen during runtime on a call to panic!(), because panic!()
// initializes its own UART instance and calls init().
//
// Hence, flush first to ensure all pending characters are transmitted.
self.flush();
// Turn the UART off temporarily.
self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
self.registers.IBRD.write(IBRD::IBRD.val(5));
self.registers.FBRD.write(FBRD::FBRD.val(13));
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1;
}
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.
fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<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,6 +303,8 @@ impl fmt::Write for PL011UartInner {
}
impl PL011Uart {
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) {
// Spin until TX FIFO empty is set.
self.inner.lock(|inner| {
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
self.inner.lock(|inner| inner.flush());
}
}
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner.lock(|inner| {
// Spin while RX FIFO empty is set.
while inner.registers.FR.matches_all(FR::RXFE::SET) {
cpu::nop();
}
// Read one character.
let mut ret = inner.registers.DR.get() as u8 as char;
// Convert carrige return to newline.
if ret == '\r' {
ret = '\n'
}
// Update statistics.
inner.chars_read += 1;
ret
})
self.inner
.lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
}
fn clear(&self) {
self.inner.lock(|inner| {
// Read from the RX FIFO until it is indicating empty.
while !inner.registers.FR.matches_all(FR::RXFE::SET) {
inner.registers.DR.get();
}
})
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
.lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
.is_some()
{}
}
}

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

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

@ -1862,20 +1862,7 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
(0x48 => @END),
}
@@ -136,6 +181,12 @@
/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;
+#[derive(PartialEq)]
+enum BlockingMode {
+ Blocking,
+ NonBlocking,
+}
+
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
@@ -151,7 +202,8 @@
@@ -157,7 +202,8 @@
/// Representation of the UART.
pub struct PL011Uart {
@ -1885,59 +1872,22 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
}
//--------------------------------------------------------------------------------------------------
@@ -192,6 +244,10 @@
self.registers
@@ -214,6 +260,14 @@
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
+ self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth); // RX FIFO fill level at 1/8
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
+ // Set RX FIFO fill level at 1/8.
+ self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth);
+
+ // Enable RX IRQ + RX timeout IRQ.
+ self.registers
+ .IMSC
+ .write(IMSC::RXIM::Enabled + IMSC::RTIM::Enabled); // RX IRQ + RX timeout IRQ
+ .write(IMSC::RXIM::Enabled + IMSC::RTIM::Enabled);
+
// Turn the UART on.
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@@ -209,6 +265,35 @@
self.chars_written += 1;
}
+
+ /// Retrieve a character.
+ fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<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.
+ ///
@@ -308,9 +362,13 @@
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
@ -1953,7 +1903,7 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
}
}
}
@@ -256,6 +347,21 @@
@@ -330,6 +388,21 @@
Ok(())
}
@ -1975,35 +1925,7 @@ diff -uNr 13_integrated_testing/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
}
impl console::interface::Write for PL011Uart {
@@ -283,25 +389,8 @@
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
- self.inner.lock(|inner| {
- // Spin while RX FIFO empty is set.
- while inner.registers.FR.matches_all(FR::RXFE::SET) {
- cpu::nop();
- }
-
- // Read one character.
- let mut ret = inner.registers.DR.get() as u8 as char;
-
- // Convert carrige return to newline.
- if ret == '\r' {
- ret = '\n'
- }
-
- // Update statistics.
- inner.chars_read += 1;
-
- ret
- })
+ self.inner
+ .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
}
fn clear(&self) {
@@ -323,3 +412,24 @@
@@ -376,3 +449,24 @@
self.inner.lock(|inner| inner.chars_read)
}
}
@ -2439,7 +2361,7 @@ diff -uNr 13_integrated_testing/src/main.rs 14_exceptions_part2_peripheral_IRQs/
#[no_mangle]
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;
@@ -42,14 +42,27 @@
@@ -42,15 +42,27 @@
bsp::driver::driver_manager().post_device_driver_init();
// println! is usable from here on.
@ -2462,24 +2384,28 @@ diff -uNr 13_integrated_testing/src/main.rs 14_exceptions_part2_peripheral_IRQs/
/// The main function running after the early init.
fn kernel_main() -> ! {
- use bsp::console::console;
- use console::interface::All;
use driver::interface::DriverManager;
+ use exception::asynchronous::interface::IRQManager;
info!("Booting on: {}", bsp::board_name());
@@ -76,9 +89,9 @@
@@ -77,12 +89,9 @@
info!(" {}. {}", i + 1, driver.compatible());
}
- info!("Echoing input now");
+ info!("Registered IRQ handlers:");
+ bsp::exception::asynchronous::irq_manager().print_handler();
+
info!("Echoing input now");
- // Discard any spurious received characters before going into echo mode.
- console().clear_rx();
- loop {
- let c = bsp::console::console().read_char();
- bsp::console::console().write_char(c);
- }
+ info!("Echoing input now");
+ cpu::wait_forever();
}

@ -226,28 +226,49 @@ impl PL011UartInner {
/// Set up baud rate and characteristics.
///
/// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt).
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is:
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field).
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will
/// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're
/// now at 0.01 %.
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
pub fn init(&mut self) {
// Turn it off temporarily.
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
//
// For example, this can happen during runtime on a call to panic!(), because panic!()
// initializes its own UART instance and calls init().
//
// Hence, flush first to ensure all pending characters are transmitted.
self.flush();
// Turn the UART off temporarily.
self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
self.registers.IBRD.write(IBRD::IBRD.val(5));
self.registers.FBRD.write(FBRD::FBRD.val(13));
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth); // RX FIFO fill level at 1/8
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Set RX FIFO fill level at 1/8.
self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth);
// Enable RX IRQ + RX timeout IRQ.
self.registers
.IMSC
.write(IMSC::RXIM::Enabled + IMSC::RTIM::Enabled); // RX IRQ + RX timeout IRQ
.write(IMSC::RXIM::Enabled + IMSC::RTIM::Enabled);
// Turn the UART on.
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -266,6 +287,26 @@ impl PL011UartInner {
self.chars_written += 1;
}
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.
fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<char> {
// If RX FIFO is empty,
@ -379,11 +420,7 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) {
// Spin until TX FIFO empty is set.
self.inner.lock(|inner| {
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
self.inner.lock(|inner| inner.flush());
}
}
@ -393,13 +430,13 @@ impl console::interface::Read for PL011Uart {
.lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
}
fn clear(&self) {
self.inner.lock(|inner| {
// Read from the RX FIFO until it is indicating empty.
while !inner.registers.FR.matches_all(FR::RXFE::SET) {
inner.registers.DR.get();
}
})
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
.lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
.is_some()
{}
}
}

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

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

@ -231,14 +231,14 @@ impl PL011UartInner {
/// Set up baud rate and characteristics.
///
/// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt).
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is:
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field).
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will
/// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're
/// now at 0.01 %.
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
///
/// # Safety
///
@ -248,19 +248,40 @@ impl PL011UartInner {
self.registers = Registers::new(addr);
}
// Turn it off temporarily.
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
//
// For example, this can happen during runtime on a call to panic!(), because panic!()
// initializes its own UART instance and calls init().
//
// Hence, flush first to ensure all pending characters are transmitted.
self.flush();
// Turn the UART off temporarily.
self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
self.registers.IBRD.write(IBRD::IBRD.val(5));
self.registers.FBRD.write(FBRD::FBRD.val(13));
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth); // RX FIFO fill level at 1/8
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Set RX FIFO fill level at 1/8.
self.registers.IFLS.write(IFLS::RXIFLSEL::OneEigth);
// Enable RX IRQ + RX timeout IRQ.
self.registers
.IMSC
.write(IMSC::RXIM::Enabled + IMSC::RTIM::Enabled); // RX IRQ + RX timeout IRQ
.write(IMSC::RXIM::Enabled + IMSC::RTIM::Enabled);
// Turn the UART on.
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -281,6 +302,26 @@ impl PL011UartInner {
self.chars_written += 1;
}
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.
fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<char> {
// If RX FIFO is empty,
@ -416,11 +457,7 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) {
// Spin until TX FIFO empty is set.
self.inner.lock(|inner| {
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
self.inner.lock(|inner| inner.flush());
}
}
@ -430,13 +467,13 @@ impl console::interface::Read for PL011Uart {
.lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
}
fn clear(&self) {
self.inner.lock(|inner| {
// Read from the RX FIFO until it is indicating empty.
while !inner.registers.FR.matches_all(FR::RXFE::SET) {
inner.registers.DR.get();
}
})
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
.lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
.is_some()
{}
}
}

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

Binary file not shown.

Binary file not shown.

@ -136,6 +136,12 @@ register_structs! {
/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;
#[derive(PartialEq)]
enum BlockingMode {
Blocking,
NonBlocking,
}
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
@ -174,24 +180,41 @@ impl PL011UartInner {
/// Set up baud rate and characteristics.
///
/// This results in 8N1 and 576000 baud (we set the clock to 48 MHz in config.txt).
/// This results in 8N1 and 921_600 baud.
///
/// The calculation for the BRD given a target rate of 576000 and a clock set to 48 MHz is:
/// `(48_000_000 / 16) / 576000 = 5.2083`. `5` goes to the `IBRD` (integer field).
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
/// `(48_000_000 / 16) / 921_600 = 3.2552083`. `3` goes to the `IBRD` (integer field).
///
/// The `FBRD` (fractional field) is only 6 bits so `0.2083 * 64 = 13.3 rounded to 13` will
/// give the best approximation we can get. A 5 % error margin is acceptable for UART and we're
/// now at 0.01 %.
/// The `FBRD` (fractional field) is only 6 bits so `0.2552083 * 64 = 16.3 rounded to 16` will
/// give the best approximation we can get. A 5% error margin is acceptable for UART and we're
/// now at 0.02%.
pub fn init(&mut self) {
// Turn it off temporarily.
// Execution can arrive here while there are still characters queued in the TX FIFO and
// actively being sent out by the UART hardware. If the UART is turned off in this case,
// those queued characters would be lost.
//
// For example, this can happen during runtime on a call to panic!(), because panic!()
// initializes its own UART instance and calls init().
//
// Hence, flush first to ensure all pending characters are transmitted.
self.flush();
// Turn the UART off temporarily.
self.registers.CR.set(0);
// Clear all pending interrupts.
self.registers.ICR.write(ICR::ALL::CLEAR);
self.registers.IBRD.write(IBRD::IBRD.val(5));
self.registers.FBRD.write(FBRD::FBRD.val(13));
// Set the baud rate.
self.registers.IBRD.write(IBRD::IBRD.val(3));
self.registers.FBRD.write(FBRD::FBRD.val(16));
// Set 8N1 + FIFO on.
self.registers
.LCRH
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on
.write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled);
// Turn the UART on.
self.registers
.CR
.write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
@ -209,6 +232,55 @@ impl PL011UartInner {
self.chars_written += 1;
}
/// Block execution until the last buffered character has been physically put on the TX wire.
fn flush(&self) {
use crate::{time, time::interface::TimeManager};
use core::time::Duration;
// The bit time for 921_600 baud is 1 / 921_600 = 1.09 µs. 8N1 has a total of 10 bits per
// symbol (start bit, 8 data bits, stop bit), so one symbol takes round about 10 * 1.09 =
// 10.9 µs, or 10_900 ns. Round it up to 12_000 ns to be on the safe side.
const CHAR_TIME_SAFE: Duration = Duration::from_nanos(12_000);
// Spin until TX FIFO empty is set.
while !self.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
// After the last character has been queued for transmission, wait for the time of one
// character + some extra time for safety.
time::time_manager().spin_for(CHAR_TIME_SAFE);
}
/// Retrieve a character.
fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<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,6 +303,8 @@ impl fmt::Write for PL011UartInner {
}
impl PL011Uart {
/// Create an instance.
///
/// # Safety
///
/// - The user must ensure to provide a correct MMIO start address.
@ -273,44 +347,23 @@ impl console::interface::Write for PL011Uart {
fn flush(&self) {
// Spin until TX FIFO empty is set.
self.inner.lock(|inner| {
while !inner.registers.FR.matches_all(FR::TXFE::SET) {
cpu::nop();
}
});
self.inner.lock(|inner| inner.flush());
}
}
impl console::interface::Read for PL011Uart {
fn read_char(&self) -> char {
self.inner.lock(|inner| {
// Spin while RX FIFO empty is set.
while inner.registers.FR.matches_all(FR::RXFE::SET) {
cpu::nop();
}
// Read one character.
let mut ret = inner.registers.DR.get() as u8 as char;
// Convert carrige return to newline.
if ret == '\r' {
ret = '\n'
}
// Update statistics.
inner.chars_read += 1;
ret
})
self.inner
.lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
}
fn clear(&self) {
self.inner.lock(|inner| {
// Read from the RX FIFO until it is indicating empty.
while !inner.registers.FR.matches_all(FR::RXFE::SET) {
inner.registers.DR.get();
}
})
fn clear_rx(&self) {
// Read from the RX FIFO until it is indicating empty.
while self
.inner
.lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
.is_some()
{}
}
}

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

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

@ -11,6 +11,8 @@ require 'io/console'
require 'colorize'
require 'serialport'
SERIAL_BAUD = 921_600
class ConnectionError < StandardError; end
# The main class
@ -41,7 +43,7 @@ class MiniTerm
def open_serial
wait_for_serial
@target_serial = SerialPort.new(@target_serial_name, 576_000, 8, 1, SerialPort::NONE)
@target_serial = SerialPort.new(@target_serial_name, SERIAL_BAUD, 8, 1, SerialPort::NONE)
# Ensure all output is immediately flushed to the device.
@target_serial.sync = true

Loading…
Cancel
Save