Expand description
The original kernel of R3-OS.
-
Traditional uniprocessor tickless real-time kernel with preemptive scheduling
-
Implements a software-based scheduler supporting a customizable number of task priorities (up to 2¹⁵ levels on a 32-bit target, though the implementation is heavily optimized for a smaller number of priorities) and an unlimited number of tasks.
-
Provides a scalable kernel timing mechanism with a logarithmic time complexity. This implementation is robust against a large interrupt processing delay.
-
The kernel is split into a target-independent portion and a target-specific portion. The target-specific portion (called a port) is provided as a separate crate. An application combines them using the trait system.
- Configuring the Kernel
- Implementation-Defined Behaviors
- Cargo Features
- Modules
- Macros
- Structs
- Constants
- Traits
- Type Definitions
Configuring the Kernel
Kernel Trait Type
The R3 original kernel utilizes Rust’s trait system to allow system designers to combine necessary components of the kernel. Components are represented by traits implemented on an application-specific kernel trait type, often named SystemTraits
by convention. The kernel trait type is then used as a parameter of r3_kernel::System
<Traits>
to form a concrete system type.
Rationale: “Trait” is an allusion to the type trait classes such as
std::iterator_traits
from the C++ standard library. You could also say that it’s named so because you implement various traits on it to configure the kernel, though.
Application code uses the following macros to build a kernel trait type in a modular way:
- A port-provided macro like
r3_xxx_port::use_port!
(named in this way by convention) instantiates port-specfific items. r3_kernel::build!
instantiates the kernel and kernel-private static data based on the kernel configuration supplied in the form of a configuration function.
type System = r3_kernel::System<SystemTraits>;
r3_port_std::use_port!(unsafe struct SystemTraits);
struct Objects { /* ... */ }
const fn configure_app(_: &mut r3_kernel::Cfg<SystemTraits>) -> Objects
{ /* ... */ }
const COTTAGE: Objects = r3_kernel::build!(System, configure_app => Objects);
Trait Mechanics
The macros presented in the previous section generate many trait implementations interconnected under a complex relationship to the end of providing all necessary components. This section provides a detailed account of how this is implemented. An application developer is usually not required to understand this.
The following diagram outlines the trait relationship around a kernel trait type.
- The arrows between traits represent a super-sub relationship. It’s not possible to create a circular dependency among them. Associated types can only be referenced in one direction.
- The
impl
s in the same crate as the implemented traits are blanketimpl
s. - The arrows between
impl
s represent trait bounds onSelf
(for blanketimpl
s) or assumedimpl
s (for the otherimpl
s). It’s not possible to create a circular dependency fully comprised of blanketimpl
s, but otherwise it’s possible to create one.
use_port!
→ System Type
The composition process revolves around a kernel trait type. The first thing to do is to define one. It could be defined by application code, but instead it’s defined by use_port!
purely for convenience. A kernel trait type is named SystemTrait
by convention.
r3_xxx_port::use_port!(unsafe struct SystemTrait);
// ----- The above macro invocation expands to: -----
struct SystemTrait;
/* ⋮ */
use_port!
→ impl Port
The first important role of use_port!
is to implement the trait Port
on the kernel trait type. Port
describes the properties of the target hardware and provides target-dependent low-level functions such as a context switcher. use_port!
can define static
items to store internal state data (this would be inconvenient and messy without a macro).
Port
is actually a group of several supertraits (such as PortThreading
), each of which can be implemented in a separate location.
r3_xxx_port::use_port!(unsafe struct SystemTraits);
// ----- The above macro invocation also produces: -----
/* ⋮ */
unsafe impl r3_kernel::PortThreading for SystemTraits { /* ... */ }
unsafe impl r3_kernel::PortInterrupts for SystemTraits { /* ... */ }
unsafe impl r3_kernel::PortTimer for SystemTraits { /* ... */ }
/* ⋮ */
// `Port` gets implemented automatically when
// all required supertraits are implemented.
The job of use_port!
doesn’t end here, but before we move on, we must first explain what build!
does.
build!
→ impl KernelCfgN
build!
assembles a database of statically defined kernel objects using a supplied configuration function. Using this database, it does things such as determining the optimal data type to represent all allowed task priority values and defining static
items to store kernel-private data structures such as task control blocks. The result is attached to a supplied kernel trait type by implementing KernelCfg1
and KernelCfg2
on it.
static COTTAGE: Objects =
r3_kernel::build!(SystemTraits, configure_app => Objects);
// ----- The above macro invocation produces (simplified for brevity): -----
static COTTAGE: Objects = {
use r3_kernel::TaskCb;
const CFG: /* ... */ = {
let mut raw_cfg = r3_kernel::cfg::CfgBuilder::new();
let mut cfg = r3_core::kernel::cfg::Cfg::new(&mut raw_cfg);
configure_app(&mut cfg);
raw_cfg
};
static TASK_CB_POOL: [TaskCb<SystemTraits>; _] = /* ... */;
// Things needed by both of `Port` and `KernelCfg2` should live in
// `KernelCfg1` because `Port` cannot refer to an associated item defined
// by `KernelCfg2`.
unsafe impl r3_kernel::KernelCfg1 for SystemTraits {
type TaskPriority = /* ... */;
}
// Things dependent on data types defined by `Port` should live in
// `KernelCfg2`.
unsafe impl r3_kernel::KernelCfg2 for SystemTraits {
fn task_cb_pool() -> &'static [TaskCb<SystemTraits>] {
&TASK_CB_POOL
}
/* ... */
}
// Make the generated object IDs available to the application
configure_app(...)
};
impl KernelTraits
The traits introduced so far are enough to instantiate the target-independent portion of the RTOS kernel. To reflect this, KernelTraits
and PortToKernel
are automatically implemented on the kernel trait type by a blanket impl
.
impl<System: Port + KernelCfg1 + KernelCfg2> KernelTraits for System { /* ... */ }
impl<System: KernelTraits> PortToKernel for System { /* ... */ }
use_port!
→ Entry Points
The remaining task of use_port!
is to generate entry points to the kernel. The most important one is for booting the kernel. The other ones are interrupt handlers.
r3_xxx_port::use_port!(unsafe struct SystemTraits);
// ----- The above macro invocation lastly produces: -----
/* ⋮ */
fn main() {
<SystemTraits as r3_kernel::PortToKernel>::boot();
}
Implementation-Defined Behaviors
QueueOrder
: This kernel supportsFifo
andTaskPriority
. Unsupported values are treated asTaskPriority
.MutexProtocol
: This kernel supportsNone
andCeiling(_)
. Unsupported values are treated asNone
.ResultCode::NoAccess
: Not supported. This kernel causes an undefined behavior (including a potential panic) when an invalid ID is given.
Cargo Features
inline_syscall
: Allows (but does not force) inlining for all application-facing methods. Enabling this feature might lower the latency of system calls but there are the following downsides: (1) The decision of inlining is driven by the compiler’s built-in heuristics, which takes many factors into consideration. Therefore, the performance improvement (or deterioration) varies unpredictably depending on the global structure of your application and the compiler version used, making it harder to design the system to meet real-time requirements. (2) Inlining increases the code working set size and can make the code run even slower. This is especially likely to happen on an execute-in-place (XIP) system with low-speed code memory such as an SPI flash.
Kernel Features
Enabling the following features might affect the kernel’s runtime performance and memory usage whether or not they are actually in use.
priority_boost
: Enables Priority Boost (raw::KernelBoostPriority
).system_time
: Enables the tracking of a global system time (raw::KernelTime
).
Modules
- Changelog
- Static configuration mechanism for the kernel
- Utility
Macros
Structs
Hunk
for a task stack.- Global kernel state.
- Wraps a provided trait type
Traits
to instantiate a kernel. This type implements the traits fromr3_core::kernel::raw
, making it usable as a kernel, ifTraits
implements some appropriate traits, which consequently make it implementKernelTraits
. - The static properties of a task.
- Task control block - the state data of a task.
Constants
- The extent of how overdue the firing of
timer_tick
can be without breaking the kernel timing algorithm. - The extent of how overdue a timed event can be made or how far a timed event can be delayed past
Duration::MAX
by a call toraw_adjust_time
.
Traits
- Associates a kernel trait type with kernel-private data. Use
build!
to implement. - Associates “system” types with kernel-private data. Use
build!
to implement. - Represents a complete kernel trait type.
- Represents a particular group of traits that a port should implement.
- Implemented by a port. This trait contains items related to controlling interrupt lines.
- Implemented by a port. This trait contains items related to low-level operations for controlling CPU states and context switching.
- Implemented by a port. This trait contains items related to controlling a system timer.
- Methods intended to be called by a port.
Type Definitions
- The instantiation of
r3_core::kernel::Cfg
used bybuild!
to configure a kernel.CfgBuilder<...>
in this alias Implements~const
raw_cfg::CfgBase
<
System
<Traits>>
and many otherraw_cfg
traits. - Numeric value used to identify various kinds of kernel objects.
- Unsigned integer type representing a tick count used by a port timer driver. The period of each tick is fixed at one microsecond.