Comments 14
Спасибо за интересную статью! Жду продолжения по разбору реализации VLSU лэйнов в Ara.
В Вашем примере (функция bar) Вы показываете как выполнить одну итерацию над vl элементами вектора, но не показываете как происходит обработка оставшейся части вектора и в том числе его хвоста. В этом нюансе, на мой взгляд, кроется одна из самых интересных отличительных особенностей RVV - для обработки хвоста вектора не требуется отдельное условие, так как в каждой итерации на вход инструкции vsetvl может передаваться остаток необработанной длины вектора. Т.е. цикл полной обработки мог бы выглядеть как-то так:
size_t add_1_mul_2(const int32_t* a_, int32_t *b_, size_t avl) {
size_t vl = vsetvl_e32m1(avl);
vint32m1_t buf_a = vle32_v_i32m1(a_, vl);
vint32m1_t buf_b = vadd(buf_a, 1, vl);
vint32m1_t buf_c = vmul(buf_b, 2, vl);
vse32_v_i32m1(b_, buf_c, vl);
return vl;
}
main() {
// подготовка
size_t avl = VERY_LONG_VECTOR_SIZE;
const int32_t* a_ = a->data;
int32_t* b_ = b->data;
// обработка всего вектора
do {
size_t vl = add_1_mul_2(a_, b_, avl);
avl -= vl;
a += vl;
b += vl;
} while(avl);
}
Рассмотрим простой пример прибавления константы к 21 элементу вектора целочисленных значений и умножения его на константу написанный на C
А там не нужен цикл на случай, если регистры реально короче (и нам, например, вернули vl = 4)?
Вычитанные из памяти значения VLDU распределяет по 4 лейнам
Ширина лейна 64 бита, ширина шины данных 128 бит. Получается, что только половина лейна для 32-бит операндов загружена? И при сохранении опять же должны браться 4ре 32-разрядных операнда, по одному из каждого лейна . У Вас написано
Все лейны заканчивают обработку одновременно и VSTU записывает принятые значения по 64 бита сразу подряд от всех лейнов.
Немножко мне непонятно. Можете обьяснить подробнее, с учетом ширины шины данных?
На сколько я понимаю, все лэйны работают параллельно и вычитывают свои регистры одновременно. Но не из памяти, а из кэша. Соответственно кэш должен быть снабжен соответствующим числом портов.
VLDU из памяти вычитывает 128-битное значение, затем оно разбивает его и побайтово распределяет в сигналы очереди результатов result_queue_d
, из которых они уже попадут в лейны через ldu_result_wdata
. В исходниках на VLDU - vldu.sv
- это выглядит так:
// Copy data from the R channel into the result queue
for (int unsigned axi_byte = 0; axi_byte < AxiDataWidth/8; axi_byte++) begin : axi_r_to_result_queue
...
// And then shuffle it
automatic int unsigned vrf_byte = shuffle_index(vrf_seq_byte, NrLanes, vinsn_issue_q.vtype.vsew);
...
// Copy data and byte strobe
result_queue_d[result_queue_write_pnt_q][vrf_lane].wdata[8*vrf_offset +: 8] = axi_r_i.data[8*axi_byte +: 8];
Функция shuffle_index
из ara_pkg.sv
и выполняет основную работу по распределению данных по лейнам. Она выдает номер лейна и номер байта в 64-битном числе куда положить пришедший из памяти байт. В комментариях перед функцией схема как распределяются байты в зависимости от размера элемента вектора SEW.
В примере сначала из памяти по AXI приходит 128-битное число 0x01010101_00000000_00000000_00000000
из которого валидны только 8 первых байт 0x01010101_00000000
. Т.к. SEW 32-бита, то первые 4 байта 0x00000000
попадают в результат для лейна 0, 0x01010101
в результат для лейна 1. Затем приходит число 0x05050505_04040404_03030303_02020202
. В нем уже все 16 байт валидны и по 4 байта 0x02020202
и 0x03030303
попадают соответственно в лейны 2 и 3. 0x04040404
попадет опять в лейн 0, но не в следующий элемент очереди, а верхние 4 байта 64-битного числа 0-го элемента очереди данных для лейна. Далее подобным образом обрабатываются остальные значения.
К сожалению цикл плохо трейсится, поэтому временные диаграммы не очень наглядны.

При сохранении в VSTU аналогично есть копирование данных из интерфейса с лейнами в интерфейс AXI. Только теперь данные собираются по индексам из stu_operand
, чтобы был правильный порядок в памяти. vstu.sv
// Copy data
axi_w_o.data[8*axi_byte +: 8] = stu_operand[vrf_lane][8*vrf_offset +: 8];
Поскольку от лейнов приходят 64-битные значения, то и записи AXI 64-битные.

Спасибо. Ответ даже слишком подробный :)
Поскольку от лейнов приходят 64-битные значения, то и записи AXI 64-битные.
У меня подозрение, что записи по 64 бита потому что у Вас в примере адрес не выровнен на границе 4-х слов. По логике записи должны быть 128-битные. Если не трудно, посмотрите,пожалуйста, с другим адресом.
Да, Вы правы. Если выровнять адрес записи по 16-байт, то записи по AXI из VSTU будут по 128 байт.

Похоже что не любит VSTU невыровненные адреса. Интересно, если бы SEW=8 и адрес невыровнен (..F1), неужели по одному байту за такт сохранение было бы? :)
На картинке у Вас получилось 5 тактов на 21 элемент? Странно.
Попробовал SEW=8 и адрес выровненый по байту (адрес 0x80001A01
). VSTU действительно сделал все записи по AXI однобайтовыми.
5 тактов на 21 элемент
Выглядит как баг симуляции. Должно быть 6 записей по AXI. Первые 5 полноценные по 128 бит. Сигнал WSTRB в каждом своем бите показывает какой из байтов валидный. При этих 5-и записях строб 0xFFFF
, т.е. все 16 байт валидны. При последней записи WSTRB 0x000F
, т.е. только поледние 4 байта валидны. Так как раз 21 элемента по 4 байта. На временной диаграмме одна запись 128-битная потеряна.
Векторизация в RISC-V. Основы