Skip to content

Bug/gbar response phase - #403

Open
RunjiaChen wants to merge 2 commits into
vortexgpgpu:masterfrom
RunjiaChen:bug/gbar-response-phase
Open

Bug/gbar response phase#403
RunjiaChen wants to merge 2 commits into
vortexgpgpu:masterfrom
RunjiaChen:bug/gbar-response-phase

Conversation

@RunjiaChen

Copy link
Copy Markdown
Contributor

tests/regression: gbar_phase — global barrier does not advance its phase

Reproducer test only — this PR does not change the RTL. Full analysis and a
suggested fix direction are in #401.

Summary

A completed global-barrier generation does not advance its phase on the RTL.
gbarrier::arrive() is documented to return the "phase (current generation
number)" (sw/kernel/include/vx_barrier.h:69), and gbarrier::wait() blocks
until it changes — so two arrive() calls either side of a completed
generation must return different values. SimX advances it
(sim/simx/barrier_unit.cpp:126); the RTL returns the same number on every
core.

Reproduction

cd build && ../configure --xlen=64 --tooldir=$HOME/tools
make -C tests/regression/gbar_phase
./ci/blackbox.sh --cores=2 --app=gbar_phase --driver=simx
./ci/blackbox.sh --cores=2 --app=gbar_phase --driver=rtlsim
driver verdict core 0 core 1
simx PASSED! 0 → 1 0 → 1
rtlsim FAILED! 0 → 0 0 → 0

Deterministic, both cores, every run. Two cores are the minimum for the global
barrier to be instantiated (USE_GBAR = VX_CFG_NUM_CORES > 1); the Makefile
defaults -DVX_CFG_NUM_CORES=2 and the host program skips on a 1-core device.

Why the existing async_gbarrier test does not catch this

It passes on both drivers today — on timing, not on the phase. Its kernel runs
32 loop iterations between arrive() and wait(), short enough that every warp
is already parked when the global response lands, and the response releases
warps unconditionally rather than by phase. Raising that one constant to 2000
makes the same test deadlock on rtlsim (active_warps=1111, stalled_warps=1111) while still passing on simx. Details in #401.

What this PR contains

One new directory, tests/regression/gbar_phase/ (4 files, +269 lines).
No existing file is modified. The test samples the phase with arrive()
rather than blocking in wait(), so the kernel always terminates and the defect
is reported as data instead of a timeout; results are printed per core, since a
global barrier is a cluster object whose phase lives in per-core state.

It is deliberately not added to the tests/regression/Makefile app list, so
CI is unaffected by a test expected to fail until a fix lands. Happy to wire it
in as a regression guard once one does.

Environment

Based on 5d62846c6. Also present byte-identical at d76b7f24e — between those
commits exactly one commit touches VX_bar_unit.sv (90a9b186b), whose entire
diff for that file is one word inside a comment, and VX_gbar_unit.sv is
untouched.

RunjiaChen and others added 2 commits August 19, 2026 14:59
… advance

vx_barrier.h documents gbarrier::arrive() as returning "phase (current
generation number)" and gbarrier::wait(phase) as blocking "until generation >
phase". Both statements only hold if a completed global-barrier generation
actually advances the phase that arrive() reports. On RTL it never does.

  - RTL  hw/rtl/core/VX_bar_unit.sv:165-170 handles the cluster response with
         phase_n = next_phase, and :186 store_write = req_valid ||
         gbar_bus_if.rsp_valid commits it at the address latched by :238
         store_waddr <= store_raddr. A response is only accepted while no
         barrier op is in execute (:57 gbar_rsp_ready = ~req_valid), so the
         previous cycle's read_addr was VX_wctl_unit.sv:177's fallback
         txbar_bus_if.data.addr, which is tied 'x with DXA off
         (VX_sfu_unit.sv:222-223). The flip therefore lands on an x-derived
         slot instead of the barrier's own slot.
  - SimX sim/simx/barrier_unit.cpp:152-166 global_resume() increments the
         phase of the same entry get_phase() reads.

So the global barrier's own phase never moves on RTL. A SYNC global barrier
still works, because its release is an unlock (VX_bar_unit.sv:167-168) and
never consults the phase -- which is exactly why nothing else notices. But an
async global arrive/wait pair can never be released by its own barrier, and
any code treating the returned value as a generation number reads a constant.

gbar_phase samples the probe barrier's phase, runs a full generation on it,
rendezvouses the cores on a SECOND global barrier (a different id, hence a
different gbar row and a different per-core slot), then samples again, and
asserts the DOCUMENTED contract that the phase advanced. Sampling uses
arrive() rather than wait() deliberately: wait() would block forever, turning
a data mismatch into a timeout with no diagnostics. Results are grouped per
core, since the flip can land on one core's slot by coincidence while every
other core is stuck.

The existing coverage cannot see it: tests/regression/async_gbarrier
self-skips when num_cores < 2 (its main.cpp:80), VX_config.toml:5 defaults
VX_CFG_NUM_CORES to 1, and ci/testcases/regression.yaml carries no multi-core
shape for it, so the async-global path is never exercised at all.

Measured (2 cores, 4 warps, 4 threads):

  ./ci/blackbox.sh --cores=2 --driver=simx   --app=gbar_phase   PASSED  0 -> 1
  ./ci/blackbox.sh --cores=2 --driver=rtlsim --app=gbar_phase   FAILED  0 -> 0

  core  phase@N  phase@N+1  advanced      (rtlsim)
    0        0          0        NO
    1        0          0        NO

The test's Makefile defaults CONFIGS to -DVX_CFG_NUM_CORES=2, because the
global barrier only exists when a cluster holds more than one core
(VX_bar_unit.sv:44, USE_GBAR = VX_CFG_NUM_CORES > 1).

This change is purely additive: no existing file is modified.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant