Files
rosenblatt/userspace/npu-probe/rkt_operands.c
T
Markus Fritsche 2b34125760 Rosenblatt: NPU operand layout (weights/features/bias/output tiling) + hwtest v2
rkt_operands.{c,h}: faithful ports of Mesa's operand layout — rkt_pack_weights
(rkt_coefs.c tiling+sign), rkt_compute_biases (zero-point correction),
rkt_pack_input (feature tiling from rkt_ml_subgraph_invoke), rkt_unpack_output
(de-tile from read_outputs). hwtest.c v2 lays out a real 4x8x16 INT8 matmul,
submits our golden regcmd with real BO addresses, and checks vs a CPU ref.

State: SUBMIT accepted; job still does NOT complete. Root cause located in the
kernel driver (rocket_job.c): completion is a DPU interrupt (INTERRUPT_MASK
DPU_0|DPU_1) and JOB_TIMEOUT_MS=500 — our job runs but the DPU never raises
done within 500ms, so drm_sched resets the core and prep_bo returns -EBUSY.
Interface/regcmd/operands all match Mesa; the stall is at hardware execution.
Next: known-good Mesa/Teflon reference run on this NPU to disambiguate operand
tiling vs early-driver/DTB corner, or NPU status-register readback post-timeout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 23:56:11 +02:00

128 lines
4.4 KiB
C

/* SPDX-License-Identifier: MIT
*
* rkt_operands.c — NPU operand layout (faithful ports of Mesa rocket).
*
* Non-depthwise conv/matmul. Row-major host tensors:
* weights_in : [oc_real][ww][wh][ic_real] (conv weights)
* input_in : [iw][ih][ic_real]
* output_out : [oh][ow][oc_real]
*
* Ported from Mesa rkt_coefs.c (rkt_fill_weights, rkt_fill_biases) and
* rkt_ml.c (feature tiling in rkt_ml_subgraph_invoke, output de-tile in
* rkt_ml_subgraph_read_outputs). Assumes input_zero_point handling as Mesa.
*/
#include "rkt_operands.h"
#define FEATURE_ATOMIC_SIZE 16
#define WEIGHT_ATOMIC_SIZE 32
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#define MAX2(a, b) ((a) > (b) ? (a) : (b))
#define MIN2(a, b) ((a) < (b) ? (a) : (b))
static unsigned alignu(unsigned v, unsigned a) { return ((v + a - 1) / a) * a; }
unsigned rkt_raw_input_size(unsigned iw, unsigned ih, unsigned ic_real)
{
return iw * ih * (DIV_ROUND_UP(ic_real, FEATURE_ATOMIC_SIZE) * 2) *
FEATURE_ATOMIC_SIZE;
}
unsigned rkt_raw_output_size(unsigned ow, unsigned oh, unsigned oc_real)
{
return ow * oh * (DIV_ROUND_UP(oc_real, FEATURE_ATOMIC_SIZE) * 2) *
FEATURE_ATOMIC_SIZE;
}
unsigned rkt_packed_weights_size(unsigned ww, unsigned wh, unsigned ic_real,
unsigned oc_real)
{
unsigned ic = alignu(MAX2(ic_real, FEATURE_ATOMIC_SIZE), FEATURE_ATOMIC_SIZE);
unsigned oc = alignu(oc_real, 2);
return ww * wh * oc * alignu(ic, WEIGHT_ATOMIC_SIZE) * 2;
}
/* Mesa rkt_fill_weights (non-depthwise). */
unsigned rkt_pack_weights(const uint8_t *win, unsigned ww, unsigned wh,
unsigned ic_real, unsigned oc_real, uint8_t wzp,
uint8_t *out)
{
unsigned ic = alignu(MAX2(ic_real, FEATURE_ATOMIC_SIZE), FEATURE_ATOMIC_SIZE);
unsigned oc = alignu(oc_real, 2);
unsigned groups = WEIGHT_ATOMIC_SIZE;
unsigned ic1n = DIV_ROUND_UP(ic, groups);
unsigned ic2n = MIN2(ic, groups);
unsigned n = 0;
for (unsigned oc1 = 0; oc1 < DIV_ROUND_UP(oc, WEIGHT_ATOMIC_SIZE); oc1++)
for (unsigned ic1 = 0; ic1 < ic1n; ic1++)
for (unsigned x = 0; x < ww; x++)
for (unsigned y = 0; y < wh; y++)
for (unsigned oc2 = 0;
oc2 < MIN2(oc, WEIGHT_ATOMIC_SIZE); oc2++)
for (unsigned ic2 = 0; ic2 < ic2n; ic2++) {
unsigned occ = oc1 * WEIGHT_ATOMIC_SIZE + oc2;
unsigned icc = ic1 * groups + ic2;
if (oc_real > 2 && occ >= alignu(oc_real, 2))
continue;
if (occ >= oc_real) {
out[n++] = 0x0;
} else if (icc >= ic_real) {
if (ic2 < 16 || (ic_real % 32) > 16)
out[n++] = (uint8_t)(wzp - 0x80);
} else {
unsigned idx = ((occ * ww + x) * wh + y) * ic_real + icc;
out[n++] = (uint8_t)(win[idx] - 0x80);
}
}
return n;
}
/* Mesa rkt_fill_biases (truncate_bits=0 path) + calculate_bias_correction. */
void rkt_compute_biases(const uint8_t *win, const int32_t *bin, unsigned ww,
unsigned wh, unsigned ic_real, unsigned oc_real,
uint8_t wzp, uint8_t izp, int32_t *bout)
{
for (unsigned oc = 0; oc < oc_real; oc++) {
int32_t corr = 0;
for (unsigned x = 0; x < ww; x++)
for (unsigned y = 0; y < wh; y++)
for (unsigned ic = 0; ic < ic_real; ic++) {
unsigned idx = ((oc * ww + x) * wh + y) * ic_real + ic;
corr += ((int32_t)win[idx] - wzp) *
((int32_t)izp - 0x80);
}
bout[oc] = bin[oc] - corr;
}
}
/* Mesa feature tiling (rkt_ml_subgraph_invoke, input_channels>1 path). */
unsigned rkt_pack_input(const uint8_t *iin, unsigned iw, unsigned ih,
unsigned ic_real, uint8_t izp, uint8_t *out)
{
unsigned n = 0;
for (unsigned u = 0; u < DIV_ROUND_UP(ic_real, FEATURE_ATOMIC_SIZE); u++)
for (unsigned x = 0; x < iw; x++)
for (unsigned y = 0; y < ih; y++)
for (unsigned c = 0; c < FEATURE_ATOMIC_SIZE; c++) {
unsigned ich = c + u * FEATURE_ATOMIC_SIZE;
if (ich < ic_real)
out[n++] = (uint8_t)(iin[(x * ih + y) * ic_real + ich] - 0x80);
else
out[n++] = (uint8_t)(izp - 0x80);
}
return n;
}
/* Mesa output de-tile (rkt_ml_subgraph_read_outputs). */
void rkt_unpack_output(const uint8_t *raw, unsigned ow, unsigned oh,
unsigned oc_real, uint8_t *out)
{
for (unsigned oc = 0; oc < oc_real; oc++)
for (unsigned x = 0; x < ow; x++)
for (unsigned y = 0; y < oh; y++) {
unsigned c = oc % FEATURE_ATOMIC_SIZE;
unsigned g = oc / FEATURE_ATOMIC_SIZE;
out[(y * ow + x) * oc_real + oc] =
(uint8_t)(raw[((g * oh + y) * ow + x) * FEATURE_ATOMIC_SIZE + c] + 0x80);
}
}