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

Скорость вызова функции обработки прерывания на Cortex M7

Есть функция, которая должна обрабатывать выборки с АЦП. Привязка к событию на таймере GPT2. Тактовая частота 600 МГц.
Время от события сравнения в таймере и входом в функцию обработчика прерывания при первом вызове обработчика около 1200 тактов процессора, а последующие вызовы занимают около 500 тактов.

Обработчик разместил в RAM_ITC. Все глобальные переменные используемые в прерывании в RAM_DTC. Вызовов каких то других функций из FLASH или SDRAM нет.
Поясните что я делаю не так и почему первый вызов занимает гораздо больше чем последующие?

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

Получается что меньше 1,5...2 микросекунд реакции на прерывание не добиться?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 minutes ago, kochevkv said:

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

А приоритеты прерываниям задали?

Вход, вроде, 12 тактов должен занимать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 minutes ago, tonyk_av said:

А приоритеты прерываниям задали?

Вход, вроде, 12 тактов должен занимать.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 minute ago, kochevkv said:

Да, наивысший приоритет назначил

Так не бывает. Если приоритеты заданы правильно, то счёт идёт на такты, а не микросекунды.

Показывайте, что и как делаете, иначе вряд ли кто сможет вам ответить.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 hours ago, tonyk_av said:

Так не бывает. Если приоритеты заданы правильно, то счёт идёт на такты, а не микросекунды.

Показывайте, что и как делаете, иначе вряд ли кто сможет вам ответить.

// Настройка таймера
void SampleFreqConfigure( void )
{
	{
		static const gpt_config_t gptConfig = {
			.clockSource = S_GPT_CLOCK_SOURCE,
			.divider = 1,
			.enableFreeRun = true,
			.enableRunInWait = false,
			.enableRunInStop = false,
			.enableRunInDoze = false,
			.enableRunInDbg = false,
			.enableMode = false };

		GPT_Init( S_GPT, &gptConfig );
	}

    GPT_SetClockDivider( S_GPT, 1 );

    GPT_SetOutputOperationMode( S_GPT, kGPT_OutputCompare_Channel3, kGPT_OutputOperation_Toggle );

    GPT_EnableInterrupts( S_GPT, kGPT_OutputCompare3InterruptEnable );

	NVIC_SetPriority( S_GPT_IRQ_ID, 0 );
    EnableIRQ( S_GPT_IRQ_ID );
}

// Включение таймера
void SampleFreqOn( float convFreq )
{
	{
		uint32_t gptClock = CLOCK_GetFreq( S_GPT_CLOCK );
		uint32_t compareValue = ( (float)gptClock / convFreq ) + (float)0.5;

		s_conversionCycles = (float)0.0000025 * gptClock;
		float mult = (float)0.000001;
		s_conversionOffCicles = mult * gptClock;
		s_conversionOnCicles = compareValue - s_conversionOffCicles;

		s_b_convOffCycle = false;
		s_ocrValue = 0;
		s_b_firstCall = true;
		S_GPT_IRQ_HANDLER();
	}

	s_dtInx = 0;

	for( ptrdiff_t i  = 0; i < 256; i++ ) {
		s_dt[i] = 0; }

    GPT_StartTimer( S_GPT ); // Start Timer
}

