Skip to content

Commit 5abbbf5

Browse files
committed
Add opt-in S2G full wait with L2 write ACKs and WT L1 invalidation
1 parent b1593b3 commit 5abbbf5

59 files changed

Lines changed: 3721 additions & 72 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎VX_config.toml‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ VX_CFG_EXT_DMA_ENABLE = false
3939
VX_CFG_EXT_DXA_ENABLE = false
4040
VX_CFG_EXT_DXA_GROUP_ENABLE = false
4141
VX_CFG_EXT_DXA_S2G_ENABLE = false
42+
VX_CFG_EXT_DXA_FULL_ENABLE = false
4243
VX_CFG_EXT_TEX_ENABLE = false
4344
VX_CFG_EXT_RASTER_ENABLE = false
4445
VX_CFG_EXT_OM_ENABLE = false
@@ -161,6 +162,15 @@ VX_CFG_PTW_WALK_CACHE_SIZE = 16
161162
VX_CFG_VM_PINNED_REGION_SIZE = 0x10000000 # 256 MB
162163

163164
[cache]
165+
# Opt-in response for selected stores. The unique write-back LLC responds only
166+
# after selected bytes commit into its data array; write-through upper levels
167+
# retain an MSHR context and relay that response.
168+
VX_CFG_CACHE_STORE_RSP_ENABLE = "expr: $VX_CFG_EXT_DXA_FULL_ENABLE"
169+
170+
# Request attribute for a non-cascading whole-L1 invalidation at the production
171+
# write-through D-cache. The initial hook requires one D-cache unit per socket.
172+
VX_CFG_CACHE_LOCAL_INVAL_ENABLE = "expr: $VX_CFG_EXT_DXA_FULL_ENABLE"
173+
164174
# Cache replacement-policy enums + LLC/topology helpers, consumed by the
165175
# per-level cache sections below and by [amo].
166176
__cache_repl_random = 0
@@ -298,6 +308,7 @@ VX_CFG_DXA_S2G_PIPELINED = false
298308
VX_CFG_DXA_S2G_PIPE_MULTI_READ = false
299309
VX_CFG_DXA_S2G_PIPE_SLOTS = 4
300310
VX_CFG_DXA_S2G_READ_CREDITS = "expr: $VX_CFG_DXA_S2G_PIPE_SLOTS"
311+
VX_CFG_DXA_S2G_WRITE_CREDITS = 16
301312
# Group slots include a reserved open group, when present.
302313
VX_CFG_DXA_GROUP_DEPTH = 8
303314
# Simultaneously source-unconsumed operations per group, not historical issues.

