Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a4c3fca
[profiling] add function tracking from binary (for CV32 ctrl core)
FrancescoConti Jul 7, 2026
6bb49d4
[mglib] prepare for mglib by adding per-tile BSS (in L1) + pin entry …
FrancescoConti Jul 7, 2026
f610133
[mglib] add mglib (draft version)
FrancescoConti Jul 7, 2026
ff6d557
[mglib] add mglib test
FrancescoConti Jul 7, 2026
41e6368
[mglib] temporary workaround on idma programming queue (depth=1?)
FrancescoConti Jul 8, 2026
ab646b9
formatting
FrancescoConti Jul 8, 2026
faed6a4
[mglib] always inline driver functions in mglib
FrancescoConti Jul 8, 2026
c260f26
[mglib] better overlap in mglib test, still not working with timeslot…
FrancescoConti Jul 8, 2026
3076a4e
[mglib] test now works with more timeslots. improved overlap
FrancescoConti Jul 8, 2026
e7a3717
[mglib] split COMMIT_TRIGGER from mm_arith (why was it there?)
FrancescoConti Jul 8, 2026
b1f6295
[mglib] some progress, still not satisficatory
FrancescoConti Jul 8, 2026
1c1f268
[make] add torch to possible 'make gvsoc_uv / gvsoc_venv' deps
FrancescoConti Jul 8, 2026
4585637
[mglib] good performance point, probably best achievable with depth=1…
FrancescoConti Jul 8, 2026
bc54938
[mglib] test generator for mglib
FrancescoConti Jul 8, 2026
cba1088
[mglib] 256x256x256 test with 8 timestamps for mglib by default
FrancescoConti Jul 8, 2026
33abd22
fix formatting
FrancescoConti Jul 8, 2026
734a73c
[mglib] reduce test size to make it feasible in RTL. tune perf with c…
FrancescoConti Jul 18, 2026
2ffcbda
make format linter happy
FrancescoConti Jul 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ llvm/

# Generated test inputs
tests/magia/mesh/gemm/*/include/*
tests/magia/mesh/mm_generator/include/*

# Editor
.vscode/
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ add_subdirectory(targets)
add_subdirectory(hal)
# add_subdirectory(devices)
add_subdirectory(drivers)
add_subdirectory(mglib)

################################################################################
# Testing #
Expand Down
10 changes: 8 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -292,17 +292,23 @@ gvsoc_init:
git fetch origin $(GVSOC_GVRUN_COMMIT) && \
git checkout $(GVSOC_GVRUN_COMMIT)

# Set TORCH=1 to also install the "gemm" extra (torch, CPU build via uv).
TORCH ?= 0
ifeq ($(TORCH),1)
PIP_EXTRAS := [gemm]
endif

gvsoc_uv:
uv venv --python 3.12 gvsoc_venv && \
source gvsoc_venv/bin/activate && \
uv pip install .
uv pip install .$(PIP_EXTRAS)

gvsoc_venv:
eval "$(pyenv init -)" && \
pyenv local 3.12 && \
python -m venv gvsoc_venv && \
source gvsoc_venv/bin/activate && \
pip install .
pip install .$(PIP_EXTRAS)

llvm:
mkdir -p $(LLVM_DIR)
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ The following steps assume the default target platform, `magia_v2`, on which Spa

From the SDK's root directory. If you have done it correctly, you'll see a "(gvsoc_venv)" preceding your terminal command line.

If you intend to generate new GEMM tests (`tests/magia/mesh/mm_generator`, which relies on PyTorch), pass `TORCH=1` to also install PyTorch (CPU build) into the environment:

`make gvsoc_uv TORCH=1` (or `make gvsoc_pyenv TORCH=1`)

This is not needed to build or run the existing tests, so it is disabled by default to keep the environment lightweight.

1. RISC-V GCC toolchain: make sure it is installed and visible in the `$PATH` environment variable. You can check if and where the compiler is installed by running the following command on your root (`/`) directory:

`find . ! -readable -prune -o -name "riscv32-unknown-elf-gcc" -print`
Expand Down
62 changes: 61 additions & 1 deletion drivers/eventunit32/include/eventunit32.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,64 @@

#pragma once

#include "eventunit.h"
#include <stdint.h>
#include "eventunit.h"
#include "regs/tile_ctrl.h" // EU_*_MASK
#include "utils/eu_isa_utils.h" // eu_wait_events()
#include "utils/performance_utils.h" // stnl_cmp_f()

/**
* @brief Wait for RedMulE completion using specified mode
* @param mode Wait mode (polling, WFE, etc.)
* @return Non-zero if RedMulE completed, 0 if timeout/error
*/
static inline __attribute__((always_inline)) uint32_t eu32_redmule_wait(eu_controller_t *ctrl,
eu_wait_mode_t mode)
{
uint32_t retval = eu_wait_events(EU_REDMULE_DONE_MASK, mode, 1000000);
#if PROFILE_CMP == 1
stnl_cmp_f();
#endif
return retval; // 1M cycle timeout
}

