Перейти к содержанию
    

repstosw

Участник
  • Постов

    2 650
  • Зарегистрирован

  • Победитель дней

    2

Сообщения, опубликованные repstosw


  1. 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.

    https://gcc.gnu.org/onlinedocs/gcc/Xtensa-Options.html

    Не работает этот параметр с компилятором. Пишет - 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.

    image.thumb.png.1900a5c81370bd0fb5d3d540838ce1bc.png

     

    Ещё касаемо ABI.... В ассемблерном листинге наблюдаю только вызовы call8, callx8.   Не вижу call4, call12, callx4, callx12. тоесть какой-то фиксированный протокол вызова выходит. Хотелось чтобы это можно было задавать либо глобально, либо для каждой функции - как в нормальном GCC.

    Я уже не говорю об атрибутах функции - pure, naked, interrupt...  Это всё нужно для системного уровня.

     

  2. 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 и вывел в консоль сообщение

    Мне нужно было сохранить регистры в стековом фрейме.  Обработчики у меня и так прекрасно работают )))

    Плюс я не использовал функции из фриртос, а писал установку прерываний самостоятельно.

  3. Столкнулся с такой проблемой. В ассемблерном сорце в коде обработчика прерывания пытаюсь сохранить в стеке регистры, которые восстанавливаются перед выходом из обработчика.

    Для этого нужно сместить(уменьшить) указатель стека на число байт, которое надо для резервирования данных. Затем сохранить регистры, вызвать обработчик(на С),  восстановить регистры, и обратно сместить(увеличить) указатель стека.  Обработчик срабатывает, но при выходе из прерывания генерируется 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).

  4. В общем этот китаец взял описание регистров 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 прерываний все прерывания не уложатся.

     

  5. 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

     

  6. Удалось заставить 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=;

     

  7. 3 hours ago, sasamy said:

    мне кажется самый реалистичный вариант спросить у автора порта freertos для т113 чтобы он поделился инфой - явно не из астрала структура с описанием INTC. Спрашивали у дистрибьютера allwinner, они дали всё что у них есть - архив с pdf-ми на китайском но там ничего нет интересного.

     

    3 hours ago, mantech said:

    Если знать разработчика - проще доки найти на подобные вещи, т.к. они могут где-то еще использоваться...

     

    Мне кажется, что это Interrupt Controller от A10.  По крайней мере большинство регистров совпадает по названиям и смещениям:

    Clipboard02.thumb.jpg.023b489f7ed6b51bad63a8beca825ed7.jpg

     

    Попробовал в pintc_regs->enable  записать 0x1.   Прерывание от DMA перестало работать на DSP.   Выводы очевидны: это оно.

     

     

  8. 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.  В общем ХЗ.  Тут только  копать исходники, либо искать документацию, либо экспериментировать. Либо всё вместе :)

  9. 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 проверить на каком бите и векторе сидят... :sun_bespectacled:

  10. 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.

    Это не тот уровень который приоритет :) Это высокий-низкий в плане сигнала.

  11. 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 лучше не устанавливать - это ошибка операции с памятью, и два таймера

  12. Обнаружил, что компилятор 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.

  13. 5 hours ago, mantech said:

    А зачем эти мутарства с ФИФО, есть же общая память? Давнул прерывание процу, что ДСП что-то сделал, а данные забираешь из общей памяти и наоборот, не??

    Прерывания происходят по порогу срабатывания FIFO: он настраивается: 1/2/4/8.

    На счёт общей памяти согласен - большие массивы данных только так и давать-забирать. 

    FIFO относительно медленный по сравнению с памятью, и к тому же, если он заполнен (тоесть кто-то ещё данные не прочёл) то передача приостанавливается. Тоесть максимум 32 байта отправил, и надо ждать пока как минимум 4 байта не прочтётся. 

    "Первый вошёл, первым и вышел"...  Поэтому обязательно как минимум: кто-то пишет И кто-то читает. Но с прерываниями - это не проблема.

    Можно заполнять память и давать 4 байта в Msgbox. Прерывание будет. И времени много не займёт 🙂

     

    Quote

    Давнул прерывание процу, что ДСП что-то сделал,

    Расскажите, как ?  Как c-инициировать искуственно аппаратное прерывание?  🙂

    Cкорее всего, линии IRQ программисту недоступны.

    image.png.886489850652b72edb949cb21d84c258.png

     

    Однако меня беспокоит другой вопрос.  Сейчас прерывание Msgbox висит на том же векторе, что и прерывание Таймера. А именно - IntLevel3. Пока не разобрался с определением источника прерывания, чтобы сделать селектор.  Сделал только селектор для исключений (там проще).

    Есть ли возможность отмапить источники прерываний  на другой вектор?  Их куча свободных: IntLevel 2,4,5.

    IntLevel 1- занят - на нём висят исключения

    IntLevel 3 - Timer.

    В C6745 можно любой источник прерывания повешать на любой вектор.

  14. Сделал 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.

    image.png.e91d4949084fa8b435ccbcf7388ba33c.png

     

     

  15. 14 hours ago, jcxz said:

    Для маленьких, для сохранения настроек, гораздо проще и надёжнее использовать кольцевой буфер.

    Можно по-подробнее? Заинтересовало.  Кольцевой буфер - в памяти или так называется принцип файловой системы/ хранения данных на носителе?

  16. 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 нужны

    И мне нужны! :ok:

    2 hours ago, sasamy said:

    исходников от неё наверно и нет в свободном доступе - китаец накидал проприетарных либ из тулзов cadence, некоторые ф-ции libc тоже оттуда берутся а не из тулчейна, отсюда и варнинги при сборке freertos

    Пока использую её.  Будет настроение - попытаюсь декомпилировать её в C-код через дизасм, как это делал с кодом инита DDR.

  17. Задействовал кеширование для HiFi4 DSP. Функция board_init( ) из файла board-init.c.  Пришлось подключать либу из FreeRTOS: libhal.a, потому что исходников на неё я пока не искал.

    Ну что же сказать... Мои ожидания оправдались - даёт ощутимый прирост после вызова board_init( ).  Так что старший ниббл в адресе с двойки ( 0x2******* ) - это ещё не всё! :biggrin:

    Протестировал несколько регионов с включением 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 МГц :bomb:

    Spoiler
    20028ce8 <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 раз! :yes:

    При этом 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 - её придётся использовать как есть или декомпилировать. Много интересых вещей находится именно в ней. :dance4:

  18. 8 hours ago, sasamy said:

    внешними прерываниями от периферии нет планов разобраться? без этого всё выглядит малопригодным.

    Не стоит недооценивать обработку системных исключений в этой архитектуре. Без "оконных разливов" (window spill) программа будет очень ограничена:

    🥇 в уровне вложенности функций

    🥈 в количестве используемых локальных переменных

    🥉 про сишный lib.c можно забыть (он весь простроен на вложенных вызовах, подтягивая ещё и проверки на повторную входимость).

    Ещё раз.  Это не ARM, где можно начать работу вообще без никаких прерываний-исключений.  Здесь регистрово-оконная архитектура, диктующая инные требования к обработке исключений, которые нельзя игнорировать.

    Лишь только после как сделан этот уровень, можно работать...

    8 hours ago, sasamy said:

    с внешними прерываниями от периферии

     

    6 hours ago, sasamy said:

    для бареметал сойдёт и так - там всё равно процессору больше нечего делать кроме поллинга 🙂 

    Собственно, мне и нужен BareMetal.  Я не пишу под Линукс в МК.

  19. 1 hour ago, sasamy said:

    Неплохо. :sun_bespectacled:

    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. Но то что есть прерывания это хорошо. Возьму на заметку.

  20. 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.

  21. 29 minutes ago, mantech said:

    Это справедливо практически для любых таймеров и архитектур)))

    Это понятно. И ожидаемо.  А вот на счёт "как это сделать" - это и нужно было понять.

    31 minutes ago, mantech said:

    А смысл?

    Удовлетворение собственного любопытства )) Ну или может как отдельную функцию использовать для запуска эмулятора чего-либо.

  22. On 9/11/2023 at 9:23 PM, repstosw said:

    Осталось доделать эксепшн при alloca/setjmp  и оформить это в SDK HiFi4 DSP :sun_bespectacled:

    Доделал обработку прерываний/исключений. Итого вышло 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

     

    Приходится смотреть в дургих местах как сделаны некоторые вещи:

    https://chromium.googlesource.com/chromiumos/third_party/sound-open-firmware/+/5b43730e8620c60b4170231f7e9d91d2d2d89652/src/arch/xtensa/up/xtos/exc-syscall-handler.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, о чем говорит даташит:

    image.thumb.png.4e6489925cde17dbec5fa8cdf1bfb874.png

    Но он особо не интересен, так как первые два работают.

     

    Лог запуска в UART (на ПК):

    Spoiler
    HiFi4 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.alibm.a  я не увидел : https://github.com/YuzukiHD/FreeRTOS-HIFI4-DSP/tree/master/lib

  23. 2 hours ago, sasamy said:

    Указанные здесь оффсеты векторов совпали с моими расчётами из скрипта линкера :yes:

     

    3 hours ago, repstosw said:

    Пока вызов пользовательской функции через call0 - в ассемблерном сорце.  Не получается задействовать сишный call8 - зависает.

    Уже задействовал. Работает.  Не работало из-за того, что в обработчике прерывания нужно понижать уровень приоритета до значения равного приоритету самого обработчика, до вызова любой пользовательской функции внутри.

    Как здесь: https://github.com/YuzukiHD/FreeRTOS-HIFI4-DSP/blob/Toolchains/kernel/portable/xtensa_vectors.S#L1451

    Для таймера приоритет 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 :sun_bespectacled:

  24. 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
    
    //...

     

  25. 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) работают нормально.

    Что я делаю не так? Чего не хватает, чтобы заработало прерывание таймера?

×
×
  • Создать...