Contributing

First make sure you've got the tools installed per Getting Started.

Heads up that there are a lot of moving parts, and it's definitely not a simple process (sorry!). We hope that in future more of this will be generated / automated, but we're waiting for the WITX specification to stabilise before this is likely. If you have any ideas for simplifying the process please do let us know!

Please note these instructions were moved from a prior monorepo version of this project, the concept is the same but the links and components are yet to be updated. You can see the original docs here

Updating the docs

The embedded-wasm/book project contains these docs, please feel free to open a PR!

Proposing an API

So you recon we're missing a useful API? (you're probably right). Before going down the implementation path you may wish to open an issue for discussion.

Once you're ready to implement, there are a few steps to the process. You'll need to be familiar with building rust and C projects, and you'll need to setup a workspace to coordinate these changes between components. If you run into any roadblocks, again, please open an issue or PR!

Setting up your workspace

First you'll need to setup a workspace containing the subprojects, we suggest using bootstrap.sh which will create an embedded-wasm directory and check out the subprojects into this.

curl https://embedded-wasm.github.io/book/assets/bootstrap.sh | bash

Updating the specification

  • Add a witx specification for the protocol to the witx folder (see witx/spi.witx for an example)
  • Update the list of specs in lib/api.rs to tell wiggle where to find the document
  • Add an abstract trait to src/ (see src/spi.rs for an example)
  • Add an abstract C object to lib/ (see inc/wasm_embedded/spi.h for an example)
  • Add a test definition to tests/ for qualification of runtimes / HALs
  • Check with cargo check --all-features

Updating the runtimes

You will first need to implement your API for the each of the underlying rt-wasm3 or rt-wasmtime engines, then in wasm-embedded-rt.

Updating rt-wasmtime

TODO: write this

Updating rt-wasm3

The rt-wasm3 C library is designed to simplify porting and embedding. A simple Object Oriented C / VTable style object is defined in the spec for each API, hiding the runtime implementation from the user and supporting dependency injection and other useful testing tricks.

To add an API:

  • Create new source and header files for your API
  • Add the new source file to CMakeLists.txt to add it to the build
  • Add the new header file to build.rs with appropriate allow-listing to support rust binding generation
    • Ensure you block generation of driver types from the spec package (eg, spi_drv_t) to avoid generating conflicting incompatible symbols
  • Create C function declarations for the new methods and a container object (vtable-esque) to hold these
  • Add m3 calls for each new method, deferring to the container object
  • Add a helper function to bind an instance of this API to the wasme runtime (see WASME_bind_i2c).
  • Add C bindings to the rust runtime, see rt/src/wasm3/ for examples.

Explaining all of this is more difficult than showing so, see lib/src/i2c.c and lib/inc/wasme/i2c.h for an example.

When working with the library you can build with make lib, or use the classic CMake approach from lib/ of:

  • mkdir build && cd build to create and switch to a build directory
  • cmake .. to setup the project
  • make to perform a build

When the runtime is built with --features=wasm3 the ewasm library will also be included. You can use this instead however, the logs exposed when building under cargo leave a lot to be desired.

Updating rt

  • Add a mock implementation to src/mock/ for mock execution, see src/mock/i2c.rs for an example
  • Add a linux implementation to src/linux/ for runtime use, see src/linux/i2c.rs for an example

Updating the HAL (rust)

This HAL exposes the API to rust users, providing an implementation of embedded-hal.

  • Create a new source file in hal_rs/src/ for the new API
  • Create an API module with extern definitions for the WASI interface
  • Create a wrapper type for the API object, using the handle and extern functions, see hal_rs/src/i2c.rs for an example
  • Update the tests list in .github/workflows/ci.yml

Updating the HAL (AssemblyScript)

This HAL exposes the API to AssemblyScript users.

  • Create a new source file in hal_rs/src/ for the new API
  • Create an API module with extern definitions for the WASI interface
  • Create a wrapper type for the API object, using the handle and extern functions, see hal_rs/src/i2c.rs for an example
  • Update the tests list in .github/workflows/ci.yml

Testing your changes

TODO

Hints

  • All APIs use integer handles for each device/peripheral managed by the platform to avoid the need to pass opaque objects
    • On initialisation a positive handle should be returned, on error a negative code
    • These handles are managed by the runtime and should be closed or will be cleaned-up on exit
  • Remember that the WASM runtime has it's own address space
    • Function calls with objects will resolve to an integer address that must be translated before access
    • If an object contains a pointer you will also need to translate this prior to accessing containing data
  • The WASM call ABI is not yet stable / widely supported
    • WITX allows multiple returns, in practice this may resolve to an extra argument in the function call (eg. fn do(a) -> Result<b, c> becomes fn do(a, &mut b) -> c in WASM)
  • A bug with wiggle means witx path resolution breaks when using workspaces, resulting in an error: proc macro panicked and a bunch of error[E0432]: unresolved imports. Patch wasm-embedded-spec using a folder outside the workspace or a git version until this is resolved