/**
* @brief Wait for any iDMA completion using specified mode
* @param mode Wait mode (polling, WFE, etc.)
* @return Non-zero if any iDMA completed, 0 if timeout/error
*/
static inline __attribute__((always_inline)) uint32_t eu32_idma_wait(eu_controller_t *ctrl,
eu_wait_mode_t mode)
{
return eu_wait_events(EU_IDMA_ALL_DONE_MASK, mode, 1000000); // 1M cycle timeout
}

/**
* @brief Wait for L2->L1 (AXI2OBI) completion specifically
* @param mode Wait mode (polling, WFE, etc.)
* @return Non-zero if L2->L1 completed, 0 if timeout/error
*/
static inline __attribute__((always_inline)) uint32_t eu32_idma_wait_a2o(eu_controller_t *ctrl,
eu_wait_mode_t mode)
{
uint32_t retval = eu_wait_events(EU_IDMA_A2O_DONE_MASK, mode, 1000000);
#if PROFILE_CMI == 1
stnl_cmi_f();
#endif
return retval; // 1M cycle timeout
}

/**
* @brief Wait for L1->L2 (OBI2AXI) completion specifically
* @param mode Wait mode (polling, WFE, etc.)
* @return Non-zero if L1->L2 completed, 0 if timeout/error
*/
static inline __attribute__((always_inline)) uint32_t eu32_idma_wait_o2a(eu_controller_t *ctrl,
eu_wait_mode_t mode)
{
uint32_t retval = eu_wait_events(EU_IDMA_O2A_DONE_MASK, mode, 1000000);
#if PROFILE_CMO == 1
stnl_cmo_f();
#endif
return retval; // 1M cycle timeout
}
86 changes: 26 additions & 60 deletions drivers/eventunit32/src/eventunit32.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,6 @@ void eu32_redmule_init(eu_controller_t *ctrl, uint32_t enable_irq)
}
}

/**
* @brief Wait for RedMulE completion using specified mode
* @param mode Wait mode (polling, WFE, etc.)
* @return Non-zero if RedMulE completed, 0 if timeout/error
*/
uint32_t eu32_redmule_wait(eu_controller_t *ctrl, eu_wait_mode_t mode)
{
uint32_t retval = eu_wait_events(EU_REDMULE_DONE_MASK, mode, 1000000);
#if PROFILE_CMP == 1
stnl_cmp_f();
#endif
return retval; // 1M cycle timeout
}

/**
* @brief Check if RedMulE is currently busy
* @return Non-zero if RedMulE is busy
Expand Down Expand Up @@ -101,16 +87,6 @@ void eu32_idma_init(eu_controller_t *ctrl, uint32_t enable_irq)
}
}

/**
* @brief Wait for any iDMA completion using specified mode
* @param mode Wait mode (polling, WFE, etc.)
* @return Non-zero if any iDMA completed, 0 if timeout/error
*/
uint32_t eu32_idma_wait(eu_controller_t *ctrl, eu_wait_mode_t mode)
{
return eu_wait_events(EU_IDMA_ALL_DONE_MASK, mode, 1000000); // 1M cycle timeout
}

/**
* @brief Wait for specific iDMA direction completion
* @param direction 0 = L2->L1 (A2O), 1 = L1->L2 (O2A)
Expand All @@ -132,33 +108,9 @@ uint32_t eu32_idma_wait_direction(eu_controller_t *ctrl, uint32_t direction, eu_
return retval; // 1M cycle timeout
}

/**
* @brief Wait for L2->L1 (AXI2OBI) completion specifically
* @param mode Wait mode (polling, WFE, etc.)
* @return Non-zero if L2->L1 completed, 0 if timeout/error
*/
uint32_t eu32_idma_wait_a2o(eu_controller_t *ctrl, eu_wait_mode_t mode)
{
uint32_t retval = eu_wait_events(EU_IDMA_A2O_DONE_MASK, mode, 1000000);
#if PROFILE_CMI == 1
stnl_cmi_f();
#endif
return retval;
}

