diff --git a/example-code/qemu-aarch64v8a/criticalup.toml b/example-code/qemu-aarch64v8a/criticalup.toml new file mode 100644 index 00000000..a7bff09a --- /dev/null +++ b/example-code/qemu-aarch64v8a/criticalup.toml @@ -0,0 +1,5 @@ +manifest-version = 1 + +[products.ferrocene] +release = "nightly-2024-04-24" +packages = ["rustc-${rustc-host}", "rust-std-${rustc-host}", "cargo-${rustc-host}", "rustfmt-${rustc-host}", "rust-src", "rust-std-aarch64-unknown-none",] diff --git a/example-code/qemu-aarch64v8a/linker.ld b/example-code/qemu-aarch64v8a/linker.ld index a02dba45..92129aaa 100644 --- a/example-code/qemu-aarch64v8a/linker.ld +++ b/example-code/qemu-aarch64v8a/linker.ld @@ -15,7 +15,7 @@ SECTIONS { /DISCARD/ : { *(.note .note*) } - . = ALIGN(8); - . += 0x10000; /* 64kB of stack memory */ + . = ALIGN(16); + . += 0x100000; /* 1024kB of stack memory */ stack_top = .; } diff --git a/example-code/qemu-aarch64v8a/src/main.rs b/example-code/qemu-aarch64v8a/src/main.rs index 89770da0..3abae862 100644 --- a/example-code/qemu-aarch64v8a/src/main.rs +++ b/example-code/qemu-aarch64v8a/src/main.rs @@ -2,7 +2,7 @@ //! //! Written by Jonathan Pallant at Ferrous Systems //! -//! Copyright (c) Ferrous Systems, 2023 +//! Copyright (c) Ferrous Systems, 2024 //! //! To @@ -11,39 +11,7 @@ use core::fmt::Write; -/// Represents an emulated QEMU UART. -struct Uart { - base: *mut u32, -} - -impl Uart { - /// Create a handle to the first UART - pub fn uart0() -> Uart { - const UART0_ADDR: usize = 0x0000_0000_0900_0000; - Uart { - base: UART0_ADDR as *mut u32, - } - } - - /// Write one byte to the QEMU virtual UART. - /// - /// We don't have to check for FIFO space as the emulated FIFO never runs - /// out of space. - pub fn putchar(&mut self, byte: u8) { - unsafe { - self.base.write_volatile(u32::from(byte)); - } - } -} - -impl core::fmt::Write for Uart { - fn write_str(&mut self, s: &str) -> core::fmt::Result { - for b in s.bytes() { - self.putchar(b); - } - Ok(()) - } -} +mod virt_uart; /// The entry-point to the Rust application. /// @@ -60,14 +28,14 @@ pub extern "C" fn kmain() { /// /// Called by [`kmain`]. fn main() -> Result<(), core::fmt::Error> { - let mut c = Uart::uart0(); - writeln!(c, "Hello, this is Rust!")?; + let mut uart0 = unsafe { virt_uart::Uart::new_uart0() }; + writeln!(uart0, "Hello, this is Rust!")?; for x in 1..=10 { for y in 1..=10 { - let z = x * y; - write!(c, "{z:4}")?; + let z = f64::from(x) * f64::from(y); + write!(uart0, "{z:>8.2} ")?; } - writeln!(c)?; + writeln!(uart0)?; } panic!("I am a panic"); } @@ -79,7 +47,7 @@ fn main() -> Result<(), core::fmt::Error> { #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { const SYS_REPORTEXC: u64 = 0x18; - let mut c = Uart::uart0(); + let mut c = unsafe { virt_uart::Uart::new_uart0() }; let _ = writeln!(c, "PANIC: {:?}", info); loop { // Exit, using semihosting diff --git a/example-code/qemu-aarch64v8a/src/virt_uart.rs b/example-code/qemu-aarch64v8a/src/virt_uart.rs new file mode 100644 index 00000000..e1bae397 --- /dev/null +++ b/example-code/qemu-aarch64v8a/src/virt_uart.rs @@ -0,0 +1,75 @@ +//! A driver the Arm PL011 Uart +//! +//! Written by Jonathan Pallant at Ferrous Systems +//! +//! Copyright (c) Ferrous Systems, 2024 + +/// A driver for a virtual PL011 Uart +/// +/// It skips almost all the important initialisation, but it works on QEMU. +pub struct Uart(); + +impl Uart<0x0900_0000> { + /// Create a new UART object for UART0 + /// + /// # Safety + /// + /// Only construct one object per UART at any given time. + pub unsafe fn new_uart0() -> Self { + let mut u = Uart(); + u.set_control(Self::CONTROL_UARTEN | Self::CONTROL_TXE); + u + } +} + +impl Uart { + const FLAG_TXFF: u32 = 1 << 5; + const CONTROL_UARTEN: u32 = 1 << 0; + const CONTROL_TXE: u32 = 1 << 8; + + const DATA_OFFSET: usize = 0x000 >> 2; + const FLAG_OFFSET: usize = 0x018 >> 2; + const CONTROL_OFFSET: usize = 0x030 >> 2; + + /// Write a byte (blocking if there's no space) + pub fn write(&mut self, byte: u8) { + // Check the TX FIFO Full bit + while (self.get_flags() & Self::FLAG_TXFF) != 0 {} + self.write_data(byte); + } + + /// Write to the data register + fn write_data(&mut self, value: u8) { + unsafe { + let ptr = (ADDR as *mut u32).add(Self::DATA_OFFSET); + ptr.write_volatile(value as u32); + } + } + + /// Read from the Flag Register + fn get_flags(&mut self) -> u32 { + unsafe { + let ptr = (ADDR as *const u32).add(Self::FLAG_OFFSET); + ptr.read_volatile() + } + } + + /// Write to the control register + fn set_control(&mut self, value: u32) { + unsafe { + let ptr = (ADDR as *mut u32).add(Self::CONTROL_OFFSET); + ptr.write_volatile(value); + } + } +} + +impl core::fmt::Write for Uart { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + for b in s.bytes() { + self.write(b); + } + Ok(()) + } +} + +// End of file diff --git a/example-code/qemu-armv8r/.cargo/config.toml b/example-code/qemu-armv8r/.cargo/config.toml index 04048e1e..9f7169d7 100644 --- a/example-code/qemu-armv8r/.cargo/config.toml +++ b/example-code/qemu-armv8r/.cargo/config.toml @@ -2,7 +2,8 @@ rustflags = [ "-Clink-arg=-Tlinker.ld", ] -runner = "/Users/jonathan/Documents/open-source/qemu/build/qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -kernel" +# Note, this requires QEMU 9 or higher +runner = "qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -kernel" [build] target = ["armv8r-none-eabihf"] diff --git a/example-code/qemu-armv8r/criticalup.toml b/example-code/qemu-armv8r/criticalup.toml new file mode 100644 index 00000000..a3b65679 --- /dev/null +++ b/example-code/qemu-armv8r/criticalup.toml @@ -0,0 +1,5 @@ +manifest-version = 1 + +[products.ferrocene] +release = "nightly-2024-04-24" +packages = ["rustc-${rustc-host}", "rust-std-${rustc-host}", "cargo-${rustc-host}", "rustfmt-${rustc-host}", "rust-src", "rust-std-armv8r-none-eabihf",] diff --git a/example-code/qemu-armv8r/linker.ld b/example-code/qemu-armv8r/linker.ld index 2687d5e5..7972f9bd 100644 --- a/example-code/qemu-armv8r/linker.ld +++ b/example-code/qemu-armv8r/linker.ld @@ -16,7 +16,7 @@ SECTIONS { /DISCARD/ : { *(.note .note*) } - . = ALIGN(8); - . += 0x10000; /* 64kB of stack memory */ + . = ALIGN(16); + . += 0x100000; /* 1024kB of stack memory */ stack_top = .; } diff --git a/example-code/qemu-armv8r/qemu.sh b/example-code/qemu-armv8r/qemu.sh index 0213e30a..6347a8a3 100755 --- a/example-code/qemu-armv8r/qemu.sh +++ b/example-code/qemu-armv8r/qemu.sh @@ -2,4 +2,4 @@ TARGET_DIR=target/production BINARY=${TARGET_DIR}/basic-rust -/Users/jonathan/Documents/open-source/qemu/build/qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -kernel ${BINARY} +qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -kernel ${BINARY} diff --git a/example-code/qemu-armv8r/src/cmsdk_uart.rs b/example-code/qemu-armv8r/src/cmsdk_uart.rs new file mode 100644 index 00000000..9cf41aae --- /dev/null +++ b/example-code/qemu-armv8r/src/cmsdk_uart.rs @@ -0,0 +1,86 @@ +//! A driver the Arm CMSDK Uart +//! +//! Written by Jonathan Pallant at Ferrous Systems +//! +//! Copyright (c) Ferrous Systems, 2024 + +/// A driver for CMSDK Uart +pub struct Uart(); + +impl Uart<0xe7c0_0000> { + /// Create a new UART object for UART0 + /// + /// # Safety + /// + /// Only construct one object per UART at any given time. + pub unsafe fn new_uart0() -> Self { + Uart() + } +} + +impl Uart { + const STATUS_TX_FULL: u32 = 1 << 0; + const CONTROL_TX_EN: u32 = 1 << 0; + + const DATA_OFFSET: usize = 0x000 >> 2; + const STATUS_OFFSET: usize = 0x004 >> 2; + const CONTROL_OFFSET: usize = 0x008 >> 2; + const BAUD_OFFSET: usize = 0x010 >> 2; + + /// Turn on TX and RX + pub fn enable(&mut self, baudrate: u32, system_clock: u32) { + let divider = system_clock / baudrate; + self.set_bauddiv(divider); + self.set_control(Self::CONTROL_TX_EN); + } + + /// Write a byte (blocking if there's no space) + pub fn write(&mut self, byte: u8) { + // Check the Buffer Full bit + while (self.get_status() & Self::STATUS_TX_FULL) != 0 {} + self.set_data(byte as u32); + } + + /// Write the data register + fn set_data(&mut self, data: u32) { + unsafe { + let ptr = (ADDR as *mut u32).add(Self::DATA_OFFSET); + ptr.write_volatile(data) + } + } + + /// Read the status register + fn get_status(&self) -> u32 { + unsafe { + let ptr = (ADDR as *mut u32).add(Self::STATUS_OFFSET); + ptr.read_volatile() + } + } + + /// Set the control register + fn set_control(&mut self, data: u32) { + unsafe { + let ptr = (ADDR as *mut u32).add(Self::CONTROL_OFFSET); + ptr.write_volatile(data) + } + } + + /// Set the baud rate divider register + fn set_bauddiv(&mut self, data: u32) { + unsafe { + let ptr = (ADDR as *mut u32).add(Self::BAUD_OFFSET); + ptr.write_volatile(data) + } + } +} + +impl core::fmt::Write for Uart { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + for b in s.bytes() { + self.write(b); + } + Ok(()) + } +} + +// End of file diff --git a/example-code/qemu-armv8r/src/main.rs b/example-code/qemu-armv8r/src/main.rs index da461ed6..4f417dde 100644 --- a/example-code/qemu-armv8r/src/main.rs +++ b/example-code/qemu-armv8r/src/main.rs @@ -1,75 +1,21 @@ -//! An example program for QEMU's Aarch64 Virtual Machine +//! An example program for QEMU's Armv8-R Virtual Machine //! //! Written by Jonathan Pallant at Ferrous Systems //! -//! Copyright (c) Ferrous Systems, 2023 -//! -//! To +//! Copyright (c) Ferrous Systems, 2024 #![no_std] #![no_main] use core::fmt::Write; +mod cmsdk_uart; + /// The clock speed of the peripheral subsystem on an SSE-300 SoC an on MPS3 board. /// /// Probably right for an MPS3- const PERIPHERAL_CLOCK: u32 = 25_000_000; -/// A driver for CMSDK Uart -struct Uart(); - -impl Uart { - const STATUS_TX_FULL: u32 = 1 << 0; - - /// Turn on TX and RX - fn enable(&mut self, baudrate: u32, system_clock: u32) { - let divider = system_clock / baudrate; - self.set_bauddiv(divider); - self.set_control(0b0000_0011); - } - - /// Write a byte (blocking if there's no space) - fn write(&mut self, byte: u8) { - // Check the Buffer Full bit - while (self.get_status() & Self::STATUS_TX_FULL) != 0 {} - self.set_data(byte as u32); - } - - /// Write the data register - fn set_data(&mut self, data: u32) { - let ptr = ADDR as *mut u32; - unsafe { ptr.write_volatile(data) } - } - - /// Read the status register - fn get_status(&self) -> u32 { - let ptr = (ADDR + 4) as *mut u32; - unsafe { ptr.read_volatile() } - } - - /// Set the control register - fn set_control(&mut self, data: u32) { - let ptr = (ADDR + 8) as *mut u32; - unsafe { ptr.write_volatile(data) } - } - - /// Set the baud rate divider register - fn set_bauddiv(&mut self, data: u32) { - let ptr = (ADDR + 16) as *mut u32; - unsafe { ptr.write_volatile(data) } - } -} - -impl core::fmt::Write for Uart { - fn write_str(&mut self, s: &str) -> core::fmt::Result { - for b in s.bytes() { - self.write(b); - } - Ok(()) - } -} - /// The entry-point to the Rust application. /// /// It is called by the start-up code in [`boot.S`](./boot.S) and thus exported @@ -85,13 +31,13 @@ pub extern "C" fn kmain() { /// /// Called by [`kmain`]. fn main() -> Result<(), core::fmt::Error> { - let mut uart0: Uart<0xe7c00000> = Uart(); + let mut uart0 = unsafe { cmsdk_uart::Uart::new_uart0() }; uart0.enable(115200, PERIPHERAL_CLOCK); writeln!(uart0, "Hello, this is Rust!")?; for x in 1..=10 { for y in 1..=10 { let z = f64::from(x) * f64::from(y); - write!(uart0, "{z:4.1} ")?; + write!(uart0, "{z:>8.2} ")?; } writeln!(uart0)?; } @@ -106,7 +52,7 @@ fn main() -> Result<(), core::fmt::Error> { fn panic(info: &core::panic::PanicInfo) -> ! { const SYS_REPORTEXC: u32 = 0x18; // We assume it is already enabled - let mut uart0: Uart<0xe7c00000> = Uart(); + let mut uart0 = unsafe { cmsdk_uart::Uart::new_uart0() }; let _ = writeln!(uart0, "PANIC: {:?}", info); loop { // Exit, using semihosting