Jump to content

    

jeka

Свой
  • Posts

    466
  • Joined

  • Last visited

Everything posted by jeka


  1. STM32H743 flash bank swap

    А трассировка момента стирания не смотрели на какой инструкции валится (если по одной ассемблерной инструкции трассировать)? И что в дампе флеш памяти после ресета? Возможно, стираете текущий исполняемый код. Не уверен что меняются местами регистры между собой при переключении банков.
  2. Еще одна казалось бы мелочь. Вы сбрасываете источник прерывания так: TIM17->SR &= ~TIM_SR_CCxIF; Эта операция не атомарна и преобразовывается компилятором в 3 команды: чтение регистра, сброс бита, запись в регистр. Теперь подумайте, что произойдет если между чтением и записью таймер установит флаг какого-то другого события? Статус считан без установленного флага события, потом таймер выставил событие, и при записи флаг события сбросится. Чтобы этого не было сбрасывать события надо так: TIM17->SR = ~TIM_SR_CCxIF; Согласно даташиту, все единичные биты игнорируются, а там где ноль - будет сброс.
  3. stm32h743, hal+lwip. Очень редко заклинивает передачу при интенсивном обмене. При заклиненной передаче вижу ситуацию: не очищен бит own в дескрипторе, освобождение которого ожидает драйвер (всего 4 дескриптора, остальные 3 дескриптора при этом очищены). Если ручками бит own очищаю, передачу расклинивает. К сожалению не посмотрел какой дескриптор ожидает периферия, жду следующего появления глюка. Кеш включен, MPU регионов с дескрипторами настроены по рекомендациям ST: // ======= memory policy (ethernet tx/rx descriptors) MPU->RNR = 11;// region number MPU->RBAR = 0x3004001B;// base address MPU->RASR = (0 << MPU_RASR_XN_Pos) |// DisableExec (1 << MPU_RASR_AP_Pos) |//AccessPermission (1 - privilegied only) ((0) << MPU_RASR_TEX_Pos) |//TypeExtField (0 << MPU_RASR_S_Pos) |//IsShareable (0 << MPU_RASR_C_Pos) |//IsCacheable (1 << MPU_RASR_B_Pos) |//IsBufferable (0 << MPU_RASR_SRD_Pos) |//SubRegionDisable (MPU_REGION_SIZE_512B<< MPU_RASR_SIZE_Pos)|//Size (1 << MPU_RASR_ENABLE_Pos);//Enable Кто-нибудь сталкивался с этим? Можно конечно механизм расклинивания дескрипторов для этой ситуации сделать, но хочется найти косяк и исправить по человечески.
  4. Я получил без трансивера ~10(+-1)мбайт/сек записи, примерно столько же чтения. Без low-voltage трансивера это близко к пределу пропускной способности интерфейса sd карты. Загрузка cpu H743 порядка 10% получилась (реально меньше, ибо в загрузку еще получение по интерфейсу и предобработка данных включается). Без кеширования скорость записи была порядка 2-3МБ, но главная неприятность в том что при записи были регулярные фризы на время около 1 секунды (предполагаю, контроллер sd карты при многократной перезаписи одной области искусственно вносит задержку чтоб не истратить ресурс быстро). При кешировании ни одного фриза не заметил. В моем случае при потоковой записи такие фризы - очень неприятная штука.
  5. Разобрался. Код этот рабочий, с размером блока напортачил. Плюс IDE в дампе EEPROM не обновляет дамп в реальном времени (обновляет только при запуске отладчика) и затирает EEPROM по умолчанию при программировании.
  6. Не пойму в чем дело. Пытаюсь писать в eeprom, и никаких признаков что пишется не наблюдаю. В гугле есть аналогичный вопрос, без ответа торчит. void WriteFlash(const void* Src, void* Dst, int Len) { while (NVMCTRL.STATUS & 3) {}; uint8_t* SrcB = (uint8_t*)Src; volatile uint8_t* DstB = (uint8_t*)Dst; CPU_CCP=0x9D;// unlock for next 4 cycles. ints skipped for 4 cycles NVMCTRL.CTRLA=NVMCTRL_CMD_PAGEBUFCLR_gc; while (NVMCTRL.STATUS & 3) {}; for (int i=0; i<Len; i++) { *(DstB++) = *(SrcB++); } CPU_CCP=0x9D;// unlock for next 4 cycles. ints skipped for 4 cycles NVMCTRL.CTRLA=NVMCTRL_CMD_PAGEERASEWRITE_gc; while (NVMCTRL.STATUS & 3) {}; } #define SETTINGS_ADDR 0x1400 WriteFlash ((void*)&settings, (void*)SETTINGS_ADDR, sizeof (struct SETTINGS)); // пробовал и штатную функцию вызывать. эффект аналогичный: #include <avr/eeprom.h> eeprom_write_block ((void*)&settings, (void*)0/*SETTINGS_ADDR*/, sizeof (struct SETTINGS)); смотрел дамп в mplab x 5.50. везде ff ff...
  7. Увы, это не так. Совсем. Во-первых, модуль кеширования как раз занимается всем этим: и группировкой секторов в блоки, и умным префетчем, и отсеканием ненужных/пустых операций, и выравниванием операций по границе. Просто увеличив размер сектора до 4к мы как минимум половину из этих проблем не решаем, ибо я детально изучал какие обращения делает fatfs на каких операциях, там много избыточных обращений, которые можно вылечить либо серьезным усложнением самого fatfs, либо на порядок более простым модулем кеширования. К тому же, размер блока SD карты обычно больше 4к, со всеми вытекающими. А при работающем кеше 4к блоки у fatfs не имеют никакого практического смысла. Еще приятный бонус кеширования - можно операции чтения-записи к fatfs делать любыми удобными кусками, хоть по 5 байт, можно бустро и часто открывать-закрывать файлы без перечитывания цепочек fat и каталогов. Модуль кеширования всё это выстраивает и оптимизирует.
  8. Я с SD карточкой тоже боролся. Тоже смотрел в сторону увеличить блок данных (точнее, почему блок указан длинный, но пишется-читается все равно по 512 байт). В процессе разбора кода понял что лучше сделать модуль кеширования. В первую очередь чтобы ресурс флешки продлить - fatfs бестолково пишет некоторые сектора по многу раз в одно и то же место. В итоге немного допилил сам fatfs, ибо модулю кеширования для правильного выбора метода кеширования нужно знать какой это сектор - fat, каталога или кластер данных. Т.е. при кешировании данных модуль кеширования использует разные стратегии кеширования для кластеров, fat и каталогов. Ну и умеет буферизировать чтение и запись.
  9. После патча косяки исчезли. Вот подправленный варинат: static ETH_BufferTypeDef RxBuff[ETH_RX_DESC_CNT];// вынес в статику, чтоб при каждом пакете не инициализировать их заново. static struct pbuf * low_level_input(struct netif *netif) { struct pbuf *p = NULL; uint32_t framelength = 0; struct pbuf_custom* custom_pbuf; if (HAL_ETH_GetRxDataBuffer(&heth, RxBuff) == HAL_OK) { HAL_ETH_GetRxDataLength(&heth, &framelength); #if !defined(DUAL_CORE) || defined(CORE_CM7) // SCB_InvalidateDCache_by_Addr((uint32_t *)RxBuff->buffer, framelength); __DSB();// если кеш принимаемых пакетов отключен #endif lan_stats.rx_frames++; custom_pbuf = (struct pbuf_custom*)LWIP_MEMPOOL_ALLOC(RX_POOL); if(custom_pbuf != NULL) { custom_pbuf->custom_free_function = pbuf_free_custom; p = pbuf_alloced_custom(PBUF_RAW, framelength, PBUF_REF, custom_pbuf, RxBuff->buffer, framelength); } } return p; } extern SemaphoreHandle_t ipSemaphore; void ethernetif_input(void* argument) { struct pbuf *p; struct netif *netif = (struct netif *) argument; memset(RxBuff, 0 , ETH_RX_DESC_CNT*sizeof(ETH_BufferTypeDef)); for(int i = 0; i < ETH_RX_DESC_CNT -1; i++) RxBuff[i].next=&RxBuff[i+1]; while (ipSemaphore==NULL) osDelay(1); for( ;; ) { if (osSemaphoreAcquire(RxPktSemaphore, TIME_WAITING_FOR_INPUT) == osOK) { do { p = low_level_input( netif ); if (p != NULL) { // if (netif->input( p, netif) != ERR_OK ) if( xSemaphoreTake( ipSemaphore, ( TickType_t ) 0 ) ) { ethernet_input(p, netif); xSemaphoreGive( ipSemaphore ); } else pbuf_free(p); /* Build Rx descriptor to be ready for next data reception */ HAL_ETH_BuildRxDescriptors(&heth); } } while(p!=NULL); } } } И в tcpip.c семафор вставил: SemaphoreHandle_t ipSemaphore = NULL; static void tcpip_thread(void *arg) { struct tcpip_msg *msg; LWIP_UNUSED_ARG(arg); ipSemaphore = xSemaphoreCreateMutex(); xSemaphoreGive( ipSemaphore ); LWIP_MARK_TCPIP_THREAD(); LOCK_TCPIP_CORE(); if (tcpip_init_done != NULL) { tcpip_init_done(tcpip_init_done_arg); } while (1) { /* MAIN Loop */ LWIP_TCPIP_THREAD_ALIVE(); /* wait for a message, timeouts are processed while waiting */ TCPIP_MBOX_FETCH(&tcpip_mbox, (void **)&msg); if (msg == NULL) { LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n")); LWIP_ASSERT("tcpip_thread: invalid message", 0); continue; } if( xSemaphoreTake( ipSemaphore, ( TickType_t ) 0 ) ) { tcpip_thread_handle_msg(msg); xSemaphoreGive( ipSemaphore ); } } }
  10. Поразбирался. Самое веселое, что куда ни ставь HAL_ETH_BuildRxDescriptors, глюк не исчезает по банальной причине - пакет обрабатывается в другом потоке... Пришел пакет -> вызвалось Ethernet IRQ -> RxPktSemaphore -> завершение IRQ далее отрабатывает Ethif task: ethernetif_input -> tcpip_inpkt -> sys_mbox_trypost -> osMessageQueuePut -> xQueueSendToBack -> и в конце вызывает HAL_ETH_BuildRxDescriptors. Причем если вызов HAL_ETH_BuildRxDescriptors перенести в tcpip_thread, HAL перестанет сдвигать буфера и будет бесконечно один и тот же пакет пытаться отправить. далее, всё это принимает поток tcpip_thread. И в ней уже обрабатывается пакет, который был освобожден предыдущим потоком, со всеми вытекающими: tcpip_thread -> ethernet_input Вообщем, сделали крайне бестолково. Думаю, весь Ethif task надо убирать ибо он не нужен, а пакет из ethernetif_input сразу отдавать на обработку в ethernet_input.
  11. Как раз хотелось бы дескрипторы поменьше, например 128 байт, но общий объем буферов около 20кб. Иначе при маленьких пакетах и больших буферах эффективность использования RAM низкая, а packet rate может быть весьма высокий. И получается, если система задумалась при обработке жирного пакета, на буферизацию мелких пакетов может памяти не хватить (что собственно и происходит на практике). Сейчас смотрю реализацию езернета в порте микропитона под STM... Код явно в разы шустрей чем hal от stm. Видимо придется взять его за основу и немного доработать, ибо он на мелкие дескрипторы не заточен. Потрассировал HAL - честно разочаровывают программисты от ST. такие жирные функции крутить на каждом пакете, которые по факту ничего не делают. Вероятно, у Вас в поллинг режиме езернет работает. Мне же нужно с IRQ и RTOS.
  12. Еще не понял. HAL_ETH_GetRxDataLength(&heth, &framelength); - это размер всего пакета же, а не одного куска в дескрипторе? т.е. далее по тексту сброс DCACHE подразумевает целый пакет одним куском. Но... ведь пакет может быть в нескольких дескрипторах и не подряд идущих (переход по кругу от последнего буфера к первому), и получается хвост может потерять, если пакет не влезает в один дескриптор?
  13. При работе езернета натолкнулся на интересный глюк - содержимое пакета во время обработки может перезаписаться (на другой пакет). Причем долго работало нормально, стало проявляться только когда много коротких пакетов после задержки в роутере пачкой выплёвываются. Стал копать. Смотрю, куб из коробки генерирует такой вот код (специально обновил куб, свежий то же самое генерит): static struct pbuf * low_level_input(struct netif *netif) { struct pbuf *p = NULL; uint32_t framelength = 0, i = 0; struct pbuf_custom* custom_pbuf; ETH_BufferTypeDef RxBuff[ETH_RX_DESC_CNT]; memset(RxBuff, 0 , ETH_RX_DESC_CNT*sizeof(ETH_BufferTypeDef)); for(i = 0; i < ETH_RX_DESC_CNT -1; i++) { RxBuff[i].next=&RxBuff[i+1]; } if (HAL_ETH_GetRxDataBuffer(&heth, RxBuff) == HAL_OK) { HAL_ETH_GetRxDataLength(&heth, &framelength); HAL_ETH_BuildRxDescriptors(&heth); #if !defined(DUAL_CORE) || defined(CORE_CM7) /* Invalidate data cache for ETH Rx Buffers */ SCB_InvalidateDCache_by_Addr((uint32_t *)RxBuff->buffer, framelength); #endif lan_stats.rx_frames++; custom_pbuf = (struct pbuf_custom*)LWIP_MEMPOOL_ALLOC(RX_POOL); if(custom_pbuf != NULL) { custom_pbuf->custom_free_function = pbuf_free_custom; p = pbuf_alloced_custom(PBUF_RAW, framelength, PBUF_REF, custom_pbuf, RxBuff->buffer, framelength); } } return p; } забавно, что HAL_ETH_BuildRxDescriptors (как я понял это функция, которая помечает буфера приема как "свободно") вызывается не дожидаясь обработки пакета (которая в традиционно в процессе RTOS крутится). Подебажил - действительно, после вызова HAL_ETH_BuildRxDescriptors буфер перезаписывается свежими пакетами. Вот думаю, что разработчики имели в виду написав так... и как проще исправить.
  14. Да, lvgl уже подключил. На первый взгляд очень понравилось - документация адекватная, архитетура сделана по уму. openvg интересная наработка, будет время попробую. Откликов как-то не густо. Сложностью испугал или ценник не озвучил? платить готовы достойно, главное получить результат. Лучше конечно в перспективе рассматривать как длительное сотрудничество, а не разовую задачу.
  15. Есть дисплей 480x272 с тачскрином, на stm32h745. Есть базовые графические библиотеки (по большей части самописные) и текущий юзер-интерфейс (он умеет рисовать графики, приборную панель, гистограмму, бегать по менюшкам). Библиотека - отрисовывать текстуры, накладывать шрифты, все с субпиксельной точностью и прозрачностью). Есть базовые графические примитивы (привет Брезенхему), но их нужно будет доработать до субпиксельного обсчета или взять что-то готовое, подходящее под наши задачи. Нужно в этот юзер-интерфейс добавить продвинутый просмотрищик графиков (примерно функционал осциллографа, но каналов на 60 и с дополнительными фишками), и другие вещи. Все должно быть красиво, с субпиксельной отрисовкий, сплайнами/кривыми безье, каналом прозрачности при наложении. Реакция на тач тоже продвинутая - с прокруткой по инерции, просчетом ускорений, замедлений) Графические элементы есть кому рисовать. Нужно чувство эстетики, внимание к деталям и к мелким недочетам. Т.е. задача - программирование интерфейса, добавление в библиотеки недостающего функционала. И работа с данными, которые собираем-отображаем-меняем (приходящие в реальном времени данные буферизируются, пишутся на sd карту, возможно в разных масштабах для ускорения загрузки при большим масштабировании; просмотрищиком графиков нужный кусок собирается из буферов ram и/или sd карты, масштабируется и отображается). По срокам - хорошо бы в пределах месяца на минимальный функионал по отрисовке графиков (чтоб как-то можно было пользоваться), потом добавление фишек, доведение и вылизывание эстетики (несколько месяцев) Территориально - Москва, м. калужская. jeka (а) 2x4 тчк ru или личка, тут можно задат вопросы.
  16. Еще немного про кеш (тестировалось на DRAM памяти, кеш включен на чтение и запись) Заметил, что при любом обращении на чтение незакешированных данных, читается _вся_ строка кеша (32 байта), а не только тот элемент что вы запросили. Т.е. достаточно одной операции (например PLD) чтоб предварительно загрузить всю строку кеша. Предположительно, при чтении на границе будут читаться 2 строки кеша (будет полезно чтоб командой PLD грузить сразу 64 байта)
  17. В итоге, сделал программный профайлер, который замеряет время блокировки прерываний у freertos. И сразу все стало ясно - мой косяк оказался. Со стеком всё нормально. как я понял, этой штукой точно замерить время не получится. Как-то пробовал запустить таймлайн через SWO, но всё наглухо подвисало, ощущение что клоки swo неверные были. Надеюсь, скоро будет j-trace pro, с ним должно быть повеселее такие штуки искать.
  18. Заметил странный спецэффект c LWIP. При интенсивном обмене по езернету (~30Мбит/с), IRQ с приоритетом ниже чем MAX_SYSCALL_INTERRUPT_PRIORITY стали очень сильно задеживаться (пордок - десятки тысяч CPU cycles). Т.е. где-то в стеке происходит длительная блокировка прерываний. Кто-нибудь сталкивался, разбирался где? такое положение вещей никуда не годится.
  19. Итак, коротко про параллельное исполнение в H7: (1) Базовая арифметика (+,-, логика, сдвиги): mov, mvn, cmp, cmn, add, sub, adc,sbc, srb, and, orr, eor, bic, orn, asr, lsl, lsr, ror, rrx - может выполняться по 2 операции за такт и параллелиться с остальным. (2) Остальная логика-арифметика (Всё остальное кроме FPU и умножения): SIMD (sadd8/16,...), PKH*, SXT,UXT, saturating - увы, только одна операция за такт. Может параллелиться со всем остальным. Поэтому их нужно чередовать с другими операциями. (3) Умножение - одна операция за такт. Может параллелиться со всем остальным. (4) FPU - может выполняться по 2 операции за такт и параллелиться с остальным. Латентность FPU инструкций - 2 такта. Т.е. результат можем использовать через 2 такта. (5) CPU load/store: одинарные операции могут параллелиться с остальными. dual load - ни с чем не параллелится, занимает 1 такт. LDM - кол-во тактов равно количеству регистров. Параллельно с LDM выполнится только одна инструкция. Т.е. для лучшего распараллеливания нужно разбивать LDM на единичные загрузки и мешать с другими инструкциями. Или использовать dual операции при работе с 64бит памятью. (6) FPU load/store: но занимают (5) и (4). В деталях не изучена. (7) CPU/FPU vmov: занимают (2) и (4). В деталях не изучена.
  20. Задался вопросом оптимизации кода. В интернетах не нашел какой-либо толковой информации по оптимизации кода под M7. Поэтому пришлось поставить несколько экспериментов по быстродействию. Пока только начал, но после изучения скудного описания cortex m7 pipeline результаты для меня весьма неожиданные. Итак, есть DWT, которым оценивал скорость выполнения инструкций на процессоре stm32h743. В нем использовался счетчик циклов CPU, счетчик циклов ожидания доступа к памяти, счетчик команд с нулевым кол-вом циклов (т.е. выполненных с чем-то параллельно). 1. максимальное число одновременно исполняемых команд за такт. Хотя в архитектуре есть 4 очереди: load-store, 2xAlu и 4xfpu, максимум что добился - 2 инструкции за такт, как ни крутил. 2. FPU. Судя по экспериментам, стандартная операция FPU длится 2 такта и модулей FPU - 4 шт. Т.е. код из только FPU инструкций исполнялся со скоростью 2 операции за такт, когда между формированием результата и его повторным использованием было не менее 3х FPU инстрцкций. Если же использовать результат в следующей инструкции, то получается по 2 такта на инструкцию. 3. Load/store. По моей логике, load-store должен выполняться в отдельной очереди и не мешать работать alu. Но на практике это не совсем так. Например, есть 16 FPU инструкций, выполняющиеся за 8 тактов. Если в начало добавить LDM 8ми CPU регистров из TCM памяти, то добавляется еще 6-7 тактов. 4. ALU. Тут всё гораздо приятней чем на FPU. Если результат использовать через 1 инструкцию, то стабильно получаем 2 инструкции за такт. За исключением умножения. Еще, в некоторых случаях, результат команд битовых манипуляций непосредственно в следующей инструкции выполнялся за 1 такт. Умножитель. Мне не удалось выполнить более одного умножения за такт (кроме инструкций SMUAD and SMUSD). т.е. умножение надо параллелить с другими операциями, либо использовать команды сдвоенного умножения. p.s. Это мои первые зксперименты, на истину не претендую, тем не менее общее представление уже начинает формироваться.
  21. Платы готовы, запаяны. С nRF52832, с ре ференс дизайна взят. Задача получается поднять на ней 5.2 low latency.
  22. Вы уверены что с этим нельзя ничего сделать? Если кто-то чегото намерял, это не значит что нельзя улучшить. Сейчас гуглю. bluetooth 5.2 поддерживает изохронную передачу, как раз для этих целей. С задержками порядка 1-2 мс как заявляют.
  23. Понимаю. Но в данном применении не нужны большие кодограммы и высокая скорость. Нужно передавать всего по несколько байт, но с минимальной задержкой. Поискал про bluetooth low latency. явно, есть варианты. Сразу например наткнулся - https://www.bluetooth.com/bluetooth-resources/low-latency-deterministic-bluetooth-technology/
  24. наверняка. И сделать самому можно. Но другой работы хватает, это хочется дать тому кто знает эти чипы. Ибо с nrf не работал, пока разберешься с периферией, нюансами, глюками - время жрёт. Предлагайте. думаю, в базовом варианте 50-100к. Обсуждаемо. Если внутрь стека залезете, это могут быть другие цифры. Вообще, это только начало. там задача довольно большая. Это понятно. поэтому и спрашиваю, ибо вроде всё просто, но чем дальше - тем сложнее. Нюансов много. Стандартов блютуса много разных, BLE из коробки при низком прирывистом трафике явно будет лагать. Возможно можно как-то таймслоты правильно резервировать или еще какие-то хитрости использовать для борьбы с лагами. в чем проблема? пакеты короткие. скорость хоть 15Мбит. По сравнению с десятками ms задержки радиоинтерфейса здержки уарта - ни о чем. upd. хотелки по зедержкам: до 20мс туда-обратно - отлично. Приемлимо до 50мс. Выше 100мс - плохо. Редкие глитчи и единичные потери не страшны.