/**
* @brief Wait for L1->L2 (OBI2AXI) completion specifically
* @param mode Wait mode (polling, WFE, etc.)
* @return Non-zero if L1->L2 completed, 0 if timeout/error
*/
uint32_t eu32_idma_wait_o2a(eu_controller_t *ctrl, eu_wait_mode_t mode)
{
uint32_t retval = eu_wait_events(EU_IDMA_O2A_DONE_MASK, mode, 1000000);
#if PROFILE_CMO == 1
stnl_cmo_f();
#endif
return retval;
}
// eu32_idma_wait_a2o / eu32_idma_wait_o2a are now static inline (see
// eventunit32.h), so they cannot be alias targets; the external symbols are
// exposed via thin wrappers further down for legacy/test callers.

/**
* @brief Check if any iDMA transfer has completed
Expand Down Expand Up @@ -344,14 +296,28 @@ extern void eu_init(eu_controller_t *ctrl)
__attribute__((alias("eu32_init"), used, visibility("default")));
extern void eu_redmule_init(eu_controller_t *ctrl, uint32_t enable_irq)
__attribute__((alias("eu32_redmule_init"), used, visibility("default")));
extern uint32_t eu_redmule_wait(eu_controller_t *ctrl, eu_wait_mode_t mode)
__attribute__((alias("eu32_redmule_wait"), used, visibility("default")));
// eu32_redmule_wait is now a static inline (see eventunit32.h), so it cannot be an
// alias target; expose the external symbol via a thin wrapper for legacy/test callers.
uint32_t __attribute__((used, visibility("default"))) eu_redmule_wait(eu_controller_t *ctrl,
eu_wait_mode_t mode)
{
return eu32_redmule_wait(ctrl, mode);
}
extern void eu_idma_init(eu_controller_t *ctrl, uint32_t enable_irq)
__attribute__((alias("eu32_idma_init"), used, visibility("default")));
extern uint32_t eu_idma_wait_a2o(eu_controller_t *ctrl, eu_wait_mode_t mode)
__attribute__((alias("eu32_idma_wait_a2o"), used, visibility("default")));
extern uint32_t eu_idma_wait_o2a(eu_controller_t *ctrl, eu_wait_mode_t mode)
__attribute__((alias("eu32_idma_wait_o2a"), used, visibility("default")));
// eu32_idma_wait_a2o / eu32_idma_wait_o2a are now static inlines (see
// eventunit32.h), so they cannot be alias targets; expose the external symbols
// via thin wrappers for legacy/test callers.
uint32_t __attribute__((used, visibility("default"))) eu_idma_wait_a2o(eu_controller_t *ctrl,
eu_wait_mode_t mode)
{
return eu32_idma_wait_a2o(ctrl, mode);
}
uint32_t __attribute__((used, visibility("default"))) eu_idma_wait_o2a(eu_controller_t *ctrl,
eu_wait_mode_t mode)
{
return eu32_idma_wait_o2a(ctrl, mode);
}
extern void eu_fsync_init(eu_controller_t *ctrl, uint32_t enable_irq)
__attribute__((alias("eu32_fsync_init"), used, visibility("default")));
extern uint32_t eu_fsync_wait(eu_controller_t *ctrl, eu_wait_mode_t mode)
Expand Down Expand Up @@ -397,13 +363,13 @@ extern uint32_t eu_spatz_is_done(eu_controller_t *ctrl)
eu_controller_api_t eu_api = {
.init = eu32_init,
.redmule_init = eu32_redmule_init,
.redmule_wait = eu32_redmule_wait,
.redmule_wait = eu_redmule_wait,
.redmule_is_busy = eu32_redmule_is_busy,
.redmule_is_done = eu32_redmule_is_done,
.idma_init = eu32_idma_init,
.idma_wait_direction = eu32_idma_wait_direction,
.idma_wait_a2o = eu32_idma_wait_a2o,
.idma_wait_o2a = eu32_idma_wait_o2a,
.idma_wait_a2o = eu_idma_wait_a2o,
.idma_wait_o2a = eu_idma_wait_o2a,
.idma_is_done = eu32_idma_is_done,
.idma_a2o_is_done = eu32_idma_a2o_is_done,
.idma_o2a_is_done = eu32_idma_o2a_is_done,
Expand Down
97 changes: 96 additions & 1 deletion drivers/redmule16/include/redmule16.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,99 @@

#pragma once

#include "redmule.h"
#include <stdint.h>
#include "redmule.h"
#include "utils/redmule_isa_utils.h" // redmule_mcnfig/marith, redmule_mm_* helpers

// The enqueue/commit/start helpers live here as static inline (rather than
// out-of-line in redmule16.c) so they fold into their mglib call sites
// (mg_redmule.c), which are compiled -O2 -flto: redmule16.c is not built with
// LTO, so an out-of-line definition there could not be inlined into mglib. This
// mirrors the eventunit32.h pattern (eu32_redmule_wait etc.).

/**
* Configure and launch an FP16 GEMM on the RedMulE accelerator.
* Computes: Y = X * W + Y where X is [M x N], W is [N x K], Y is [M x K].
*
* The function issues the configuration and arithmetic commands to RedMulE
* via custom RISC-V instructions (REDMULE_MM=0) or memory-mapped HWPE
* registers (REDMULE_MM=1), then returns immediately. The caller must wait
* for completion (e.g. via eu_redmule_wait).
*
* This variant enqueues the job without triggering it.
*
* @param ctrl RedMulE controller handle (unused internally, reserved for API consistency).
* @param x OBI (L1) address of input matrix X [M x N].
* @param w OBI (L1) address of weight matrix W [N x K].
* @param y OBI (L1) address of bias/output matrix Y [M x K]. Accumulated in-place.
* @param m Number of rows of X and Y.
* @param n Number of columns of X / rows of W (inner dimension).
* @param k Number of columns of W and Y.
*
* @return 0 on successful dispatch.
*/
static inline __attribute__((always_inline)) int redmule16_gemm_enqueue(redmule_controller_t *ctrl,
uint32_t x,
uint32_t w,
uint32_t y,
uint16_t m,
uint16_t n,
uint16_t k)
{
#if REDMULE_MM == 0
redmule_mcnfig(k, m, n); // Set GEMM dimensions via custom RISC-V instruction
redmule_marith(y, w, x); // Launch GEMM with matrix addresses via custom RISC-V instruction
#else
redmule_mm_mcnfig(k, m, n); // Set GEMM dimensions via memory-mapped HWPE registers
redmule_mm_marith(
y, w, x); // Launch GEMM with matrix addresses via memory-mapped HWPE registers
#endif

return 0;
}