// Прерывание
__RAMFUNC(RAM2) void S_GPT_IRQ_HANDLER( void )
{
	// Clear interrupt flag
    if( GPT_GetStatusFlags( S_GPT, kGPT_OutputCompare3Flag ) )
    {
    	GPT_ClearStatusFlags( S_GPT, kGPT_OutputCompare1Flag | kGPT_OutputCompare2Flag | kGPT_OutputCompare3Flag |
        	    kGPT_InputCapture1Flag | kGPT_InputCapture2Flag | kGPT_RollOverFlag );
    }
    else
    {
    	GPT_ClearStatusFlags( S_GPT, kGPT_OutputCompare1Flag | kGPT_OutputCompare2Flag |
    	    kGPT_InputCapture1Flag | kGPT_InputCapture2Flag | kGPT_RollOverFlag );

    	if( !s_b_firstCall ) {
    	    return; }
    }

    uint32_t test = 0;
    uint32_t ocr = s_ocrValue;

	if( s_dtInx < s_dtInxMax )
	{
		SysTick->VAL = 0;
		s_dt[s_dtInx] = S_GPT->CNT - ocr;
		s_st[s_dtInx] = s_stConst - SysTick->VAL;
		s_dtInx++;

		SysTick->VAL = 0;
		test = S_GPT->CNT;
		s_st[s_dtInx] = s_stConst - SysTick->VAL;
		s_dtInx++;

		SysTick->VAL = 0;
		s_dt[s_dtInx] = 0xFF;
		s_st[s_dtInx] = s_stConst - SysTick->VAL;
		s_dtInx++;
	}

    s_b_firstCall = false;

    if( s_b_convOffCycle )
    {
    	s_ocrValue += s_conversionOnCicles;

    	GPT_SetOutputCompareValue( S_GPT, kGPT_OutputCompare_Channel3, s_ocrValue );
    	GPT_SetOutputOperationMode( S_GPT, kGPT_OutputCompare_Channel3, kGPT_OutputOperation_Clear );

    	s_b_convOffCycle = false;
    }
    else
    {
    	s_ocrValue += s_conversionOffCicles;

    	GPT_SetOutputCompareValue( S_GPT, kGPT_OutputCompare_Channel3, s_ocrValue );
		GPT_SetOutputOperationMode( S_GPT, kGPT_OutputCompare_Channel3, kGPT_OutputOperation_Set );

		s_b_convOffCycle = true;
    }

    // Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F, Cortex-M7, Cortex-M7F Store immediate overlapping
    // exception return operation might vector to incorrect interrupt
    SDK_ISR_EXIT_BARRIER;

    if( s_dtInx < s_dtInxMax ) {
    	s_st[s_dtInx] = s_stConst - SysTick->VAL;
    	s_dtInx++; }
}

s_dt - массив с разницей между установленным значением срабатывания таймера OCR и значением счетчика таймера при входе в обработчик. На картинке видно что первый раз эта разница 116 циклов таймера, а в последующих 31 и 44 в зависимости от текущей фазы 0 или 1 на выходе и значения OCR. Генерится красивый сигнал 10 кГц с импульсами 1 мкс.

s_st - массив значений системного счетчика. Что к чему я подписал на картинке. Видно что читает регистр счетчика GPT очень долго. Похоже проблема где-то в этом. Частота тактирования таймера GPT 75 МГц. Т.е. в 8 раз ниже частоты ядра.

 

Все "функции" GPT это макросы в хедере к нему. Это не должно тормозить

test.jpg

Изменено пользователем kochevkv

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Подозреваю, что первое измерение числа тактов выполняется между моментами 1 и 2, а последующие интервалы между моментом установки бита прерывания и установкой вывода в единицу. 
В обоих случаях процессору нужно пробраться через огород инструкций перед тем, как переключить ножку в единицу. Для повышения точности по времени сразу при входе в прерывание нужно выполнить нужное действие, а потом уже все организационные вопросы решать и сбрасывать флаг. Да и слишком объёмный код в обработчике. Эти все проверки можно было бы оставить в программе, а в прерывании использовать только результат в виде одного флага. image.thumb.png.8a498c801488fdbce29ba34abede2a83.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

7 hours ago, ДЕЙЛ said:

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

Не, первый вызов не обрабатывается. s_b_firstCall = true. Там чтение и запись регистра долгое, вот и вся проблема. Почему пока не выяснил. Ножка управляется таймером, все точно работает, пока период больше времени записи регистров таймера. У меня такой вот программный ШИМ получился) пока набросок.

И какой смысл все из обработчика выметать если контроллер только этим и занимается?

