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

repstosw

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

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

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

    2

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


  1. Обнаружил ещё одну особенность 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

  2. Есть ещё Sigma Studio от Analog Devices.   Там есть поддержка ADAU1472 - это ДСП от АД   с HiFi4 ядром.  Но это не для программистов.  Скорее для музакеров и производителей акустических систем.  Типа визарда - накидать через GUI акустический конвеер (фильтры, ресемплеры, дисторшены, эхо,  итп..).  Сильно узкоспецифичен.

    Плагин на каждый чип там в виде DLL. Наверное проприетарный формат.  DSP от T113-s3 туда не воткнёшь.  Но можно  выдирать  бинарник (код) для ДСП через линуксовый парсер или ручками из *.dspproj.

    14 minutes ago, mantech said:

    У меня большое подозрение, что на этом процессоре придется делать оптимизацию "руками", путем написания асмовых программ(((

    Чего мелочиться - сразу в машинных кодах писать фрагменты с VLIW/SIMD :biggrin:  Документация на опкоды есть.  Возможно оформить  в виде макросов и вызывать их.

  3. 36 minutes ago, mantech said:

    Удалось ли повысить быстродействие теста вышеприведенного DSP?

    Нет.  С ABI-call0 она осталась прежней.  Собственно, я и не надеялся увеличить скорость через ABI. 

    Для ускорения того бенчмарка нужно распараллеливание на VLIW. И то - у меня есть сомнения: что тот код в исходном виде будет работать быстрее - его придётся переписать под архитектуру ДСП.  У C6745 с которым я возился несколько лет назад, там от силы 2-3 распараллеливания из доступных 8 получалось. И там довольно хороший оптимизатор - иногда по пол-часа компилируется...  На обычном сишном алгоритме. VLIW - это ещё не так просто. Не каждый алгоритм поддастся распарралеливанию.  Так что VLIW - не более чем коммерческий ход. :big_boss:

    Так что тему пора переименовать в "Allwinner T113-s3 уделал HiFi4 DSP, смеяться или плакать?"  :lol:

     

    26 minutes ago, sasamy said:

    чтобы повысить быстродействие надо расширения ISA использовать, сомневаюсь что они есть в GCC, не зря же они свой оптимизирующий компилятор делают.

    C Cadense Xtensa Tools всё гибло:  требуют лицензию. И постоянно включенный комп с их сервером, который пасёт лицензию.  Крякнутых версий не нашёл.  Так что придётся довольствоваться General Purpose выхлопом от GCC без VLIW и SIMD.  Хотя, вполне допускаю, что какой-нибудь местный хакер сможет в коде программ с command-line Inteface найти место где проверяется лицензия и заменить опкод перехода.  Тот кто дружит с трассировкой и дебагингом могут это сделать. Если конечно в Cadence защиту от трассировки не сделали :biggrin:

    А что за оптимизирующий компилятор?  Где его можно скачать чтоб опробовать?

  4. Собрал в Линуксе тулчейн для Винды под 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 из исходников.

    Скачать GCC-тулчейн и исходники либы libhal (вместе с готовой либой) под ABI=call0 для T113-s3 HiFi4 DSP.

     

    • Thanks 1
  5. 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: особенности архитектуры чувствуешь на себе ))

  6. Поймал забавную багу.  Если включено кеширование у 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.

  7. Запустил тест на быстродействие:  длинный кодер-декодер Рида Соломона 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

  8. 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...  Это всё нужно для системного уровня.

     

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

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

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

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

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

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

     

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

     

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

     

  14. 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.   Выводы очевидны: это оно.

     

     

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

  16. 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:

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

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

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

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

  20. 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 можно любой источник прерывания повешать на любой вектор.

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

     

     

  22. 14 hours ago, jcxz said:

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

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

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

  24. Задействовал кеширование для 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:

  25. 8 hours ago, sasamy said:

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

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

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

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

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

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

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

    8 hours ago, sasamy said:

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

     

    6 hours ago, sasamy said:

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

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

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