‎docs/designs/dxa_s2g_full_wait.md‎

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# Optional S2G destination completion and local visibility
2+
3+
This branch extends the source-only group implementation at `b1593b3d2`. It
4+
does not change what `vx_dxa_wait_group_read<N>()` guarantees. Enable the new
5+
path with `VX_CFG_EXT_DXA_FULL_ENABLE`; it defaults off. Full wait is a separate
6+
instruction, `vx_dxa_wait_group<N>()` (custom DXA funct3=4, five-bit immediate).
7+
8+
## Supported contract
9+
10+
* Groups remain warp-owned, with independent ordered queues for each warp.
11+
* READ waits for source consumption, permitting SMEM reuse before destination
12+
writes finish. It does not invalidate a cache or imply destination visibility.
13+
* FULL waits for the older committed groups to reach the configured WB L2
14+
commit point, then invalidates the issuing socket's WT L1 and waits for its
15+
acknowledgement. The newest N groups and the uncommitted open group remain
16+
excluded from the destination wait.
17+
* This first implementation requires enabled write-through L1, one physical
18+
D-cache per socket, write-back L2, and no L3. Unsupported configurations fail
19+
assertions. It neither discards dirty L1 data nor claims global coherence.
20+
The DXA store path reaches L2 directly; the optional generic WT ACK relay
21+
does not support an opted-in store racing an older same-line fill and asserts
22+
on that case. It is not used by this full-wait path.
23+
* A consuming warp on another socket needs its own refresh and explicit
24+
inter-warp/CTA synchronization. A full wait is not a CTA barrier. Concurrent
25+
unsynchronized accesses to an excluded or open group's destination are races.
26+
27+
## Data and completion paths
28+
29+
```
30+
{core, wid, gid}
31+
warp ISSUE ──► source + destination rings ──► fixed-mapped DXA worker
32+
│ │
33+
│ stable LMEM source capture
34+
│ │
35+
◄──── SOURCE_CONSUMED ────────┤
36+
│ │
37+
│ strsp-marked store requests
38+
│ ▼
39+
│ WB L2 array commit
40+
│ │
41+
│ one ACK per request beat
42+
◄──── DESTINATION_COMPLETE ──┘
43+
│
44+
FULL<N> parks warp; destination depth <= N
45+
│
46+
▼
47+
zero-byte local-invalidate command
48+
│
49+
▼
50+
local WT L1 ingress serialization
51+
drain older requests / MSHRs / old fills
52+
invalidate clean tags; no lower-level invalidation
53+
│
54+
▼
55+
tagged invalidate ACK ──► unlock issuing warp
56+
```
57+
58+
`VX_dxa_store_ack` wraps either existing S2G data engine. It does not send the
59+
data through LSU. The worker retains one high-level operation until its write
60+
ACK count drains. It may have `VX_CFG_DXA_S2G_WRITE_CREDITS` requests outstanding
61+
(default 16); ACK order within this operation is irrelevant to the count. The
62+
cache transport must deliver exactly one ACK for every opted-in write. The
63+
wrapper forwards SOURCE_CONSUMED independently and emits exactly one later
64+
DESTINATION_COMPLETE. No per-packet warp/group bookkeeping is added to L2.
65+
66+
The L2 ACK is enqueued atomically with the actual array write, including
67+
write-miss fill/replay and byte-mask merge. Accepting a write into an upstream
68+
queue is not sufficient. Zero-length and wholly out-of-bounds operations still
69+
produce one event in each domain, with zero memory writes.
70+
71+
## Why two retirement fronts are necessary
72+
73+
```
74+
source pending destination pending
75+
issue G0 1 1
76+
commit G0 1 1
77+
source capture G0 0 1
78+
wait.read<0> PASS 1
79+
overwrite SMEM safe 1
80+
L2 ACK drain G0 0 0
81+
wait.full<0> refresh L1 -> ACK -> PASS
82+
```
83+
84+
Two direct-indexed instances of the existing small per-warp ring track the
85+
source and destination domains. Both see the same accepted ISSUE and COMMIT;
86+
their tails and open group identities match, but their retirement heads may
87+
differ. An issue succeeds only when both have room. Thus a source-complete row
88+
cannot be recycled while its old destination identity is live. This is
89+
deliberately conservative; an independently sized destination pool is future
90+
work, not hidden in this patch.
91+
92+
For every live row, destination pending is at least source pending. Every
93+
DESTINATION_COMPLETE follows acceptance of that operation's SOURCE_CONSUMED
94+
event. Assertions check these invariants. Existing exact-once transport and
95+
owner drain before warp reuse avoid the need for new generation tags. A reset
96+
must reset/flush both transport and trackers; individual warp kill cannot
97+
silently recycle an outstanding identity.
98+
99+
The depth and pending bounds stay configurable (`VX_CFG_DXA_GROUP_DEPTH=8`,
100+
`VX_CFG_DXA_GROUP_MAX_PENDING=8`). Counts denote currently outstanding
101+
operations, not historical issues. Because the destination count drains later,
102+
enabling FULL may impose stronger issue backpressure than a source-only build.
103+
Commit closes an already reserved row, never allocating a new row.
104+
105+
## Invalidation transport
106+
107+
The endpoint injects `{rw=1, byteen=0, local_inval=1, tag=wid}` through a
108+
two-input arbiter on local L1 request port zero. One extra route bit keeps its
109+
ACK separate from LSU responses. Other L1 ports still operate normally until
110+
the cache takes the invalidate lock. Each endpoint allows one invalidate in
111+
flight and remembers other ready full waiters. Only the matching ACK unlocks
112+
the corresponding warp; SFU is not occupied while it waits.
113+
114+
The cache consumes this control request locally. Its ordinary flush machinery
115+
drains older bank ingress and outstanding fills before clearing tags, preventing
116+
an old refill from restoring stale data afterward. It returns an explicit ACK
117+
without issuing a dummy read or zero-byte write downstream. Unrelated clean
118+
lines may be evicted: this loses hit rate, not architectural data. Dirty-cache
119+
support would require a different drain/writeback policy and is not enabled.
120+
121+
## Validation and limitations
122+
123+
* `hw/unittest/cache_strsp`: real WB array ACK timing, miss replay, byte masks.
124+
* `hw/unittest/cache_strsp_hier`: opted-in ACK transport through WT to WB.
125+
* `hw/unittest/cache_wt_invalidate`: actual cache data; stale primed line,
126+
outstanding old fill, unrelated clean data, standalone ACK/backpressure.
127+
* `tests/unittest/cache_visibility`: matching SimX lifecycle/cache checks.
128+
* `hw/unittest/dxa_s2g`: serial and pipelined workers, delayed/reordered ACKs,
129+
source poisoning, credit bounds, partial/OOB/zero-length transfers.
130+
* `hw/unittest/dxa_full_wait`: independent source/destination heads, retained
131+
group identities on wrap, full thresholds 0/1/2, parked waiters and ACK-gated
132+
wakeup.
133+
* `tests/regression/dxa_s2g_full_wait`: one and two issuer modes; each group has
134+
a 512-byte tile plus 32-byte vector, old destination priming, source poisoning
135+
after READ, FULL followed by same-kernel reload, unrelated data and canaries.
136+
137+
SimX's functional LSU accesses can bypass cached data. Its integrated PASS
138+
therefore validates lifecycle/control, not by itself stale-L1 data visibility.
139+
Real RTL cache tests and the whole RTL regression must be reported separately.
140+
No performance, ASIC area, FPGA, or cross-socket coherence claim is made here.
141+
142+
## Reproduction
143+
144+
Configure out of tree with the installed Vortex toolchain; the regression
145+
Makefile supplies DXA, S2G, groups, full wait and L2 flags consistently to the
146+
kernel, runtime and model. Example commands from the configured build:
147+
148+
```bash
149+
make -C tests/regression/dxa_s2g_full_wait -j8 THREADS=8 run-simx
150+
make -C tests/regression/dxa_s2g_full_wait -j8 THREADS=8 run-rtlsim
151+
make -C hw/unittest/dxa_full_wait THREADS=8 run
152+
make -C hw/unittest/cache_strsp THREADS=8 run
153+
make -C hw/unittest/cache_strsp_hier THREADS=8 run
154+
make -C hw/unittest/cache_wt_invalidate THREADS=8 run
155+
make -C tests/unittest/cache_visibility -j8 run
156+
```
157+
158+
Use a fresh build directory for each cache latency variant
159+
(`PARAMS=-GLATENCY=4` or `6` on `cache_strsp`) and for serial/pipelined worker
160+
variants. Full serial worker flags are DXA/GROUP/S2G/FULL/L2; the pipelined
161+
variant additionally defines `VX_CFG_DXA_S2G_PIPELINED` and
162+
`VX_CFG_DXA_S2G_PIPE_MULTI_READ`. Debug builds (`DEBUG=4`) report source and
163+
destination completion separately, plus full-wait invalidate/wakeup events.
164+
The regression defaults to four warps of four lanes, one core/socket. Both one
165+
issuer and two issuer modes run; each issuer commits four heterogeneous groups.
166+
167+
Negative control: keep all destination ACK tracking, replace endpoint
168+
`full_unlock` with `destination_unlock`, and stop scheduling cache invalidate
169+
requests in an isolated source/build copy. The unchanged kernel then reports
170+
544 mismatches in its one-issuer mode (all 4 x 136 payload words); the real
171+
implementation reports zero. This demonstrates a cache-visibility benefit,
172+
not a speedup. Never ship the negative-control modification.
173+
174+
Checkpoint limitation: default-off L2 `vecadd_v1 -n64` passes in RTL, but the
175+
newer queue-based `vecadd -n64` and `async_barrier -n16` time out at CP sequence
176+
26 (target 27) in the tested tool environment. No untouched-baseline comparison
177+
has established whether those failures predate this branch. They remain open;
178+
the successful full-wait tests must not be presented as an all-regressions-green
179+
release. The existing plain `hw/unittest/cache` executable is an empty harness,
180+
so its successful elaboration is not counted as functional validation.