/**
* Commits execution of an enqueued job on the HW queue.
*
* @param ctrl RedMulE controller handle (unused internally, reserved for API consistency).
*
* @return 0 on successful dispatch.
*/
static inline __attribute__((always_inline)) int redmule16_gemm_commit(redmule_controller_t *ctrl)
{
#if REDMULE_MM != 0
redmule_mm_commit();
#endif
return 0;
}

/**
* Commits and starts execution of an enqueued job on the HW queue.
*
* @param ctrl RedMulE controller handle (unused internally, reserved for API consistency).
*
* @return 0 on successful dispatch.
*/
static inline __attribute__((always_inline)) int
redmule16_gemm_commit_start(redmule_controller_t *ctrl)
{
#if REDMULE_MM != 0
redmule_mm_commit_trigger();
#endif
return 0;
}

/**
* Starts execution of an enqueued set of jobs.
*
* @param ctrl RedMulE controller handle (unused internally, reserved for API consistency).
*
* @return 0 on successful dispatch.
*/
static inline __attribute__((always_inline)) int redmule16_gemm_start(redmule_controller_t *ctrl)
{
#if REDMULE_MM != 0
redmule_mm_trigger();
#endif
return 0;
}
17 changes: 17 additions & 0 deletions drivers/redmule16/src/redmule16.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ int32_t redmule16_acquire(redmule_controller_t *ctrl)
#endif
}

/**
* Writes the hardware COMMIT_TRIGGER register, which commits and triggers
* a hardware job.
*/
int32_t redmule16_trigger(redmule_controller_t *ctrl)
{
#if REDMULE_MM == 1
redmule_mm_trigger();
#else
return 0;
#endif
}

/**
* Configure and launch an FP16 GEMM on the RedMulE accelerator.
* Computes: Y = X * W + Y where X is [M x N], W is [N x K], Y is [M x K].
Expand Down Expand Up @@ -77,10 +90,14 @@ int redmule16_gemm(redmule_controller_t *ctrl,
redmule_mm_marith(
y, w, x); // Launch GEMM with matrix addresses via memory-mapped HWPE registers
#endif
redmule_mm_commit_trigger();

return 0;
}

// redmule16_gemm_enqueue / _commit / _start are defined as static inline in
// redmule16.h so they fold into their mglib call sites (see the note there).

extern int redmule_init(redmule_controller_t *ctrl)
__attribute__((alias("redmule16_init"), used, visibility("default")));

Expand Down
Loading
Loading