Science and technology

Use Rust for embedded improvement

Over the previous a number of years, Rust has gained a passionate following amongst programmers. Tech traits come and go, so it may be troublesome to separate pleasure simply because one thing is new versus pleasure over the deserves of a know-how, however I believe Rust is a very well-designed language. It goals to assist builders construct dependable and environment friendly software program, and it was designed for that function from the bottom up. There are key options you may hear about Rust, and on this article, I exhibit that many of those options are precisely why Rust additionally occurs to be nice for embedded methods. Here are some examples:

  • High efficiency: It’s quick, with excessive reminiscence utilization
  • Reliability: Memory errors may be eradicated throughout compilation
  • Productivity: Great documentation, a pleasant compiler with helpful error messages, and top-notch tooling. There’s an built-in bundle supervisor and construct device, sensible multi-editor help with auto-completion and sort inspections, an auto-formatter, and extra.

Why use Rust for embedded improvement?

Rust is designed to ensure each safety and excessive efficiency. Embedded software program can have issues, largely resulting from reminiscence. Rust is, in a means, a compiler-oriented language, so you’ll be able to ensure that you are utilizing reminiscence safely whereas compiling. Here are a few of the advantages of utilizing Rust to develop on embedded units:

  • Powerful static evaluation
  • Flexible reminiscence
  • Fearless concurrency
  • Interoperability
  • Portability
  • Community-driven

In this text, I take advantage of the open supply RT-Thread operating system to exhibit methods to use Rust for embedded improvement.

How to name Rust in C

When calling Rust code in C code, you need to bundle the Rust supply code as a static library file. When the C code compiles, hyperlink it in.

Creating a static library with Rust

There are two steps on this course of.

1. Use cargo init --lib rust_to_c to construct a lib library in Clion. Add the next code to the lib.rs. The following perform evaluates the sum of two values of kind i32 and returns the outcome:

#![no_std]
use core::panic::PanicInfo;

#[no_mangle]
pub extern "C" fn sum(a: i32, b: i32) -> i32 {
    a + b
}

#[panic_handler]
fn panic(_info:&PanicInfo) -> !{
    loop{}
}

2. Add the next code to your Cargo.toml file to inform Rustc what kind of library to generate:

[lib]
title = "sum"
crate-type = ["staticlib"]
path = "src/lib.rs"

Cross-compilation

You can cross-compile on your goal. Assuming your embedded system is Arm-based, the steps are easy:

$ rustup goal add armv7a-none-eabi

2. Generate the static library file:

$ cargo construct --target=armv7a-none-eabi --release --verbose
Fresh rust_to_c v0.1.0
Finished launch [optimized] goal(s) in 0.01s

Generate Header File

You want header information, too.

1. Install cbindgen. The cbindgen device generates a C or C++11 header file from the Rust library:

$ cargo set up --force cbindgen

2. Create a brand new cbindgen.toml file below your challenge folder.

3. Generate a header file:

$ cbindgen --config cbindgen.toml --crate rust_to_c --output sum.h

Call the Rust library file

Now you may make calls to your Rust libraries.

1. Put the generated sum.h and sum.a information into the rt-thread/bsp/qemu-vexpress-a9/functions listing.

2. Modify the SConscript file and add a static library:

   from constructing import *
   
   cwd     = GetCurrentDir()
   src     = Glob('*.c') + Glob('*.cpp')
   CPPPATH = [cwd]
   
   LIBS = ["libsum.a"]
   LIBPATH = [GetCurrentDir()]
   
   group = DefineGroup('Applications', src, rely = [''], CPPPATH = CPPPATH, LIBS = LIBS, LIBPATH = LIBPATH)
   
   Return('group')

3. Call the sum perform in the primary perform, get the return worth, and printf the worth.

   #embrace <stdint.h>
   #embrace <stdio.h>
   #embrace <stdlib.h>
   #embrace <rtthread.h>
   #embrace "sum.h"
   
   int predominant(void)
   {
       int32_t tmp;
   
       tmp = sum(1, 2);
       printf("call rust sum(1, 2) = %dn", tmp);
   
       return 0;
   }

4. In the RT-Thread Env atmosphere, use scons to compile the challenge and run:

$ scons -j6
scons: Reading SConscript information ...
scons: accomplished studying SConscript information.
scons: Building targets ...
[...]
scons: accomplished constructing targets.

$ qemu.sh
  | /
- RT -     Thread Operating System
 / |     4.0.4 construct Jul 28 2021
2006 - 2021 Copyright by rt-thread crew
lwIP-2.1.2 initialized!
[...]
name rust sum(1, 2) = 3

Add, subtract, multiply, and divide

You can implement some sophisticated math in Rust. In the lib.rs file, use the Rust language to implement add, subtract, multiply, and divide:

#![no_std]
use core::panic::PanicInfo;

#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[no_mangle]
pub extern "C" fn subtract(a: i32, b: i32) -> i32 {
    a - b
}

#[no_mangle]
pub extern "C" fn multiply(a: i32, b: i32) -> i32 {
    a * b
}

#[no_mangle]
pub extern "C" fn divide(a: i32, b: i32) -> i32 {
    a / b
}

#[panic_handler]
fn panic(_info:&PanicInfo) -> !{
    loop{}
}

Build your library information and header information and place them within the software listing. Use scons to compile. If errors seem throughout linking, discover the answer on the official Github page.

Modify the rtconfig.py file, and add the hyperlink parameter --allow-multiple-definition:

       DEVICE = ' -march=armv7-a -marm -msoft-float'
       CFLAGS = DEVICE + ' -Wall'
       AFLAGS = ' -c' + DEVICE + ' -x assembler-with-cpp -D__ASSEMBLY__ -I.'
       LINK_SCRIPT = 'hyperlink.lds'
       LFLAGS = DEVICE + ' -nostartfiles -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,system_vectors,--allow-multiple-definition'+
                         ' -T %s' % LINK_SCRIPT
   
       CPATH = ''
       LPATH = ''

Compile and run QEMU to see your work.

Call C in Rust

Rust may be known as in C code, however what about calling C in your Rust code? The following is an instance of calling the rt_kprintf C perform in Rust code.

First, modify the lib.rs file:

    // The imported rt-thread capabilities checklist
    extern "C" {
        pub fn rt_kprintf(format: *const u8, ...);
    }
   
    #[no_mangle]
    pub extern "C" fn add(a: i32, b: i32) -> i32 {
        unsafe {
            rt_kprintf(b"this is from rustn" as *const u8);
        }
        a + b
    }

Next, generate the library file:

$ cargo construct --target=armv7a-none-eabi --release --verbose
Compiling rust_to_c v0.1.0
Running `rustc --crate-name sum --edition=2018 src/lib.rs --error-format=json --json=diagnostic-rendered-ansi --crate-type staticlib --emit=dep-info,hyperlink -C opt-level=3 -C embed-bitcode=no -C metadata=a
Finished launch [optimized] goal(s) in 0.11s

And now, to run the code, copy the library information generated by Rust into the applying listing and rebuild:

$ scons -j6 scons: Reading SConscript information ... scons: accomplished studying SConscript information. [...]
scons: Building targets ... scons: accomplished constructing targets.

Run QEMU once more to see the ends in your embedded picture.

You can have all of it

Using Rust on your embedded improvement provides you all of the options of Rust with out the necessity to sacrifice flexibility or stability. Try Rust in your embedded system at the moment. For extra details about the method of embedded Rust (and about RT-Thread itself), try the RT-Thread challenge’s YouTube channel. And keep in mind, embedded may be open, too.

Most Popular

To Top