I've been toying with the idea of using Rust as an alternative language for writing ARTIQ kernels. Here's a proof-of-concept for a few basic things (on ARTIQ-5):

#![crate_name = "rustkernel"]
#![crate_type = "lib"]
#![no_std]

use core::ptr::write_volatile;

// RTIO channel numbers
const LED0: i32 = 25;
const LED1: i32 = 26;

extern "C" {
    #[no_mangle]
    pub static mut now: i64;
    fn rtio_get_counter() -> i64;
    fn rtio_init();
    fn rtio_output(target: i32, data: i32);
}

fn at_mu(t: i64) {
    unsafe {
        write_volatile(((&mut now as *mut i64) as usize) as *mut i32, (t >> 32) as i32);
        write_volatile(((&mut now as *mut i64) as usize + 4) as *mut i32, t as i32);
    }
}

fn now_mu() -> i64 {
    unsafe { now }
}

fn delay_mu(d: i64) {
    at_mu(now_mu() + d);
}

macro_rules! parallel {
    ($($v:stmt),*) => (
            let origin = now_mu();
            let mut finalt = origin;
            $(at_mu(origin); $v; if now_mu() > finalt { finalt = now_mu()})*
            at_mu(finalt);
        )
}

#[export_name = "__modinit__"]
pub extern fn run() {
    unsafe {
        rtio_init();
        at_mu(rtio_get_counter() + 1250000);
        loop {
            parallel!(
                {
                    rtio_output(LED0 << 8, 1);
                    delay_mu(1_000_000_000);
                    rtio_output(LED0 << 8, 0);
                    delay_mu(1_000_000_000);
                },
                {
                    rtio_output(LED1 << 8, 1);
                    delay_mu(250_000_000);
                    rtio_output(LED1 << 8, 0);
                    delay_mu(250_000_000);
                }
            );
        }
    }
}

Compile and run with:

rustc --target or1k-unknown-none -g -C target-feature=+mul,+div,+ffl1,+cmov,+addc rustkernel.rs -Z force-overflow-checks=off
rm *.o
or1k-linux-ar x librustkernel.rlib
or1k-linux-ld -shared --eh-frame-hdr *.o -o rustkernel.elf
artiq_run rustkernel.elf 

If there is interest, this could be developed further. One of the main areas is obviously interaction with Python and ARTIQ-Python.

Other languages such as C++ may also be supported.

Does one achieve a performance enhancement from being able to write in Rust vs in ARTIQ-Python? It seems to me that if something can be done more easily/cleanly in Rust in a kernel, ideally one would just add it to the runtime so that this routine could then be called from ARTIQ-Python kernels, right? This allows one the flexibility of adding Rust features without having to recompile the runtime or reflash the board, is that the point? From an end-user standpoint, I think programming in ARTIQ-Python would be the preferred implementation for most tasks anyway. The ability to write efficient kernels in Python is a substantial selling point for ARTIQ. The one thing I wonder is whether or not something like this could be used to avoid some of the struggles with compiling kernels? One would avoid the Python type inferencer issues, but at the cost of having to write in a different language (probably less than ideal).

There are no clear performance enhancements. The main advantage is a powerful type and memory management system, which is also suited to complex situations without a heap allocator. This is about writing kernels, and not adding features to the runtime.

My take on this is that python is great for users to get up and running quickly. But, it's not really well suited to what one wants to do on the core device. Rust is a better fit in many ways (e.g. the type system is strict about sizes of integers, rather than python's polymorphic ints). This means that the python kernels have quite a few corner-cases which are confusing for users. Having said that, I'm not sure how much I'd want to teach physics grad students about Rust's borrow checker etc.

My sentiment is that the most profitable thing would be to do something with Python (if we can) to force it to accept some strict types. It seems that the type inferencing system is the main bottleneck with slow compilation, and that if we could put some flags in to force Python to treat certain things as certain fixed types it would be easier. There is obviously a tradeoff with usability for physics grad students as one gets more constrained, but this would be closer to the mark than going full Rust.

    dhslichter There is obviously a tradeoff with usability for physics grad students as one gets more constrained, but this would be closer to the mark than going full Rust.

    Just to clarify, I didn't propose tearing down ARTIQ-Python, but adding the option of writing kernels in Rust (or C++) that would be interoperable with ARTIQ-Python kernels.

    14 days later

    interoperable with ARTIQ-Python kernels.

    How would this work in terms of defining/passing variables? One advantage for us of using Rust kernels might be if we want to use them to define and manipulate larger data structures (e.g. lists of timestamps), with the idea that we could avoid the compilation slowness associated with large Python lists and the type inferencer. Would this work? Could one use Rust kernels to define data structures, which then can be passed to/used by ARTIQ-Python kernels? Would this require more fiddling with the internals of the compiler?

      dhslichter How would this work in terms of defining/passing variables? One advantage for us of using Rust kernels might be if we want to use them to define and manipulate larger data structures (e.g. lists of timestamps), with the idea that we could avoid the compilation slowness associated with large Python lists and the type inferencer. Would this work?

      Yes, but if that's the only reason for Rust then there are simpler options (e.g optimize the existing compiler for that use case).

      Could one use Rust kernels to define data structures, which then can be passed to/used by ARTIQ-Python kernels?

      Yes (with limitations of course).

      Would this require more fiddling with the internals of the compiler?

      Yes.