-
Постов
2 650 -
Зарегистрирован
-
Победитель дней
2
Сообщения, опубликованные repstosw
-
-
Опубликовано · Изменено пользователем 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. Я не пишу под Линукс в МК.
-
1 hour ago, sasamy said:
Неплохо.
3 minutes ago, sasamy said:а потом поллингом мониторить буфер ? фигня какая-то, зачем тогда нужен этот периферийный DSP сопроцессор, с msgbox ядра друг друга аппаратно могут прерывать
1. ядро-1 делает рендеринг
2. далее ядро-1 проверяет свободно ли ядро-2 и передаёт указатель на результат рендеринга
3. ядро-2 начинает работать с результатом и делает над ним второе преобразование
4. в это время ядро-1 делает новый фрейм рендеринга
Опрос готовности ядра-2 делается ядром-1 по завершению рендеринга ядром-1. Обычно ядро-2 к тому моменту уже свободно. Ожидание сведено к минимуму, никто циклически не опрашивает.
Время рендеринга ядром-1 20 мс, время второго преобразования ядром-2 15 мс.
PS. Но то что есть прерывания это хорошо. Возьму на заметку.
-
43 minutes ago, sasamy said:
с внешними прерываниями от периферии нет планов разобраться?
Внешние прерывания - это когда сигнал на ножку извне приходит с внешнего модуля? Или под внешними понимаются любые прерывания от периферии T113-s3 ?
У меня нет мануала на периферию этого DSP. В сорцах линукса например есть такое: настроить прерывание по уровню/фронту с какого-нибудь GPIO?
Что касается периферии - так этот DSP видит всю периферию от T113-s3. Тоесть ему особо и не нужна своя периферия.
И я думаю что у него её нет. Всё что есть - это числомолотилка с VLIW архитектурой + SIMD + быстрая выделенная память кода/данных.
45 minutes ago, sasamy said:В freertos есть пример для msgbox но этого мало - непонятно по какому принципу мапятся прерывания от периферии, есть инит контроллера (предположил по названию - прерываний периферии)
А что такое этот msgbox? Буфер памяти куда флудит этот DSP для обмена сообщениями между A7 и DSP? Не совсем понятно, зачем это нужно. Всю жизнь контекст общения между двумя ядрами делал самостоятельно в удобном месте.
33 minutes ago, GenaSPB said:Насчёт прерываний от периферии. Самое интересное прерывание, которое может быть прицеплено к dsp ядру это прерывание по обмену с кодеками. DMAC. В данном микропроцессоре этот единственное прерывание на все 16 каналов. А с учётом того что dsp ядро единственное а cortex a7 пара, острой необходимости как-то не наблюдается. Ну интересно, но прикрутить в проект иначе как возложив на hifi4 (и только на него) всю dsp обработку не получится. Usb dma я например поднял - но это то же единственное прерывание. распределять крайне неудобно.
Так а где смотреть как коммутировать источник прерывания на линию DSP? Допустим у аудиокодек работает по прерываниям с ядром A7. Как это прерывание загнать на DSP? Ну не софтовый же поллинг делать ? 🙂
Собственно я начал ковырять этот DSP, чтобы на него повешать кодер Рида-Соломона. А декодер повешаю на второе A7 ядро. А первое A7 ядро - главное: управление и остальное.
Кодер Рида-Соломона на A7 работает на 30 FPS. Декодер - на 15 FPS. Учитывая целевой FPS системы - 12..15 : нужна параллельность вычислений.
Моя тема про кодек RS здесь:
Интересно, протащит ли DSP кодирование хотя бы на уровне 15 FPS? Мне главное чтобы параллельно шло кодирование-декодирование-главная программа. Самое слабое звено должно давать не меньше 12-15 FPS.
-
29 minutes ago, mantech said:
Это справедливо практически для любых таймеров и архитектур)))
Это понятно. И ожидаемо. А вот на счёт "как это сделать" - это и нужно было понять.
31 minutes ago, mantech said:А смысл?
Удовлетворение собственного любопытства )) Ну или может как отдельную функцию использовать для запуска эмулятора чего-либо.
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
On 9/11/2023 at 9:23 PM, repstosw said:Осталось доделать эксепшн при alloca/setjmp и оформить это в SDK HiFi4 DSP
Доделал обработку прерываний/исключений. Итого вышло 10 исключений:
1. Прерывание таймера (1)
2. Исключения Overflow регистрового окна (3)
3. Исключения Underflow регистрового окна (3)
4. Исключение Alloca (инструкция MOVESP) (1)
5. Исключение SetJmp (инструкция SYSCALL) (1)
6. Исключение User - просто корректный выход из исключения (1)
4,5,6 - сидят на IntLevel1. необходимо было сделать селектор (сепаратор), чтобы определять причину исключения и отдельно её обработать.
Стресс-тест на всех уровнях оптимизации не завалил программу.
Пришлось много возиться и курить. Считаю, что обработка исключений - самая трудная часть этого DSP. Без написания корректного кода исключений программа будет падать.
Все исключения (за исключением SetJmp) необходимы! Самое сложное исключение - SetJmp: нужно было сделать некоторые манипуляции со стеком, регистровыми окнами, и ручками корректировать адрес возврата.
Контекст для сохранения регистров в прерываниях(там где он нужен - Timer и SetJmp) сделал в отдельной секции памяти. Пробовал использовать стек - программа падает, если используется одновременно много прерываний (как минимум два: Таймер + оконный Overflow/Underflow).
Поэтому чтобы не нарушать работу оконных обработчиков, прерывание таймера не использует стек для сохранения регистров. При этом программа не падает на любой оптимизации.
В прерывании таймера важно было правильно подтвердить его. Иначе прерывание начнёт снова выполняться сразу же после выхода - много раз.
В исходниках FreeRTOS обработчики написаны с учётом много-задачности. Анализ кода может привести к взрыву мозга.
https://github.com/YuzukiHD/FreeRTOS-HIFI4-DSP/blob/master/kernel/portable/xtensa_vectors.S
Приходится смотреть в дургих местах как сделаны некоторые вещи:
Реализовал псевдо-функцию макрос для printf, с помощью sprintf по всем канонам стандарта (c возвращаемым значением):
#define printf(format,...) \ ({ \ char printf_buffer[128]; \ int printf_return=sprintf(printf_buffer,format,##__VA_ARGS__); \ UART_puts(printf_buffer); \ printf_return; \ }) \
Этот DSP также может исполнять код из DDR T113-s3.
Тестировал на адресах: 0x41800000 , 0xC1800000 (кешируемый), и 0x31800000 (не работает. Возвращает Fault = 0x2).
Здесь к адресам добавлены +24 МБ (0x1800000), потому что в начале DDR программа для T113-s3.
Последний адрес скорее всего не работает из-за того что нужны кое-какие манипуляции с AHB, о чем говорит даташит:
Но он особо не интересен, так как первые два работают.
Лог запуска в UART (на ПК):
SpoilerHiFi4 DSP... RO Data: 0x41803073, Data Start: 0x41810080, Data End: 0x418108AC BSS Start: 0x418108AC, BSS End: 0x41810A1C Stack: 0x41820000, Heap Start: 0x41818000, Heap End: 0x4181C000 malloc() Test malloc OK! malloc String foo 0 srand() & rand() Test: 2132416883854391803722155897918388512453687072798722520304017882391507922335899285399047465674001819 Timer Test: ccount: 804 ccount: 747999 ccount: 1669194 ccount: 2647569 ccount: 3626769 foo 1 setjmp() & longjmp() Test: setjmp longjmp setjmp 123 Loop... !!!!!!!!!!Timer Interrupt / !!!!!!!!!!Timer Interrupt ± !!!!!!!!!!Timer Interrupt !!!!!!!!!!Timer Interrupt ó !!!!!!!!!!Timer Interrupt ‡ !!!!!!!!!!Timer Interrupt !!!!!!!!!!Timer Interrupt H !!!!!!!!!!Timer Interrupt Ž !!!!!!!!!!Timer Interrupt ¤ !!!!!!!!!!Timer Interrupt ì !!!!!!!!!!Timer Interrupt Š !!!!!!!!!!Timer Interrupt !!!!!!!!!!Timer Interrupt Ó !!!!!!!!!!Timer Interrupt ¼ !!!!!!!!!!Timer Interrupt ò !!!!!
Ещё мысли по поводу стандартных сишных библиотек, входящих в hifi4-dsp компилятор от FreeRTOS. По-хорошему их надо пересобирать для BareMetal. Потому что они ориентированы на повторную входимость и многозадачность, и содержат кучу лишних действий и доставляют проблемы с корректным запуском штатных функций.
Но исходников libc.a, libm.a я не увидел : https://github.com/YuzukiHD/FreeRTOS-HIFI4-DSP/tree/master/lib
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
2 hours ago, sasamy said:Указанные здесь оффсеты векторов совпали с моими расчётами из скрипта линкера
3 hours ago, repstosw said:Пока вызов пользовательской функции через call0 - в ассемблерном сорце. Не получается задействовать сишный call8 - зависает.
Уже задействовал. Работает. Не работало из-за того, что в обработчике прерывания нужно понижать уровень приоритета до значения равного приоритету самого обработчика, до вызова любой пользовательской функции внутри.
Для таймера приоритет 3:
movi a3,PS_UM|PS_WOE_ABI|PS_INTLEVEL(3) wsr.ps a3 rsync
3 hours ago, repstosw said:далось из стартапа вызвать сишные функции libc_init() и cледом за ней main() с помощью call8. Раньше был просто переход на libc_init.
Чтобы вызов второй функции main() не вешал программу - нужно добавить entry:
Теперь никакой entry не нужен. Нужно старт окна сделать в 1. Тогда можно будет использовать windowed-вызовы из ассемблерного сорца.
movi a3,1 //=1 - allow windowed call from assembly startup wsr.WINDOWSTART a3 //Call-window start bits //... call8 libc_init call8 main
Осталось доделать эксепшн при alloca/setjmp и оформить это в SDK HiFi4 DSP
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
44 minutes ago, GenaSPB said:Прерывание произошло но обработчик обсыпался...
Сделай первой командой обработчика переход сам на себя. Если перестанет сообщать что fault - доходит. Если не перестанет - ошибка раньшеЭто не помогло 🙂 Проблема оказалась в том, что вектора прерываний были не в том месте.
Вот этот товарисЧ ввёл меня в заблуждение: https://github.com/esp-rs/xtensa-lx-rt/issues/5#issuecomment-578954586
Всё, что дальше оконных исключений - всё не то 🙂
Правильные смещения в таблице векторов - они идут уже не с шагом +64 :
.org 0x17C //IntLevel2 .org 0x19C //IntLevel3 : Timer0, Timer1 .org 0x1BC //IntLevel4 .org 0x1DC //IntLevel5, NMI .org 0x1FC //Kernel .org 0x21C //User .org 0x23C //Double
Я ещё по началу в скрипте линкера от FreeRTOS увидел эту "кривизну" смещений.
Поймал прерывания таймеров и исключение UserException при использовании alloca( ).
Написал обработчик прерывания таймера. Пока вызов пользовательской функции через call0 - в ассемблерном сорце. Не получается задействовать сишный call8 - зависает.
Удалось из стартапа вызвать сишные функции libc_init() и cледом за ней main() с помощью call8. Раньше был просто переход на libc_init.
Чтобы вызов второй функции main() не вешал программу - нужно добавить entry:
j XXX //обход для выравнивания .align 4 XXX: entry a1,32 call8 libc_init call8 main //...
-
Опубликовано · Изменено пользователем repstosw · Пожаловаться
15 hours ago, GenaSPB said:volatile?
Нет. Цикл копирования из LMA в VMA. Вот так будет работать на любой оптимизации:
extern char _sidata,_sdata,_edata; //set by linker extern char _sbss,_ebss; //set by linker void libc_init(void) { memcpy(&_sdata,&_sidata,&_edata-&_sdata); //copy LMA to VMA memset(&_sbss,0,&_ebss-&_sbss); //clear BSS main(); }
Заставил работать функции rand(), srand(). Для этого подправил ещё одну функцию:
struct _reent *_EXFUN(__getreent,(void)) { static struct _reent REENT; //memory space for srand() & rand() return &REENT; }
15 hours ago, mantech said:Там еще есть много асмового кода, в частности по прерываниям, но чет уж много очень, что там за контроллер прерываний такой, походу сложный((
Второй день бьюсь над тем, чтобы заставить DSP нормально выдавать прерывание. Вначале пытался сделать UserException, затем пока временно оставил его и начал возиться с таймерами DSP.
Таймеры работают. Пара функций:
void Timer_Init(void) { u32 ccount=0; __asm__ __volatile__ ("rsr.ccount %0" : "=r"(ccount)); ccount+=7506204; __asm__ __volatile__ ("wsr.ccompare0 %0" :: "r"(ccount)); ccount+=7506204; __asm__ __volatile__ ("wsr.ccompare1 %0" :: "r"(ccount)); u32 intenable; __asm__ __volatile__ ("rsr.intenable %0" : "=r"(intenable)); intenable|=(1<<2); //бит 1 - timer1, бит 2 - timer 0 __asm__ __volatile__ ("wsr.intenable %0" :: "r"(intenable)); } u32 Timer_Get(void) { u32 ccount; __asm__ __volatile__ ("rsr.ccount %0" : "=r"(ccount)); return ccount; }
При разрешении прерывания от таймера (биты 1,2 в регистре intenable) при достижении таймера до значения ccompare0,1 должно происходить прерывание. Но программа зависает. Причем время до зависания зависит от - ccompare0,1 : тоесть я делаю вывод, что прерывание именно от таймера по достижении значения. Но почему оно не работает?
Причём, я определил все вектора прерываний с адресов 0x180 до 0x3C0 (шаг +64): просто поставил команду перехода в код выдачи байта по UART:
.org 0x180 j OutChar .org 0x1C0 j OutChar //... .org 0x340 //UserException j OutChar .org 0x380 j OutChar .org 0x3C0 j OutChar //... #define UART_NUMBER 3 /* UART Number: 0..5 */ #define UART_BASE (0x02500000+(0x400*UART_NUMBER)) #define UART_THR (UART_BASE+0x00) /* transmit holding register */ OutChar: movi a8,UART_THR movi a9,101 memw s32i.n a9,a8,0 Loop: j Loop
Но символ не печатается из прерывания. Отдельно проверял код с метки OutChar - он работает.
Со стороны T113-s3 проверил статус из регистра - DSP_STAT_REG : значение 2 - BIT_PFAULT_ERROR. В нормальном состоянии должно быть =0 .
Всё сложно.
При этом обработчики исключений переключения окна (смещения 0 ... 0x140 от базы вектров прерываний - задан в регистре vecbase) работают нормально.
Что я делаю не так? Чего не хватает, чтобы заработало прерывание таймера?
Allwinner T113-s3 уделал HiFi4 DSP. Смеяться или плакать?
в TI, Allwinner, GigaDevice, Nordic, Espressif и другие
Опубликовано · Изменено пользователем repstosw · Пожаловаться
Не работает этот параметр с компилятором. Пишет - unrecognized option.
Для линкера аналогичные:
Работает только -Wl,--abi-windowed, c сall0 вываливаются ошибки во время линковки - этот тулчейн сконфигурирован только для windowed. Жаль... ((
Скорее всего можно пересобрать и для call0-abi.
Ещё касаемо ABI.... В ассемблерном листинге наблюдаю только вызовы call8, callx8. Не вижу call4, call12, callx4, callx12. тоесть какой-то фиксированный протокол вызова выходит. Хотелось чтобы это можно было задавать либо глобально, либо для каждой функции - как в нормальном GCC.
Я уже не говорю об атрибутах функции - pure, naked, interrupt... Это всё нужно для системного уровня.