|
|
  |
Измерение Загрузки CPU, ARM7, lpc2367 |
|
|
|
Aug 10 2011, 09:36
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 25-07-11
Пользователь №: 66 407

|
Захотел сделать измерение загрузки CPU. Основная программа построена на прерываниях и работает с несколькими кнопками, сегментным индикатором и ком-портом. Идея состоит в том, чтобы при начале любой (более-менее серъезной) процедуры стояло слово ImWorking, и заканчивалась процедура словом ImFinished. Измеритель по этим словам выставляет тестовый диодик и считает сколько времени этот диодик простоял в высоком уровне (TimeInHighLevel). Таймер в измерителе выдает прерывание раз в секунду, по которому значение TimeInHighLevel переписывается в буфер, доступный основной программе. Вроде бы все получилось, но загрузка CPU, измеренная осцилографом, отличается от загрузки, измереной этим модулем. Без оптимизации по осцилографу получается порядка 23% загрузки, модулем 24%. (вроде бы все нормально, списываем на погрешность) С оптимизацией по времени выполнения получается: осцилограф - 13%, модуль 4%. (вот тут уже терзают сомнения) И, так как я начинающий програмист, буду рад критике этого кода.  Код #include <LPC23xx.h> #include "SystemMonitor.h"
// диодик #define Work (1 << 1) // @ 1
#define PCTIM3 0x00800000; // 23
#define PCLK_TIMER3_OFFSET 14 // @ 1
#define INTTIM3 (1 << 27)
void Work_SET() { IOSET1 = Work; } void Work_CLR() { IOCLR1 = Work; }
short WorkRefs = 0;
unsigned short CPUUsage = 0;
int TimeInHighLevel = 0; int TimeSetHighLevel = 0;
__irq void Timer3Handler() { if (WorkRefs > 0) TimeInHighLevel += 10000 - TimeSetHighLevel;
CPUUsage = TimeInHighLevel; TimeInHighLevel = 0; TimeSetHighLevel = 0;
T3IR = 0x01; VICVectAddr = 0; }
void InitSystemMonitor() { TimeInHighLevel = 0; WorkRefs = 0;
/* Initialize Timer 3 */ PCONP |= PCTIM3;
PCLKSEL1 &= ~(3 << PCLK_TIMER3_OFFSET); PCLKSEL1 |= (1 << PCLK_TIMER3_OFFSET); T3PR = 6399; // 100 mks T3MR0 = 9999; // 1 s T3MCR = 3;
VICIntEnClr = INTTIM3; VICVectAddr27 = (unsigned long)Timer3Handler; VICVectPriority27 = 15;
T3TCR = 0x01;
VICIntEnable += INTTIM3; }
void ImWorking() { WorkRefs++; if (WorkRefs == 1) { Work_SET(); TimeSetHighLevel = T3TC; } }
void ImFinished() { if (WorkRefs > 0) WorkRefs--;
if (WorkRefs == 0) { TimeInHighLevel += T3TC - TimeSetHighLevel; Work_CLR(); } }
|
|
|
|
|
Aug 10 2011, 10:51
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 25-07-11
Пользователь №: 66 407

|
Цитата(AHTOXA @ Aug 10 2011, 14:43)  Если вы работаете с какой-то переменной и в основной программе и в прерывании (напр. TimeInHighLevel), то надо её как-то защищать, иначе возможны большие неприятности. Простейший вариант - запрещать прерывания на время обращения к переменной в основной программе. Неприятности, если в момент записи в переменную приходит прерывание (т.е. цепочка команд ассемблера разрывается прерыванием)? При возобновлении в регистре, который он хотел записать может оказаться мусор. Я правильно понимаю?
|
|
|
|
|
Aug 10 2011, 11:16
|
self made
   
Группа: Свой
Сообщений: 855
Регистрация: 7-03-09
Из: Toronto, Canada
Пользователь №: 45 795

|
Цитата(whiteTigr @ Aug 10 2011, 06:51)  Неприятности, если в момент записи в переменную приходит прерывание (т.е. цепочка команд ассемблера разрывается прерыванием)? При возобновлении в регистре, который он хотел записать может оказаться мусор. Я правильно понимаю? Сделайте проще. Два синхронно бегущих таймера, T3 & T2. В ImWorking пишите T3_START В ImFinished T3_STOP. Раз в 1/4 секунды (измеренной с помощью T2) смотрите сколько накопилось в Т3, после чего его стираете.
|
|
|
|
|
Aug 10 2011, 11:17
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(whiteTigr @ Aug 10 2011, 16:51)  Неприятности, если в момент записи в переменную приходит прерывание (т.е. цепочка команд ассемблера разрывается прерыванием)? При возобновлении в регистре, который он хотел записать может оказаться мусор. Я правильно понимаю? Ну вот смотрите: Код TimeInHighLevel += T3TC - TimeSetHighLevel; Здесь происходит считывание значения переменной TimeInHighLevel в регистр, прибавление к нему какого-то значения и запись значения этого регистра в переменную TimeInHighLevel. Теперь представьте, что после считывания, но перед записью возникло прерывание. А там вот что: Код TimeInHighLevel = 0; Получается, что это обнуление пропадает, ибо в переменную TimeInHighLevel тут же запишется старое значение TimeInHighLevel, увеличенное на T3TC - TimeSetHighLevel. Вот так и возрастает подсчитанная загрузка
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Aug 10 2011, 12:02
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 25-07-11
Пользователь №: 66 407

