MnemOS introduction

What is MnemOS?

MnemOS is a small, general purpose operating system (as opposed to "real time operating system", or RTOS), designed for constrained environments.

As an operating system, it is aimed at making it easy to write portable applications, without having to directly interact with the hardware.

At the moment, MnemOS is not a multitasking operating system, so only one application (and the kernel) can run at any given time.

Why MnemOS?

I guess there's two parts to this: (for me, James) "Why did I write it", and (as a user/developer) "Why should I use MnemOS"?

Why did I write it?

For "Why did I write it": This is a "spin off" of part of my previous project, Powerbus, which is a networked home automation project. It honestly started getting too complicated with too many "crazy ideas" (it's a network stack! and a scripting language! and an operating system!) for one single hobby project. So I split the "networking" part off to a much smaller, simple project (lovingly named "Dumb Bus" at the moment), and MnemOS, which was more focused on building a single computer system.

This split helped to better focus BOTH parts of the (former) Powerbus system, and may in the future be recombined, when the separate parts have had more time to bake and solidify on their own.

Why should I [or you] use MnemOS?

As to "Why should I [or you] use MnemOS?", I don't have a good answer! There is certanly no commercial or technical reasons you would choose MnemOS over any of its peers in the "hobbyist space" (e.g. Monotron OS, or projects like RC2014), or even choose it over existing commercial or popular open source projects (like FreeRTOS, or even Linux). It's mainly for me to scratch a personal itch, to learn more about implementing software within the realm of an OS, which is relatively "high level" (from the perspective of embedded systems), while also being relatively "low level" (from the perspective of an application developer).

At the moment, it has the benefit of being relatively small (compared to operating system/kernel projects like Linux, or Redox), which makes it easier to change and influence aspects of the OS. I don't think it will ever be anything "serious", but I do plan to use to it to create a number of projects, including a portable text editor, a music player, and maybe even music making/sythesizer components. Some day I'd like to offer hardware kits, to make it easier for more software-minded folks to get started.

For me, it's a blank slate, where I can build things that I intrinsically understand, using tools and techniques that appeal to me and are familiar to me. I'd love to have others come along and contribute to it (I am highly motivated by other people's feedback and excitement!), but I'll keep working on it even if no one else ever shows up. By documenting what I do, I'll gain a better understanding (and an easier route to picking it up if I have to put it down for a while), and that work serves to "keep the lights on" for any kindred spirits interested in building a tiny, simple, PC in Rust.

If that appeals to you, I invite you to try it out. I am more than happy to explain any part of MnemOS. Much like the Rust Programming Language project - I believe that if any part of the OS is not clear, that is a bug (at least in the docs), and should be remedied, regardless of your technical skill level.

Where does the name come from/how do I pronounce it?

"MnemOS" is named after Mnemosyne, the greek goddess of memory, and the mother of the 9 muses. Since one of the primary responsibilities of an OS is to manage memory, I figured it made sense.

In IPA/Greek, it would be mnɛːmos. Roughly transcribed, it sounds like "mneh-moss".

To listen to someone pronounce "Mnemosyne", you can listen to this youtube clip, and pretend he isn't saying the back half of the name.

If you pronounce it wrong, I won't be upset.

Where can I get a MnemOS computer?

You can't buy one "off the shelf" currently. For the currently required parts, you can refer to the Supported (and Required) Hardware section of the Developers Guide chapter.

You can find the source code and hardware design files for components of a MnemOS based computer on GitHub

How do I use a MnemOS based computer?

You can find information on how to use a MnemOS based computer, including uploading and running applications, in the Users Guide section of this book.

How do I write applications for a MnemOS based computer?

The primary development environment is in the Rust programming language.

MnemOS provides libraries that can be included in your project to create an application.

You can find information on how to create and build applications in the Building User Applications section of the Developers Guide chapter.

How do I modify or debug the MnemOS kernel?

You can find required hardware and software for modifying or debugging the MnemOS kernel in the Developers Guide section of this book.


As a user or developer of MnemOS, you are likely to run into two main parts, the kernel and userspace.

The kernel handles hardware-level operations, including memory management, event handling for hardware and driver events, and isolation of userspace from the hardware.

The userspace is where applications run. Applications are provided a standard interface from the kernel, that allows them to perform operations like reading or writing to a serial port, or reading or writing to a block storage device (sort of like a hard drive).

Additionally, there is a common library, which contains software components used by both the kernel and userspace.

MnemOS Common Library

At the moment, the common crate primarily defines the message protocol used by system calls, and helper functions necessary for handling system calls in a convenient, idiomatic Rust way.

Message defintions live in src/syscall/, and helper functions live in src/porcelain/

For more information, refer to the Common Library API documentation.

Kernel Binary


The kernel is still in early and active development, however here are a couple of design choices that have been made:

User Isolation

Currently, the kernel runs with its own stack, using the Cortex-M MSP (Main Stack Pointer). Additionally, it runs in priviledged mode, while userspace runs in unpriviledged mode. The userspace has its own separate stack, using the Cortex-M PSP (Process Stack Pointer).

Support for Cortex-M Memory Protection Units (MPUs) for additional isolaton is planned, but not currently implemented.

As the userspace is in unpriviledged, it is limited to interacting with kernel through the SVCall interrupt, which triggers a system call.

System Calls

In order to interact with the kernel, the userspace application makes system calls, which trigger an interrupt which the kernel responds to.

Before making a system call, the userspace prepares two things:

  • An "input slice", a pointer and length pair, which can together be considered as a Rust &[u8]. The contents of this slice is the requested system call action.
  • An "output slice", a pointer and length pair, which can together be considered as a Rust &mut [u8]. Initially this contains nothing, and the length represents the maximum output contents. The kernel will fill the contents of this slice with the result of the requested system call, and the length of the output slice will be reduced to the used output area.

As Rust does not have a stable ABI, MnemOS instead relies on serialized data. MnemOS uses the postcard crate (built on top of Serde) to define the message format for system calls.

Put together, the process of making a system call is generally:

  1. The userspace prepares the request, and serializes it to the input slice
  2. The userspace prepares a destination buffer, to be used as the output slice
  3. The userspace triggers an SVCall interrupt
  4. The kernel receives the SVCall interrupt
  5. The kernel deserializes the input slice, and performs the requested action
  6. The kernel prepares a response, and serializes it to the output slice
  7. The kernel returns control to userspace
  8. The userspace deserializes the contents of the output slice

More information on the details of the system call protocol can be found in the common chapter of this book.

Program Loading

At the moment, user applications are loaded to RAM, and executed from RAM. Applications declare their stack size, which is prepared by the operating system.

MnemOS Userspace Library

This Rust library (or crate) serves as the primary interface for userspace applications to the services provided by the kernel.

For more information on how to use the userspace library, including a complete step-by-step guide to create an application, see the Building User Applications chapter of the Developers Guide.

The Userspace Library contains a couple of important things:

An entry function declaration (not definition!)

The crate provides a declaration of an entry point function that looks like this:

fn main() {
extern "Rust" {
    fn entry() -> !;

When creating an application for MnemOS, your binary project will need to declare/define an entry point with the same name. A minimal application looks something like this:

fn main() {
// Your application will generally be no_std, MnemOS does not currently provide
// a version of the standard library

// Your application will generally need the no_main attribute (similar to
// embedded rust programs) - as we do not use Rust's built-in main function,
// and instead use `entry() -> !`

/// Even if you use no system calls, you should probably include the
/// userspace library as shown here, to ensure the panic handler (and
/// other necessary components) are linked in.
use userspace as _;

/// The entry point function MUST:
/// * Be declared with the #[no_mangle] attribute
/// * Must never return
fn entry() -> ! {
    // ...

Linker Scripts

The userspace contains two linker scripts:

link.x - the main linker script, which tells the compiler and linker how to properly lay out your application so that it can be loaded by the kernel. You typically should not ever modify this file, and it will be copied automatically into the build directory via the included script.

If you have experience with embedded Rust development, this is similar to how cortex-m-rt works.

You WILL need to configure your application project to use this linkerscript, typically by creating a .cargo/config.toml file in your project.

For an example (that you can copy), see the .cargo/config.toml of the app-loader application.

The second linkerscript is stack.x. You should copy this file into your application project.

By default, the linkerscript will be configured to use 16KiB of space as a stack for your program.

This can be modified by editing the stack.x file (in your project) to change the amount of space to be allocated as stack memory. For example, to set the stack size to 64KiB, you would add this line to your project's stack.x:

_stack_size = 0x10000;

Again, if you are familiar with embedded rust, this is similar to the device.x file you are expected to provide in each project.

Library Code

The other main component of the userspace crate are the types and functions necessary to interact with the kernel.

To view the documentation for the provided interfaces and types, you can view the userspace documentation on, or from the userspace folder, you can run:

cargo doc --open

Which will open the developer API documentation for these functions.

Users Guide

This chapter contains information relevant to users of a MnemOS based computer.

For development information see the Developers Guide Chapter.


If you are getting started as a user, you will probably need to at least initially flash the MnemOS kernel to your device. At the moment, no device ships with the MnemOS kernel pre-loaded.

For now, please refer to the Developers Guide for information and software/hardware required, and necessary steps to do this.

Once you have finished flashing the kernel, you can move on to the rest of this section.

Connecting to Virtual Serial Ports

MnemOS has a concept of Virtual Serial Ports. These are used to provide different services (known as "multiplexing") over a single "hardware" serial port. You can think of these sort of like TCP or UDP ports, but for a serial port.

By convention, Virtual Port 0 acts like stdio on a desktop PC, and typically provides a human-readable terminal interface. Other ports may be human-readable, or a specific binary format, defined by the application.

At the moment, MnemOS only supports talking over the USB port of the nRF52840 Feather.

You generally shouldn't open the serial port with your operating system directly (using tools like screen, minicom, or putty), as the MnemOS system won't "understand" your messages. The format used on the wire is described below.

Using the CrowTTY tool

For convenience, MnemOS provides a tool called crowtty that maps Virtual Serial Ports to TCP ports on the local system. This allows you to connect to individual Virtual Serial Ports separately.

At the moment, the CrowTTY tool only supports the following ports:

  • Virtual Port 0: Mapped to TCP IP Address and TCP Port 10000
  • Virtual Port 1: Mapped to TCP IP Address and TCP Port 10001

You can run the CrowTTY tool with the following command:

cd tools/crowtty
cargo run --release
    Finished release [optimized] target(s) in 0.90s
     Running `target/release/crowtty`

You will need to leave this running in the background to act as the TCP server.

In another window, you can connect to the mapped TCP port. In the following example, stty is also used to disable local echo of characters (which would duplicate the output of the MnemOS hardware), as well as disable line buffering. ncat is used to open a persistent TCP connection to the port, acting similar to a "console" for our MnemOS device.

There may not initially be a prompt on the target device. You may need to hit 'ENTER' once to see output. The following is the output if the app-loader program, which is the default user program loaded by the operating system.

stty -icanon -echo && ncat 10000

Input was empty?

> help
  block <idx>
  upload <idx>
  boot <idx>
  ucomplete <kind> <name>
  help [ <command> ]


For more information on how to use the app-loader program, see the Uploading and Running Programs section.

Wire Format

⚠️ WARNING - unstable!

It is not generally recommended (for now) to directly communicate to the MnemOS device over the serial port (for example, if you are writing your own tools), as the format described above may change at any time in breaking kernel updates.

Instead, it is recommended to have your tool connect to a TCP port via the CrowTTY tool, which will be updated with whatever changes are made between kernel versions.

The protocol is described below for educational reasons.

Messages over the serial port transported using Postcard as a protocol, and COBS as framing.

This means if you want to send the following four data bytes to virtual port 1:

[ 0x00 ][ 0x01 ][ 0x02 ][ 0x03 ]

We will need to do two things:

  1. Append the virtual port to the FRONT of the message, in a little-endian order
  2. COBS encode the message, to provide message framing

After appending the Port ID, the message will look like this:

[ 0x01 ][ 0x00 ][ 0x00 ][ 0x01 ][ 0x02 ][ 0x03 ]
  ^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           |                               |
           |                               `----------------->  Data Bytes
           `------------------------------------------------->  Port (u16, LE)

The message will then be COBS encoded. COBS encoding replaces all of the 0x00 bytes of the message with the number of bytes to the next 0x00 in the message. It places one byte at the front that has the number of bytes to the first (replaced) 0x00 byte. It also places a "real" 0x00 byte at the end of the message, so the receiver knows it has reached the end of a single frame.

The message actually sent over the serial port like this:

   |               |       |                               |    COBS Linked List
   |               v       v                               v
[ 0x02 ][ 0x01 ][ 0x01 ][ 0x04 ][ 0x01 ][ 0x02 ][ 0x03 ][ 0x00 ]
  ^^^^    ^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^    ^^^^
   |               |                               |       |
   |               |                               |       `->  End of COBS frame
   |               |                               |
   |               |                               `--------->  Data Bytes
   |               |
   |               `----------------------------------------->  Port (u16, LE)
   `--------------------------------------------------------->  COBS header

Uploading and Running Programs

The following is a minimal guide to uploading and running programs on MnemOS.

This guide assumes you already have a binary built, or are using one of the 'official' binaries in the firmware/kernel/appbins/ directory.

If you are building your own binary, see the Building User Applications in the Developers Guide chapter.

This guide also assumes you already have the crowtty tool running and connected. If you have not done that yet, see the Connecting to Virtual Serial Ports section.

Enabling Upload Mode

The first step to uploading a program is to use the app-loader program to begin an upload.

Connect to Virtual Port 0, and select a block as the upload destination. If a block that already contains data is selected, the previous contents will be erased and replaced.

stty -icanon -echo && ncat 10000
> info
Block Storage Device Information:
blocks: 15, capacity: 65536

> block 3
03: name: None, kind: Unused, status: Idle, size: 0/65536

> upload 3
Opened block 3.
Listening to port 1 for data...

> ustat
Uploading:    Block 3
Disk Written: 0
Done:         false

Uploading contents

Once the app-loader is waiting for an image, you will need to open a new terminal, and use the dumbloader tool.

The dumbloader tool takes three arguments:

  1. The destination IP address (probably
  2. The destination Port (probably 10001)
  3. The path to the firmware image to upload
cd tools/dumbloader
cargo run --release -- 10001 ../../firmware/kernel/appbins/p1echo.bin
    Finished release [optimized] target(s) in 0.02s
     Running `target/release/dumbloader 10001 ../../firmware/kernel/appbins/p1echo.bin`
Connected to ''.
Loaded file. 1936 bytes.
Sending 0x00000000
Sending 0x00000100
Sending 0x00000200
Sending 0x00000300
Sending 0x00000400
Sending 0x00000500
Sending 0x00000600
Sending 0x00000700

Once the dumbloader tool has completed the upload, you can return to the app-loader console (on virtual port 0).

Here, we check the upload state, and complete the upload. Completing the upload writes the program header, and closes the file on the block device storage.

> ustat
Uploading:    Block 3
Disk Written: 2048
Done:         true

> ucomplete program p1echo.bin
Closed successfully!

Running the program

You can then select your uploaded program to be run.

At the moment, this will cause the device to reset, which will close the crowtty tool, meaning you will need to close your ncat session and restart the crowtty tool before continuing.

> boot 3
(input/output stops...)

If you uploaded the p1echo.bin program, you should be able to open Virtual Port 0 and Virtual Port 1 in separate ncat sessions. Any characters typed into Port 0 will be printed out on Port 1.

It should look roughly like the following screenshot, with the top window being Port 0, which is selected and being typed into, and the bottom window being Port 1, showing the output:

p1echo screenshot


You do not need to re-upload your program on every boot, as the block storage that the programs are uploaded to is on non-volatile external flash storage.

For now, you will always need to use the app-loader tool to run the boot N command to launch programs

Developer Guide

This chapter contains information relevant to developers working on either The MnemOS Kernel, or MnemOS Userspace Applications.

For usage information see the Users Guide Chapter.

Supported (and Required) Hardware

At the moment, two pieces of hardware are required for MnemOS development:

  • A Main CPU
  • An SWD debugger

Optionally, you may want additional "accessory cards". At the moment however, there are no accessory cards (we're still in early days!).

Main CPU

At the moment, the only supported CPU is the Nordic nRF52840, specifically on the Adafruit nRF52840 Feather Express development board.

The Adafruit nRF52840 Feather Express

Support for other main CPUs is possible, but not currently supported (again, early days!).

This board has a number of important features, including:

  • The nRF52840 Microcontroller
  • A Micro USB connector, used for power and as a USB-Serial port
  • A 2MiB QSPI Serial Flash chip, used as a Block Storage device for storing data and programs
  • A WS2812B RGB LED (also known as a "NeoPixel" or "Smartled") for notifications

The board has many other features, but they are not currently used.

This board can be purchased directly from Adafruit, or from a number of resellers.

An SWD Debugger

In order to program, debug, or update the kernel, a Cortex-M SWD adapter is necessary. This SWD adapter must be supported by the probe-run tool.

Common adapters include:

  • SWD/JTAG debuggers from JLink
  • DAPLink adapters
  • Official and unofficial STLink adapters

Chances are, if you have written an Embedded Rust program before, and uploaded it with probe-run, cargo-embed, or probe-rs, your adapter will work with the feather board.

You will also need to connect your debugger to the Main CPU board. The main CPU has two main ways to attach a debugger:

  • Using the 2x5 pin, 1.27mm pitch Cortex-M Debugging connector located on the top of the board
    • This is a common/standard footprint used by JLink adapters, and many development boards, though may not be compatible with lower cost adapters that have 0.1" (or 2.54mm) pitch cables.
    • This may require you to purchase an adapter/special cable, but will not require soldering.
  • Using the test pads located on the bottom of the board
    • Using these test pads requires soldering wires to these test ports
    • You will need to connect the SWDIO and SWCLK test pads, as well as a GND connection to your debugger. It is not necessary to connect the RESET pin.

The test pads can be seen on the picture below. They are on the two gold circles on the left side of the board, and are labeled "SWCLK" and "SWDIO".

The Adafruit nRF52840 Feather Express

Required (and Recommended) Software Tools

Definitely Required Tools

In order to develop the MnemOS Kernel or Userspace applications, you will DEFINITELY need the following tools installed:

  • The Rust Compiler (at least v1.59.0, though this may change in the future)
  • The thumbv7em-none-eabihf target, needed for compiling for the nRF52840
    • If you have rustup, this can be accomplished with rustup target add thumbv7em-none-eabihf
  • The probe-run tool, used for flashing and debugging the kernel
  • A version of the objcopy binutil, used for preparing MnemOS applications. You will need a version that supports Cortex-M4F applications. This can typically be obtained through one of the following ways:
    • The cargo-binutils package
    • The Arm GNU Toolchain, either from Arm's main website, or your system's package manager
    • A "multiarch" build of the GNU toolchain, typically provided by your system's package manager
  • An installation of git, or a tool that is compatible with git.

You'll also need to check out the source repository of the MnemOS project.

git clone

Practically Required Tools

The following tools are generally required, but are a bit more flexible in terms of the specific tool you use.

  • You will need a tool that allows you to use a TCP port as a terminal interface. On my Linux PC:
    • I use stty (to configure the terminal not to echo characters or buffer lines - this is probably installed on a typical linux system)
    • I use the ncat tool to provide the TCP-port to terminal adapter
    • I am unsure what tools will work on OSX/Windows, but I am happy to help you figure it out! Feel free to open an issue on the repo, or reach out to me via Twitter or Matrix.

Useful (But Not Required) Tools

Other tools I find helpful for development include:

  • Other parts of the Arm GNU toolchain, including:
    • [arm-none-eabi]-nm, for viewing compiled code component sizes and locations in memory
    • The Cargo Bloat tool, for similar information
    • [arm-none-eabi]-gdb, in case step-through debugging is necessary
  • A GDB Server, such as JLinkGDBServer or OpenOCD, for use with [arm-none-eabi]-gdb.

Flashing the Kernel

The following steps can be used to build and flash the kernel.

Make sure you've taken a look at the required hardware and required software pages first.

1. Attach the debugger to the CPU

You'll need to attach your SWD debugger of choice to your nRF52840 Feather board.

If you are using the top connector, you should only need to connect the debugging cable.

If you are using the bottom test points, make sure you have Ground (GND), SWDIO, and SWCLK connected.

2. Power on the devices

Connect the debugger and Feather to your PC. The order does not matter.

3. Flash with probe-run

From the top of the repository, move to the kernel folder.

cd firmware/kernel

Then, flash the firmware with the following command:

cargo run --release

After building the kernel, you should see roughly the following output:

cargo run --release
   Compiling mnemos-common v0.1.0 (/home/james/hardware-v6/pellegrino/firmware/common)
   Compiling mnemos v0.1.0 (/home/james/hardware-v6/pellegrino/firmware/kernel)
    Finished release [optimized + debuginfo] target(s) in 3.71s
     Running `probe-run --chip nRF52840_xxAA target/thumbv7em-none-eabihf/release/mnemos`
(HOST) INFO  flashing program (20 pages / 80.00 KiB)
(HOST) INFO  success!
Hello, world!
└─ mnemos::app::idle @ src/

4. You're done!

At this point, you have flashed the kernel.

If you press Control-C, the kernel will be halted. However if you un-plug/replug the power to the CPU, the kernel will boot and run again.

If you'd like to build your own user applications, you can move on the the Building Applications section.

If you'd like to upload or run existing applications, you can move on to the User's Guide section.

Building User Applications

The following guide walks through the process to make a minimal userspace application.

This guide assumes you are (vaguely) familiar with building and running #![no_std] Rust applications.

Create a new project

You will need to create a new project with cargo, and move into the directory.

cargo new --bin demo-app
     Created binary (application) `demo-app` package
cd ./demo-app

Add Required Files

We will need to create a couple of files needed to correctly build a MnemOS application.

The first is the .cargo/config.toml file. You'll need to create the folder and file inside the project folder. For example:

mkdir ./.cargo
touch ./.cargo/config.toml

Inside that file, you should add the following contents:

[target.'cfg(all(target_arch = "arm", target_os = "none"))']
rustflags = [
  # Use the MnemOS linker script
  "-C", "link-arg=-Tlink.x",
  # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
  # See
  "-C", "link-arg=--nmagic",

# MnemOS only supports thumbv7em-none-eabihf (or above) currently.
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)

Save and exit this file.

Next, we will need to add a stack configuration file at the root of your project (e.g. in the demo-app/ folder).

For example:

touch ./stack.x


You MUST have a stack.x file, even if it is empty. Otherwise you will get a linker error.

You'll probably want to place the following contents in the stack.x file:

/* You must have a stack.x file, even if you    */
/* accept the defaults.                         */

/* How large is the stack? Defaults to 16KiB    */
/*                                              */
/* _stack_size = 0x4000;                        */

/* Where should the stack start? Defaults to    */
/* _stack_size bytes after the end of all other */
/* application contents (__eapp), which is four */
/* byte aligned.                                */
/*                                              */
/* _stack_start = __eapp + _stack_size;         */

If you'd like to change the _stack_size or _stack_start variables, you can uncomment or add lines as follows:

/* You must have a stack.x file, even if you    */
/* accept the defaults.                         */

/* How large is the stack? Defaults to 16KiB    */
/*                                              */
/* _stack_size = 0x4000;                        */

/* Set the stack size to 64KiB                  */
_stack_size = 0x10000;

/* Where should the stack start? Defaults to    */
/* _stack_size bytes after the end of all other */
/* application contents (__eapp), which is four */
/* byte aligned.                                */
/*                                              */
/* _stack_start = __eapp + _stack_size;         */

/* Trivially change the _stack_start for demo   */
/* reasons. You probably should never do this.  */
_stack_start = __eapp + _stack_size + 4;

Add the userspace library as a Cargo dependency

You'll need to add the userspace library as a dependency. You will need a version that matches the version of the kernel you are using.

The userspace library is published to as the mnemos-userspace crate.

For more information about the userspace library, refer to the Library Documentation on

In your Cargo.toml:

# Using - Check the version is correct!
mnemos-userspace = "0.1.0"

# OR - using git (don't do both!)
version = "0.1.0"
git = ""
branch = "main"

Update your

The following is a minimal template you can use for your file. Delete the existing contents, and replace it with the following code:

fn main() {
// Your application will generally be no_std, MnemOS does not currently provide
// a version of the standard library

// Your application will generally need the no_main attribute (similar to
// embedded rust programs) - as we do not use Rust's built-in main function,
// and instead use `entry() -> !`

/// Even if you use no system calls, you should probably include the
/// userspace library as shown here, to ensure the panic handler (and
/// other necessary components) are linked in.
/// Note: Although the crate name is `mnemos-userspace`, it is imported
/// as just `userspace`.
use userspace as _;

/// Though in this example, we will use a couple of system calls for
/// demonstration purposes.
use userspace::common::porcelain::{serial, time};

/// The entry point function MUST:
/// * Be declared with the #[no_mangle] attribute
/// * Must never return
fn entry() -> ! {
    loop {
        // Note: You probably should handle errors, but this is a demo.
        serial::write_port(0, b"Hello, world!\r\n").ok();

Build and Create a binary file

You should now be able to compile your application.

cargo build --release
   Compiling demo-app v0.1.0 (/tmp/demo-app)
    Finished release [optimized] target(s) in 8.31s

Once it has compiled, we can also make a binary file that can be uploaded using the app-loader tool.

Here, we take the produced demo-app, in target/thumbv7em-none-eabihf/release, and place the binary file in ./target, so it won't be version controlled.

arm-none-eabi-objcopy \
    -O binary \
    target/thumbv7em-none-eabihf/release/demo-app \

Now you can follow the steps in the Uploading and Running section of the Users Guide to see how to upload and run your new project.