|
|
|
@ -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);
|
|
|
|
|