ch32c407/467, наличие в нем RVV, авто-векторизация кода в текущей
версии GCC: спешите хотеть... спешите иметь Итак...с одной стороны китайский винчип выкатил ch32v407/467 c ядром qkv3v.... казалось бы... не qkv4f как у 307... но если посмотреть внимательно... то мы обнаружим наличие целочисленного 64 битного векторного сопроцессора.
с другой в текщей ветка в gcc интенсивно пилится поддержка RVV для riscv, что постоянно отслеживаю проверяю ковыряю и по возможности ума подпиливаю.
Очень интересно заставить компилятор без интринсиков генерить векторные инструкции и ускорять код.
Использование интринсиков было реализовано давно и в общем то это общее место. обсуждать нечего. в примере покажу.
чтоб не растекаnсья буквами по пикселям - сразу пример в MRS
CH32V467VET_KGP.7z
для этих опытов я добавил в gcc описание ядра qkv3v и прибиндил -mcpu=ch32v407 к нему.
выложен сборки тулсов архив и описание сопутствующих вопросов тут
код main.c
#include "ch32v4x7.h"
#include <riscv_vector.h>
#include <stdint.h>
void vec_add_rv32(uint32_t *res, const uint32_t *a, const uint32_t *b, size_t n) {
for (size_t vl; n > 0; n -= vl, a += vl, b += vl, res += vl) [[likely]]
{
// 1. Установка длины вектора (vl) для 32-битных элементов (e32)
vl = __riscv_vsetvl_e32m1(n);
// 2. Загрузка векторов из памяти
vuint32m1_t va = __riscv_vle32_v_u32m1(a, vl);
vuint32m1_t vb = __riscv_vle32_v_u32m1(b, vl);
// 3. Векторное сложение
vuint32m1_t vres = __riscv_vadd_vv_u32m1(va, vb, vl);
// 4. Сохранение результата в память
__riscv_vse32_v_u32m1(res, vres, vl);
}
}
void auto_vec_add_rv32(uint32_t *res, const uint32_t *a, const uint32_t *b, size_t n)
{
for (size_t i = 0 ; i < n ; i++) [[likely]]
{
res[i] = a[i] + b[i] ;
}
}
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
int main(void)
{
SystemCoreClockUpdate();
uint32_t a[4] ;
a[0]=1 ;
a[1]=2 ;
a[2]=3 ;
a[3]=4 ;
uint32_t b[4] ;
b[0]=10 ;
b[1]=20 ;
b[2]=30 ;
b[3]=40 ;
uint32_t res[4];
asm volatile ("nop") ;
vec_add_rv32(res, a, b, 4);
asm volatile ("nop") ;
auto_vec_add_rv32(res, a, b, 4);
asm volatile ("nop") ;
while(1)
{
}
}
имеем две функции - в первой
void vec_add_rv32(uint32_t *res, const uint32_t *a, const uint32_t *b, size_t n)
явно используем intrinsics функции использующие и облегчающие кодирование параллельную обработку целых чисел.
вторая ее полный С-аналог.
void auto_vec_add_rv32(uint32_t *res, const uint32_t *a, const uint32_t *b, size_t n)
конечно хотелось бы чтоб компиллер САМ мог понять что цикл можно веторизировать и сгенерить примерно тожечто мы ручками делаем интринсиками
если скомпилировать это код моей сборкой и с правильными ключиками - он таки сможет показать волшебство!
riscv32-kgp-elf-gcc -msmall-data-limit=8 -msave-restore -fmax-errors=20 -fdump-rtl-expand -mcpu=ch32v407 -mcmodel=medlow -mabi=ilp32 -fopt-info-vec-all -ftree-vectorize -Ofast -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -fno-common -Wunused -Wuninitialized .......
0000021a <main>:
21a: fbbff2ef jal t0,1d4 <__riscv_save_0>
21e: 7179 addi sp,sp,-48
220: 2439 jal 42e <SystemCoreClockUpdate>
222: cd017057 vsetivli zero,2,e32,m1,ta,ma
226: 5208a0d7 vid.v v1
22a: 47a9 li a5,10
22c: 9617e157 vmul.vx v2,v1,a5
230: 47f9 li a5,30
232: 0210b1d7 vadd.vi v3,v1,1
236: 0211b0d7 vadd.vi v1,v1,3
23a: 0227c257 vadd.vx v4,v2,a5
23e: 02253157 vadd.vi v2,v2,10
242: 0808 addi a0,sp,16
244: 01810313 addi t1,sp,24
248: 00810893 addi a7,sp,8
24c: 02056127 vse32.v v2,(a0)
250: 02036227 vse32.v v4,(t1)
254: 020161a7 vse32.v v3,(sp)
258: 0208e0a7 vse32.v v1,(a7)
25c: 0001 nop
25e: 4711 li a4,4
260: 1014 addi a3,sp,32
262: 85aa mv a1,a0
264: 860a mv a2,sp
266: 0d0777d7 vsetvli a5,a4,e32,m1,ta,ma
26a: 02066087 vle32.v v1,(a2)
26e: 0205e107 vle32.v v2,(a1)
272: 8f1d sub a4,a4,a5
274: 20c7c633 sh2add a2,a5,a2
278: 20b7c5b3 sh2add a1,a5,a1
27c: 021100d7 vadd.vv v1,v1,v2
280: 0206e0a7 vse32.v v1,(a3)
284: 20d7c6b3 sh2add a3,a5,a3
288: ff79 bnez a4,266 <main+0x4c>
28a: 0001 nop
28c: cd017057 vsetivli zero,2,e32,m1,ta,ma
290: 02016107 vle32.v v2,(sp)
294: 02056207 vle32.v v4,(a0)
298: 02036087 vle32.v v1,(t1)
29c: 0208e187 vle32.v v3,(a7)
2a0: 101c addi a5,sp,32
2a2: 02220157 vadd.vv v2,v2,v4
2a6: 021180d7 vadd.vv v1,v1,v3
2aa: 0207e127 vse32.v v2,(a5)
2ae: 103c addi a5,sp,40
2b0: 0207e0a7 vse32.v v1,(a5)
2b4: 0001 nop
2b6: a001 j 2b6 <main+0x9c>
000002b8 <SystemInit>:
2b8: f1dff2ef jal t0,1d4 <__riscv_save_0>
nop-ами я разметил границы первой и второй функции. код разный и оба использую векторные инструкции, результат должен быть одинаковый. я не проверял - но проверь как только получу 4x7 микросхему. видно что циклы развернулись и сгруппировались по разному, чего бы это ни значило, что быстрее отработает - узнаем в результате опытов.
вывод - современный компиллер умеет ЭТО ДЕЛАТЬ. и нет причин этим не пользоватся.
для справки информирую - ST тоже пытается не курить бамбук, хотя по всем признакам на пару вместе с ARM. ЁбНах. Ltd. охцуевают от увиденного в сторону китайщины. и таки тоже всунули вектороный сопроцессор Helium (M-Profile Vector Extension) в микроконтроллер STM32N6x5, это как бы Неон, но кастрированный для микроконтроллерных ядер.
таким образом кто дочитал до этого мета, находится в месте из которого он может пойити и сразу написать sdr радиоприемник на микроконтроллере. все эти векторные расширения чудо как хороши при цифровой обработке сигнала с ацп...
все было бы прекрасно и весело если не гавено и грустно - это мое утробное вещание содержит контент про буржуйские и китайские продукты. а не про русские... по ка что у нас только один поскрепный амур, который как бы соответсвует простейшим МК, хорошо что хоть 32 битный. и то радость. Микросхемы НИИЭТ имеют определенную ценность, но чтото я струдом верю что они как Амур делаются у нас в стране. да и векторного расширения я еще у наших не встречал.. а жаль. оч хоцца применить поскрепную микросхему. аж в заду жжет как хоца.