Не должно чтение и запись регистра так долго длиться.

Думаю поменяю таймер, уж больно частота тактирования у этого низкая, 75 МГц. На 16 битных и ШИМ есть аппаратный и частота тактирования выше в 2 раза. А на низких частотах буду предделители использовать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

15 hours ago, kochevkv said:

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

Возможно, частота тактирования CPU относительно низкая по сравнению с частотой тактирования таймера?

 

15 hours ago, kochevkv said:

И какой смысл все из обработчика выметать если контроллер только этим и занимается?

У меня такая философия, что в обработчике прерывания должно быть минимум кода. Только самое необходимое и то, что нельзя вынести в главный бесконечный цикл или в задачу FreeRTOS. Это нормальная практика. Представь, что в обработчике прерывания телефонного звонка выполняется просмотр фильма, а в это время сработало прерывание пожарной сигнализации с меньшим приоритетом. Результат будет неприятный.   

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, ДЕЙЛ сказал:

У меня такая философия, что в обработчике прерывания должно быть минимум кода. Только самое необходимое и то, что нельзя вынести в главный бесконечный цикл или в задачу FreeRTOS. Это нормальная практика.

Это не нормальная практика. Это какие-то ваши личные фобии. Не надо личные фобии называть "нормальной практикой".

Даже разработчики ядра ARM так не думают. Ибо в самом ядре Cortex-M заложен режим работы "только в прерываниях". Когда вообще абсолютно весь рабочий код - только в ISR, а никакого фонового кода нет. Вместо него процессор спит. И вот это - нормальная практика. Почитайте хотя-бы документацию на обсуждаемое ядро.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, ДЕЙЛ сказал:

Не соглашусь. При работе FreeRTOS задействован системный таймер с минимальным приоритетом. Если в обработчик прерывания UART вставить дополнительно длительные алгоритмы всяких проверок буферов, расчётов контрольных сумм и прочих увлекательных действий, то операционка будет уже не реального времени.

С чем именно несогласны? С тем, что сами же сделали кривую систему?

Если кто-то не умеет играть на скрипке, это ведь не значит, что скрипка - неправильный инструмент.

Если разработчик не может правильно распланировать работу ISR-ов в своём же проекте - это говорит только об уровне компетенции этого разработчика. Значит нужно или повышать квалификацию этого разработчика; или искать другого, более компетентного.

 

PS: Советую прочитать описание бита SLEEPONEXIT регистра "System Control Register" в мануале Cortex-M и подумать - зачем он нужен, раз по вашему - нельзя сколь угодно долго оставаться в ISR?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Оставаться сколь угодно можно - не спорю. Только кто будет заниматься кодом или переключением контекста операционки, если процессор будет свои лучшие годы проводить в более высокоприоритетном прерывании? Работать всё будет, но операционка будет тупить, если, например, таймер TIM1 будет генерировать прерывание 10000 раз в секунду или по I2C будем получать 50 кБ в секунду. 

Было бы интересно узнать какие-то новые принципиальные моменты, о которых не знаю. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 минуту назад, ДЕЙЛ сказал:

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

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

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

 

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

Например: может все задачи организовать в виде ISR. Раздав им нужные приоритеты. И "переключение контекста" - это будет всего лишь вход/выход в ISR. Заниматься им будет сам процессор.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 minutes ago, jcxz said:

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

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

Конкретно в коде автора сразу бросается в глаза объявление переменных в обработчике прерывания. Неразумно на мой взгляд. Я их объявил бы заранее и сделал бы статическими, чтобы к вопросу их создания больше не возвращаться. 

image.thumb.png.7d60d7b3221c2a289dacc40b3a825024.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 минуты назад, ДЕЙЛ сказал:

Считаю, что в любых прерываниях нужно находиться минимально возможное время.

Это справедливо для любых функций. Не только ISR.

А в исходниках ТС-а конечно бурелом непроходимый. Так делать конечно не следует.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

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

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