drone-core

repository·master·Indexed 19 days ago

https://github.com/drone-os/drone-core

The central crate for Drone, an Embedded Operating System, providing fundamental building blocks for the OS. It includes procedural macros for hardware abstraction such as register definitions (reg), peripheral mapping (periph), and bitfield manipulation (Bitfield). It also provides tools for memory management via the heap! macro, conditional compilation logic using CfgCond and CfgCondExt, and memory layout configuration through the override_layout! macro.

Tokens
12.8K
Snippets
45
Records
63
Agent score
67%

What's inside drone-core

  1. Install drone-core via Cargo

    master

    To use drone-core in your project, add it to your Cargo.toml dependencies. Note that while the current repository version is v0.14.3, the documentation example uses v0.15.0.

    [dependencies]
    drone-core = { version = "0.15.0" }
  2. How LinkedList and Node work together

    master

    The LinkedList<T> is a singly-linked list where each element is wrapped in a Node<T>.

    • LinkedList<T>: Manages the head pointer using atomic operations to ensure thread safety (lock-free). It handles the high-level logic of pushing, popping, and iterating.
    • Node<T>: A #[repr(C)] structure containing the actual value: T and a raw pointer next to the subsequent node.

    When using high-level methods like push(data), the list automatically allocates a Node on the heap using Box. When using pop(), the list takes ownership of the node, extracts the value, and deallocates the node memory automatically.

  3. Manage critical sections with Interrupts

    master

    A critical section is a block of code that is protected from being interrupted. In drone-core, you can create a critical section using the Interrupts struct. This disables all interrupts for the current CPU upon creation and re-enables them (restoring the previous state) when the instance is dropped. Critical sections can be nested.

    Warning: Priority Inversion Hazard On devices using XIP (eXecute In Place) for flash memory (like the RP2040), a cache miss during a critical section can prevent higher-priority interrupts from executing while the cache loads. To mitigate this, enable the xip cargo feature. This moves Interrupts::paused to a .time_critical link section that is copied to RAM at startup. When using the xip feature, ensure you do not access XIP memory regions inside the critical section.

    use drone_core::platform::Interrupts;
    
    let mut x = 0;
    {
        // Creating an instance of `Interrupts` disables interrupts.
        // Interrupts are re-enabled when `_critical` is dropped at the end of the scope.
        let _critical = Interrupts::pause();
        x += 1;
    }
    // Interrupts are now enabled again
    dbg!(x);
  4. Understand the Fiber input (In) and output (Out) types

    master

    The communication between the asynchronous session and the synchronous fiber is mediated by In and Out types.

    Fiber Input (In<Cmd, ReqRes>)

    A union representing what is sent into the fiber. It can be either:

    • cmd: A command to be executed by the ProcLoop.
    • req_res: The result of a previously made request.

    Fiber Output (Out<Req, CmdRes>)

    An enum representing what the fiber yields back to the session:

    • Req(Req): The fiber is requesting a resource or action, suspending itself until the request is answered.
    • CmdRes(CmdRes): The fiber has completed the current command and is returning the result.
  5. Access protected data via `MutexGuard`

    master

    When you acquire a lock via lock() or try_lock(), you receive a MutexGuard. This guard implements Deref and DerefMut, allowing you to access the underlying data directly. The lock is automatically released when the guard is dropped (RAII).

    Warning: If you do not use the returned guard, the mutex will be immediately unlocked.

  6. Understand the CfgCond structure for conditional compilation

    master

    The CfgCond struct is used to represent and generate conditional compilation attributes (#[cfg(...)]) based on feature-based configurations. It supports representing logical combinations of clauses, specifically handling any conditions and generating the appropriate Rust TokenStream for attributes.

    Key behaviors:

    • It can represent a set of clauses in Conjunctive Normal Form (CNF).
    • It can generate #[cfg(any(...))] or #[cfg(not(any(...)))] attributes via the .attrs() method.
    • It provides a mechanism to add clauses to an existing condition using .add_clause().
  7. How fiber stream rings work for async data flow

    master

    Fiber stream rings allow a fiber running in one thread to yield data to a stream that can be consumed in another thread. This is achieved by attaching a fiber to a ThrToken (like a thread token) and using ring buffer methods to bridge the data flow.

    There are two primary overflow behaviors when the underlying ring buffer is full:

    1. Saturating: New items are skipped if the buffer is full.
    2. Overwriting: New items overwrite existing items in the buffer.

    Additionally, you can choose between:

    • Standard Streams: Yielding T directly via FiberStreamRing<T>.
    • Try Streams: Yielding Result<T, E> via TryFiberStreamRing<T, E>, which allows the fiber to signal errors.
    • Factory Methods: If your fiber is !Send, use the _factory variants which take a closure to instantiate the fiber within the target thread.
  8. Naming conventions for generated peripheral traits and types

    master

    The periph macro automatically generates a large set of traits and types based on the identifiers provided in the macro input. Understanding these naming patterns is essential for interacting with the generated code.

    Register and Variant Naming

    For a block BlockName, a register RegName, and a variant VarName:

    • Register Traits: BlockNameRegName (CamelCase).
    • Register Options: BlockNameRegNameOpt (if Option is used).
    • Register Extensions: BlockNameRegNameExt (if Option is used).
    • Register Values: BlockNameRegNameVal.
    • Register Types (U/S/C):
      • UBlockNameRegName (Unsafe/Unrestricted)
      • SBlockNameRegName (Shared)
      • CBlockNameRegName (Critical/Constrained)
      • (And Opt suffixes if Option is used, e.g., UBlockNameRegNameOpt).

    Field Naming

    For a field FieldName within a register:

    • Field Traits: BlockNameRegNameFieldName (CamelCase).
    • Field Options: BlockNameRegNameFieldNameOpt (if Option is used).
    • Field Extensions: BlockNameRegNameFieldNameExt (if Option is used).
    • Field Types (U/S/C):
      • UBlockNameRegNameFieldName
      • SBlockNameRegNameFieldName
      • CBlockNameRegNameFieldName
      • (And Opt suffixes if Option is used).

    Field Accessors and Structs

    • Field Accessors: The macro generates methods named after the field (snake_case) that return references to the field types (e.g., fn field_name(&self) -> &T::UFieldName).
    • Field Structs: The macro generates marker structs for field collections: UBlockNameRegNameFieldNameFields, SBlockNameRegNameFieldNameFields, and CBlockNameRegNameFieldNameFields.
  9. Check if a stream is enabled

    master

    Streams are not necessarily active by default. To avoid unnecessary processing, check if a stream is explicitly enabled by a debug probe using is_enabled(). It is highly recommended to wrap write operations in this check.

    use drone_core::stream;
    
    let stream_id = 11;
    if stream::Stream::new(stream_id).is_enabled() {
        stream::write_str(stream_id, "hello there!\n");
    }
    use drone_core::stream;
    
    if stream::Stream::new(11).is_enabled() {
        stream::write_str(11, "hello there!\n");
    }