Skip to content

Defer RISC-V callee-saved registers on no-switch timer interrupts - #1482

Open
zhouzhou-code wants to merge 2 commits into
FreeRTOS:mainfrom
zhouzhou-code:feat/riscv-mtimer-deferred-callee-save
Open

Defer RISC-V callee-saved registers on no-switch timer interrupts#1482
zhouzhou-code wants to merge 2 commits into
FreeRTOS:mainfrom
zhouzhou-code:feat/riscv-mtimer-deferred-callee-save

Conversation

@zhouzhou-code

Copy link
Copy Markdown

Description

The dedicated RISC-V machine-timer handler currently saves and restores the
complete integer context even when xTaskIncrementTick() returns pdFALSE.
The called C code must preserve s0-s11 according to the RISC-V ABI, so those
registers do not need to be written to the task stack when execution returns to
the interrupted task.

This change keeps the existing task-frame layout and optional-context behavior,
but splits integer context handling into caller-saved and callee-saved parts:

  1. Save caller-saved registers on machine-timer entry.
  2. If no context switch is required, restore only those registers and return.
  3. If a context switch is required, save s0-s11 into their reserved frame
    slots before calling vTaskSwitchContext().

FPU, VPU, and chip-specific additional contexts retain their existing save and
restore semantics. Other interrupt, exception, unified-trap, and yield paths
continue to save the complete context.

For RV64, the no-switch path avoids 12 stores and 12 loads, reducing stack
traffic by 192 bytes per tick.

Test Steps

A standalone QEMU virt M-mode test uses different sentinel values in
s0-s11 for two equal-priority tasks and checks them after 2,000 timer ticks.
The following configurations passed against the current main branch:

  • no switch
  • task switch on every tick
  • FPU, with and without task switching
  • VPU, with and without task switching
  • synthetic chip-specific additional context
  • FPU + VPU + additional context + task switching

The modified portASM.S was also assembled successfully for RV32 (ilp32)
and RV64 (lp64d). Disassembly confirms that the pdFALSE path contains no
s0-s11 stores or loads and that the pdTRUE path stores all twelve registers
before vTaskSwitchContext().

The same implementation was previously validated on a JH7110/U74 in M-mode:

  • baseline and optimized single-task and two-task tests passed 1,000,000 ticks
  • 10,000 no-switch entry samples: median 59 -> 50 cycles; mean 155.281 ->
    144.746 cycles
  • P99 and maximum did not improve

Checklist:

  • I have tested my changes. No regression in existing tests.
  • I have modified and/or added unit-tests to cover the code changes in this Pull Request. (The standalone architecture test is maintained outside this kernel-only repository.)

Related Issue

Closes #1464

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Signed-off-by: zhouzhou_code <2031145985@qq.com>
Signed-off-by: zhouzhou_code <2031145985@qq.com>
@sonarqubecloud

Copy link
Copy Markdown

@zhouzhou-code
zhouzhou-code marked this pull request as ready for review August 26, 2026 06:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Defer RISC-V callee-saved register handling on no-switch timer interrupts

1 participant