libtock-rs

repository·master·Indexed 41 days ago

https://github.com/tock/libtock-rs

A Rust userland library for the Tock operating system, providing high-level abstractions for interacting with Tock boards. Version 0.1.0 includes build script helpers via libtock_build_scripts, integration with the embedded-graphics crate, and a specialized fork of ufmt for high-performance, low-footprint formatting in embedded environments. It provides APIs for managing screen displays (resolution, rotation, and power) and monitoring button states.

Tokens
38.8K
Snippets
148
Records
194
Agent score
47%

What's inside libtock-rs

  1. Overview of ufmt (Tock fork)

    master
    The ufmt crate in libtock-rs is a specialized fork of the original ufmt library. It is designed as a high-performance, low-footprint alternative to core::fmt for embedded environments. It is optimized for binary size and speed, aiming to be significantly smaller and faster than the standard library formatting tools while remaining panic-free in optimized builds.
  2. Use `SyncAdapter` for synchronous API access

    master

    If you prefer a synchronous programming model over the default asynchronous callback model, use SyncAdapter from the libtock_sync crate.

    SyncAdapter wraps a Syscalls implementation and uses a MethodCallback to capture asynchronous responses. You can then call .wait() to block (via yieldk) until the response is available.

    // Example usage pattern
    let mut sync_adapter = SyncAdapter::new(syscalls);
    // ... trigger async operation ...
    let response = sync_adapter.wait();
  3. Understand Integration Testing in libtock-rs

    master

    Integration tests in libtock-rs are actual Tock process binaries. Unlike unit tests, these are designed to run on either an emulated Tock system or a real Tock hardware system.

    Integration tests are located in the tests/ directory of the libtock crate.

  4. Understand the libtock-rs startup sequence

    master

    The libtock_runtime manages a multi-stage startup process to transition from hardware reset to your application's main function. Understanding this sequence is helpful for debugging low-level initialization issues or memory layout problems.

    1. start (Assembly): Architecture-specific assembly that verifies the program counter, sets up the process break (RAM limits), initializes the stack, copies .data to RAM, zeroes .bss, and calls rust_start.
    2. rust_start (Rust): Performs high-level initialization, such as reporting stack and heap addresses to the kernel, then calls libtock_unsafe_main.
    3. libtock_unsafe_main (Shim): A shim generated by the libtock_runtime::set_main! macro that bridges the runtime to your code. It calls your main and then handles the return value via Termination::complete().
    4. main (User Code): Your application logic begins here.
  5. Understand the `libtock-rs` crate naming convention

    master

    To avoid name collisions on crates.io, most crates in this repository use the libtock prefix (e.g., libtock_gpio).

    • Public/Driver Crates: Use the libtock_ prefix.
    • Internal Crates: Crates intended only for internal use (such as syscall_tests) do not use the prefix.
    • Directory Names: The actual directory names within the repository do not include the libtock_ prefix.
  6. How `DriverShareRef` manages shared state in unit tests

    master

    To avoid circular dependencies and Rust lifetime/drop-check issues between fake::Kernel and fake::SyscallDriver, the design uses a DriverShareRef pattern.

    Instead of drivers holding a direct reference to the fake::Kernel, they receive a DriverShareRef. This handle provides access to ShareData (which contains common data like the upcall queue) without requiring lifetime parameters. This allows the unit test case to own both the kernel and the drivers directly.

    DriverShareRef is provided to drivers via a registration method on the SyscallDriver trait.

    struct DriverShareRef {
        driver_num: u32,
        share_data: Rc<ShareData>,
    }
    
    trait SyscallDriver {
        /* ... */
    
        fn register(&self, share_ref: DriverShareRef);
    }
  7. How to unit test `libtock-rs` code

    master

    To run libtock-rs code on a development machine or in CI, the library uses a platform abstraction layer. Most crates in the repository are designed to be platform-independent to facilitate this.

    To enable testing, you must provide an implementation of the libtock_platform::Syscalls trait. There are two primary ways to do this:

    1. For Unit Tests: Use the libtock_unittest crate. This provides a 'fake kernel' implementation of the Syscalls trait and relies on the Rust std library for the runtime.
    2. For Real Tock Processes: Use the libtock_runtime crate. This provides the actual syscall interface to the Tock kernel and includes the necessary linker scripts and Rust runtime to start a Tock process binary.
  8. Understand the unit testing architecture for libtock-rs

    master

    The libtock_unittest framework (design phase) uses a component-based architecture to simulate a Tock environment on a host system. The architecture involves four primary components:

    1. Unit test: The #[test] function that sets up the environment and drives execution.
    2. Code under test: The application code being tested. It must remain agnostic of libtock_unittest so it can run on both Tock and host systems.
    3. fake::Kernel: A single instance that handles system calls and manages syscall drivers.
    4. fake::SyscallDriver: One or more instances implementing specific system call interfaces.

    Component Interactions

    • Unit test $\rightarrow$ Code under test: Instantiates and calls the code.
    • Unit test $\rightarrow$ fake::Kernel: Configures the kernel and registers drivers.
    • Unit test $\rightarrow$ fake::SyscallDriver: Configures or inspects driver state for verification.
    • Code under test $\rightarrow$ fake::Kernel: Executes syscalls.
    • fake::Kernel $\rightarrow$ Code under test: Executes upcalls (via function pointers passed through the Subscribe syscall).
    • fake::Kernel $\rightarrow$ fake::SyscallDriver: Registers drivers and invokes Command syscalls.
    • fake::SyscallDriver $\rightarrow$ fake::Kernel: Queues upcalls and accesses Allow buffers via shared data.
  9. Understand the role of libtock_platform

    master

    The libtock_platform crate provides core abstractions used by drivers in libtock_core. Its primary purpose is to offer lightweight asynchronous APIs that are more efficient in terms of code size and RAM usage than standard Rust futures.

    Key features include:

    • Asynchronous Abstractions: Provides traits like FreeCallback to handle asynchronous responses without the overhead of full Rust futures.
    • Platform Agnostic Traits: Defines generic traits (e.g., FreeCallback<AsyncResponse>) that allow drivers to be implemented once and used across different environments, including unit tests running on host machines (like Linux) rather than on Tock hardware.
  10. ufmt design goals and constraints

    master

    When using ufmt, be aware of its design priorities, which influence how the generated code behaves:

    1. Optimization: Prioritizes binary size and execution speed over compilation time.
    2. No Dynamic Dispatch: The generated code avoids dynamic dispatch to remain efficient.
    3. Panic-Free: When optimized, the generated code contains no panicking branches.
    4. Minimal Recursion: The implementation avoids recursion wherever possible to save stack space.