Digi 0 24 сентября, 2019 Опубликовано 24 сентября, 2019 · Жалоба С мутексами вроде немного разобрался. Прога перестала падать при передаче большого,непрерывного потока данных. Создал три мутекса: irq, receieve, transmit. irq - повесил на прерывание, остальные соответственно блокируют обращения к данным обработчика приема и передачи соответственно. То есть я при начале работы с буфером приемника блокирую мутекс, а после снимаю блокировку. Если мне нужно принудительно пнуть прерывание приёма/передачи, то я отправляю mutex_signal(¤t_uart->irq,0); Правильно ли я понял ? Код функций под спойлером. Spoiler Инициализация void uart422_init (uartpkt_t *u, small_uint_t port, int prio, unsigned int khz, unsigned long baud) { u->khz = khz; u->port = (port) ? (volatile unsigned*)(0xb82f3800) : (volatile unsigned*)(0xb82f3000); mutex_init(&u->irq); mutex_init(&u->receiver); mutex_init(&u->transmitter); /* Setup baud rate generator. */ setup_baud_rate (u->port, u->khz, baud); /* Create uart receive task. */ uart422_task_s = task_create (uart422_receiver, u, "uart422", prio, u->rstack, sizeof (u->rstack)); } Задача обработчика порта /* * Receive interrupt task. */ void uart422_receiver (void *arg) { uartpkt_t *u = arg; mutex_lock_irq (&u->irq, RECEIVE_IRQ(u->port), 0, 0); enable_receiver (u->port); enable_receive_interrupt (u->port); debug_printf("Uart422 started.\n"); for (;;) { mutex_wait (&u->irq); // Ожидаем событие по приему или по принудительному пинку для передачи uart422_rtx_proc(u); // Вызываем функцию обработчика события с USART } } Обработчки прерывания RX и TX static bool_t uart422_rtx_proc (uartpkt_t *u) { unsigned char tx_char=0; unsigned char c=0; // Check that receive data is available, and get the received byte. if (test_get_receive_data(u->port, &c)) { mutex_lock(&u->receiver); switch (c) { case 0xff: ....... } mutex_unlock(&u->receiver); } // ------------ Transmit section ------------------ // Check that transmitter buffer is busy. if (! test_transmitter_empty (u->port)) { return 1; } mutex_lock(&u->transmitter); // Блокируем мутекс передатчика if (u->tx_packet.data_in_process==2) { ...... } .... mutex_unlock(&u->transmitter); // Снимаем блокировку передатчика return true; } Обработка данных в другой задаче void ctl_task (void *arg) { for (;;) { // Главный цикл задачи. Выходить из него нельзя current_uart=&uart422; if (current_uart) // Начинаем обработку пакета { mutex_lock(¤t_uart->receiver); command = current_uart->rx_packet.pkt_buffer.c_pack.data[0]; switch (command) // Обработчик команд живёт здесь { case 0x01: setFreqChan(current_uart->rx_packet.pkt_buffer.c_pack.data[1]); createCommonAns(src, dst, current_uart, command, 1); // Тут формируется обратный ответ mutex_signal(¤t_uart->irq,0); // пинаем прерывание, чтобы передатчик начал передавать break; .... } current_uart->rx_packet.data_ready=0; // Считаем что пакет обработали. И сбрасываем признак готовности mutex_unlock(¤t_uart->receiver); current_uart=0; } } } //Функция отправки ответа void createCommonAns(unsigned char src, unsigned char dst, uartpkt_t* current_uart,unsigned char command, unsigned char status ) { unsigned char len; mutex_lock(¤t_uart->transmitter); current_uart->tx_packet.pkt_buffer.c_pack.src=dst; current_uart->tx_packet.pkt_buffer.c_pack.dst=src; len=0; current_uart->tx_packet.pkt_buffer.c_pack.data[len++] = command; current_uart->tx_packet.pkt_buffer.c_pack.data[len++] = status; current_uart->tx_packet.len = len; current_uart->tx_packet.data_ready=1; mutex_unlock(¤t_uart->transmitter); } И ещё вопрос. Как понять, что приводит к краху ? Программа работает, никаких действий не произвожу. В интервале полчаса - несколько часов программа падает вот с такой ошибкой (Спойлер) Spoiler *** 0x8000bb58: exception 'Address Load' *** cause=0x00808410, badvaddr=0x80000208, config=0x80060183, errEPC=0x39c002c8 t0 = 0 s0 = 0 t8 = 0 lo = 0 at = 0 t1 = 0 s1 = 0 t9 = 8001a7b4 hi = 2 v0 = 800e0000 t2 = 51088754 s2 = 0 status = 30001c03 v1 = 400 t3 = 80000000 s3 = 0 epc = 8000bb58 a0 = 800e1d98 t4 = 0 s4 = 0 gp = 8001eb48 a1 = 400 t5 = fffffffc s5 = 0 sp = 800a6868 a2 = 0 t6 = 0 s6 = 0 fp = 800a6868 a3 = 3ff71412 t7 = 0 s7 = 0 ra = 8000bb44 Halt... Task Address Prio Stack Space Msg Ticks (damaged) 8001f374 0 8001f738 760 (nil) 0 (damaged) 800a6920 1 800a77e8 3408 (nil) 8383959 (damaged) 8001f830 80 800206d8 3376 (nil) 8383961 (damaged) 800a5920 *70 800a6790 3044 (nil) 8383961 (damaged).stack {0x800a7374/0x800a6950..0x800a6790/0x800a6750, 0x80018d78, 0x800 a66a8} [(damaged).800a694c] 800a77e8 00000001 00000001 8001d99c [(damaged).800a693c] 00000000 00000000 800a6930 800a6930 [(damaged).800a692c] 00000000 00000000 800a5920 8001f830 [(damaged).800a691c] 00000000 00000000 00000000 00000000 [(damaged).800a690c] 00000000 00000000 00000000 00000000 [(damaged).800a68fc] 00370037 00000000 00000000 00000d50 [(damaged).800a68ec] 3efb068b f2abe454 00000000 00020001 [(damaged).800a68dc] 4092f44b 05faebc4 00000000 00000001 [(damaged).800a68cc] 00000000 00000001 3efb068b f2abe454 [(damaged).800a68bc] 3efb0d4c bd8f32e3 795a62d2 00000000 [(damaged).800a68ac] 00000000 00000384 3ff37208 00000000 [(damaged).800a689c] 00000400 80132760 800103e4 800a6898 [(damaged).800a688c] c0018f70 a63ffdbc c01a7640 466c72e2 [(damaged).800a687c] c0139e79 e79e79e8 c013cf3c 00000400 [(damaged).800a686c] 800e1d98 80132760 8000bb58 30001c03 [(damaged).800a685c] 00000002 00000000 8000bb44 800a6868 [(damaged).800a684c] 8001eb48 8001a7b4 00000000 00000000 [(damaged).800a683c] 00000000 00000000 00000000 00000000 [(damaged).800a682c] 00000000 00000000 00000000 00000000 [(damaged).800a681c] 00000000 fffffffc 00000000 80000000 [(damaged).800a680c] 51088754 00000000 00000000 3ff71412 [(damaged).800a67fc] 00000000 00000400 800e1d98 00000400 [(damaged).800a67ec] 800e0000 00000000 00000002 00000000 [(damaged).800a67dc] 8001a670 800a6840 8001eb48 8001a7b4 [(damaged).800a67cc] 00000000 00000000 00000000 00000000 [(damaged).800a67bc] 00000000 00000000 00000000 00000000 [(damaged).800a67ac] 80020000 00000000 80000000 fffffff9 [(damaged).800a679c] 00000001 80000000 ef3f0000 =0000000f [(damaged).800a678c] 8000bb44 00000000 00000000 00000000 [(damaged).800a677c] 00000000 00000000 0084458f 39c002c8 [(damaged).800a676c] 80060183 80000208 00808410 801407b0 [(damaged).800a675c] 80019c9c 800a67e8 00000002 8000bb44 *** Please report this information *** System halted. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlexRayne 7 25 сентября, 2019 Опубликовано 25 сентября, 2019 · Жалоба Диги, оч тяжелый код Вы сделали для этой простой задачи. Блокировки мутехов очень тяжелы на платформе мипс, на арм может полегче. Имхо, все операцию с портом<->очередью стоит вынести в хэндл прерывания. Он выполнится атомарно, на уровне прерывания. А В мутехи приемника/передатчика можно послать сигналы из прерывания, тем ниткам кто их ждет. Имхо, не хватает цикла, выбирающего из очереди порта все что там есть. С проверкой, на наличие в приемнике символов, перед выходом из обработчика. Это может сильно облегчить нагрузку на проц при интенсивном потоке, если не успевапет за приемом. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlexRayne 7 25 сентября, 2019 Опубликовано 25 сентября, 2019 · Жалоба ещё вопрос. Как понять, что приводит к краху ? " попробуйте добавить стекам задач байт по 250? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Digi 0 25 сентября, 2019 Опубликовано 25 сентября, 2019 · Жалоба 4 hours ago, AlexRayne said: Диги, оч тяжелый код Вы сделали для этой простой задачи. Блокировки мутехов очень тяжелы на платформе мипс, на арм может полегче. Всем большое спасибо за замечания, советы и разъяснения. Да, код действительно тяжеловат, но со временем переделаю, как только придёт полное осознание происходящих процессов. Для информации: фактическое время обработчика составляет 16-25 мкс, этого достаточно до скоростей вплоть до 350 кбит/с. По поводу увеличения стека - сейчас стеки всех задач в два раза (было 0x1000 ). Возможно одной задаче его и не хватало. Время покажет. Сейчас в процессе работы смотрю место в стеке unused = task_stack_avail (uart422_task_s); Stack rcv 7472 Stack RS422 7504 Stack RS485 7500 Stack CTL 3376 Stack Print 7504 Вот что выдаёт. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 25 сентября, 2019 Опубликовано 25 сентября, 2019 · Жалоба 59 минут назад, Digi сказал: Да, код действительно тяжеловат, но со временем переделаю, как только придёт полное осознание происходящих процессов. Для информации: фактическое время обработчика составляет 16-25 мкс, этого достаточно до скоростей вплоть до 350 кбит/с. Это что - при такой скорости один только обработчик одного прерывания будет на 100% грузить процессор??? А зачем нужен такой обработчик, что кроме него больше ничего работать не сможет? Для ISR-ов UART даже 1% времени CPU - это уже очень много. Если не слишком много. На ARM-процессорах. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlexRayne 7 25 сентября, 2019 Опубликовано 25 сентября, 2019 (изменено) · Жалоба 24 минуты назад, jcxz сказал: Для ISR-ов UART даже 1% времени CPU - это уже очень много. Если не слишком много. На ARM-процессорах. На медленных АРМ (до 100Мгц) время обработки прерывания любого 5-10мкс. так что я б сказал что у него обработчик совсем неплох, учитывая что он заодно и шедулинг ведет. во фриРТОС вызов шедулера/прерывания тоже имеет такие порядки. Другое дело что на одим символ - это расточительно так тратиться. надо включать фифо, и обрабатывать пореже но пачку символов 1 час назад, Digi сказал: Stack rcv 7472 Stack RS422 7504 Stack RS485 7500 Stack CTL 3376 Stack Print 7504 прекрасные стеки. надо искать почему портится очередь задач. скорее всего изза этого при переключении он переключается на задачу по какомуто левому адресу. для отладки таких бед я лично накручивал систему контроля целостности очередей, и стеков. жаль что этот код утерян Пожалуй надо такую систему включить в проект уОСи. Сейчас могу Вам только предложить в main.c:task_schedule ввести свои функции контроля качества задачи на которую переключаетесь. и ассертить подозрительные вещи - выход указателя кода за пределы кода, и/или стека за пределы рам. Я думаю стоит потратить время на этот контроль, он очень пригодиться дальше. ибо это имхо не последнее крашение проекта. Имхо, самая вероятная причина потери очереди - потеря указателей, ваш код гдето пишет мимо буферов, и очень удачно крашит очередь. если вы введете глобальных пременных, то поползет раскладка ваших указателей, и портить начнет чтото другое. галюн вылезет в другом месте. Изменено 25 сентября, 2019 пользователем AlexRayne Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 25 сентября, 2019 Опубликовано 25 сентября, 2019 · Жалоба 2 часа назад, AlexRayne сказал: На медленных АРМ (до 100Мгц) время обработки прерывания любого 5-10мкс. так что я б сказал что у него обработчик совсем неплох, учитывая что он заодно и шедулинг ведет. во фриРТОС вызов шедулера/прерывания тоже имеет такие порядки. Любого??? Это под этой осью такие накладные расходы на неё что-ль? В обычных применениях даже при <100МГц - это очень большое время. 5 мкс - это 500 тактов на 100МГц если что. Посчитайте хотя-бы сколько занимает реакция на прерывание + сохранение/восстановление контекста (аппаратное) + программные PUSH/POP. Всё это будет во много раз меньше чем 500 тактов. Не понимаю - что так долго можно делать в ISR? Сейчас померил у себя: У меня в ПО есть один ISR такой долгий - несколько сотен тактов. Так в нём идёт усреднение 176 отсчётов АЦП (сложение+деление). И то это - 542 такта. Но с UART - я вообще не понимаю что там (в ISR) так долго можно что-то делать. Особенно если он ещё и без FIFO. Обычные ISR - во много раз меньше. И "вызов шедулера" - это тоже только несколько десятков тактов максимум. Это даже если весь FPU-контекст сохраняется/восстанавливается. Но никак не 500-1000. Ну по-крайней мере - это вменяемой ОС столько нужно, только на переключение контекста задачи. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlexRayne 7 25 сентября, 2019 Опубликовано 25 сентября, 2019 (изменено) · Жалоба Я практически мерял переключение контекста на МИПС32 - ВМ10Я на 240МГц тактовой. так вот у него до 5мкс доходило время захвата мутекса. Но это специфика МИПСа. и неоптимизированный код мутеха в уОС. Про АРМ летом был разговор - приятель пытался понять можно ли сделать переключение задачи меньше чем за 10мкс (вроде такой порядок там был). повозились, пошарились по форумам, выяснили что лучше чем фриРТОС делает, уже не сделать - у них ассемблерные оптимизированные процедуры все это шедулят. И тоже получалось какоето сумасшествие - более 300-500 тактов это занимает. Вполне соглашусь что чисто прерывание, без шедулера, с оптимизированным прологом, будет занимать ваши сотни тактов и сделает кучу работы. но в реальности в которой крутился я - таких олимпийских рекордов нет. Если касаться СТМ23 - то у них еще очень ударяет по скорости кода латентность флеши. код прерывания лучше переносить в ОЗУ. Изменено 25 сентября, 2019 пользователем AlexRayne Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Digi 0 26 сентября, 2019 Опубликовано 26 сентября, 2019 · Жалоба 13 hours ago, jcxz said: Любого??? Это под этой осью такие накладные расходы на неё что-ль? Может неправильно выразился. Я измерял время выполнения кода внутри прерывания, время реакции на прерывание не измерял. Ещё для информации, программа и данные лежит во внешней SDRAM. Во внутренней пока не стал размещать, производительности хватает, а объем данных большой, внутренней памяти не хватает. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlexRayne 7 26 сентября, 2019 Опубликовано 26 сентября, 2019 (изменено) · Жалоба Цитата Любого??? Это под этой осью такие накладные расходы на неё что-ль ну вообчето 5мкс - это максимум, по осцилу видимый, а в среднем 0.5-2мкс было. это время - следствие отсутствия оптимизации атомарного доступа к данным мутеха, все ведется через тяжеловесное блокирование прерываний. АРМ и МИПС имеют специальные инструкции для атомарного теста переменой, и ГЦЦ даже сумеет их из С кода внедрить. но этого никто пока не сделал. Изменено 26 сентября, 2019 пользователем AlexRayne Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 26 сентября, 2019 Опубликовано 26 сентября, 2019 · Жалоба 13 часов назад, AlexRayne сказал: Я практически мерял переключение контекста на МИПС32 - ВМ10Я на 240МГц тактовой. так вот у него до 5мкс доходило время захвата мутекса. Но это специфика МИПСа. и неоптимизированный код мутеха в уОС. Ну MIPS - это другой разговор. Не знаю сколько там регистров нужно сохранять/восстанавливать. В DSP обычно тоже дофига регистров и там переключение тоже медленное. ARM же - другое дело. Цитата Про АРМ летом был разговор - приятель пытался понять можно ли сделать переключение задачи меньше чем за 10мкс (вроде такой порядок там был). повозились, пошарились по форумам, выяснили что лучше чем фриРТОС делает, уже не сделать - у них ассемблерные оптимизированные процедуры все это шедулят. И тоже получалось какоето сумасшествие - более 300-500 тактов это занимает. Не соглашусь. Посмотрите как сделано переключение у uCOS - там в разы меньше. Не знаю что так долго делает FreeRTOS, но лучше тогда её и не использовать коли действительно так. У меня своя RTOS, самописная. И вот её процедура переключения контекста: THUMB isrStartCtxSw: ADD SP, SP, #8*4 MVN LR, #2 isrPendSV: MRS R0, PSP ;PSP is process stack pointer LDR R3, =os STMFD R0!, {R4-R11} ;Save remaining regs R4-R11 on process stack CPSID I ;Prevent interruption during context switch LDR R2, [R3, #OStcb] ;os.tcb #ifdef __ARMVFP__ TST LR, #1 << 4 STRB LR, [R2, #OSTCBexcRet] IT EQ VSTMDBEQ R0!, {S16-S31} LDR R1, =osTcbPrioTbl STR R0, [R2, #OSTCBstk] ;os.tcb->stk = SP; LDRB R2, [R3, #OSprioRdy] ;os.prioCur = os.prioRdy STRB R2, [R3, #OSprioCur] LDR R2, [R1, R2, LSL #2] STR R2, [R3, #OStcb] ;os.tcb = osTcbPrioTbl[os.prioCur] LDR R0, [R2, #OSTCBstk] ;R0 is new process SP; SP = os.tcb->stk LDRSB LR, [R2, #OSTCBexcRet] ;os.tcb->excRet TST LR, #1 << 4 IT EQ VLDMIAEQ R0!, {S16-S31} #else LDR R1, =osTcbPrioTbl STR R0, [R2, #OSTCBstk] ;os.tcb->stk = SP; LDRB R2, [R3, #OSprioRdy] ;os.prioCur = os.prioRdy STRB R2, [R3, #OSprioCur] LDR R2, [R1, R2, LSL #2] STR R2, [R3, #OStcb] ;os.tcb = osTcbPrioTbl[os.prioCur] LDR R0, [R2, #OSTCBstk] ;R0 is new process SP; SP = os.tcb->stk #endif CPSIE I LDMFD R0!, {R4-R11} ;Restore r4-11 from new process stack MSR PSP, R0 ;Load PSP with new process SP BX LR ;Exception return will restore remaining context Можете сами такты подсчитать (от метки isrPendSV до конца). Без плавучки будет около ~40 тактов. Это только само переключение контекста (задач). Выбор текущей приоритетной задачи (реприоретизация) тоже занимает некоторое время, но там совсем крохи. Цитата Вполне соглашусь что чисто прерывание, без шедулера, с оптимизированным прологом, будет занимать ваши сотни тактов и сделает кучу работы. но в реальности в которой крутился я - таких олимпийских рекордов нет. Чисто прерывание для чтения из UART (без FIFO) без уведомления ОС (которое совсем не нужно делать на каждый принятый байт) - достаточно 20-30 тактов на ISR + аппаратные траты на вход/выход в ISR и опциональное сохранение/восстановлением контекста. "Сотни" - это уже перебор. Цитата Если касаться СТМ23 - то у них еще очень ударяет по скорости кода латентность флеши. код прерывания лучше переносить в ОЗУ. Не факт. У них (STM32) часто есть кеш. И тогда по скорости перенос в ОЗУ может даст как раз проигрыш. С младшими STM32 я не работал, а со старшими это так. 4 часа назад, Digi сказал: Может неправильно выразился. Я измерял время выполнения кода внутри прерывания, время реакции на прерывание не измерял. Я о длительности выполнения кода и говорил. Не о времени реакции. 100 тактов - уже слишком много для простого приёма с UART. Ну только если туда (в ISR) не засунуть ещё и обработку прикладного протокола. А как измеряли? Надеюсь - по таймеру, а не по ножкам GPIO? Цитата Ещё для информации, программа и данные лежит во внешней SDRAM. Во внутренней пока не стал размещать, производительности хватает, а объем данных большой, внутренней памяти не хватает. Ну и что? У меня тоже в одном проекте программа исполняется из SDRAM (в режиме отладки). Разница в скорости исполнения по сравнению с flash почти незаметна. Так как есть кеш. 1 час назад, AlexRayne сказал: ну вообчето 5мкс - это максимум, по осцилу видимый, а в среднем 0.5-2мкс было. "По осциллу" Вы измеряете температуру на Луне скорость работы GPIO. Скорость выполнения кода мерить нужно таймером. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Digi 0 26 сентября, 2019 Опубликовано 26 сентября, 2019 · Жалоба 46 minutes ago, jcxz said: Я о длительности выполнения кода и говорил. Не о времени реакции. 100 тактов - уже слишком много для простого приёма с UART. Ну только если туда (в ISR) не засунуть ещё и обработку прикладного протокола. А как измеряли? Надеюсь - по таймеру, а не по ножкам GPIO? Измерял по осцилу )) с ножки GPIO. Давно мерил время переключения ноги, получалось около 100 нс Но мог и ошибиться Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 26 сентября, 2019 Опубликовано 26 сентября, 2019 · Жалоба 3 часа назад, Digi сказал: Измерял по осцилу )) с ножки GPIO. Давно мерил время переключения ноги, получалось около 100 нс Но мог и ошибиться Ноги ногам - рознь. Но даже 100нс - это 10 тактов на 100МГц. Итого: суммарная погрешность замера интервала будет 20 тактов. Не понятно - зачем мерить грубым инструментом, если есть в 10 раз более точный? PS: Да и ещё - а на время измерения прерывания запрещали? А то может такие гигантские времена намеряли, потому что у вас в это время произошло более приоритетное прерывание и прибавилась ещё и его длительность? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Digi 0 27 сентября, 2019 Опубликовано 27 сентября, 2019 · Жалоба 15 hours ago, jcxz said: PS: Да и ещё - а на время измерения прерывания запрещали? Нет, не запрещал. Меня интересовало сколько времени уходит на этот процесс при штатной работе. Пришёл к выводу: так как время нахождения в прерывании не превышает периода следования данных, то нет необходимости проверять в этом же цикле оставшиеся данные в FIFO. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 27 сентября, 2019 Опубликовано 27 сентября, 2019 · Жалоба 1 час назад, Digi сказал: Нет, не запрещал. Меня интересовало сколько времени уходит на этот процесс при штатной работе. Пришёл к выводу: Как в этом случае вообще можно прийти к какому-то выводу? Вы не знаете сколько реально затратилось времени на ISR, и что-то решаете на основании некорректных данных? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться