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

repstosw

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

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

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

    2

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


  1. Как-то понадобилось мне сделать задержки через DSP-таймер.  В board_init.c есть такая функция: uint64_t xbsp_get_ccount(void). Задержку решил сделать так:

    u64 t=xbsp_get_ccount();
    while(xbsp_get_ccount()-t<60000); //100 us wait (600 MHz timer)

    Не работает  xbsp_get_ccount(): https://github.com/YuzukiHD/FreeRTOS-HIFI4-DSP/blob/master/arch/board-init.c#L130

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

    64 бита уже дадут переполнение через 355 839 cуток.

    Сделал так:

    Spoiler
    u32 Timer_Get(void)
    {
     u32 ccount;
     __asm__ __volatile__ ("rsr.ccount %0" : "=r"(ccount));
     return ccount;
    }
    
    u64 Timer_Get64(void) //get 64 bit timer value
    {
     static u32 oldlo=0; //old LSW timer value
     static u32 oldhi=0; //MSW of 64 bit timer
    
     u32 curlo;
     u32 curhi;
     u64 tmr64;
    
     curlo=Timer_Get(); //get the 32 bit counter/timer value
     curhi=oldhi;       //get the upper 32 bits of the 64 bit counter/timer
    
     if(curlo<oldlo)curhi++; //detect rollover
    
     oldhi=curhi;
     oldlo=curlo;
    
     tmr64=curhi;
     tmr64<<=32;
     tmr64|=curlo;
    
     return tmr64;
    }

     

     

     

  2. 10 hours ago, _pv said:

    да вроде имеет право

    А меня нервирует, что C++ не любит void main(void). пишет, что main() должен возвращать int. У меня программа постоянно в main() в вечном цикле. Даже освобождение ресурсов не нужно, так как программа работает вечно, пока питание не закончится.

    Тут ещё споры разгорелись, по поводу того, если функция не имеет аргументов - надо ли void писать в скобках или нет. Я по всей строгости - пишу void, если нет параметров.  Но коллеги не согласны с моим решением - оставляют пустые скобки.  Это принципиально - для C, а для C++ ?

  3. 19 hours ago, jcxz said:

    Просто чудный компилятор - даже на отсутствие return в int main() не обратил внимания.  :shok:

    Проверил с -Wall -Wextra -pedantic.  Да, действительно пропускает 🤣

     

    17 hours ago, Arlleex said:

    Тоже объяснимо, но в нынешних реалиях мало соотносится с логикой.

    Я полагаю, Вы имели в виду вызов func("string"), где void func(char *str). Проблема в том, что в Си строка (строковый литерал) имеет тип char [] (не char * и не const char *). Поэтому компиляция "перлов" в виде func("string") происходит молча. Ну хоть в C++ это уже не пройдет - там символьная строка имеет тип char const [].

    В защищённых режимах всяких это может вылезти боком. Потому что char const* это Read Only.   А передача char* подразумевает, что в вызываемой функции может производиться запись в память.

  4. 1 hour ago, Arlleex said:

    Ну, вернее, написать сможете (только не для CPU с внеочередным исполнением), но результирующий код Вам не понравится совершенно😆

    А если я дам гарантии, что ни один обработчик прерывания не работает со спинлоками и с расшаренной памятью?

    У меня строго цикл. Только в main() идут операции с SHARED memory.  А обработчики прерываний работают в пределах ресурсов управляющего процессора

  5. 38 minutes ago, jcxz said:

    Ну так - не кешировать её. Зачем кешировать расшаренную память, используемую для межъядерного обмена? Выигрыш - копейки (или вообще нет), а проблем сами же себе создаёте вагон.

    Что определяет расшаренную память?   Мануал на контроллер или бит SHARED в MMU ?  Вопрос в контексте Cortex A7.  Конкретнее T113-s3.

     

    В мануале перечислены адреса памяти.  Некоторые адреса подключены к Internal Bus DSP, от того и быстрые.  Может ли она быть SHARED?  Для ARM CPU

  6. On 10/18/2023 at 1:21 AM, Сергей Борщ said:
    int volatile * volatile pd; // volatile-указатель на volatile-int

    Шедевр! Никогда не встречал такой конструкции.

     

    On 10/18/2023 at 10:50 PM, jcxz said:

    Или (в качестве shared memory) использовать внутреннюю ОЗУ. Доступную всем ядрам. Без всяких кешей.

    У Allwinner можно закешировать SRAM A1 и не только.  И она быстрее работает. Хотя по логике вещей скорость не должна была измениться (как у того же TMS320C6745)

  7. Вот есть ещё такие - софтовые спинлоки для камней Allwinner: https://github.com/uli/allwinner-bare-metal/blob/master/spinlock.c :biggrin:

    38 minutes ago, Arlleex said:

    Да, спинлоки реализуются на инструкциях атомарного доступа CPU

    Так а где эта атомарность полезна?  Допустим во время операции спинлока произошло прерывание.  Почему это может быть нежелательно?

     

    P.S. Вопрос-оффтопик:  как запретить прерывания на уровне  ядра Xtensa?

    Пока делаю так, но хочется более атомарнее:

    static inline void CLI(void) //disable all interrupt levels
    {
     u32 i;
     __asm__ __volatile__ ("rsr.ps %0" : "=r"(i));
     i|=0xF;
     __asm__ __volatile__ ("wsr.ps %0" :: "r"(i));
     __asm__ __volatile__ ("rsync");
    }
    
    static inline void STI(void) //enable all interrupt levels
    {
     u32 i;
     __asm__ __volatile__ ("rsr.ps %0" : "=r"(i));
     i&=~0xF;
     __asm__ __volatile__ ("wsr.ps %0" :: "r"(i));
     __asm__ __volatile__ ("rsync");
    }

     

  8. 36 minutes ago, repstosw said:

    Получилась немного инверсная логика работы, но на практике тот результат, что мне нужен.

    Переделал на прямую логику. Тоже работает:

    CPU0:

    void FEC_Init(u8 mln)
    {
     SPINLOCK_ENABLE();       //разрешение и сброс спинлоков.  Все спинлоки свободны -начальное состояние
     printf("T113-s3 FEC\n");
    }

    CPU1 Декодер:

    void FEC_Decode(void)
    {
     if(SPINLOCK_STATUS&(1<<1)) //если спинлок 1 занят
     {
      cache_inv_range((u32)RSD,((u32)RSD)+Bsize);
    
      memcpy(DecodeBuf,RSD,Fsize);
      memcpy(&DecodeBuf[KK],&RSD[Fsize>>1],EE<<1);
    
      int r=eras_dec_rs(DecodeBuf,NULL,0); //декодирование
    
      if(r==-1)printf("Decode Error\n");
      else if(r>0)
      {
       memcpy(RSD,DecodeBuf,Fsize);
       memcpy(&RSD[Fsize>>1],&DecodeBuf[KK],EE<<1);
    
       cache_flush_range((u32)RSD,((u32)RSD)+Bsize);
      }
    
      SPINLOCK_LOCK(1)=0; //освобождаем спинлок 1
     }
    }

    DSP - команда декодеру (CPU1):

    void FEC_Decode(u16 *buf)
    {
     while(SPINLOCK_STATUS&(1<<1)); //ждём пока спинлок  занят
    
     memcpy(DecodeBuf,buf,Bsize);
    
     xthal_dcache_region_invalidate(RSD,Bsize);
    
     memcpy(buf,RSD,Bsize);
    
     memcpy(RSD,DecodeBuf,Bsize);
    
     xthal_dcache_region_writeback(RSD,Bsize);
    
     while(SPINLOCK_LOCK(1)); //захват спинлока 1
    }

     

  9. Наконец-то удалось заставить работать спинлоки, как нужно для моего алгоритма. Как всегда, в даташите пример программы с опечатками. Мне эта картинка дала 100% понимание о работе спинлоков:

    image.png.398fcbdc4801dcc04cd3ec1cfdaec5c0.png

    CPU0, инициализация кодера и декодера:

    void FEC_Init(u8 mln)
    {
     SPINLOCK_ENABLE(); //разрешение и сброс спинлоков
    
     while(SPINLOCK_LOCK(0)); //захват спинлока 0 (кодер)
     while(SPINLOCK_LOCK(1)); //захват спинлока 1 (декодер)
    
     printf("T113-s3 FEC\n");
    }

     

    CPU0 - кодер:

    void FEC_Encode(void)
    {
     if(!(SPINLOCK_STATUS&(1<<0))) //если спинлок 0 свободен
     {
      cache_inv_range((u32)RSC,((u32)RSC)+Fsize);
    
      memcpy(EncodeBuf,RSC,Fsize);
    
      encode_rs(EncodeBuf); //собственно кодирование
    
      memcpy(&RSC[Fsize>>1],&EncodeBuf[KK],EE<<1);
    
      cache_flush_range((u32)&RSC[Fsize>>1],((u32)&RSC[Fsize>>1])+(EE<<1));
    
      while(SPINLOCK_LOCK(0)); //захват спинлока 0 (для DSP, индикатор того, что данные закодированы и готовы)
     }
    }

     

    DSP - кодер (даёт команду для CPU0):

    void FEC_Encode(u16 *buf)
    {
      while(!(SPINLOCK_STATUS&(1<<0))); //ждём, пока спинлок 0 свободен
    
      memcpy(EncodeBuf,buf,Fsize); //сохраняем текущий не закодированный буфер 
    
      xthal_dcache_region_invalidate(RSC,Bsize);
    
      memcpy(buf,RSC,Bsize); //копируем результат предыдущей итерации
    
      memcpy(RSC,EncodeBuf,Fsize); //загружаем данные для CPU0 - новая итерация
    
      xthal_dcache_region_writeback(RSC,Fsize);
    
      SPINLOCK_LOCK(0)=0; //освобождаем спинлок 0 (запуск CPU0)
    }

     

    Хедер:

    #define SPINLOCK_BASE 0x03005000
    
    #define SPINLOCK_STATUS  (*(IO u32*)(SPINLOCK_BASE+0x010))
    #define SPINLOCK_LOCK(N) (*(IO u32*)(SPINLOCK_BASE+((N)<<2)+0x100))
    
    #define SPINLOCK_ENABLE()                          \
    {                                                  \
     SPINLOCK_BGR_REG&=~(1<<16); /* assert reset    */ \
     SPINLOCK_BGR_REG|=1;        /* gating pass     */ \
     SPINLOCK_BGR_REG|=(1<<16);  /* de-assert reset */ \
    }                                                  \

     

    Получилась немного инверсная логика работы, но на практике тот результат, что мне нужен.

    И всё-же мне непонятно, чем эти спинлоки лучше, чем волатильные некешируемые флаги.  Именно в моём примере.  Атомарность? Вроде - нет, запись в регистр никогда не атомарна.  И немного выглядит странной логика захвата спинлока - чтением регистра.  Обычно статус-флаги записываются и потом читаются.  А тут непривычно.

  10. 33 minutes ago, sasamy said:

    если от msgbox прерывания DSP 3,4 полагаю от спинлоков где-то рядом, для арма номер в таблице есть

    Вы слишком усложняете всё. Не нужны мне прерывания от спинлоков. Мне достаточно в нужное премя прочесть его состояние, что свободен.

    Потому что дизайн полностью синхронный: 1 итерация цикла main = 1 итерация кодирования = 1 итерация декодирования.

    33 minutes ago, sasamy said:

    интересно посмотреть как с флагом в памяти можно сделать такое и не накручивать бесполезные циклы ожидания

    Что значит "бесполезные циклы ожидания"?  Если данные не готовы, то по любому нужно будет ждать.  В моём случае кодер-декодер отрабатывают быстрее, чем итерация цикла в main.

    И запускаются кодер-декодер синхронно. А это значит, что к следующей итерации цикла- кодер и декодер будут гарантированно свободны, и не нужно будет ожидать их освобождения.

    В общем, я думаю, что спинлоки здесь не подойдут. Потому что мне не нужно лочить DSP, пока CPU считает данные.

    Кроме того, мне не понятна сама реализация спинлоков. Есть пара регистров - STATUS и LOCK.  Первый проверяет занят ли спинлок.  Чтение второго приводит к захвату, =0 если захват успешен.  Запись в него нуля освобождает спинлок.   Теперь как это соотнести к функциям CPU и DSP?

    Что первично?  Если  в цикле CPU сделать захват спинлока, затем произвести манипуляции с памятью, затем освободить спинлок, то CPU входит в бесконечный цикл - эти действия (захват- работа - освобождение)  делаются бесконечно.

    #define SPINLOCK_STATUS  (*(IO u32*)(SPINLOCK_BASE+0x010))
    #define SPINLOCK_LOCK(N) (*(IO u32*)(SPINLOCK_BASE+((N)<<2)+0x100))
    
    #define CHECK_SPINLOCK_STATUS(N) while(SPINLOCK_STATUS&(1UL<<(N)))
    
    #define CHECK_SPINLOCK_LOCK(N) while(SPINLOCK_LOCK(N))
    
    #define SPINLOCK_FREE(N) SPINLOCK_LOCK(N)=0
    
    void FEC_Encode(void)
    {
     while(1)
     {
      CHECK_SPINLOCK_STATUS(0);
      CHECK_SPINLOCK_LOCK(0);
    
      shared_memory_read();
    
      encode_rs(shared_memory);
    
      shared_memory_write();
    
      SPINLOCK_FREE(0);
     }
    }

      А мне надо, чтобы DSP пинал CPU когда нужно.

    В общем нестыковка с этими спинлоками. И не уверен, что в моём контексте они точно подходят.

    C флагами намного прозрачнее:  CPU ждёт флаг=0.   DSP производит действия с памятью и выставляет флаг=0.   CPU увидев что флаг=0 начинает  кодирование.  В конце CPU выставляет флаг =1 и снова ждёт пока DSP не скинет его в 0.   DSP в следующей итерации видит флаг =1,  готовит новые данные/забирает старые  и снова выставляет флаг =0 для CPU.   CPU ждал флага=0  и дождался: новая итерация кодирования.   А со спинлоками как??

     

    От CPU требуется только считать кодер. Более он не нужен.

    void main_secondary(void) //CPU1 - decode
    {
     LowLevel_Init();
    
     while(1)FEC_Decode(); //decode cycles (when DSP set flag_decode=0)
    } 
    
    void main(void) //CPU0
    {
     LowLevel_Init();
    
     FEC_Init();          //init codec
    
     CPU1_Run(main_secondary);                  //Run CPU1 - decoder
     sunxi_dsp_init(DSPimage,sizeof(DSPimage)); //Run DSP - main
    
     while(1)FEC_Encode();  //encode cycles (when DSP set flag_encode=0)
    }

     

    P.S. Кстати, проблема этого топика решена.  Была некорректная работа с кешем после сброса:

    https://electronix.ru/forum/topic/157083-allwinner-t113-s3-udelal-hifi4-dsp-smeyatsya-ili-plakat/?do=findComment&comment=1882985

     

     

     

  11. 1 hour ago, sasamy said:

    прочтите мануал - у спинлока как раз такая семантика, одно ядро прочитало 0 значит блокировка захвачена и можно выполнять код над шареным буфером, а второе будет читать 1 и накручивать циклы ожидания 0, первое ядро закончило работу и пишет в спинлок 0 освобождая его, второе читает 0 захватывает блокировку и начинает свою работу над шареными данными, первое ядро при попытке получить блокировку будет читать 1 и накручивать циклы ожидания 0, и так по очереди.

    Читаю сейчас соответствующую главу в мануале.   Я не вижу отличий между спинлоком и некешируемым волатильным флагом в памяти.   В чём преимущества спинлоков перед волатильными флагами?

    Когда работал с TMS320C6745, то синхронизировал их без спинлоков, всё работало.

    Ниже попытался объяснить, что в конечном итоге мне надо.

    Со стороны CPU - код кодера.  Данные во время работы гарантированно не используются другими ядрами:

    while(1) //вечный цикл, более ничего не делаем
    {
     if(coder_flag==0) //DSP дал команду запустить кодер и произвести чтение-запись данных
     {
      read_data(); //читаем SHARED данные :  входные
    
      code();  //проводим вычисления
    
      write_data(); //записываем в SHARED данные : выходные
       
      barrier(); //барьер компилятору
    
      coder_flag=1; // готово!  флаг для DSP
     }
    }

    Cо стороны DSP - управляющий:

    main()
    {
     init_all(); // инит всего и вся
      
     while(1) //вечный цикл
     {
      do_something(); //что-то делаем
    
      encode_to_cpu(); //даём команду на запуск кодера для CPU
    
      do_something2(); //что-то другое делаем
     }
      
    }
    
    void encode_to_cpu()
    {
     while(!coder_flag); //CPU свободен?  если нет, то ждём (на самом деле этот цикл быстро завершается,
                         //так как длительность итерации цикла главной программы - больше, чем время кодирования CPU)
    
     read_data(); //читаем SHARED данные - CPU отработал и дал нам данные предыдущей итерации
    
     write_data(); //записываем новые данные SHARED для CPU
    
     barrier(); //барьер компилятору
    
     coder_flag=0; //запускаем команду для CPU (кодер) - для новой итерации
    }

     

    В чём преимущества использования здесь спинлоков вместо coder_flag ?

  12. 53 minutes ago, mantech said:

    Да уж, странно, после повторного перезапуска оно понятно, как перевод управления из бутлоадера в осн. программу, но после аппаратного сброса...

    Такое впечатление, что аппаратный сброс != сброс ДСП)))

     

    Так в асмовском стартап-коде для Cortex A7   также запрещаются кеши и проводятся инвалидации.

    Entry:
             MRC     p15, 0, r0, c1, c0, 0         @ Read CP15 System Control register
             BIC     r0, r0, #(0x1 << 12)          @ Clear I bit 12 to disable I Cache
             BIC     r0, r0, #(0x1 <<  2)          @ Clear C bit  2 to disable D Cache
             BIC     r0, r0, #0x1                  @ Clear M bit  0 to disable MMU
             BIC     r0, r0, #(0x1 << 11)          @ Clear Z bit 11 to disable branch prediction
             MCR     p15, 0, r0, c1, c0, 0         @ Write value back to CP15 System Control register
    
             MOV     r0,#0
             MCR     p15, 0, r0, c8, c7, 0         @ I-TLB and D-TLB invalidation
             MCR     p15, 0, r0, c7, c5, 6         @ BPIALL - Invalidate entire branch predictor array

     

    Вопрос в том, как это правильно сделать для DSP и желательно - с особой педантичностью, чтобы в любых случаях положительный результат был 100%.

  13. 1 hour ago, sasamy said:

    аппаратный спинлок пробовали или капризный DSP мешает ? в мануале по шагам алгоритм расписан как обращаться к общим данным с двух ядер с поллингом спинлока

    А вы точно уверены, что он для моих целей подойдёт?

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

    Семафор нужен.

  14. 3 hours ago, repstosw said:

    HiFi4 DSP не сбрасывается вместе с CPU 0/1 при нажатии кнопки RESET.  Результат непредсказуем: после сброса DSP либо улетает в экспешн, либо  работает.  

    Вопрос решён.

    Проблема была в повторной инициализации регионов кеширования, которая приводила к эксепшену.  В функции _cache_config :

    https://github.com/YuzukiHD/FreeRTOS-HIFI4-DSP/blob/master/arch/board-init.c#L11

    Для повторных вызовов (при нажатии кнопки RESET на плате с T113-s3) нужно сделать инвалидацию кешей:

    #include "hal.h"
    
    static void _cache_config(void)
    {
      //для корректной работы DSP после сброса T113-s3
      xthal_icache_all_invalidate();
      xthal_dcache_all_invalidate();
    
      /* 0x0~0x20000000-1 is non-cacheable */
      xthal_set_region_attribute((void *)0x00000000, 0x20000000, XCHAL_CA_WRITEBACK,0);
      xthal_set_region_attribute((void *)0x00000000, 0x20000000, XCHAL_CA_BYPASS,   0);
    //...

     

    По-хорошему, после сброса надо запрещать кеширование и отключать предвыборку(prefetch) и сбрасывать конвеер. Чтобы потом снова разрешать всё.

    Вариант-минимум выше работает.

  15. 16 minutes ago, mantech said:

    А если это делать на CPU, все норм?

    На CPU всё давно отлажено и отлично работает при любых условиях.   Проблема с DSP: слишком капризная архитектура.

  16. HiFi4 DSP не сбрасывается вместе с CPU 0/1 при нажатии кнопки RESET.  Результат непредсказуем: после сброса DSP либо улетает в экспешн, либо  работает.  

    Запись в регистр DSP_ALT_RESET_VEC_REG не приводит к положительному результату.

    Вопрос:  как остановить DSP?

    Код для запуска DSP в спойлере:

    Spoiler
    void sunxi_dsp_init(const void *img_addr,u32 img_size)
    {
     sram_remap_set(1); //set system boot use local ram
    
     dsp_freq_default_set();
    
     //clock gating
     u32 reg_val=readl_dsp(CCU_BASE+CCMU_DSP_BGR_REG);
     reg_val|=(1<<BIT_DSP0_CFG_GATING);
     writel_dsp(reg_val,CCU_BASE+CCMU_DSP_BGR_REG);
    
     //reset
     reg_val=readl_dsp(CCU_BASE+CCMU_DSP_BGR_REG);
     reg_val|=(1<<BIT_DSP0_CFG_RST);
     reg_val|=(1<<BIT_DSP0_DBG_RST);
     writel_dsp(reg_val,CCU_BASE+CCMU_DSP_BGR_REG);
    
     writel_dsp(DSP0_START_ADDRESS,DSP0_CFG_BASE+DSP_ALT_RESET_VEC_REG);
     reg_val=readl_dsp(DSP0_CFG_BASE+DSP_CTRL_REG0);
     reg_val|=(1<<BIT_START_VEC_SEL);
     writel_dsp(reg_val,DSP0_CFG_BASE+DSP_CTRL_REG0);
    
     sunxi_dsp_set_runstall(1); //set runstall
    
     //set dsp clken
     reg_val=readl_dsp(DSP0_CFG_BASE+DSP_CTRL_REG0);
     reg_val|=(1<<BIT_DSP_CLKEN);
     writel_dsp(reg_val,DSP0_CFG_BASE+DSP_CTRL_REG0);
    
     //de-assert dsp0
     reg_val=readl_dsp(CCU_BASE+CCMU_DSP_BGR_REG);
     reg_val|=(1<<BIT_DSP0_RST);
     writel_dsp(reg_val,CCU_BASE+CCMU_DSP_BGR_REG);
    
     load_image(img_addr,img_size); //load image to ram
    
     sram_remap_set(0); //set dsp use local ram
    
     sunxi_dsp_set_runstall(0); //clear runstall
    }

     

     

  17. Распределил задачи на три ядра:

    1. CPU0 - инит периферии, загрузка задачи в CPU1 и DSP, кодер Рида-Соломона

    2. CPU1 - декодер Рида-Соломона

    3. DSP - управляющая программа + всё остальное.

     

    Основная проблема: при подаче питания на плату - кодер Рида-Соломона на CPU0 возвращает правильнные данные, но DSP при приёме этих данных пакета из эфира с помощью приёмника (трансивер Si4463) - возвращает мусор в проверочных символах. При этом сам пакет приходит правильный.  Проблема именно в проверочных символах.  

    Если же сделать сброс (нажать кнопку  RESET на плате MangoPi - allwinner T113-s3), то проверочные символы верны.  И после каждого сброса - данные верны.  Проблема возникает только при подаче питания.

     

    Взаимодействие CPU0,CPU1 с DSP : управление - через волатильный флаг, слово в памяти - регион не кеширован, не буферизован, SHARED=1.  Данные - пара буферов - для кодера и декодера. Кешированы, буферизованы, SHARED=1.

    #define FEC_CODER_DATA      0xC7D00000 //адрес буфера кодера (кеширован)
    #define FEC_DECODER_DATA    0xC7D80000 //адрес буфера декодера (кеширован)
    
    #define FEC_CODER_CONTROL   0x47E00000 //контроль кодера (не кеширован)
    #define FEC_DECODER_CONTROL 0x47E80000 //контроль декодера (не кеширован)
    
    #define rsc (*(IO u32*)FEC_CODER_CONTROL)
    #define rsd (*(IO u32*)FEC_DECODER_CONTROL)

     

    Процедура запуска кодирования со стороны DSP - buf - входной и выходной буфер.  Для простоты - приведен пример синхронной работы кодера (вынуждающий ждать его завершения работы, что сводит "на нет" преимущества многоядерности, но зато более понятен).  Есть и асинхронный режим - когда возвращается закодированное содержимое с предыдущей итерации, а даётся команда на кодирование новых данных - без ожидания.

    static dtype *RSC=(dtype*)FEC_CODER_DATA;
    static dtype *RSD=(dtype*)FEC_DECODER_DATA;
    
    void FEC_Encode(u16 *buf)
    {
     memcpy(RSC,buf,Fsize);
     __asm__ __volatile__ ("" ::: "memory");
     xthal_dcache_region_writeback(RSC,Fsize);
     __asm__ __volatile__ ("" ::: "memory");
     rsc=0;
     __asm__ __volatile__ ("" ::: "memory");
     while(!rsc);
     __asm__ __volatile__ ("" ::: "memory");
     xthal_dcache_region_invalidate(&RSC[Fsize>>1],EE<<1);
     __asm__ __volatile__ ("" ::: "memory");
     memcpy(&buf[Fsize>>1],&RSC[Fsize>>1],EE<<1);
    }

    Процедура кодирования со стороны CPU0:

    void FEC_Encode(void)
    {
     if(!rsc)
     {
      cache_inv_range((u32)RSC,((u32)RSC)+Fsize);
      __asm__ __volatile__ ("" ::: "memory");
      memcpy(EncodeBuf,RSC,Fsize);
      __asm__ __volatile__ ("" ::: "memory");
      encode_rs(EncodeBuf);
      memcpy(&RSC[Fsize>>1],&EncodeBuf[KK],EE<<1);
      __asm__ __volatile__ ("" ::: "memory");
      cache_flush_range((u32)&RSC[Fsize>>1],((u32)&RSC[Fsize>>1])+(EE<<1));
      __asm__ __volatile__ ("" ::: "memory");
      rsc=1;
     }
    }

    При приёме вместо содержимого проверочных символов:

     memcpy(&RSC[Fsize>>1],&EncodeBuf[KK],EE<<1);

    Прилетает мусор (при каждом новом включении - разный).  При этом проверял пакет данных : перед передачей, во время передачи, после передачи - он правильный.  Проблема при приёме - проверочные символы битые.  При сбросе проблема уходит.

    Если же сделать кодирование на одном ядре - средствами DSP, то проблема не возникает:

    void FEC_Encode(u16 *buf)
    {
     memcpy(EncodeBuf,buf,Fsize);
     encode_rs(EncodeBuf);
     memcpy(&buf[Fsize>>1],&EncodeBuf[KK],EE<<1);
    }

    Настройка MMU CPU0,1 для "регистров" rsc/rsd (управление):

    		mmu_tlb_address[i + (dram_base>>20)] =
                                (dram_base + (i << 20))   |
    									     (0 << 19)    |
    										 (0 << 18)    |
    										 (0 << 17)    |
    										 (1 << 16)    | //SHARED
    										 (0 << 15)    |
    										 (0 << 12)    | //TEX
    										 (3 << 10)    |
    									         (0 <<  9)    |
    										 (15 << 5)    |
    										 (0  << 4)    |
    										 (0  << 3)    |  //Cacheble
    										 (0  << 2)    |  //Bufferable
    										 (2  << 0);

    Настройка MMU для буферов:

    	mmu_tlb_address[i + (dram_base>>20)] =
                                (dram_base + (i << 20))   |
    							             (0 << 19)    |
    										 (0 << 18)    |
    										 (0 << 17)    |
    										 (1 << 16)    | //SHARED
    										 (0 << 15)    |
    										 (0 << 12)    | //TEX
    										 (3 << 10)    |
    									         (0 <<  9)    |
    										 (15 << 5)    |
    										 (0  << 4)    |
    										 (1  << 3)    |  //Cachable
    										 (1  << 2)    |  //Bufferable
    										 (2  << 0);

     

     

    В чём может быть проблема?

     

     

  18. 1 hour ago, Arlleex said:
    u32 volatile *p = (какой-то адрес); // указатель на volatile: например, периферийный регистр
    
    u32 *volatile p = (какой-то адрес); // вот этот вот "какой-то адрес" нельзя оптимизировать; например, ISR "двигает" позиции программного FIFO

    Какой из ваших двух примеров будет означать буфер с изменчивыми данными?   Нужен буфер с volatile-содержимым.  Статический массив не подходит, потому что длина может меняться.

    Указатель на массив - тоже не годится : потому что надо указывать размер: 

    char (*mem)[256];

     

  19. 5 hours ago, sasamy said:

    вообще не очень понятно как вам может подойти ядро которое считает в несколько раз медленней судя по вашим тестам и при этом вы будете поллить результат на более быстром ядре. В чём смысл ? Я бы ещё понял что считается параллельно пока CPU что-то делает другое полезное и прерывается по готовности результата аппаратно.

    ARM CPU 0 - кодер Рида-Соломона.  34 FPS

    ARM CPU 1 - декодер Рида-Соломона 15 FPS

    HiFI4 DSP - главная программа +  JPEG encoder/decoder(аппаратно), CELT coder/decoder (софтово), эхоподавитель(софтово), G2D(аппаратно), захват с камеры(аппаратно),  ADC/DAC(аппаратно).

    Сделал HiFi4 DSP главным, потому что Рида-Соломона он не вывозит на 12 FPS.   Зато на остальное его хватает.  Нужно одновременно кодирование и декодирование на 12..15 FPS.

    34 FPS + 15 FPS = 10.4 FPS < 12..15 FPS

    Итого: все ТРИ ядра активны.

     

    5 hours ago, Сергей Борщ said:

    Имя массива не может быть volatile. Как и имя любой другой переменной. Оно имя. volatile относится к содержимому переменной/массива.

    А если указатель volatile? Что в этом случае что изменчиво - само значение указателя (адрес) или содержимое памяти (при разыменовывании) , на которое указывает этот указатель?

     

    5 hours ago, sasamy said:

    Кстати а сброс кеша по вашему мгновенный ?

    Quote
      cache_flush_range((u32)RSC,((u32)RSC)+fsize);

    или это другое 🙂

    И сколько времени надо ждать, прежде чем другое ядро сможет обращаться к этим данным?

  20. 9 hours ago, Сергей Борщ said:

    Если сам указатель не меняется - ему совершенно незачем быть volatile. Он должен быть указателем на volatile. Вы бы показали его объявление, тогда вам бы посоветовали правильный вариант.

    #define FEC_CODER_CONTROL   0x47E00000 //контроль кодера (не кеширован)
    #define rsc (*(volatile unsigned int*)FEC_CODER_CONTROL)
    
    #define FEC_CODER_DATA      0xC7D00000 //адрес буфера кодера (кеширован)
    volatile dtype *RSC=(volatile unsigned short*)FEC_CODER_DATA;

     

    9 hours ago, Сергей Борщ said:
    volatile int a1[10];
    int volatile a2[10];	// эти два объявления эквивалентны, выбирайте по вкусу.

    Это точно, что в этих случаях каждый элемент массива будет volatile? Или только имя массива?

     

    9 hours ago, jcxz said:

    Тривиальное приведение типа спасёт предводителя дворянства и отца демократии от козней mamcpy.  :wink:

    Так и сделал.

     

    10 hours ago, sasamy said:

    опять поллинг 🙂 там ведь есть аппапртные спинлоки с генерацией прерываний

    В настоящий момент не до конца раскурил эти спинлоки.

    Они тормозят ядро?  Тогда не подходит.  То, что флаг установился/сбросился, это ещё недостаточно, чтобы тормозить ядро. Потому что оно кроме этого ещё некоторые вещи делает.

     

    8 hours ago, amaora said:

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

    Для компилятора и предназначалось. Чтобы не переставил местами несвязанные между собой конструкции.

    Для DSP барьер памяти :  memw,   для CPU - инструкция DSB

  21. 2 hours ago, vov4ick said:

    extern volatile ?

    vloatile сделал. Но это указатель.  И ещё когда mamcpy, memset, то требует убрать volatile.

    Как сделать массив volatile?  Чтобы компилятор имел ввиду - каждый элемент массива может быть volatile.

    Ну ещё и барьер памяти, потому что важна последовательность действий - вызов encode_rs() не раньше инвалидации и не позже flush. rsc=1 - в самом конце - это флаг другому ядру:

    void FEC_Encode(void)
    {
     if(rsc==0)
     {
      cache_inv_range((u32)RSC,((u32)RSC)+fsize);
    
      __asm__ __volatile__ ("" ::: "memory");
      
      encode_rs(RSC);
    
      __asm__ __volatile__ ("" ::: "memory");
    
      cache_flush_range((u32)RSC,((u32)RSC)+fsize);
    
      __asm__ __volatile__ ("" ::: "memory");
      
      UART_putc('^');
    
      __asm__ __volatile__ ("" ::: "memory");
    
      rsc=1; //ready flag for first CPU
     }
    
    }

    rsc - это некешируемая ячейка памяти, хранящая флаг - volatile : оба ядра могут её читать-писать.   RSC - указатель на буфер данных, тоже volatile.

  22. Пишу задачу для второго ядра. Кодер Рида-Соломона.  Данные в буфере. Они читаются в задаче, далее производятся манипуляции с данными, и они обратно читаются в тот же буфер.

    Эти данные использует другой процессор. Ядро другой архитектуры, не как архитектура первого ядра. И компилятор уже другой.

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

    Выкидывается функция  encode_rs(RSC);

    void FEC_Encode(void)
    {
     if(rsc==0)
     {
      cache_inv_range((u32)RSC,((u32)RSC)+fsize);
      
      encode_rs(RSC);
    
      cache_flush_range((u32)RSC,((u32)RSC)+fsize);
      
      UART_putc('^');
    
      rsc=1; //ready flag for first CPU
     }
    
    }

     

    Вопрос, как сделать так, чтобы код не выкидывался и выполнялся?

    Вариант вывести результат функции через printf() - не рассматривается.

  23. Собственно, для чего?

    flush dcache - понятно.  Скинуть  с кеша данных в память.  Чтобы DMA или периферал какой-нибудь прочитал правильные данные, подготовленные CPU

    invalidate dcache - понятно.  Объявить кеш данных недостоверным.  Чтобы CPU прочёл корректные данные, после того как периферал или DMA записали в память данные/

    invalidate icache - тоже понятно.  Объявить кеш инструкций недостоверным. Для самомодифицирующегося кода: если мы записали байт-код какой-нибудь функции в память и её нужно выполнить.

    А flush icache - зачем? Какова практическая цель его применения?  Сбросить кеш кода в память. Для чего?

     

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