Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
strategy:
fail-fast: false
matrix:
preset: [native-gcc, native-clang, native-release, native-tiny, native-periodic, native-asan, native-tsan, native-coverage]
preset: [native-gcc, native-clang, native-release, native-tiny, native-periodic, native-timeslice, native-timeslice-tickless, native-asan, native-tsan, native-coverage]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install tools
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ User-visible changes are recorded here. There are no released versions or releas

### Added

- Round-robin time slicing (`CONFIG_EMB_TIME_SLICING`, off by default): a thread at or below `CONFIG_EMB_TIMESLICE_PRIORITY` that runs for `CONFIG_EMB_TIMESLICE_TICKS` without blocking moves behind its READY equal-priority peers; without a peer its quantum is renewed (KRN-SCH-007, 026, 037; A63). The quantum is a deadline in the tickless computation and a comparison in the periodic tick. The reference model implements it, the new `native-timeslice` preset checks it through the differential bridge, and `native-timeslice-tickless` runs the conformance cases with the tickless clock. List scheduler only.

- The M2 contract decisions are accepted: amendments A29 to A61 and ADR-045 to ADR-048 and ADR-050 (project-owner review, 2026-10-31).

- The M2 exit review (`docs/engineering/m2-exit-review.md`): every checklist item with its pull requests and evidence, the reconciliation of APIs, configuration, model and requirements, the defects found, and the remaining M3a hardware work.
Expand Down
40 changes: 40 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@
"EMB_CONF": "configs/native-periodic.conf"
}
},
{
"name": "native-timeslice",
"displayName": "native, GCC, base profile, checked, round-robin time slicing",
"inherits": "native-gcc",
"binaryDir": "${sourceDir}/build/native-timeslice",
"cacheVariables": {
"EMB_CONF": "configs/native-timeslice.conf"
}
},
{
"name": "native-timeslice-tickless",
"displayName": "native, GCC, base profile, checked, time slicing with the tickless clock",
"inherits": "native-gcc",
"binaryDir": "${sourceDir}/build/native-timeslice-tickless",
"cacheVariables": {
"EMB_CONF": "configs/native-timeslice-tickless.conf"
}
},
{
"name": "native-release",
"displayName": "native, GCC, base profile, release build kind",
Expand Down Expand Up @@ -270,6 +288,14 @@
"name": "native-periodic",
"configurePreset": "native-periodic"
},
{
"name": "native-timeslice",
"configurePreset": "native-timeslice"
},
{
"name": "native-timeslice-tickless",
"configurePreset": "native-timeslice-tickless"
},
{
"name": "native-gcc",
"configurePreset": "native-gcc"
Expand Down Expand Up @@ -375,6 +401,20 @@
"outputOnFailure": true
}
},
{
"name": "native-timeslice",
"configurePreset": "native-timeslice",
"output": {
"outputOnFailure": true
}
},
{
"name": "native-timeslice-tickless",
"configurePreset": "native-timeslice-tickless",
"output": {
"outputOnFailure": true
}
},
{
"name": "native-gcc",
"configurePreset": "native-gcc",
Expand Down
8 changes: 8 additions & 0 deletions configs/native-timeslice-tickless.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2026 Junior Deogracias
# Time slicing with the tickless clock: the quantum is one more programmed deadline
# (SPEC-003 §8). Conformance only; the differential bridge needs the periodic clock
# (configs/native-timeslice.conf).
CONFIG_EMB_TIME_SLICING=y
CONFIG_EMB_TIMESLICE_TICKS=2
CONFIG_EMB_TIMESLICE_PRIORITY=20
10 changes: 10 additions & 0 deletions configs/native-timeslice.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2026 Junior Deogracias
# Round-robin time slicing on the deterministic native test port (KRN-SCH-007, 026, 037):
# a short quantum so a few ticks rotate, and a threshold so levels above 20 are not sliced.
CONFIG_EMB_TIME_SLICING=y
CONFIG_EMB_TIMESLICE_TICKS=2
CONFIG_EMB_TIMESLICE_PRIORITY=20
# Periodic clock: the quantum then counts delivered ticks exactly, as the model does. In
# tickless builds it starts from the free-running counter, which the model cannot see.
# CONFIG_EMB_TICKLESS is not set
2 changes: 1 addition & 1 deletion docs/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This page is the authoritative inventory of implementation and available evidenc

| Area | Current implementation | Evidence and limits |
|---|---|---|
| Execution and scheduling | UP fixed priority, base bitmap/FIFO scheduler, tiny thread table, scheduler lock, thread lifecycle; idle context unreachable through handles, and slim (control-block prefix only) on tiny without an idle thread (ADR-050) | Common conformance cases; tiny has unique base priorities and restricted scheduling semantics |
| Execution and scheduling | UP fixed priority, base bitmap/FIFO scheduler, tiny thread table, scheduler lock, thread lifecycle; idle context unreachable through handles, and slim (control-block prefix only) on tiny without an idle thread (ADR-050); optional round-robin time slicing below a threshold level (A63, off by default) | Common conformance cases; tiny has unique base priorities and restricted scheduling semantics; slicing is list-scheduler only, bridge-checked with the periodic clock and conformance-tested in both clock modes on native, not measured on hardware |
| Waiting and time | Shared wait protocol, sleep, deadlines, cancellation, suspend overlay, ordered timeouts | Native virtual time exercises periodic and tickless modes; embedded ports currently reject tickless |
| Synchronization | Semaphores, mutexes with inheritance, ceiling, recursion and owner-death policy; event flags with ANY/ALL/CLEAR waits and binding (M2-05); condition variables with the mandatory relock (M2-06) | Conformance and native differential bridge; inheritance and deadlock detection have a configured depth limit |
| Communication | Copy-by-value message queues: direct hand-off to a waiting receiver, slot hand-off from a waiting sender, send to front, peek, `EMB_NO_WAIT` calls from ISRs and a receive-side binding (M2-07) | Conformance and native differential bridge; no pipes, buffer pools or zero-copy ownership queues; the masked copy time is bounded by configuration, not measured on hardware |
Expand Down
14 changes: 7 additions & 7 deletions docs/requirements/KRN-SCH.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ KRN-SCH-036 to 043 originate in 03 §2 and are restated verbatim. KRN-SCH-001 to
### KRN-SCH-007 Optional round robin
**Statement.** Round-robin time slicing among threads of equal effective priority shall be optional, selected by `CONFIG_EMB_TIME_SLICING`.
**Rationale.** 03 §2.1 ("optional round-robin"); KRN-SCH-037 restricts where it applies.
**Status.** Accepted 2026-10-31 (restated); not implemented.
**Verification.** Test: planned round-robin rotation and quantum cases.
**Status.** Accepted 2026-10-31 (restated); implemented (A63), list scheduler only.
**Verification.** Test: `sched_quantum_expiry_rotates_behind_all_peers`, `sched_two_spinners_share_their_level` on `native-timeslice` and `native-timeslice-tickless`; model: the TIMESLICE scenarios, explored and checked by the differential bridge on `native-timeslice`.
**Trace.** 03 §2.1; SPEC-003 §8; KRN-TIM-029; v0.1 §4

### KRN-SCH-008 Yield never to lower priority
Expand Down Expand Up @@ -102,8 +102,8 @@ KRN-SCH-036 to 043 originate in 03 §2 and are restated verbatim. KRN-SCH-001 to
### KRN-SCH-026 Quantum expiry rotates only among peers
**Statement.** When a running thread's quantum expires, it shall move behind its equal-priority peers only if one is READY; otherwise its quantum shall be renewed.
**Rationale.** SPEC-003 §8 (v0.1 §5.8).
**Status.** Accepted 2026-10-31 (restated); not implemented.
**Verification.** Test: planned quantum expiry cases with and without a peer.
**Status.** Accepted 2026-10-31 (restated); implemented (A63).
**Verification.** Test: `sched_quantum_expiry_rotates_behind_all_peers`, `sched_quantum_expiry_without_a_peer_renews`; model: `timeslice_rotates_equal_priority`, `timeslice_no_peer_renews_the_quantum`.
**Trace.** SPEC-003 §8; KRN-TIM-029; v0.1 §5.8

### KRN-SCH-036 Configurable priority count
Expand All @@ -116,8 +116,8 @@ KRN-SCH-036 to 043 originate in 03 §2 and are restated verbatim. KRN-SCH-001 to
### KRN-SCH-037 Time slicing below a threshold
**Statement.** Time slicing shall apply only to threads at or below a configured threshold priority level (`CONFIG_EMB_TIMESLICE_PRIORITY`), with one configurable quantum, so that round robin can be enabled for background levels only without a per-level table.
**Rationale.** Revised per R-003 §3.1; answers 08 Q8.
**Status.** Accepted (03 §2.1); not implemented.
**Verification.** Test: planned quantum cases above and below the threshold.
**Status.** Accepted (03 §2.1); implemented with `CONFIG_EMB_TIMESLICE_PRIORITY` (A63).
**Verification.** Test: `sched_levels_above_the_threshold_are_not_sliced` and the cases at level 3 below the threshold; model: `timeslice_threshold_exempts_higher_levels`.
**Trace.** 03 §2.1; R-003 §3.1

### KRN-SCH-038 Class interface for the ready structure
Expand Down Expand Up @@ -145,7 +145,7 @@ KRN-SCH-036 to 043 originate in 03 §2 and are restated verbatim. KRN-SCH-001 to
**Statement.** A thread preempted by a higher-priority thread shall be placed ahead of its equal-priority peers when it becomes eligible again, because its quantum is unspent; a thread that yields or exhausts its quantum shall be placed behind them.
**Rationale.** 03 §2.1.
**Status.** Accepted (03 §2.1); implemented.
**Verification.** Test: `sched_preempted_thread_goes_ahead_of_its_peers`; model.
**Verification.** Test: `sched_preempted_thread_goes_ahead_of_its_peers`, `sched_quantum_expiry_rotates_behind_all_peers`; model: `timeslice_preempted_thread_keeps_its_place`.
**Trace.** 03 §2.1

### KRN-SCH-042 Tiny thread table
Expand Down
4 changes: 2 additions & 2 deletions docs/requirements/KRN-TIM.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ KRN-TIM-001 to 007 originate in the v0.1 specification (§7.5); KRN-TIM-008 to 0
### KRN-TIM-029 Time slicing quantum as a deadline
**Statement.** When time slicing is enabled for the running thread's priority level, the kernel shall track its quantum expiry as a per-CPU deadline included in the next-deadline computation, and at expiry shall move the thread to the tail of its class only if another READY thread exists at the same effective priority.
**Rationale.** Round-robin without a timer object per thread, and v0.1 §5.8's rule that an uncontested quantum does not rotate.
**Status.** Accepted 2026-10-07
**Verification.** Test: round-robin ordering tests in both clock modes; uncontested quantum does not cause a switch.
**Status.** Accepted 2026-10-07; implemented (A63): the quantum is a per-CPU deadline in the tickless next-deadline computation and a comparison in the periodic tick.
**Verification.** Test: the time-slicing cases of `tests/conformance/test_sched.c` on `native-timeslice` (periodic) and `native-timeslice-tickless`; uncontested expiry: `sched_quantum_expiry_without_a_peer_renews`.
**Trace.** SPEC-003 §8; KRN-SCH-007, KRN-SCH-026, KRN-SCH-037

### KRN-TIM-030 Cycle counter API
Expand Down
1 change: 1 addition & 0 deletions docs/specs/AMENDMENTS-M2.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ Corrections found while preparing the M2 primitives, where an accepted specifica
| A60 | SPEC-003 §14, §15 | The reference model covers timers (ISR-context callbacks running model operations, work-queue callbacks through the embedded work item); the differential bridge follows timer starts, stops, expiries and handler runs as events. An exploration horizon stops time from advancing while a thread could run once a periodic timer has run long enough, as the runner does. The tiny profile has no timers in M2 (maintainer decision, 2026-10-27). | `tools/model/embmodel/kernel.py` |
| A61 | SPEC-006 §4.1, §4.2, §4.3, KRN-WQ-007 | `emb_dwork_t` is a public `emb_work_t` (the handler's argument) followed by opaque storage for an internal ISR_CONTEXT timer. The queue is named at schedule time and holds a timer reference while the delay runs, so it cannot be destroyed. `schedule` returns `EMB_EEXIST` while the delay runs or the item is queued, `EMB_EBUSY` while its handler runs on another queue; `reschedule` replaces a running delay and returns `EMB_EEXIST` for a queued item. Cancel reports `EMB_WORK_PENDING` for a stopped delay or a removed item. The trace record is `EMB_TRACE_DWORK_SCHEDULE`; the delay's own start and expiry are the timer records. | `kernel/work/dwork.c`, `include/emb/work.h` |
| A62 | KRN-SCH-036, 03 §2.1 | The priority count's lower bound is 2, as `CONFIG_EMB_PRIORITY_COUNT` has always allowed, not 4: idle plus one application level is a valid tiny configuration. Accepted by the project owner 2026-10-31. | `kernel/Kconfig` |
| A63 | SPEC-003 §8, KRN-SCH-007, KRN-SCH-026, KRN-SCH-037, KRN-SCH-041, KRN-TIM-029 | Time slicing is implemented with one quantum (`CONFIG_EMB_TIMESLICE_TICKS`) and one threshold (`CONFIG_EMB_TIMESLICE_PRIORITY`, 255 for every level). It requires the list scheduler: `CONFIG_EMB_TIME_SLICING` depends on `!CONFIG_EMB_SCHED_TABLE`, since a table level holds one thread. The quantum restarts at every dispatch, also for a preempted thread resuming ahead of its peers, and a yield without a peer keeps it running. Expiry renews the quantum and, with a READY peer, sets a per-CPU rotation flag that makes the next decision place the thread behind its peers. A thread raised above the threshold stops being sliced at its next expiry; one lowered below it starts at its next dispatch. The reference model implements the same rules; the differential bridge checks them with the periodic clock only (`native-timeslice`), because a tickless quantum starts from the free-running counter, which can run ahead of the ticks the model counts. Implemented 2026-10-31 at the project owner's request; the details await review. | `kernel/time/time.c`, `kernel/sched/sched.c`, `kernel/Kconfig` |
4 changes: 3 additions & 1 deletion docs/specs/SPEC-003-time-timeouts-and-timers.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,9 @@ Overruns are therefore counted in two cases: whole periods missed because the de

## 8. Time slicing hook

When `CONFIG_EMB_TIME_SLICING=y` and the running thread's priority level has a nonzero quantum (KRN-SCH-037), the per-CPU state holds `quantum_deadline`, set at dispatch to `now + quantum`. It is not a timer object: it is one more input to the next-deadline computation (tickless) or one more comparison in the tick ISR (periodic). At expiry, if another READY thread exists at the same effective priority, the running thread is moved to the tail of its class and `reschedule_pending` is set (v0.1 §5.8); otherwise the quantum is simply renewed. Temporal-protection budgets (03 §2.3) will plug into the same place when their specification lands.
When `CONFIG_EMB_TIME_SLICING=y` and the running thread's effective priority is at or below `CONFIG_EMB_TIMESLICE_PRIORITY` (KRN-SCH-037), the per-CPU state holds `quantum_deadline`, set at dispatch to `now + CONFIG_EMB_TIMESLICE_TICKS`. It is not a timer object: it is one more input to the next-deadline computation (tickless) or one more comparison in the tick ISR (periodic). At expiry, after the pass that expires timeouts and timers, the quantum is renewed; if another READY thread exists at the same effective priority, the scheduler also marks the running thread for rotation and sets `reschedule_pending`, and the decision at the outermost interrupt exit moves it to the tail of its class (v0.1 §5.8). With the scheduler locked, the rotation waits for the unlock.

Details (A63): a thread gets a fresh quantum each time it is dispatched, including after a preemption, where it resumes ahead of its peers (KRN-SCH-041); a yield that finds no peer does not restart it. A thread raised above the threshold while running stops being sliced at its next expiry; one lowered below it is sliced from its next dispatch. In tickless mode `now` is the free-running counter, which may be ahead of the last tick the interrupt delivered. The table scheduler has one thread per level, so slicing requires the list scheduler. Temporal-protection budgets (03 §2.3) will plug into the same place when their specification lands.

## 9. Cycle counter

Expand Down
10 changes: 10 additions & 0 deletions docs/user/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ The configuration pipeline resolves Kconfig defaults seeded by a port manifest,
| `native-clang` | Clang compiler coverage; requires Clang |
| `native-asan` / `native-tsan` | Sanitizer configurations; require supported compiler runtimes |
| `native-coverage` | GCC `--coverage` build; after CTest, `tools/coverage/report.py` prints kernel statement and branch coverage |
| `native-timeslice` | GCC, checked base with round-robin time slicing (quantum 2 ticks, levels up to 20) and a periodic clock, so the differential bridge checks rotation |
| `native-timeslice-tickless` | The same slicing with the tickless clock; conformance only |

Use the same preset name for configure, build and CTest. A tiny result does not verify base-only features; a release result does not exercise checked-build faults. Embedded presets additionally require their cross-compiler, libc and emulator; do not interpret a build with no registered tests as conformance.

Expand All @@ -33,6 +35,14 @@ The later [timer-delivery validation](../engineering/m2-01-timer-correction.md)
| `CONFIG_EMB_TIMER` | y | software timers; not available with the table scheduler |
| `CONFIG_EMB_OBJ_LIST` | y (n on tiny) | exact refusal of init on live storage |

## Scheduler options

| Option | Default | Notes |
|---|---|---|
| `CONFIG_EMB_TIME_SLICING` | n | round robin among equal effective priorities; not available with the table scheduler |
| `CONFIG_EMB_TIMESLICE_TICKS` | 10 | the quantum, started at each dispatch |
| `CONFIG_EMB_TIMESLICE_PRIORITY` | 255 | the highest sliced level; higher levels run until they block or yield |

The tiny `footprint_t1` reference configuration turns event flags and message queues off.

## Add diagnostic options without changing the reference preset
Expand Down
2 changes: 1 addition & 1 deletion docs/user/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Expected output is `periodic: 5 iterations, exit=7`, followed by process exit st

## Choose priorities and deadlines deliberately

Application priorities are `1 .. EMB_PRIORITY_MAX`; larger is more urgent. A high-priority thread must block when it has no work. Yield rotates equal-priority peers and cannot let a lower-priority thread run. Tiny uses unique base-priority slots, so these two priorities also avoid a slot collision there.
Application priorities are `1 .. EMB_PRIORITY_MAX`; larger is more urgent. A high-priority thread must block when it has no work. Yield rotates equal-priority peers and cannot let a lower-priority thread run. With `CONFIG_EMB_TIME_SLICING`, a thread at or below `CONFIG_EMB_TIMESLICE_PRIORITY` that runs for a whole quantum without blocking also moves behind its READY equal-priority peers; it never lets a lower priority run either. Tiny uses unique base-priority slots, so these two priorities also avoid a slot collision there.

Advancing `next` from the previous deadline keeps the schedule anchored. Sleeping for a relative period after each iteration adds the work time to the period. If work overruns an absolute deadline, `sleep_until()` yields and the next iteration catches up; it does not count misses, skip periods or impose an execution budget. Define an application policy for overruns. In a 32-bit time configuration, compare instants using `emb_instant_before()` and keep scheduled deadlines within `EMB_TIMEOUT_MAX_TICKS` ahead. Avoid the maximum tick value as a finite object-wait deadline: it is the current forever sentinel.

Expand Down
Loading