Fixed the RISC-V64 trap frame size mismatch in the regression test BSP - #708
Merged
fdesbiens merged 1 commit intoSep 9, 2026
Merged
Conversation
The RISC-V64 port moved its interrupt frame to 528 bytes (65 slots plus 8
bytes of padding, so sp stays 16-byte aligned at a call) and published the
size as TX_RISCV_TRAP_FRAME_SIZE. The port sources were converted to use
it, but the shared regression test BSP was not: its trap_entry still
allocated a hardcoded 65 * REGBYTES, or 520 bytes.
Every interrupt therefore unwound 8 bytes more than it allocated:
trap_entry: addi sp,sp,-520
_tx_thread_context_restore: addi sp,sp,528
On the RISC-V64 regression suite that left 24 of 95 tests failing in the
default configuration, typically as an illegal instruction once execution
reached a corrupted frame. The example BSP under the port directory was
converted with the port and was unaffected, which is why the functional
QEMU test kept passing.
The test BSP now takes both frame sizes from the port it is linked
against, so the two cannot drift apart again. A port that publishes no
contract keeps the historical layout, so the RISC-V32 side is unchanged
until its own port publishes one.
TX_RISCV_TRAP_CALL_FRAME_SIZE is restored to the RISC-V64 tx_port.h. It
was removed as unused when the frame sizes were introduced, but it is
part of the same contract: it is the space a trap entry reserves around a
call into C, and the psABI requires 16 bytes there rather than one
register slot.
Verified on QEMU with every linkable test built, comparing against the
commit before the port change:
before the port change 2 failures out of 95 (both unlinkable)
current dev 24 failures out of 95
with this change 2 failures out of 95 (both unlinkable)
The two remaining failures predate all of this: newlib pulls _impure_ptr
out of R_RISCV_HI20 range for time(), so those two binaries do not link.
RISC-V32 is unchanged at 2 failures across all five configurations.
Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
fdesbiens
added a commit
to akifejaz/threadx
that referenced
this pull request
Sep 9, 2026
tx_port.h published TX_RISCV_TRAP_FRAME_SIZE for the GNU BSP assembly, but nothing in the port consumed it. Six .S files each rebuilt the same numbers from their own #if, so the interrupt frame size was written out in seven places and the solicited frame size in three. That is the shape that produced the RISC-V64 fault fixed in eclipse-threadx#708, where the port moved to a padded frame and one copy of the constant did not. The sources now include tx_port.h and take both sizes from it, and no literal frame size remains in the port. TX_RISCV_SOL_FRAME_SIZE joins the contract, since the solicited frame was never published at all. The emitted code is unchanged: 400 and 176 bytes for ILP32D, 128 for soft-float, confirmed by disassembly before and after. Two further corrections: _tx_initialize_low_level carried .global immediately followed by .weak, so the symbol stayed weak and the .global did nothing. Weak is what the port wants, because the example and regression BSPs both provide their own definition, so the stray .global is removed rather than the .weak. Verified with nm that the symbol is still W. The QEMU runner seeded fpu_verified from skip_fpu, so a soft-float run satisfied the FPU gate whether or not the script ever reported the skip. It now starts false and is set only when the skip marker is present, so a run that dies before reaching that point fails instead of passing. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
fdesbiens
added a commit
that referenced
this pull request
Sep 9, 2026
* riscv32: spec compliance and regression test fix Signed-off-by: Akif Ejaz <akifejaz40@gmail.com> * Derived the RISC-V32 frame sizes from the port contract in one place tx_port.h published TX_RISCV_TRAP_FRAME_SIZE for the GNU BSP assembly, but nothing in the port consumed it. Six .S files each rebuilt the same numbers from their own #if, so the interrupt frame size was written out in seven places and the solicited frame size in three. That is the shape that produced the RISC-V64 fault fixed in #708, where the port moved to a padded frame and one copy of the constant did not. The sources now include tx_port.h and take both sizes from it, and no literal frame size remains in the port. TX_RISCV_SOL_FRAME_SIZE joins the contract, since the solicited frame was never published at all. The emitted code is unchanged: 400 and 176 bytes for ILP32D, 128 for soft-float, confirmed by disassembly before and after. Two further corrections: _tx_initialize_low_level carried .global immediately followed by .weak, so the symbol stayed weak and the .global did nothing. Weak is what the port wants, because the example and regression BSPs both provide their own definition, so the stray .global is removed rather than the .weak. Verified with nm that the symbol is still W. The QEMU runner seeded fpu_verified from skip_fpu, so a soft-float run satisfied the FPU gate whether or not the script ever reported the skip. It now starts false and is set only when the skip marker is present, so a run that dies before reaching that point fails instead of passing. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com> --------- Signed-off-by: Akif Ejaz <akifejaz40@gmail.com> Co-authored-by: Frédéric Desbiens <frederic.desbiens@eclipse-foundation.org>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The RISC-V64 port moved its interrupt frame to 528 bytes in #698 — 65 slots plus 8 bytes of padding, so
spstays 16-byte aligned at a call — and published the size asTX_RISCV_TRAP_FRAME_SIZE. The port sources were converted to consume it. The shared regression test BSP was not:trap_entryintest/tx/cmake/riscv/bsp/tx_initialize_low_level.Sstill allocates a hardcoded65 * REGBYTES.Disassembled from the current
dev:Every interrupt unwinds 8 bytes more than it allocated.
The example BSP under the port directory was converted along with the port, which is why the QEMU functional target kept passing and this went unnoticed. The RISC-V regression jobs are commented out in
regression_test.yml, so CI did not catch it either.Measured effect
RISC-V64,
default_build, 95 tests, QEMU. Each tree built withninja -k 0so that every linkable target is actually present — without that, missing binaries are counted as failures and the numbers are meaningless:515ab8ab)devThis restores the suite exactly to its pre-#698 state. The failure signature was an illegal instruction (
mcause=0x2) once execution reached a corrupted frame.The two residual failures predate all of this and are unrelated: newlib pulls
_impure_ptrout ofR_RISCV_HI20range fortime(), sothreadx_thread_delayed_suspension_testandthreadx_thread_wait_abort_and_isr_testdo not link. #691 addresses that with a localtime()implementation.The change
The test BSP now derives both frame sizes from the port it is linked against, so the two cannot drift apart again. A port that publishes no contract keeps the historical layout, so RISC-V32 is byte-identical until its own port publishes one.
TX_RISCV_TRAP_CALL_FRAME_SIZEis restored to the RISC-V64tx_port.h. It was removed as unused when the frame sizes were introduced, but it belongs to the same contract — the space a trap entry reserves around a call into C, which the psABI requires to be 16 bytes rather than one register slot.Verification
default_build: 24 → 2 failures, matching the pre-riscv64: spec compliance and regression test fix #698 baseline.trap_entrynowaddi sp,sp,-528; RV32 unchanged at-260.Note on ordering
#691 was intended to land before #698. It is what teaches the shared BSP to consume the port contract, and it fixes the newlib
time()link error. Merging #698 first left the shared BSP on the old hardcoded size against a port that had moved. This change supplies that missing groundwork for RISC-V64; #691 can then drop its now-redundant__riscv_xlen == 32guard on the same include.