-
Постов
2 582 -
Зарегистрирован
-
Победитель дней
2
Сообщения, опубликованные repstosw
-
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
Есть ещё Sigma Studio от Analog Devices. Там есть поддержка ADAU1472 - это ДСП от АД с HiFi4 ядром. Но это не для программистов. Скорее для музакеров и производителей акустических систем. Типа визарда - накидать через GUI акустический конвеер (фильтры, ресемплеры, дисторшены, эхо, итп..). Сильно узкоспецифичен.
Плагин на каждый чип там в виде DLL. Наверное проприетарный формат. DSP от T113-s3 туда не воткнёшь. Но можно выдирать бинарник (код) для ДСП через линуксовый парсер или ручками из *.dspproj.
14 minutes ago, mantech said:У меня большое подозрение, что на этом процессоре придется делать оптимизацию "руками", путем написания асмовых программ(((
Чего мелочиться - сразу в машинных кодах писать фрагменты с VLIW/SIMD
Документация на опкоды есть. Возможно оформить в виде макросов и вызывать их.
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
36 minutes ago, mantech said:Удалось ли повысить быстродействие теста вышеприведенного DSP?
Нет. С ABI-call0 она осталась прежней. Собственно, я и не надеялся увеличить скорость через ABI.
Для ускорения того бенчмарка нужно распараллеливание на VLIW. И то - у меня есть сомнения: что тот код в исходном виде будет работать быстрее - его придётся переписать под архитектуру ДСП. У C6745 с которым я возился несколько лет назад, там от силы 2-3 распараллеливания из доступных 8 получалось. И там довольно хороший оптимизатор - иногда по пол-часа компилируется... На обычном сишном алгоритме. VLIW - это ещё не так просто. Не каждый алгоритм поддастся распарралеливанию. Так что VLIW - не более чем коммерческий ход.
Так что тему пора переименовать в "Allwinner T113-s3 уделал HiFi4 DSP, смеяться или плакать?"
26 minutes ago, sasamy said:чтобы повысить быстродействие надо расширения ISA использовать, сомневаюсь что они есть в GCC, не зря же они свой оптимизирующий компилятор делают.
C Cadense Xtensa Tools всё гибло: требуют лицензию. И постоянно включенный комп с их сервером, который пасёт лицензию. Крякнутых версий не нашёл. Так что придётся довольствоваться General Purpose выхлопом от GCC без VLIW и SIMD. Хотя, вполне допускаю, что какой-нибудь местный хакер сможет в коде программ с command-line Inteface найти место где проверяется лицензия и заменить опкод перехода. Тот кто дружит с трассировкой и дебагингом могут это сделать. Если конечно в Cadence защиту от трассировки не сделали
А что за оптимизирующий компилятор? Где его можно скачать чтоб опробовать?
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
Собрал в Линуксе тулчейн для Винды под ABI-call0. Так называемая "канадская" кросс-компиляция 🙂
Теперь можно забыть о головной боли, связанной с регистровыми окнами, контроле/подмене стека, исключениях.
В ABI=call0 эти исключения не нужны: Window owerflow/underflow, Alloca, Syscall.
Вся работа с регистрами процессора прозрачна и в стиле "обычных" архитектур. А также доступны манипуляции с указателем стека для резервирования временных данных(стековые фреймы).
Простейший рабочий стартап выглядит так:
.section .entry.text,"x" .align 4 .global Start Start: j BypassLiteral .section .literal,"a" .global Literal Literal: //Literals located here .text .align 4 .global BypassLiteral BypassLiteral: movi a0,0 //disable all interrupts wsr.intenable a0 movi a0,VECTORS //vector table base address wsr.vecbase a0 movi a0,0 //PS.WOE = 0, PS.UM = 0, PS.EXCM = 0, PS.INTLEVEL = 0 wsr.ps a0 rsync /* Xtensa® Instruction Set Architecture (ISA) Reference Manual 8.1.9 Stack Initialization. p. 618 */ movi a0,0 movi sp,__stack-16 addi a4,sp,32 //point 16 past extra save area s32e a4,sp,-12 //access to extra save area call0 libc_init call0 board_init call0 main
Пример как выглядит обработчик прерывания:
//... .org 0x1FC //Kernel (IntLevel1) Vector j Kernel //... .align 4 Kernel: addi sp,sp,-64 s32i a0,sp,0 s32i a2,sp,4 s32i a3,sp,8 s32i a4,sp,12 s32i a5,sp,16 s32i a6,sp,20 s32i a7,sp,24 s32i a8,sp,28 s32i a9,sp,32 s32i a10,sp,36 s32i a11,sp,40 s32i a12,sp,44 s32i a13,sp,48 s32i a14,sp,52 s32i a15,sp,56 movi a2,PS_INTLEVEL(1) //mask interrupts to prevent stack corrupt in C/C++ calls wsr.ps a2 rsync call0 INTC_Handler l32i a15,sp,56 l32i a14,sp,52 l32i a13,sp,48 l32i a12,sp,44 l32i a11,sp,40 l32i a10,sp,36 l32i a9,sp,32 l32i a8,sp,28 l32i a7,sp,24 l32i a6,sp,20 l32i a5,sp,16 l32i a4,sp,12 l32i a3,sp,8 l32i a2,sp,4 l32i a0,sp,0 addi sp,sp,64 rfi 1
Дополнительно собрал libhal(включение кеширования для DSP) под ABI-call0 из исходников.
-
1
-
-
4 hours ago, sasamy said:
кеширование чего - кода или данных ?
И того и другого.
4 hours ago, sasamy said:диапазон MMIO периферии не кешируется
Очевидные вещи говорите.
Факт есть факт. И он установлен. Причина: процессор при включенном кеше кода бежит быстрее, и выход из прерывания происходит раньше, чем регистр успевает обновиться. Известная проблема. Я лишь акцентировал внимание на то, как это можно избежать. Три варианта:
1. отключить кеш (самый плохой вариант)
2. прочитать регистр после его записи (кросс-портабельный вариант)
3. вставить инструкцию memw (самый лучший вариант для DSP, но платформенно-зависимый)
4 hours ago, sasamy said:я пока кроме экспериментов не касался прерываний от периферии, доделал загрузку кода в DSP из Linux на ARM
Линукс, RTOS и другие ОС, не покажут вам спорадические прерывания. Слишком многое остаётся скрыто "за капотом" )))
Вот за это мне и нравится BareMetal: особенности архитектуры чувствуешь на себе ))
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
Поймал забавную багу. Если включено кеширование у DSP, то подтверждение прерывания для DMA должно быть таким:
void DMA_Handler(void) { //... DMA_IRQ_PEND_REG1=DMA_IRQ_PEND_REG1; __asm__ __volatile__ ("memw"); }
или таким:
void DMA_Handler(void) { //... DMA_IRQ_PEND_REG1=DMA_IRQ_PEND_REG1; (void)DMA_IRQ_PEND_REG1; }
Иначе после завершения прерывания - залетает один раз повторно! Только ОДИН раз! И так до следующего момента когда прерывание наступит - снова 2 раза.
Если кеш отключить, то можно просто вот так:
void DMA_Handler(void) { //... DMA_IRQ_PEND_REG1=DMA_IRQ_PEND_REG1; }
Регистр объявлен как volatile.
На T113-s3 синхронизировать память-регистр было не нужно (но в конце обработчика прерывания стоит инструкция ISB).
Ещё особенность: если разрешить прерывание GPIO обоим ядрам, то первым на него реагирует DSP, следом T113-s3.
-
Запустил тест на быстродействие: длинный кодер-декодер Рида Соломона GF(2^16). Результаты огорчили: 4 FPS / 2 FPS на DSP:
Encode: 4 FPS Decode: 2 FPS Encode: 4 FPS Decode: 2 FPS Encode: 4 FPS Decode: 2 FPS
На CPU быстрее:
Encode: 26.2 FPS Decode: 12.4 FPS Encode: 26.2 FPS Decode: 12.2 FPS Encode: 26.2 FPS Decode: 12.3 FPS
Кеширование включено. Без него вообще - менее 0.1 FPS.
Бенчмарк: RS(2^16)_test.zip
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
2 hours ago, sasamy said:abi можно поменять через параметры компилятора но непонятно как потом будет стыковаться такой винегрет
Quote-
-mabi=name
-
Generate code for the specified ABI. Permissible values are: ‘call0’, ‘windowed’. Default ABI is chosen by the Xtensa core configuration.
Не работает этот параметр с компилятором. Пишет - unrecognized option.
Для линкера аналогичные:
--abi-windowed Choose windowed ABI for the output object --abi-call0 Choose call0 ABI for the output object
Работает только -Wl,--abi-windowed, c сall0 вываливаются ошибки во время линковки - этот тулчейн сконфигурирован только для windowed. Жаль... ((
Скорее всего можно пересобрать и для call0-abi.
Ещё касаемо ABI.... В ассемблерном листинге наблюдаю только вызовы call8, callx8. Не вижу call4, call12, callx4, callx12. тоесть какой-то фиксированный протокол вызова выходит. Хотелось чтобы это можно было задавать либо глобально, либо для каждой функции - как в нормальном GCC.
Я уже не говорю об атрибутах функции - pure, naked, interrupt... Это всё нужно для системного уровня.
-
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
4 hours ago, repstosw said:В чём может быть дело? Как вообще с этим стеком работать так, чтобы не нарушать работу оконных переключений во время выполнения сишного обработчика?
Вопрос решён. Нужно использовать DSP в режиме Kernel, а не User. В режиме Kernel манипуляции со стековыми фреймами работают.
//Было: movi a3,PS_UM|PS_WOE_ABI|PS_INTLEVEL(0) wsr.ps a3 //Стало: movi a3,PS_WOE_ABI|PS_INTLEVEL(0) wsr.ps a3
При этом обработчики прерываний уже будут висеть на Kernel: .org 0x1FC
1 hour ago, sasamy said:abi можно поменять через параметры компилятора но непонятно как потом будет стыковаться такой винегрет
Это при линковке всего и вся. Речь шла о том как сделать это с одной функцией, чтобы компилятор не создавал пролог: entry a1,N и вместо оконного выхода retw использовал регистровый ret.
Эту функцию можно было вызвать в ассемблерном сорце через call0. Если писать в ассемблере, то это работает. А вот как сишную функцию сделать ABI=CALL0 - ХЗ, через GCC #pragma опция игнорируется.
1 hour ago, sasamy said:У себя я экспериментировал с freertos - ассемблера вообще не касался, добавил обработчик intc по аналогии с msgbox, там отпендил intc и gpio и вывел в консоль сообщение
Мне нужно было сохранить регистры в стековом фрейме. Обработчики у меня и так прекрасно работают )))
Плюс я не использовал функции из фриртос, а писал установку прерываний самостоятельно.
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
Столкнулся с такой проблемой. В ассемблерном сорце в коде обработчика прерывания пытаюсь сохранить в стеке регистры, которые восстанавливаются перед выходом из обработчика.
Для этого нужно сместить(уменьшить) указатель стека на число байт, которое надо для резервирования данных. Затем сохранить регистры, вызвать обработчик(на С), восстановить регистры, и обратно сместить(увеличить) указатель стека. Обработчик срабатывает, но при выходе из прерывания генерируется Double Exception и программа падает.
Если же место сишной функции вызвать call0 (не оконный вызов), то всё работает нормально.
В чём может быть дело? Как вообще с этим стеком работать так, чтобы не нарушать работу оконных переключений во время выполнения сишного обработчика?
Ну или с другого края зайду - как компилятору принудительно указать, что именно эту функцию оформить для вызова call0, вместо виндовых call4,8,12 ?
Interrupt: wsr.excsave1 a0 //save a0 addi sp,sp,-4 //decrease stack pointer up -4 s32i a3,sp,4 //a3 => [sp+4] movi a3,PS_UM|PS_WOE_ABI|PS_INTLEVEL(1) wsr.ps a3 rsync call8 Handler movi a3,PS_UM|PS_WOE_ABI|PS_INTLEVEL(0) wsr.ps a3 rsync l32i a3,sp,4 //[sp+4] => a3 rsr.excsave1 a0 //restore a0 addi sp,sp,4 //correct stack ------ !!! rfe .. //Double exception
Сейчас вынужден хранить регистры либо в выделенной памяти, либо в самом начале стека и не изменять содержимое регистра sp (=a1).
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
В общем этот китаец взял описание регистров R_INTC из Tina Linux T113-s3:
Tina-t113\lichee\rtos-dsp\rtos-hal\hal\source\intc\platform\intc-sun8iw20.h Tina-t113\lichee\rtos-dsp\rtos-hal\hal\source\intc
Сделал архив с сорцами, всё что касается INTC: intc.zip
В принципе, ничего нового там я не увидел.
Номера источников прерываний для T113-s3 интересно было бы увидеть. Иначе, методом бинарного отсева приходится искать.
Заявление: "номер обоаботчика в DSP = номеру в T113-s3 минус 32" - не работает. К тому же, в 96 прерываний все прерывания не уложатся.
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
1 hour ago, GenaSPB said:Из мануала:
If the corresponding bit is set, the interrupt with the lower or the same priority level is masked
Разрешение вложенности при том же приореитете как-бы.Любое ненулевое значение в этих регистрах приводит к тому, что прерывание переставало работать. Оставил их как есть (при включении питания они =0).
В идеале конечно бы хотелось сдвинуть вектор INTC с Level1/User на свободные Level 2,3,4. Но это можно только в XEA3. В XEA2 вроде намертво прибито.
Итого в Level1/User уже три случая работают:
.org 0x21C //User User: wsr.excsave1 a0 //save a0 rsr.exccause a0 beqi a0,EXCCAUSE_LEVEL1,_xt_lowint1 //INTC beqi a0,EXCCAUSE_ALLOCA,_xt_alloca_exc //Alloca MOVSP exception beqi a0,EXCCAUSE_SYSCALL,_xt_syscall_exc //Syscall setjmp exception Dummie: j Dummie
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
Удалось заставить DSP видеть прерывания от GPIOC. А также произвести настройку дочернего контроллера прерываний INTC.
Номер источника прерываний от GPIOC - 42:
#define GPIOC_INTC_SOURCE 42
Общее прерывание INTC - номер 20, как об этом упомянули выше. Инициализация INTC для GPIOC:
#define INTC_IRQ 20 extern void *INTC_Vectors; //объявлена в ассемблере void Init_INTC(u32 intsource) { pintc_regs->base_addr=(u32)&INTC_Vectors; //установаить базоввый адрес таблицы векторов //замаскировать все источники прерываний pintc_regs->mask =~0; pintc_regs->mask1=~0; pintc_regs->mask2=~0; //запретить все источники прерываний pintc_regs->enable =0; pintc_regs->enable1=0; pintc_regs->enable2=0; //демаскировать нужный источник прерывания if(intsource<32)pintc_regs->mask=~(1U<<intsource); else if(intsource<64)pintc_regs->mask1=~(1U<<(intsource-32)); else pintc_regs->mask2=~(1U<<(intsource-64)); //разрешить нужный источник прерывания if(intsource<32)pintc_regs->enable=(1U<<intsource); else if(intsource<64)pintc_regs->enable1=(1U<<(intsource-32)); else pintc_regs->enable2=(1U<<(intsource-64)); Int_Enable(1<<INTC_IRQ); //enable INTC interrupt } void Int_Enable(u32 intenable) { __asm__ __volatile__ ("wsr.intenable %0" :: "r"(intenable)); } int main(void) { printf("\nHiFi4 DSP...\n"); Init_INTC(GPIOC_INTC_SOURCE); while(1) { UART_putc('_'); DelayMS(20); } //...
INTC_Vectors - это таблица векторов прерываний - источников от T113-s3:
.align 4 //здесь выравнивание обязательно! INTC_Vectors: .word INTC_Dummie //+0 *4 .word INTC_Dummie .word INTC_Dummie //... .word INTC_Dummie .word GPIOC_Handler //+42 *4 .word INTC_Dummie //... INTC_Dummie: movi a3,'I' movi a2,UART_THR memw s32i.n a3,a2,0 j .
INTC_Dummie - заглушка на остальные источники прерываний. GPIOC_Handler - обработчик для GPIO C :
void GPIOC_Handler(void) { printf("GPIOC Interrupt %c\n",rand()); PC_EINT_STATUS=PC_EINT_STATUS; //подтверждение прерывания GPIOC }
Общее прерывание INTC висит на смещении .org 0x21C в таблице векторов прерывний DSP - прерывание IntLevel1/User. Вызов обработчика прерывания:
//... movi a3,PS_UM|PS_WOE_ABI|PS_INTLEVEL(1) wsr.ps a3 rsync call8 INTC_Handler movi a3,PS_UM|PS_WOE_ABI|PS_INTLEVEL(0) wsr.ps a3 //... //или так (быстрее, вместо INTC_Handler): #define INTC_VECTOR 0x01700800 //... movi a3,INTC_VECTOR memw l32i a3,a3,0 l32i a3,a3,0 callx8 a3 //...
INTC_Handler - обёртка вызова по содержимому указателя на регистр:
void INTC_Handler(void) { ((void (*)(void))(*(u32*)pintc_regs->vector))(); }
Тоесть работает это так: вначале происходит прерывание Level1/User от INTC (разрешение - бит 20 регистра intenable) - INTC_Handler, затем считывается содержимое регистра pintc_regs->vector, там должен быть адрес обработчика прерывания источника: базовый адрес таблицы векторов источников прерваний + актуальное смещение для источника (для GPIOC: + (42*4) - в байтах). Далее вызывается функция по этому адресу - GPIOC_Handler.
На самом деле таблицу векторов источников прерываний делать необязательно - можно просто написать одну функцию (вместо INTC_Handler), а выделить источник прерывания можно через регистры:
pintc_regs->pending; pintc_regs->pending1; pintc_regs->pending2;
Но с таблицей векторов источников прерываний выглядит более системнее.
P.S. Остальные регистры - их назначение понять не удалось:
pintc_regs->resp =0; //все должны быть нулями, иначе не работает pintc_regs->resp1=0; pintc_regs->resp2=0; pintc_regs->priority0=; //не влияет pintc_regs->priority1=; pintc_regs->priority2=; pintc_regs->priority3=; pintc_regs->priority4=; pintc_regs->priority5=; pintc_regs->group_config0=; //не влияет pintc_regs->group_config1=; pintc_regs->group_config2=; pintc_regs->group_config3=;
-
3 hours ago, sasamy said:
мне кажется самый реалистичный вариант спросить у автора порта freertos для т113 чтобы он поделился инфой - явно не из астрала структура с описанием INTC. Спрашивали у дистрибьютера allwinner, они дали всё что у них есть - архив с pdf-ми на китайском но там ничего нет интересного.
3 hours ago, mantech said:Если знать разработчика - проще доки найти на подобные вещи, т.к. они могут где-то еще использоваться...
Мне кажется, что это Interrupt Controller от A10. По крайней мере большинство регистров совпадает по названиям и смещениям:
Попробовал в pintc_regs->enable записать 0x1. Прерывание от DMA перестало работать на DSP. Выводы очевидны: это оно.
-
28 minutes ago, sasamy said:
вопрос как завести прерывания нужной периферии и возможно ли это, а не тыкать только то что после сброса по умолчнию подключено
С GPIOC номер не прокатил - DSP не видит прерывание от нажатия кнопки (CPU видит).
На счёт каналов DMA, что с 8 по 15 доступны для DSP - явно сказано в даташите на T113-s3.
В board-init.c есть такой таинственный кусок:
#define SUNXI_DSP_IRQ_NMI 0 #define SUNXI_DSP_IRQ_DSP_TIMER1 1 #define SUNXI_DSP_IRQ_DSP_TIMER0 2 #define RINTC_IRQ_MASK 0xffff0000 #define SUNXI_R_INTC_PBASE (0x01700800) #define SUNXI_DSP_IRQ_R_INTC 20 #define SUNXI_RINTC_IRQ_NMI (RINTC_IRQ_MASK | 0) /* not use */ #define SUNXI_RINTC_IRQ_SOURCE_MAX 88 /* FIXME: can be decreased */ struct intc_regs { /*offset 0x00 */ volatile uint32_t vector; volatile uint32_t base_addr; volatile uint32_t reserved0; volatile uint32_t control; /*offset 0x10 */ volatile uint32_t pending; volatile uint32_t pending1; volatile uint32_t pending2; volatile uint32_t reserved1[9]; /*offset 0x40 */ volatile uint32_t enable; volatile uint32_t enable1; volatile uint32_t enable2; volatile uint32_t reserved2[1]; /*offset 0x50 */ volatile uint32_t mask; volatile uint32_t mask1; volatile uint32_t mask2; volatile uint32_t reserved3[5]; /*offset 0x70 */ volatile uint32_t fast_forcing; volatile uint32_t reserved4[3]; /*offset 0x80 */ volatile uint32_t priority0; volatile uint32_t priority1; volatile uint32_t reserved5[14]; /*offset 0xc0 */ volatile uint32_t group_config0; volatile uint32_t group_config1; volatile uint32_t group_config2; volatile uint32_t group_config3; }; static volatile struct intc_regs *(pintc_regs)=(volatile struct intc_regs*)SUNXI_R_INTC_PBASE; void board_init(void) { _cache_config(); pintc_regs->enable = 0x0; pintc_regs->mask = 0x0; pintc_regs->pending = 0xffffffff; pintc_regs->enable1 = 0x0; pintc_regs->mask1 = 0x0; pintc_regs->pending1 = 0xffffffff; pintc_regs->enable2 = 0x0; pintc_regs->mask2 = 0x0; pintc_regs->pending2 = 0xffffffff; }
Может быть эти регистры отвечают за коммутацию прерываний на DSP. В общем ХЗ. Тут только копать исходники, либо искать документацию, либо экспериментировать. Либо всё вместе :)
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
1 hour ago, repstosw said:Я думаю, что всё прозаически просто. У регистра intenable 32 бита. Скорее всего, некоторые из них разрешают прерывания от периферии T113-s3. Msgbox именно по такому принципу сделан: биты 3,4 разрешают его прерывание.
Моя гипотеза подтвердилась.
Запустил кодек в режиме вывода звука (DAC) через DMA. Поймал прерывание со стороны DSP - в регистре intenable нужно установить бит 8.
Работает только на DMA каналах с 8 до 15. Прерывания от них доступны DSP, не доступны CPU.
Каналы DMA 0..7 : прерывания не доступны DSP, но доступны CPU.
Проверил на прерываниях по заполнению половины буфера(Half) и по полному заполнению(Full).
Тот же самый вектор - Level3Int.
Итого, что уже известно:
Регистр intenable. Установка бит: 1 - Timer1 2 - Timer0 3 - MsgBox 8 - DMA
Надо ещё GPIO проверить на каком бите и векторе сидят...
-
9 minutes ago, sasamy said:
External interrupts are connected to the BInterrupt input pins and can be configured
to be of three types:
Level-sensitive
Edge-triggered
NMI (edge-triggered non-maskable interrupt)
Note: Only one NMI interrupt signal is allowed.Это не тот уровень который приоритет :) Это высокий-низкий в плане сигнала.
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
1 hour ago, repstosw said:Обнаружил, что компилятор xtensa-hifi4-elf-gcc для DSP не выравнивает константные строки, если задан уровень оптимизации -Os (по размеру).
Проблему решили с помощью таких замечательных макросов:
#define __PACKED_STRUCT struct __attribute__((packed,aligned(1))) #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ*)(const void*)(addr))->v) #define __UNALIGNED_UINT32_WRITE(addr,val) (void)((((struct T_UINT32_WRITE*)(void*)(addr))->v)=(val)) __PACKED_STRUCT T_UINT32_READ{uint32_t v;}; __PACKED_STRUCT T_UINT32_WRITE{uint32_t v;};
1 hour ago, sasamy said:но есть возможность любое внешнее прерывание смапить на NMI а у него level 5, правда как это делать не знаю - в freertos NMI не используется
Откуда эта информация?
1 hour ago, sasamy said:Ещё теоретически должен быть коммутатор с выбором какое прерывание периферии на какое прерывание DSP подключить и в таком случае периферийные прерывания можно раскидать на разные уровни, но это сейчас главная загадка 🙂
Я думаю, что всё прозаически просто. У регистра intenable 32 бита. Скорее всего, некоторые из них разрешают прерывания от периферии T113-s3. Msgbox именно по такому принципу сделан: биты 3,4 разрешают его прерывание.
7 minutes ago, mantech said:Вот меня тоже вопрос одолевает, скажем, нужно сделать систему авторегулирования скорости, нужно подключить энкодер к МК, реализовать ПИД алгоритм и вывести выходную частоту для внешнего ПЧН. И вот можно-ли это реали зовать на ДСП? Потребуются минимум прерывания от 2х внешних линий энкодера, и таймера. Для посл. прерывание есть, а вот для линий GPIO?
В настоящий момент, можно сконфигурить GPIO как вход прерывания и размаскировать весь регистр intenable и помотреть на какой вектор упадёт прерывание. Или не упадёт, если предположения не верны. Правда биты 31, 1, 2 лучше не устанавливать - это ошибка операции с памятью, и два таймера
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
Обнаружил, что компилятор xtensa-hifi4-elf-gcc для DSP не выравнивает константные строки, если задан уровень оптимизации -Os (по размеру).
Конкретно вот здесь:
dsp_msgbox_channel_send_2CPU(MSGBOX_CHANNEL,(u32*)"Hello, T113-s3 CPU! I'm HiFi4 DSP :) I have receive your message!\n\n",67);
Несмотря на то, что в прототипе функции явно указан указатель на 4-байтный тип:
void dsp_msgbox_channel_send_2CPU(u32 ch,const u32 *bf,u32 len);
Строка выводится с артефактами.
Если же сделать так:
u8 tmp[]="Hello, T113-s3 CPU! I'm HiFi4 DSP :) I have receive your message!\n\n"; dsp_msgbox_channel_send_2CPU(MSGBOX_CHANNEL,(u32*)tmp,67);
Или так:
static /*const*/ u8 tmp[]="Hello, T113-s3 CPU! I'm HiFi4 DSP :) I have receive your message!\n\n"; dsp_msgbox_channel_send_2CPU(MSGBOX_CHANNEL,(u32*)tmp,67);
То всё работает нормально.
Проверил адреса указателей: в случае когда строка сразу загоняется в функцию - её адрес не кратен 4-м: 0x0003877B.
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
5 hours ago, mantech said:А зачем эти мутарства с ФИФО, есть же общая память? Давнул прерывание процу, что ДСП что-то сделал, а данные забираешь из общей памяти и наоборот, не??
Прерывания происходят по порогу срабатывания FIFO: он настраивается: 1/2/4/8.
На счёт общей памяти согласен - большие массивы данных только так и давать-забирать.
FIFO относительно медленный по сравнению с памятью, и к тому же, если он заполнен (тоесть кто-то ещё данные не прочёл) то передача приостанавливается. Тоесть максимум 32 байта отправил, и надо ждать пока как минимум 4 байта не прочтётся.
"Первый вошёл, первым и вышел"... Поэтому обязательно как минимум: кто-то пишет И кто-то читает. Но с прерываниями - это не проблема.
Можно заполнять память и давать 4 байта в Msgbox. Прерывание будет. И времени много не займёт 🙂
QuoteДавнул прерывание процу, что ДСП что-то сделал,
Расскажите, как ? Как c-инициировать искуственно аппаратное прерывание? 🙂
Cкорее всего, линии IRQ программисту недоступны.
Однако меня беспокоит другой вопрос. Сейчас прерывание Msgbox висит на том же векторе, что и прерывание Таймера. А именно - IntLevel3. Пока не разобрался с определением источника прерывания, чтобы сделать селектор. Сделал только селектор для исключений (там проще).
Есть ли возможность отмапить источники прерываний на другой вектор? Их куча свободных: IntLevel 2,4,5.
IntLevel 1- занят - на нём висят исключения
IntLevel 3 - Timer.
В C6745 можно любой источник прерывания повешать на любой вектор.
-
Сделал Messagebox. От CPU к DSP. И от DSP к CPU. По прерываниям. Использовал только прерывания по приёму данных. Прерывания передачи данных не использовал (мне не нужно).
В FreeRTOS нет примера как работать с messagebox.
Как всегда, ничего просто не бывает, пришлось разбираться. Намудрили с базовыми адресами - было не понятно, кому какой принадлежит в случае если передавать туда и обратно.
Базовые адреса в рабочем примере распределены следующим образом:
//Со стороны DSP: SUNXI_MSGBOX_ARM_BASE - посылка сообщения к CPU SUNXI_MSGBOX_DSP_BASE - инициализация, обработчик прерывания по приёму данных от CPU //Со стороны CPU: SUNXI_MSGBOX_DSP_BASE - посылка сообщения к DSP SUNXI_MSGBOX_ARM_BASE - инициализация, обработчик прерывания по приёму данных от DSP
Вот таким мудрёным образом.
При этом все смещения в адресах регистров (0x100*N), N=0 - обмен сообщениями работает в обе стороны . С N=1 не работает ни в одну сторону.
Смещения (P*4) работают при P=0..3 - все 4 канала работают.
Cо стороны DSP прерывания по приёму данных от CPU и передача данных - прерывания уровня 3, также как и прерывание от таймера. Необходима сепарация, в случае, если одновременно использовать прерывания таймера и messagebox. Смещение от начала таблицы векторов прерываний: +0x19C. Бит номер 3 регистра intenable - включает прерывание приёма данных от CPU, бит 4 этого же регистра - прерывание передачи данных к CPU.
Со стороны CPU прерывание по приёму данных от DSP - номер 32. Номер прерывания передачи данных к DSP - номер 117. GIC.
Данные передаются через FIFO буферы 8 4-байтных слов. Длина сообщения должна быть кратна 4. Придётся передавать нули в конце 1..3 штуки, если длина не кратна 4.
-
14 hours ago, jcxz said:
Для маленьких, для сохранения настроек, гораздо проще и надёжнее использовать кольцевой буфер.
Можно по-подробнее? Заинтересовало. Кольцевой буфер - в памяти или так называется принцип файловой системы/ хранения данных на носителе?
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
25 minutes ago, mantech said:Да уж, я-то думал, что выделенная статика для DSP IRAM-DRAM по сути и есть кэш с 1-тактовым доступом, типа TCM в СТМ МК, а тут вон оно что...
A1 SRAM у T113-s3 без кеширования тоже тормозная.
А у C6745 память L1/L2 на частоте ядра и кастомно можно задавать сколько тратить её на кеши и на обычные буфера, доступные программисту. Да и там доступ ко всей памяти кеша есть: можно даже случайно наехать...
Проверил соответствие адресных линий DDR для DSP и T113-s3.
Проверяемый фрагмент памяти 40 МБ заполнялся псевдо-рэндомом со стороны DSP. Спустя какое-то время T113-s3 начинал проверять эту область памяти. Тестирование успешно. Свопинга адресных линий (интерлив адресов) для DDR к счастью нет.
Про свопинг вопрос подымался здесь:
2 hours ago, sasamy said:мне прерывания от периферии в DSP нужны
И мне нужны!
2 hours ago, sasamy said:исходников от неё наверно и нет в свободном доступе - китаец накидал проприетарных либ из тулзов cadence, некоторые ф-ции libc тоже оттуда берутся а не из тулчейна, отсюда и варнинги при сборке freertos
Пока использую её. Будет настроение - попытаюсь декомпилировать её в C-код через дизасм, как это делал с кодом инита DDR.
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
Задействовал кеширование для HiFi4 DSP. Функция board_init( ) из файла board-init.c. Пришлось подключать либу из FreeRTOS: libhal.a, потому что исходников на неё я пока не искал.
Ну что же сказать... Мои ожидания оправдались - даёт ощутимый прирост после вызова board_init( ). Так что старший ниббл в адресе с двойки ( 0x2******* ) - это ещё не всё!
Протестировал несколько регионов с включением board_init( ) и без неё.
Код на котором проверялось быстродействие (оптимизация компилятора специально была выключена: -O0 ) :
void delay(volatile u64 d) { while(d--); } extern void board_init(void); u32 t; int main(void) { board_init(); Loop: AVS_CNT0_REG=0; //Замер времени delay(50000); t=AVS_CNT0_REG; printf("%d\n",t); goto Loop; //...
Код функции delay( ). 30 инструкций за 13000 тактов 6 МГц-таймера. В процедуре 50 000 итераций.
Грубый расчёт по времени: (6000000/13000)*30*50000 = 692307692. Похоже, что и вправду 600 МГц
Spoiler20028ce8 <delay>: 20028ce8: 006136 entry a1, 48 20028ceb: 017d mov.n a7, a1 20028ced: 0020c0 memw 20028cf0: 0729 s32i.n a2, a7, 0 20028cf2: 0020c0 memw 20028cf5: 1739 s32i.n a3, a7, 4 20028cf7: f03d nop.n 20028cf9: 0020c0 memw 20028cfc: 0728 l32i.n a2, a7, 0 20028cfe: 0020c0 memw 20028d01: 1738 l32i.n a3, a7, 4 20028d03: fcde81 l32r a8, 2002807c <Literal+0x78> 20028d06: fcdd91 l32r a9, 2002807c <Literal+0x78> 20028d09: 428a add.n a4, a2, a8 20028d0b: 160c movi.n a6, 1 20028d0d: 013427 bltu a4, a2, 20028d12 <delay+0x2a> 20028d10: 060c movi.n a6, 0 20028d12: 539a add.n a5, a3, a9 20028d14: 665a add.n a6, a6, a5 20028d16: 065d mov.n a5, a6 20028d18: 0020c0 memw 20028d1b: 0749 s32i.n a4, a7, 0 20028d1d: 0020c0 memw 20028d20: 1759 s32i.n a5, a7, 4 20028d22: 024d mov.n a4, a2 20028d24: 204430 or a4, a4, a3 20028d27: fce456 bnez a4, 20028cf9 <delay+0x11> 20028d2a: f03d nop.n 20028d2c: f03d nop.n 20028d2e: f01d retw.n
Результаты следующие:
0x40000000 .. 0x7FFFFFFF (не кеширован, только буферизован): без board_init() : 385000 тактов с board_init() : 385000 тактов 0xC0000000 .. 0xFFFFFFFF (кеширован, буферизован): без board_init() : 385700 тактов с board_init() : 13000 тактов - самый быстрый! 0x20000000 .. 0x3FFFFFFF (кеширован, буферизован): без board_init() : 595000 тактов с board_init() : 13000 тактов - самый быстрый! 0x00000000 .. 0x1FFFFFFF (не кеширован, только буферизован): без board_init() : 595000 тактов с board_init() : 595000 тактов
Тоесть, делаю выводы, что с кешированными регионами выходит быстрее: в 29 .. 45 раз!
При этом T113-s3 грузит код по следующим адресам:
0x41800000 или 0xC1800000 (что одно и то же) - для DDR.
0x00028000 - для DSP0 IRAM/DRAM
Очевидно, что адреса, куда грузит T113-s3 программу для DSP не влияют на кеширование.
Влияет значение регистра DSP_ALT_RESET_VEC_REG + на какие адреса слинкована программа для DSP его компилятором + включение функции board_init().
Во всех замерах - такты таймера 6 МГц, его 6000000 тактов - 1 секунда.
Что касается библиотеки libhal.a - её придётся использовать как есть или декомпилировать. Много интересых вещей находится именно в ней.
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
8 hours ago, sasamy said:внешними прерываниями от периферии нет планов разобраться? без этого всё выглядит малопригодным.
Не стоит недооценивать обработку системных исключений в этой архитектуре. Без "оконных разливов" (window spill) программа будет очень ограничена:
🥇 в уровне вложенности функций
🥈 в количестве используемых локальных переменных
🥉 про сишный lib.c можно забыть (он весь простроен на вложенных вызовах, подтягивая ещё и проверки на повторную входимость).
Ещё раз. Это не ARM, где можно начать работу вообще без никаких прерываний-исключений. Здесь регистрово-оконная архитектура, диктующая инные требования к обработке исключений, которые нельзя игнорировать.
Лишь только после как сделан этот уровень, можно работать...
8 hours ago, sasamy said:с внешними прерываниями от периферии
6 hours ago, sasamy said:для бареметал сойдёт и так - там всё равно процессору больше нечего делать кроме поллинга 🙂
Собственно, мне и нужен BareMetal. Я не пишу под Линукс в МК.
Allwinner T113-s3 уделал HiFi4 DSP. Смеяться или плакать?
в TI, Allwinner, GigaDevice, Nordic, Espressif и другие
Опубликовано · Изменено пользователем repstosw · Пожаловаться
Обнаружил ещё одну особенность DSP. Если кеширование включено и используются IRAM, DRAM0,1 (собственная память DSP), то в дескрипторы DMA должны записываться адреса с обнулённым битом кеширования:
address_dma = address&(~0x20000000)
При этом DSP может писать в кешированные регионы, не забывая делать сброс с кеша в память:
#define CACHE_MASK (~0x20000000) void DMA_DAC_Init(u32 dst,u32 src,u32 bc) { DMA_Clock(); DMA_IRQ_EN_REG1|=(3<<((DMA_DAC_CHANNEL-8)*4)); //DMA 8..15 Interrupts Enable: Half & Full DMA_IRQ_PEND_REG1|=(7<<((DMA_DAC_CHANNEL-8)*4)); //Reset DMA 8..15 INT static u32 DMA_DAC_Descriptor[6]; // dw=16 dm=IO db=8 dd=AC sw=16 sm=Lin sb=8 sd=DDR DMA_DAC_Descriptor[0]=(1<<25)|(1<<24)|(2<<22)|(7<<16)|(1<<9)|(0<<8)|(2<<6)|(1<<0); //DMA Configuration DMA_DAC_Descriptor[1]=src&CACHE_MASK; //DMA Source Address DMA_DAC_Descriptor[2]=dst; //DMA Destination Address DMA_DAC_Descriptor[3]=bc; //DMA Byte Counter DMA_DAC_Descriptor[4]=0; //DMA Parameter (Wait Clock Cycles=0) DMA_DAC_Descriptor[5]=((u32)DMA_DAC_Descriptor)&CACHE_MASK; //DMA Link xthal_dcache_all_writeback(); DMA_DESC_ADDR_REG(DMA_DAC_CHANNEL)=((u32)DMA_DAC_Descriptor)&CACHE_MASK; //DMA Descriptor Address while(DMA_DESC_ADDR_REG(DMA_DAC_CHANNEL)!=(((u32)DMA_DAC_Descriptor)&CACHE_MASK)); }
Здесь у src и адреса DMA_DAC_Descriptor сбрасывается старший бит кеширования. При CPU ведёт запись кешированные адреса, после чего вызывается xthal_dcache_all_writeback() (функция библиотеки libhal.a). dst- это регистр DAC (AC_DAC_TXDATA), поэтому он остаётся без изменения:
DMA_DAC_Init((u32)&AC_DAC_TXDATA,(u32)DACBuffer,SAMPLE*2*sizeof(s16));
Если же код и данные находятся в DDR, то для DMA пофигу какие адреса: 0x40000000(не кеширован) или 0xC0000000(кеширован). Скорее всего это связано с тем, что DSP и DMA по-разному трактуют адреса памяти, лежащие в диапазоне 0x00000000 .. 0x1FFFFFFF