Pull to refresh

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);
}

Интересный проект - подключить к VexRiscV :)

Можно, но это будет выглядеть как инородное тело. У автора VexRiscv есть что-то своё, но я пока еще погружался в эту тему.

Рассмотрим простой пример прибавления константы к 21 элементу вектора целочисленных значений и умножения его на константу написанный на C

А там не нужен цикл на случай, если регистры реально короче (и нам, например, вернули vl = 4)?

Автор , похоже, рассматривает только случай работы с Ara, а там длина векторного регистра ого-го :) Поэтому все "за раз"(с).

Вычитанные из памяти значения 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-битная потеряна.

за этим векторным ускорителем всё нужно проверять скалярным процессором. Иначе можно попасть в просак :)

Sign up to leave a comment.

Articles