1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
//! Shared host/device ABI for the ET-SoC-1: the kernel-launch argument structs,
//! defined **once** and used by both the host launcher and the device kernel.
//!
//! Kernel arguments are passed by pointer: the host stages an argument struct in
//! device memory and the firmware delivers its address to the kernel (in `a0`).
//! Because both the host (x86-64) and the device (RV64) are little-endian, the
//! in-memory `#[repr(C)]` layout *is* the wire layout, so no explicit
//! serialisation is needed -- the host takes the struct's bytes and the kernel
//! reinterprets the pointer. Defining each struct here keeps the two sides from
//! drifting (mismatched field order, sizes, or padding).
/// ET-SoC-1 cache-line size, in bytes.
///
/// Per-hart outputs are laid out at this stride on both sides: the host strides
/// its padded arrays by it and the device writes each hart's cell at
/// `base + hart * CACHE_LINE`. Defining it once here keeps the two from drifting,
/// which on this software-coherent part would cause silent false-sharing
/// corruption.
pub const CACHE_LINE: usize = 64;
/// A wrapper that aligns `T` to a cache-line boundary.
///
/// On the ET-SoC-1 (a software-coherent architecture), two values sharing a
/// cache line that are written by distinct harts without explicit cache
/// operations cause false-sharing corruption. Wrapping per-hart output data
/// in `CachePadded` ensures each instance occupies a distinct 64-byte line,
/// making cross-hart false sharing structurally impossible regardless of the
/// surrounding allocation layout.
///
/// The inner value is accessed directly via the public tuple field `0`.
///
/// # Example
///
/// ```
/// use et_abi::CachePadded;
/// let cell: CachePadded<u64> = CachePadded(0);
/// assert_eq!(core::mem::align_of::<CachePadded<u64>>(), 64);
/// ```
;
/// Harts per compute shire on the ET-SoC-1 (architectural constant).
pub const HARTS_PER_SHIRE: u32 = 64;
/// Harts per neighbourhood on the ET-SoC-1 (architectural constant).
pub const HARTS_PER_NEIGHBOURHOOD: u32 = 16;
/// A plain-old-data kernel-argument struct exchanged between host and device.
///
/// # Safety
/// Implementors must be `#[repr(C)]`, contain only integer fields with no
/// padding, and be valid for any bit pattern. Then [`DeviceArgs::as_bytes`] and
/// [`DeviceArgs::from_ptr`] are a faithful round-trip on little-endian hosts and
/// devices.
pub unsafe
// ---------------------------------------------------------------------------
// Tensor-extension constants
// ---------------------------------------------------------------------------
/// Required alignment for all matrix pointers and row strides used with the
/// ET-SoC-1 tensor-load/store instructions. TensorLoad and TensorStore each
/// require the source or destination address to be 64-byte aligned.
pub const TENSOR_ALIGN: usize = 64;
/// Number of addressable cache lines in each Minion's L1 scratchpad.
/// TensorLoad START field is 6 bits, spanning lines 0..47 inclusive.
pub const SCP_LINES: usize = 48;
/// Bytes per L1 scratchpad line (one cache line).
pub const SCP_LINE_BYTES: usize = 64;
/// Minion cores per compute shire on the ET-SoC-1.
/// Each shire has 32 dual-threaded Minion cores (64 harts total).
pub const MINIONS_PER_SHIRE: u32 = 32;
// ---------------------------------------------------------------------------
// GEMM tile dimensions
// ---------------------------------------------------------------------------
/// Number of C output rows computed per tile by TensorFMA32.
/// Equals the maximum AROWS+1 value (4-bit field, max 15 -> 16 rows).
pub const GEMM_TILE_M: usize = 16;
/// Inner-dimension (K) slice processed per TensorFMA32 call.
/// Limited to 16 f32 values per A-matrix row fitting in one 64-byte
/// scratchpad line (ACOLS field is 4-bit, max 15 -> 16 columns).
pub const GEMM_TILE_K: usize = 16;
/// Number of f32 output columns produced per TensorFMA32 call (BCOLS=3 gives
/// 4*(3+1) = 16 columns). Each tile row occupies exactly 64 bytes in the FP
/// register file. N need not be a multiple of this value; the last tile column
/// may be partial, with the hardware writing 64 bytes per row regardless --
/// the caller reads only the N valid columns from the 64-byte-aligned allocation.
pub const GEMM_TILE_N: usize = 16;
// ---------------------------------------------------------------------------
// GemmArgs
// ---------------------------------------------------------------------------
/// Arguments for the single-precision general matrix multiplication (sGEMM)
/// kernel (`sgemm-rs`), implementing C = alpha*A*B + beta*C.
///
/// # Layout invariants (v0.1 restrictions)
/// - `alpha` must be `1.0` and `beta` must be `0.0`.
/// - `n` may be any positive integer; partial last-column tiles are handled
/// transparently via 64-byte-aligned row padding.
/// - `a`, `b`, `c` must be [`TENSOR_ALIGN`]-byte aligned device addresses.
/// - `lda`, `ldb`, `ldc` must be multiples of [`TENSOR_ALIGN`] (64 bytes).
///
/// All dimensions are in elements; leading dimensions are in bytes.
///
/// # ABI layout
/// The four 8-byte fields (`a`, `b`, `c`, `n_shires`) are grouped first to
/// give the struct 8-byte alignment with no internal or trailing padding:
/// `4*8 + 8*4 = 64 bytes` total.
// SAFETY: repr(C); 4 u64 fields followed by 8 u32/f32 fields, ordered by
// decreasing size -> no padding. 4*8 + 8*4 = 64 bytes, a multiple of the
// struct's 8-byte alignment.
unsafe
const _: = assert!;
/// Arguments for the data-parallel reduction kernel (`reduce-rs`).
// SAFETY: repr(C), only u64/u32 fields ordered by decreasing size -> no padding.
unsafe
const _: = assert!;