Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixup arm demos #153

Merged
merged 4 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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