|
Цитата(AHTOXA @ Aug 10 2011, 15:17)  Ну вот смотрите: Код TimeInHighLevel += T3TC - TimeSetHighLevel; Здесь происходит считывание значения переменной TimeInHighLevel в регистр, прибавление к нему какого-то значения и запись значения этого регистра в переменную TimeInHighLevel. Теперь представьте, что после считывания, но перед записью возникло прерывание. А там вот что: Код TimeInHighLevel = 0; Получается, что это обнуление пропадает, ибо в переменную TimeInHighLevel тут же запишется старое значение TimeInHighLevel, увеличенное на T3TC - TimeSetHighLevel. Вот так и возрастает подсчитанная загрузка  Угук, спасибо за разъяснение.  А то у меня только на подсознательному уровне из книжек отложилось, что нежелательно так делать, а вот до осмысления так и не дошло. А как запретить прерывания на время записи переменной? И будут ли обработаны пришедшие за это время прерывания? Ошибка в показаниях нашлась. Таймер был настроен на отсчет по 100 мкс, и получалось так, что без оптимизации длительность сигнлов была больше этого времени, а с оптимизацией слишком сильно сократилась и перестала считаться.
|
|
|
|
|
Aug 10 2011, 20:02
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(kan35 @ Aug 10 2011, 22:37)  А не проще ли использовать RTOS... А не проще ли, там, где ртос не нада, ея не использовать?  В общем случае, нужен один таймер для отсчета интервала времени и одна переменная для хранения кол-ва тактов, с атомарным доступом для записи. Далее - в суперпуперлупе вызываете функцию, которая ничего не делает, кроме того, что прибавляет число тактов на выполнение самой себя (это - из профайлера узнаем) к счетчику циклов. По прерыванию таймера - получаем число "полезных" тактов как разность между числом тактов в заданном интервале и содержимым счетчика. Самое правильное здесь в том, что мы считаем так и прерывания/исключения без особого напряга
|
|
|
|
|
Aug 10 2011, 20:18
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 25-07-11
Пользователь №: 66 407

|
Цитата(AHTOXA @ Aug 10 2011, 17:21)  Зависит от используемого компилятора. Что-то типа disable_irq()/enable_irq(). Спасибо. Цитата(kan35 @ Aug 10 2011, 23:37)  А не проще ли использовать RTOS, в них чаще всего определение загрузки процессора уже заложено, можно даже по отдельным задачам все проанализировать. Например: http://www.freertos.org/rtos-run-time-stats.htmlПосмотрел на скриншот. ОС, судя по всему, откликается по веб-интерфейсу, которого в моей платке и в помине нет. Идея то у меня банальная: 1) Обнаружить, если мы написали что-то страшное, и оно сожрало все процессорное время; 2) Потренироваться в программировании. Завтра только допишу блокировку прерываний на время работы с переменной, и, я думаю, будет достаточно. На последних тестах перед уходом домой показания осцилографа и модуля были примерно одинаковыми.
|
|
|
|
|
Aug 11 2011, 07:39
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 25-07-11
Пользователь №: 66 407

|
Нашел только такой вариант отключение прерываний: Код VICIntEnClr = INTTIM3; TimeSetHighLevel = T3TC; VICIntEnable += INTTIM3; Код VICIntEnClr = INTTIM3; TimeInHighLevel += T3TC - TimeSetHighLevel; VICIntEnable += INTTIM3; Как такой код отреагирует, если прерывание придет во время записи в переменные? Выполнится после разрешения прерывания, или проигнорируется?
|
|
|
|
|
Aug 11 2011, 09:52
|

Гуру
     
Группа: Свой
Сообщений: 2 957
Регистрация: 19-09-06
Из: Москва
Пользователь №: 20 514

|
после Код VICIntEnClr = INTTIM3; прерывание от таймера НЕ произойдет, посему присвоение значения переменной выполнится атомарно после этого прерывание снова разрешается, но мавр дело уже сделал  Если флаг прерывания выставлен во время операции присвоения, то после разрешения прерывания автоматом попадаем в обработку прерывания
|
|
|
|
|
Aug 11 2011, 12:20
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 25-07-11
Пользователь №: 66 407

|
Цитата(toweroff @ Aug 11 2011, 13:52)  Если флаг прерывания выставлен во время операции присвоения, то после разрешения прерывания автоматом попадаем в обработку прерывания Спасибо.  Кажется, я стал немножко больше понимать механизмы работы arm'а. А то, 2 недели назад, все это было темным лесом и непонятно было с какой стороны к этому зверю подойти.
|
|
|
|
|
Aug 11 2011, 13:07
|
Знающий
   
Группа: Участник
Сообщений: 537
Регистрация: 22-02-06
Пользователь №: 14 594

|
Цитата(whiteTigr @ Aug 11 2011, 00:18)  Посмотрел на скриншот. ОС, судя по всему, откликается по веб-интерфейсу, которого в моей платке и в помине нет.
Идея то у меня банальная: 1) Обнаружить, если мы написали что-то страшное, и оно сожрало все процессорное время; 2) Потренироваться в программировании. выводить можно хоть как, хоть в компорт, если почитаете, там просто текстовый массив заполняется. В общем там идея подсчета загрузки примерно такая же как и вы предложили, просто все это твориться без вашего ручного участия. Поделюсь своим опытом, в 100% своих проектов на АРМах я ставлю Ось, по моему иначе такой сильной машиной не реально управлять оптимально, выжимать из нее всю мощность. Отклонился от темы я..
|
|
|
|
|
  |
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|