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

Rst7

Модератор
  • Постов

    4 619
  • Зарегистрирован

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

    2

Весь контент Rst7


  1. Верно описывает результат только для Cortex-M. Потому что объяснение неверное. А зачем писать в переменную, к которой Вы организуете эксклюзивный доступ, обычным способом? Только менее универсальный. Например, взять элемент из списка на ARM выглядит так: #define ExAddr(VAL) ((volatile unsigned int *)(&(VAL))) typedef struct LLITEM { struct LLITEM *next; unsigned char data[100]; }LLITEM; volatile LLITEM *FreeBlocks; LLITEM *AllocBlock(void) { LLITEM *p; LLITEM *next; do { p=(LLITEM*)__LDREX(ExAddr(FreeBlocks)); if (!p) {__CLREX(); return p;} next=p->next; }while(__STREX((unsigned long)next,ExAddr(FreeBlocks))); return p; } А на x86 с его единственным lock'ом надо извращаться.
  2. Кстати, это только к Cortex-M относится, всякие -А вроде не сбрасывают.
  3. Я не об этом. Обычная запись в память (или чтение) между LDREX/STREX не сбрасывает флаг. volatile unsigned int testvar; volatile unsigned int testvar2; void TestEX(void) { unsigned int tv; tv=__LDREX(&testvar); __no_operation(); __no_operation(); __no_operation(); testvar2=1; testvar2; __no_operation(); __no_operation(); __no_operation(); tv=__STREX(tv,&testvar); DEBUG_PRINTF("__STREX result %d\r\n",tv); } Такой код печатает "__STREX result 0", несмотря на наличие LDR/STR между LDREX/STREX. То, что заход в прерывания сбрасывают флаг- это какой-то отдельный тонкий момент, с ним надо разбираться, потому что в теории так быть не должно. Ага, вот в чем дело (из мануала на CM4) Т.к. прерывания - это тоже эксепшн, то вот и ответ.
  4. Например непонимание работы exclusive monitor'а ядра тут:
  5. Какие-то очень сложные у вас всех TFTP. У меня от LPC1768 загрузчик весит Из которых 1132 байта - это библиотечный printf, который можно выбросить, если сильно прижмет. Кстати, я его где-то выкладывал тут.
  6. USB-стек в МК

    Там все хуже, там просто не работает. Виснет намертво сразу.
  7. Очень жаль, что участник нашего форума оказался таким провальным. Если бы он это все высказывал тут в дискуссиях - то пофиг. А вот то, что он это несет в неокрепшие умы (я же не просто так цитату принес) - вот за это нет прощения.
  8. USB-стек в МК

    Давайте, кстати, проверим на вшивость Ваш прекрасный дорогой софт. Что у Вас в заголовочнике с регистрами от RT1020 описано для USB_USBCMD_ATDTW_MASK и USB_USBCMD_ATDTW_SHIFT?
  9. USB-стек в МК

    А причем тут "слабый софт"? Это зачем мне чужой код осваивать? Я за свое могу нести ответственность, а за чужое - пардон. Чтобы за чужое нести ответственность - надо его досконально изучить, а это сравнимо с написанием своего.
  10. Там еще и дичь про LDREX/STREX написана. Я бы вообще дал этому дьяволу по лицу. Именно за вот это:
  11. USB-стек в МК

    Вот прям могу с пылу с жару рассказать про борьбу с USB-стеком. Камень - iMX RT1020. Имею принцип в жизни - писать самому, готовое не использовать, максимум - подглядеть в примеры какие-то опущенные в даташитах подробности. Я не особо часто бодался с USB, последний раз лет эдак 15 назад делал эмулятор FT232 на AT90USB162. А теперь пришлось сделать ECM/RNDIS устройство. Официальные примеры для этих iMX RT есть, но со страшным нагромождением уровней абстракции, разбросано по куче файлов, местами - плюсы на Си врукопашную, в общем - все прелести. Довольно быстро все поехало в простом варианте типа суперцикла, без всяких прерываний, просто с проверкой флагов. Затем начал переделывать на вменяемую работу, т.е. прерывания, обеспечение zero copy, минимальные простои и так далее. И в конце концов наступил на мертвое зависание алгоритма добавления нового буфера в цепочку буферов USB DMA на ходу. Алгоритм описан в даташите, более того, именно его реализацию (не без индусских делов) я обнаружил при изучении примеров (после того, как моя реализация не заработала). Убито было несколько дней. Оказалось, что в заголовочном файле с регистрами периферии был неверно описан один из битов в регистрах (не 1<<14, а 1<<12). Обнаружил это совершенно случайно, когда уже начал проверять совсем все. Заголовочный файл с регистрами был взят именно из официальных примеров. Так что официальные примеры, конечно, компилируются, но вот никто их на таргете никогда не запускал и не проверял. Вообще оказалось, что во всей этой iMX-линейке вроде бы и один и тот же USB, но этот бит пляшет произвольно от типа к типу, в каких-то - 12й, в каких-то - 14й, в некоторых это считалось ошибкой и описано в errata, в некоторых нет, в общем - полный хаос. Это в общем я к тому, что взяв "готовый" можно влететь крепко с точки зрения "я возьму готовый, сэкономлю дофига времени". Ну и да, по итогу весь этот RNDIS у меня - один сишный файл вполне обозримых размеров. С минимумом оверхэда, как я люблю.
  12. Я - никак не делаю, живу жизнь без ассертов. Конкретно в данном случае это всего лишь пример, там можете строить что хотите сами. Но если бы конкретно мне понадобилась такая конструкция, то я бы сгенерировал исключение каким-то образом, а потом разбирался уже исключительно по PC (например, напечатав его в обработчике исключения). Немногим труднее, чем имя функции и номер строки, но зато куда меньше попутных приколов, например, совершенно не нужной в обычной жизни работе со стеком.
  13. Конкретно в данном случае - без использования CMSIS. Теперь о рукопашности этого ассерта и вообще кривости конкретно данного кода. Правильно писать вот так, если не отключать ассерты: if (portNum>=8) return LPC_GPIO_PORT->PIN[portNum]; else Abort(); Потому что ветка true - это более частая ветка, скорее всего с вероятностью 100%.
  14. Я не об этом. А о том, что куча таких же кривых проверок и насилия над стеком есть и в других процедурах библиотеки. Или у Вас только GPIO через библиотеку, а упомянутая остальная периферия - вся аккуратненько врукопашную?
  15. Там же небось такой рукопашный assert один на всех. И прекрасно живет и во всех остальных библиотечных процедурах, в том числе и тех, которые
  16. На каждое чтение порта вот такое вот? Зачем тогда сама тема про анализ листингов? Смысл их анализировать, если быстродействие релиза реально проваливается просто отказом от удаления отладочных пирогов? И да, неужели настолько сложно написать работу с портами с предсказуемыми последствиями?
  17. Тут главное не забыть в релизе сделать так: #define CHECK_PARAM(expr) Чтобы функция приняла вменяемый вид без насилия над стеком: \ In section .text, align 2, keep-with-next 137 uint32_t GPIO_ReadValue( uint8_t portNum ) 138 { 139 CHECK_PARAM(portNum < 8); 140 return LPC_GPIO_PORT->PIN[portNum]; \ GPIO_ReadValue: (+1) \ 0x0 0x.... LDR.N R1,??DataTable4 ;; 0x400f6100 \ 0x2 0xF851 0x0020 LDR R0,[R1, R0, LSL #+2] \ 0x6 0x4770 BX LR ;; return Ну и, к сожалению, inline - это вообще редкая птица во всяких этих библиотеках для не умеющих прочитать содержимое порта сразу по месту в собственном коде.
  18. Ну все-таки не совсем. Если бы я делал asm("UQSUB16 %0,%0,%1":"+r"(max):"r"(*_s++)); - вот тогда да, ассемблер. А так пока никакого выхода за границу разумного для чистого ЯВУшника - ну подумаешь, есть функция __uqsub16(), которая хитро вычитает половинки long'ов. Приведу пример с большого брата (да, Intel'у отдельный котел в аду черти растопили за то, что в SIMD-инструкциях нет знакового сложения с сатурацией для пачки 32хбитных операндов, приходится вот такое изобретать): static inline __m128i selectb(__m128i s, __m128i a, __m128i b) { #if 1 // SSE4.1 supported return _mm_blendv_epi8(b, a, s); #else return _mm_or_si128( _mm_and_si128(s, a), _mm_andnot_si128(s, b)); #endif } static inline __m128i add_saturated(__m128i a, __m128i b) { __m128i sum = _mm_add_epi32(a, b); // a + b __m128i axb = _mm_xor_si128(a, b); // check if a and b have different sign __m128i axs = _mm_xor_si128(a, sum); // check if a and sum have different sign __m128i overf1 = _mm_andnot_si128(axb, axs); // check if sum has wrong sign __m128i overf2 = _mm_srai_epi32(overf1, 31); // -1 if overflow __m128i asign = _mm_srli_epi32(a, 31); // 1 if a < 0 __m128i sat1 = _mm_srli_epi32(overf2, 1); // 7FFFFFFF if overflow __m128i sat2 = _mm_add_epi32(sat1, asign); // 7FFFFFFF if positive overflow 80000000 if negative overflow return selectb(overf2, sat2, sum); // sum if not overflow, else sat2 } Это asm или не asm?
  19. Если нельзя, но очень хочется - то можно: #define PROCESS2(V) max=__UQSUB16(max,V)+V; unsigned long findmaxv2(unsigned short *_s) { unsigned long max=0; unsigned long a0,a1,a2,a3,a4,a5,a6,a7; for(unsigned long i=0; i<512/(2*8); i++) { asm("LDMIA %0!,{%1,%2,%3,%4,%5,%6,%7,%8}": "+r"(_s), "=r"(a0),"=r"(a1),"=r"(a2),"=r"(a3), "=r"(a4),"=r"(a5),"=r"(a6),"=r"(a7)); PROCESS2(a0); PROCESS2(a1); PROCESS2(a2); PROCESS2(a3); PROCESS2(a4); PROCESS2(a5); PROCESS2(a6); PROCESS2(a7); } if ((max>>16)>(max&0xFFFF)) return max>>16; return max&0xFFFF; } Конечно, в реальной жизни надо подавить warning, который понятно почему возникает. И цикл минимально развернуть. Понятное дело, без знания asm'а такое не напишешь, но по сравнению с чистым asm'ом - не надо самому корячиться с прологами/эпилогами и всей остальной сопутствующей шляпой.
  20. Ну допустим так: #include <intrinsics.h> #define PROCESS4() acc=*s++; max=__UQSUB16(max,acc)+acc; max=__UQSUB16(max,acc>>32)+(acc>>32) unsigned long findmax(unsigned short *_s) { unsigned long max=0; unsigned long long acc; unsigned long long *s=(unsigned long long *)_s; for(unsigned long i=0; i<512/(4*4); i++) { PROCESS4(); PROCESS4(); PROCESS4(); PROCESS4(); } if ((max>>16)>(max&0xFFFF)) return max>>16; return max&0xFFFF; } Понятное дело, что развернуть можно чуть больше, если сильно надо. Основной провал по сравнению с asm-вариантом - невозможность загрузить сразу кучу переменных при помощи LDM, потому LDRD в качестве оптимизации. Но это IAR такой, насколько я знаю, GCC вроде бы умеет. А так, конечно, да, по сравнению с тем, что может накалякать какой-нибудь залетный формошлеп - это магия ;)
  21. Зато данные всегда выровнены. Нет штрафа за запись по некруглому адресу. Что скорее всего перекроет расходы на TBB. К сожалению, для IAR'а есть проблема - он принципиально не желает генерить LDM для разгрузки многих переменных. Потому самый оптимальный вариант не получится. А так intrinsic'и вполне решают задачу. Щас продемонстрирую.
  22. Ну и пайплайн врукопашную: void DumpDuff(long *d, long **s, int len32) { long *p=*s++; long v; switch(len32) { default: case 28: v=*p; p=*s++; *d++=v; case 27: v=*p; p=*s++; *d++=v; case 26: v=*p; p=*s++; *d++=v; case 25: v=*p; p=*s++; *d++=v; case 24: v=*p; p=*s++; *d++=v; .... 29 default: 30 case 28: v=*p; p=*s++; *d++=v; \ ??DumpDuff_1: (+1) \ 0x2A 0x681A LDR R2,[R3, #+0] \ 0x2C 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x30 0xF840 0x2B04 STR R2,[R0], #+4 31 case 27: v=*p; p=*s++; *d++=v; \ ??DumpDuff_2: (+1) \ 0x34 0x681A LDR R2,[R3, #+0] \ 0x36 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x3A 0xF840 0x2B04 STR R2,[R0], #+4 32 case 26: v=*p; p=*s++; *d++=v; \ ??DumpDuff_3: (+1) \ 0x3E 0x681A LDR R2,[R3, #+0] \ 0x40 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x44 0xF840 0x2B04 STR R2,[R0], #+4 .... Кстати, а вот еще момент: Так что загрузка адреса из списка более эффективна.
  23. Это в ARM бесплатно. Все я правильно задал. Циклы разворачиваются не на константное число. А все что можно, делается блоками по несколько итераций, а остальное - уже по одной штучке. На самом деле, если у Вас действительно ограничено число переменных, то Duff Device - это самое то в данном случае. Ага, ну тогда - DD: void DumpDuff(long *d, long **s, int len32) { switch(len32) { default: case 28: *d++=*(*s++); case 27: *d++=*(*s++); case 26: *d++=*(*s++); case 25: *d++=*(*s++); case 24: *d++=*(*s++); case 23: *d++=*(*s++); case 22: *d++=*(*s++); case 21: *d++=*(*s++); case 20: *d++=*(*s++); case 19: *d++=*(*s++); case 18: *d++=*(*s++); case 17: *d++=*(*s++); case 16: *d++=*(*s++); case 15: *d++=*(*s++); case 14: *d++=*(*s++); case 13: *d++=*(*s++); case 12: *d++=*(*s++); case 11: *d++=*(*s++); case 10: *d++=*(*s++); case 9: *d++=*(*s++); case 8: *d++=*(*s++); case 7: *d++=*(*s++); case 6: *d++=*(*s++); case 5: *d++=*(*s++); case 4: *d++=*(*s++); case 3: *d++=*(*s++); case 2: *d++=*(*s++); case 1: *d++=*(*s++); case 0: ; } } И все, бинго: Аналогично допиливается вторая итерация с short'ами. И да, возможно, что для short'ов будет веселее вот так: void DumpDuff16(long *d, short **s, int len32) { unsigned long v=0; switch(len32) { default: case 28: v=*(*s++); case 27: *d++=(v<<16)|*(*s++); case 26: v=*(*s++); case 25: *d++=(v<<16)|*(*s++); .... 64 { 65 default: 66 case 28: v=*(*s++); \ ??DumpDuff16_1: (+1) \ 0x28 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x2C 0x5ED3 LDRSH R3,[R2, R3] 67 case 27: *d++=(v<<16)|*(*s++); \ ??DumpDuff16_2: (+1) \ 0x2E 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x32 0xF9B2 0x4000 LDRSH R4,[R2, #+0] \ 0x36 0xEA44 0x4403 ORR R4,R4,R3, LSL #+16 \ 0x3A 0xF840 0x4B04 STR R4,[R0], #+4 68 case 26: v=*(*s++); \ ??DumpDuff16_3: (+1) \ 0x3E 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x42 0xF9B3 0x3000 LDRSH R3,[R3, #+0] 69 case 25: *d++=(v<<16)|*(*s++); \ ??DumpDuff16_4: (+1) \ 0x46 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x4A 0xF9B2 0x4000 LDRSH R4,[R2, #+0] \ 0x4E 0xEA44 0x4403 ORR R4,R4,R3, LSL #+16 \ 0x52 0xF840 0x4B04 STR R4,[R0], #+4 .... Пардон, там, конечно, unsigned short ** нужен.
  24. Идея, в общем, ничего так (если флеша вагон), но есть нюанс, касающийся именно ARM. Загрузка регистра из произвольной переменной стоит столько же, сколько *(*list++). На самом деле для ARM загрузка переменной в регистр - это *(*(pc+disp)). Потому никакого выигрыша не будет.
×
×
  • Создать...