Skip to content

Commit

Permalink
Merge pull request #153 from ferrous-systems/fixup-arm-demos
Browse files Browse the repository at this point in the history
Fixup arm demos
  • Loading branch information
miguelraz authored Apr 30, 2024
2 parents 696e6e4 + abeb6cc commit 0c9b713
Show file tree
Hide file tree
Showing 10 changed files with 193 additions and 107 deletions.
5 changes: 5 additions & 0 deletions example-code/qemu-aarch64v8a/criticalup.toml
Original file line number Diff line number Diff line change
@@ -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",]
4 changes: 2 additions & 2 deletions example-code/qemu-aarch64v8a/linker.ld
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ SECTIONS {
/DISCARD/ : {
*(.note .note*)
}
. = ALIGN(8);
. += 0x10000; /* 64kB of stack memory */
. = ALIGN(16);
. += 0x100000; /* 1024kB of stack memory */
stack_top = .;
}
48 changes: 8 additions & 40 deletions example-code/qemu-aarch64v8a/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! Written by Jonathan Pallant at Ferrous Systems
//!
//! Copyright (c) Ferrous Systems, 2023
//! Copyright (c) Ferrous Systems, 2024
//!
//! To
Expand All @@ -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.
///
Expand All @@ -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");
}
Expand All @@ -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
Expand Down
75 changes: 75 additions & 0 deletions example-code/qemu-aarch64v8a/src/virt_uart.rs
Original file line number Diff line number Diff line change
@@ -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<const ADDR: usize>();

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<const ADDR: usize> Uart<ADDR> {
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<const N: usize> core::fmt::Write for Uart<N> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
for b in s.bytes() {
self.write(b);
}
Ok(())
}
}

// End of file
3 changes: 2 additions & 1 deletion example-code/qemu-armv8r/.cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
5 changes: 5 additions & 0 deletions example-code/qemu-armv8r/criticalup.toml
Original file line number Diff line number Diff line change
@@ -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",]
4 changes: 2 additions & 2 deletions example-code/qemu-armv8r/linker.ld
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ SECTIONS {
/DISCARD/ : {
*(.note .note*)
}
. = ALIGN(8);
. += 0x10000; /* 64kB of stack memory */
. = ALIGN(16);
. += 0x100000; /* 1024kB of stack memory */
stack_top = .;
}
2 changes: 1 addition & 1 deletion example-code/qemu-armv8r/qemu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}
86 changes: 86 additions & 0 deletions example-code/qemu-armv8r/src/cmsdk_uart.rs
Original file line number Diff line number Diff line change
@@ -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<const ADDR: usize>();

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<const ADDR: usize> Uart<ADDR> {
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<const N: usize> core::fmt::Write for Uart<N> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
for b in s.bytes() {
self.write(b);
}
Ok(())
}
}

// End of file
68 changes: 7 additions & 61 deletions example-code/qemu-armv8r/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<const ADDR: usize>();

impl<const ADDR: usize> Uart<ADDR> {
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<const N: usize> core::fmt::Write for Uart<N> {
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
Expand All @@ -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)?;
}
Expand All @@ -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
Expand Down

0 comments on commit 0c9b713

Please sign in to comment.