Demo: Asynchronous Triangular Solver Testcase
“Demo” is not a hardware accelerator — mosaic_demo.pl runs a software algorithm (an asynchronous triangular solver) entirely on plain pico/spad tiles, using the DRAM/cache tile for shared storage. There is no dedicated accelerator tile involved.
tools/generate/mosaic_demo.pl sets use lib "$ENV{PWD}/../picorv_c/c_trisolver" and $fw_path = "$path/../picorv_c/c_trisolver", but that directory does not exist in the repository — the firmware actually lives in tools/picorv_c/c_demo/. This looks like a leftover from a directory rename; update the path to c_demo before running this testcase.
What It Does
The firmware (tools/picorv_c/c_demo/async_trisolve.c) implements an asynchronous sparse triangular solver across the mesh — each pico tile owns one row/column block of a sparse matrix (by default, a 2D Laplace/5-point-stencil matrix, ConstructLaplace2D5pt()), and tiles pass partial results to each other as soon as their dependencies are satisfied, rather than waiting on a global synchronization barrier. See P38-IUSG-Graphs for background on the algorithm.
Tile Layout
$param{'r'} = 7;
$param{'c'} = 8;
# generic_tile_array() fills the grid with 'pico' tiles, then:
$tile_array[0][1] = 'spad'; # one scratchpad tile inserted into the mesh
$param{'ddr4_flag'} = 1; # tile memory manager / cache enabled
$param{'ddr_cache_lines'} = 8;
$param{'ddr_init_file'} = 'test_tile_nop.hex';
A 7x8 = 56-tile mesh, mostly pico cores, with one scratchpad and the DRAM/cache tile providing shared backing storage. Firmware is compiled from async_trisolve.c, and check_pico_spad.sh, check_pico_ddr4_ctrl.sh, and check_pkt_cache.sh validate the run.
Software: async_trisolve.c
Each tile computes its own logical tile ID (accounting for the scratchpad tile occupying a slot in the middle of the grid) and, on tile 0, constructs the input matrix:
int tid_h = atoi(argv[1]);
int tid_r = tid_h % 8;
int tid_c = tid_h / 8;
int tid = tid_c * IN_HW_ROW + tid_r;
if (tid_h > 8) { tid = tid - 1; } // account for the scratchpad occupying slot 8
if (tid_h == 0) {
ConstructLaplace2D5pt(); // build the sparse input matrix (or smallExample())
}
Tiles then pass a “start” token down the array (qPut/qWait) to sequence dependent work, and use qGet to receive their initialization message (header + data) before beginning their portion of the triangular solve:
if (last tile in the array) {
qPut(0, 0); // signal completion back to tile 0
} else {
qWait(0, header);
qPut(next_tid_h, 0); // kick off the next tile in sequence
}
qGet(0, header);
qGet(0, data);
This models a data-flow-driven (rather than bulk-synchronous) solve: each tile only proceeds once it has received the values it depends on from its predecessor(s), which is the core idea behind an asynchronous triangular solver.