Jump to content

    

uOS и MultiCore

С мутексами вроде немного разобрался. Прога перестала падать при передаче большого,непрерывного потока данных. Создал три мутекса: irq, receieve, transmit. irq - повесил на прерывание, остальные соответственно блокируют обращения к данным обработчика приема и передачи соответственно. То есть я при начале работы с буфером приемника блокирую мутекс, а после снимаю блокировку. Если мне нужно принудительно пнуть прерывание приёма/передачи, то я отправляю  mutex_signal(&current_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(&current_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(&current_uart->irq,0);							// пинаем прерывание, чтобы передатчик начал передавать
				 break;
                
                ....
            }
   			current_uart->rx_packet.data_ready=0;								// Считаем что пакет обработали. И сбрасываем признак готовности
			mutex_unlock(&current_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(&current_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(&current_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.

 


 

Share this post


Link to post
Share on other sites

Диги, оч тяжелый код Вы сделали для этой простой задачи. Блокировки мутехов очень тяжелы на платформе мипс, на арм может полегче.

Имхо, все операцию с портом<->очередью стоит вынести в хэндл прерывания. Он выполнится атомарно, на уровне прерывания.

А В мутехи приемника/передатчика можно послать сигналы из прерывания, тем ниткам кто их ждет.

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

 

Share this post


Link to post
Share on other sites

ещё вопрос. Как понять, что приводит к краху ? "

 

попробуйте добавить стекам задач байт по 250?

Share this post


Link to post
Share on other sites
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

Вот что выдаёт.
     

Share this post


Link to post
Share on other sites
59 минут назад, Digi сказал:

Да, код действительно тяжеловат, но со временем переделаю, как только придёт полное осознание происходящих процессов. Для информации: фактическое время обработчика составляет 16-25 мкс, этого достаточно до скоростей вплоть до 350 кбит/с.     

Это что - при такой скорости один только обработчик одного прерывания будет на 100% грузить процессор??? :shok:

А зачем нужен такой обработчик, что кроме него больше ничего работать не сможет?

Для ISR-ов UART даже 1% времени CPU - это уже очень много. Если не слишком много. На ARM-процессорах.

Share this post


Link to post
Share on other sites
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 ввести свои функции контроля качества задачи на которую переключаетесь. и ассертить подозрительные вещи - выход указателя кода за пределы кода, и/или стека за пределы рам. Я думаю стоит потратить время на этот контроль, он очень пригодиться дальше. ибо это имхо не последнее крашение проекта.

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

Edited by AlexRayne

Share this post


Link to post
Share on other sites
2 часа назад, AlexRayne сказал:

На медленных АРМ (до 100Мгц) время обработки прерывания любого 5-10мкс. так что я б сказал что у него обработчик совсем неплох, учитывая что он заодно и шедулинг ведет. во фриРТОС вызов шедулера/прерывания тоже имеет такие порядки.

Любого??? :shok:  Это под этой осью такие накладные расходы на неё что-ль?

В обычных применениях даже при <100МГц - это очень большое время. 5 мкс - это 500 тактов на 100МГц если что. Посчитайте хотя-бы сколько занимает реакция на прерывание + сохранение/восстановление контекста (аппаратное) + программные PUSH/POP. Всё это будет во много раз меньше чем 500 тактов. Не понимаю - что так долго можно делать в ISR?

 

Сейчас померил у себя: У меня в ПО есть один ISR такой долгий - несколько сотен тактов. Так в нём идёт усреднение 176 отсчётов АЦП (сложение+деление). И то это - 542 такта.

Но с UART - я вообще не понимаю что там (в ISR) так долго можно что-то делать.  :unknw: Особенно если он ещё и без FIFO.

Обычные ISR - во много раз меньше.

И "вызов шедулера" - это тоже только несколько десятков тактов максимум. Это даже если весь FPU-контекст сохраняется/восстанавливается. Но никак не 500-1000. Ну по-крайней мере - это вменяемой ОС столько нужно, только на переключение контекста задачи.

Share this post


Link to post
Share on other sites

Я практически мерял переключение контекста на МИПС32 - ВМ10Я на 240МГц тактовой. так вот у него до 5мкс доходило время захвата мутекса. Но это специфика МИПСа. и неоптимизированный код мутеха в уОС.

Про АРМ летом был разговор - приятель пытался понять можно ли сделать переключение задачи меньше чем за 10мкс (вроде такой порядок там был). повозились, пошарились по форумам, выяснили что лучше чем фриРТОС делает, уже не сделать - у них ассемблерные оптимизированные процедуры все это шедулят. И тоже получалось какоето сумасшествие - более 300-500 тактов это занимает.

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

Если касаться СТМ23 - то у них еще очень ударяет по скорости кода латентность флеши. код прерывания лучше переносить в ОЗУ.

Edited by AlexRayne

Share this post


Link to post
Share on other sites
13 hours ago, jcxz said:

Любого??? :shok:  Это под этой осью такие накладные расходы на неё что-ль?

Может неправильно выразился. Я измерял время выполнения кода внутри прерывания, время реакции на прерывание не измерял. Ещё для информации, программа и данные лежит во внешней SDRAM. Во внутренней пока не стал размещать, производительности хватает, а объем данных большой, внутренней памяти не хватает.

Share this post


Link to post
Share on other sites
Цитата

Любого??? :shok:  Это под этой осью такие накладные расходы на неё что-ль

ну вообчето 5мкс - это максимум, по осцилу видимый, а в среднем 0.5-2мкс было. это время - следствие отсутствия оптимизации атомарного доступа к данным мутеха, все ведется через тяжеловесное блокирование прерываний.

АРМ и МИПС имеют специальные инструкции для атомарного теста переменой, и ГЦЦ даже сумеет их из С кода внедрить. но этого никто пока не сделал.

Edited by AlexRayne

Share this post


Link to post
Share on other sites
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?  :biggrin:

Цитата

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

Ну и что? У меня тоже в одном проекте программа исполняется из SDRAM (в режиме отладки). Разница в скорости исполнения по сравнению с flash почти незаметна. Так как есть кеш.

1 час назад, AlexRayne сказал:

ну вообчето 5мкс - это максимум, по осцилу видимый, а в среднем 0.5-2мкс было.

"По осциллу" Вы измеряете температуру на Луне скорость работы GPIO. Скорость выполнения кода мерить нужно таймером.

Share this post


Link to post
Share on other sites
46 minutes ago, jcxz said:

Я о длительности выполнения кода и говорил. Не о времени реакции. 100 тактов - уже слишком много для простого приёма с UART. Ну только если туда (в ISR) не засунуть ещё и обработку прикладного протокола.

А как измеряли? Надеюсь - по таймеру, а не по ножкам GPIO?  :biggrin:

Измерял по осцилу )) с ножки GPIO. Давно мерил время переключения ноги, получалось около 100 нс Но мог и ошибиться

Share this post


Link to post
Share on other sites
3 часа назад, Digi сказал:

Измерял по осцилу )) с ножки GPIO. Давно мерил время переключения ноги, получалось около 100 нс Но мог и ошибиться

Ноги ногам - рознь. Но даже 100нс - это 10 тактов на 100МГц. Итого: суммарная погрешность замера интервала будет 20 тактов.

Не понятно - зачем мерить грубым инструментом, если есть в 10 раз более точный?  :unknw:

 

PS: Да и ещё - а на время измерения прерывания запрещали? А то может такие гигантские времена намеряли, потому что у вас в это время произошло более приоритетное прерывание и прибавилась ещё и его длительность?  :biggrin:

Share this post


Link to post
Share on other sites
15 hours ago, jcxz said:

PS: Да и ещё - а на время измерения прерывания запрещали?

Нет, не запрещал. Меня интересовало сколько времени уходит на этот процесс при штатной работе.  Пришёл к выводу: так как время нахождения в прерывании не превышает периода следования данных, то нет необходимости проверять в этом же цикле оставшиеся данные в FIFO.

Share this post


Link to post
Share on other sites
1 час назад, Digi сказал:

Нет, не запрещал. Меня интересовало сколько времени уходит на этот процесс при штатной работе.  Пришёл к выводу:

Как в этом случае вообще можно прийти к какому-то выводу? Вы не знаете сколько реально затратилось времени на ISR, и что-то решаете на основании некорректных данных?  :russian_ru:

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now