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.
Components
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/mod.rs
, and helper functions live in src/porcelain/mod.rs
.
For more information, refer to the Common Library API documentation.
Kernel Binary
Theory
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:
- The userspace prepares the request, and serializes it to the input slice
- The userspace prepares a destination buffer, to be used as the output slice
- The userspace triggers an SVCall interrupt
- The kernel receives the SVCall interrupt
- The kernel deserializes the input slice, and performs the requested action
- The kernel prepares a response, and serializes it to the output slice
- The kernel returns control to userspace
- 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:
#![allow(unused)] 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:
#![allow(unused)] fn main() { // Your application will generally be no_std, MnemOS does not currently provide // a version of the standard library #![no_std] // 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() -> !` #![no_main] /// 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 #[no_mangle] 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 build.rs
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 docs.rs, 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.
⚠️ NOTE
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 127.0.0.1 and TCP Port 10000
- Virtual Port 1: Mapped to TCP IP Address 127.0.0.1 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 127.0.0.1 10000
>
Input was empty?
> help
AVAILABLE ITEMS:
info
block <idx>
upload <idx>
boot <idx>
ustat
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:
- Append the virtual port to the FRONT of the message, in a little-endian order
- 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 127.0.0.1 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:
- The destination IP address (probably
127.0.0.1
) - The destination Port (probably
10001
) - The path to the firmware image to upload
cd tools/dumbloader
cargo run --release -- 127.0.0.1 10001 ../../firmware/kernel/appbins/p1echo.bin
Finished release [optimized] target(s) in 0.02s
Running `target/release/dumbloader 127.0.0.1 10001 ../../firmware/kernel/appbins/p1echo.bin`
Connected to '127.0.0.1:10001'.
Loaded file. 1936 bytes.
Sending 0x00000000
Sending 0x00000100
Sending 0x00000200
Sending 0x00000300
Sending 0x00000400
Sending 0x00000500
Sending 0x00000600
Sending 0x00000700
Done.
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:
NOTE
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 theboot 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.
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".
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)
- See the Rust installation instructions for how to do this
- The
thumbv7em-none-eabihf
target, needed for compiling for the nRF52840- If you have
rustup
, this can be accomplished withrustup target add thumbv7em-none-eabihf
- If you have
- The
probe-run
tool, used for flashing and debugging the kernel- See the Probe Run Documentation for installation instructions
- 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 withgit
.
You'll also need to check out the source repository of the MnemOS project.
git clone https://github.com/jamesmunns/pellegrino
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.
- I use
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
orOpenOCD
, 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/main.rs:180
...
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 https://github.com/rust-embedded/cortex-m-quickstart/pull/95
"-C", "link-arg=--nmagic",
]
[build]
# 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
⚠️ IMPORTANT
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 crates.io as the mnemos-userspace
crate.
For more information about the userspace
library, refer to the Library Documentation on docs.rs
In your Cargo.toml
:
# Using crates.io - Check the version is correct!
[dependencies]
mnemos-userspace = "0.1.0"
# OR - using git (don't do both!)
[dependencies.mnemos-userspace]
version = "0.1.0"
git = "https://github.com/jamesmunns/pellegrino"
branch = "main"
Update your main.rs
The following is a minimal template you can use for your main.rs
file. Delete the existing contents, and replace
it with the following code:
#![allow(unused)] fn main() { // Your application will generally be no_std, MnemOS does not currently provide // a version of the standard library #![no_std] // 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() -> !` #![no_main] /// 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 #[no_mangle] fn entry() -> ! { loop { // Note: You probably should handle errors, but this is a demo. serial::write_port(0, b"Hello, world!\r\n").ok(); time::sleep_micros(500_000).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 \
./target/demo-app.bin
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.