‎hw/rtl/VX_cluster.sv‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ module VX_cluster import VX_gpu_pkg::*, VX_tlb_pkg::*;
226226
.TAG_WIDTH (L2_TAG_WIDTH),
227227
.WRITE_ENABLE (1),
228228
.WRITEBACK (`VX_CFG_L2_WRITEBACK),
229+
.STORE_RSP_COMMIT (`VX_CFG_CACHE_STORE_RSP_ENABLED && L2_IS_LLC),
229230
.DIRTY_BYTES (`VX_CFG_L2_DIRTYBYTES),
230231
.REPL_POLICY (`VX_CFG_L2_REPL_POLICY),
231232
.CORE_OUT_BUF (3),

‎hw/rtl/VX_gpu_pkg.sv‎

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ package VX_gpu_pkg;
210210
// offset (VX_cache_bank), so inserting a bit below it would silently shift
211211
// MEM_ATTR_AMO_OFFS and reinterpret the AMO sideband.
212212
typedef struct packed {
213+
`ifdef VX_CFG_CACHE_LOCAL_INVAL_ENABLE
214+
logic local_inval; // non-cascading invalidate at designated cache
215+
`endif
216+
`ifdef VX_CFG_CACHE_STORE_RSP_ENABLE
217+
logic strsp; // MEM_ATTR_STRSP_OFFS (store-response request)
218+
`endif
213219
amo_req_t amo; // MEM_ATTR_AMO_OFFS = 4
214220
logic is_addr_om; // MEM_ATTR_OM_OFFS = 3
215221
logic is_addr_local; // MEM_ATTR_LOCAL_OFFS = 2
@@ -222,6 +228,20 @@ package VX_gpu_pkg;
222228
localparam MEM_ATTR_LOCAL_OFFS = 2;
223229
localparam MEM_ATTR_OM_OFFS = 3;
224230
localparam MEM_ATTR_AMO_OFFS = 4;
231+
`ifdef VX_CFG_CACHE_STORE_RSP_ENABLE
232+
// Above the AMO field so every pre-existing offset is untouched; the
233+
// attr width grows only when opt-in store responses are compiled in.
234+
localparam MEM_ATTR_STRSP_OFFS = MEM_ATTR_AMO_OFFS + AMO_REQ_BITS;
235+
`endif
236+
`ifdef VX_CFG_CACHE_LOCAL_INVAL_ENABLE
237+
// Also appended above all pre-existing fields. When strsp is present,
238+
// local_inval occupies the next bit; otherwise it directly follows AMO.
239+
`ifdef VX_CFG_CACHE_STORE_RSP_ENABLE
240+
localparam MEM_ATTR_LOCAL_INVAL_OFFS = MEM_ATTR_STRSP_OFFS + 1;
241+
`else
242+
localparam MEM_ATTR_LOCAL_INVAL_OFFS = MEM_ATTR_AMO_OFFS + AMO_REQ_BITS;
243+
`endif
244+
`endif
225245

226246
// Total width of the mem-bus attr field. Use this as the parameter
227247
// default for VX_mem_bus_if's ATTR_WIDTH parameter and as the
@@ -947,6 +967,7 @@ package VX_gpu_pkg;
947967
localparam INST_DXA_ISSUE_S2G = 3'd1;
948968
localparam INST_DXA_COMMIT_GROUP = 3'd2;
949969
localparam INST_DXA_WAIT_READ = 3'd3;
970+
localparam INST_DXA_WAIT_FULL = 3'd4;
950971

951972
typedef struct packed {
952973
logic [INST_ARGS_BITS-8-1:0] __padding;
@@ -1523,17 +1544,22 @@ package VX_gpu_pkg;
15231544
// shared walker lives at the cluster and fetches PTEs through the L2 cache,
15241545
// so the dcache tag is the base width in every mode.
15251546
localparam DCACHE_TAG_WIDTH = DCACHE_TAG_WIDTH_BASE;
1547+
`ifdef VX_CFG_EXT_DXA_FULL_ENABLE
1548+
localparam DCACHE_INPUT_TAG_WIDTH = DCACHE_TAG_WIDTH + 1;
1549+
`else
1550+
localparam DCACHE_INPUT_TAG_WIDTH = DCACHE_TAG_WIDTH;
1551+
`endif
15261552

15271553
// Memory request data bits (mem transacts in sectors)
15281554
localparam DCACHE_MEM_DATA_WIDTH = (DCACHE_SECTOR_SIZE * 8);
15291555

1530-
// Memory request tag bits (computed with DCACHE_TAG_WIDTH since
1556+
// Memory request tag bits (computed with DCACHE_INPUT_TAG_WIDTH since
15311557
// VX_cache_cluster sees the post-arb tag width). The bypass tag encodes the
15321558
// in-block offset at the mem granule, so it uses the sector, not the line.
15331559
`ifdef VX_CFG_DCACHE_ENABLE
1534-
localparam DCACHE_MEM_TAG_WIDTH = `CACHE_CLUSTER_NC_MEM_TAG_WIDTH(`VX_CFG_DCACHE_MSHR_SIZE, DCACHE_NUM_BANKS, DCACHE_NUM_REQS, L1_MEM_PORTS, DCACHE_SECTOR_SIZE, DCACHE_WORD_SIZE, DCACHE_TAG_WIDTH, `VX_CFG_SOCKET_SIZE, `VX_CFG_NUM_DCACHES, UUID_WIDTH);
1560+
localparam DCACHE_MEM_TAG_WIDTH = `CACHE_CLUSTER_NC_MEM_TAG_WIDTH(`VX_CFG_DCACHE_MSHR_SIZE, DCACHE_NUM_BANKS, DCACHE_NUM_REQS, L1_MEM_PORTS, DCACHE_SECTOR_SIZE, DCACHE_WORD_SIZE, DCACHE_INPUT_TAG_WIDTH, `VX_CFG_SOCKET_SIZE, `VX_CFG_NUM_DCACHES, UUID_WIDTH);
15351561
`else
1536-
localparam DCACHE_MEM_TAG_WIDTH = `CACHE_CLUSTER_BYPASS_MEM_TAG_WIDTH(DCACHE_NUM_REQS, L1_MEM_PORTS, DCACHE_SECTOR_SIZE, DCACHE_WORD_SIZE, DCACHE_TAG_WIDTH, `VX_CFG_SOCKET_SIZE, `VX_CFG_NUM_DCACHES);
1562+
localparam DCACHE_MEM_TAG_WIDTH = `CACHE_CLUSTER_BYPASS_MEM_TAG_WIDTH(DCACHE_NUM_REQS, L1_MEM_PORTS, DCACHE_SECTOR_SIZE, DCACHE_WORD_SIZE, DCACHE_INPUT_TAG_WIDTH, `VX_CFG_SOCKET_SIZE, `VX_CFG_NUM_DCACHES);
15371563
`endif
15381564

15391565
// L1 dcache is the LLC iff neither L2 nor L3 is enabled.

0 commit comments

Comments
 (0)