/zynq-ultrascale-plus-rs

Rust utilities for working with Zynq UltraScale Plus hardware

Primary LanguageRust

zynq-ultrascale-plus-rs

This repo contains two crates:

  • zynq-ultrascale-plus contains high-ish level, mostly safe code for interfacing with Zynq UltraScale+ devices, including...
    • Interrupt controller
    • UART driver
    • Real time clock
    • SD card controller
    • Async runtime
    • GEM controller with smoltcp integration
    • APU controller with idiomatic hardware thread spawning and joining.
  • zynq-ultrascale-plus-modules contains tock-registers definitions generated automatically from the Zynq UltraScale+ Devices Register Reference.

These crates pair especially well with the aarch64-std crate, which provides standard library components for aarch64 bare-metal targets.

Getting Started

If you're already using Rust with your Zynq UltraScale+ device, you can use this crate like any other library.

If you're using C with your Zynq UltraScale+ device, you should create a new staticlib Rust crate with C exports that you can call from your C code.

If you're building a new project from scratch, you should start with a C "hello world" like described in this bare-metal example for the Kria SOM. Then delete all the C files from the project, create a new staticlib Rust crate with a lib.rs that looks like the following, and link it to your project.

#![no_std]

#[no_mangle]
pub extern "C" fn main() {
    unsafe {
        zynq_ultrascale_plus::uart::Controller::uart1().send_bytes("hello from rust!\n\r");
    }
}

#[panic_handler]
fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
    // todo: dump the info via uart or reboot or something
    loop {}
}

Tips for Working with Bare Metal

Using an Allocator

If you want to use alloc, you can use rust-osdev/linked-list-allocator to add an allocator like this:

#![feature(default_alloc_error_handler)]

#[macro_use]
extern crate alloc;

use linked_list_allocator::LockedHeap;

#[global_allocator]
static ALLOCATOR: LockedHeap = LockedHeap::empty();

extern "C" {
    // these are defined by the project's linker script (usually src/lscript.ld)
    static mut _heap_start: u32;
    static mut _heap_end: u32;
}

// CALL THIS BEFORE DOING ANYTHING ELSE!
pub fn init_heap() {
    unsafe {
        let heap_start_ptr = &mut _heap_start as *mut u32;
        let heap_end_ptr = &mut _heap_end as *mut u32;
        ALLOCATOR.lock().init(heap_start_ptr as *mut u8, heap_end_ptr as usize - heap_start_ptr as usize);
    }
}

References

  • The specs which this crate is based on can be found here.