Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Измерение Загрузки CPU
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
whiteTigr
Захотел сделать измерение загрузки CPU.
Основная программа построена на прерываниях и работает с несколькими кнопками, сегментным индикатором и ком-портом.

Идея состоит в том, чтобы при начале любой (более-менее серъезной) процедуры стояло слово ImWorking, и заканчивалась процедура словом ImFinished.
Измеритель по этим словам выставляет тестовый диодик и считает сколько времени этот диодик простоял в высоком уровне (TimeInHighLevel).
Таймер в измерителе выдает прерывание раз в секунду, по которому значение TimeInHighLevel переписывается в буфер, доступный основной программе.

Вроде бы все получилось, но загрузка CPU, измеренная осцилографом, отличается от загрузки, измереной этим модулем.
Без оптимизации по осцилографу получается порядка 23% загрузки, модулем 24%. (вроде бы все нормально, списываем на погрешность)
С оптимизацией по времени выполнения получается: осцилограф - 13%, модуль 4%. (вот тут уже терзают сомнения)

И, так как я начинающий програмист, буду рад критике этого кода. sm.gif

Код
#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();
  }
}
AHTOXA
Если вы работаете с какой-то переменной и в основной программе и в прерывании (напр. TimeInHighLevel), то надо её как-то защищать, иначе возможны большие неприятности. Простейший вариант - запрещать прерывания на время обращения к переменной в основной программе.
whiteTigr
Цитата(AHTOXA @ Aug 10 2011, 14:43) *
Если вы работаете с какой-то переменной и в основной программе и в прерывании (напр. TimeInHighLevel), то надо её как-то защищать, иначе возможны большие неприятности. Простейший вариант - запрещать прерывания на время обращения к переменной в основной программе.


Неприятности, если в момент записи в переменную приходит прерывание (т.е. цепочка команд ассемблера разрывается прерыванием)? При возобновлении в регистре, который он хотел записать может оказаться мусор. Я правильно понимаю?
ar__systems
Цитата(whiteTigr @ Aug 10 2011, 06:51) *
Неприятности, если в момент записи в переменную приходит прерывание (т.е. цепочка команд ассемблера разрывается прерыванием)? При возобновлении в регистре, который он хотел записать может оказаться мусор. Я правильно понимаю?

Сделайте проще. Два синхронно бегущих таймера, T3 & T2. В ImWorking пишите T3_START В ImFinished T3_STOP.

Раз в 1/4 секунды (измеренной с помощью T2) смотрите сколько накопилось в Т3, после чего его стираете.
AHTOXA
Цитата(whiteTigr @ Aug 10 2011, 16:51) *
Неприятности, если в момент записи в переменную приходит прерывание (т.е. цепочка команд ассемблера разрывается прерыванием)? При возобновлении в регистре, который он хотел записать может оказаться мусор. Я правильно понимаю?

Ну вот смотрите:
Код
    TimeInHighLevel += T3TC - TimeSetHighLevel;

Здесь происходит считывание значения переменной TimeInHighLevel в регистр, прибавление к нему какого-то значения и запись значения этого регистра в переменную TimeInHighLevel.
Теперь представьте, что после считывания, но перед записью возникло прерывание. А там вот что:
Код
  TimeInHighLevel = 0;

Получается, что это обнуление пропадает, ибо в переменную TimeInHighLevel тут же запишется старое значение TimeInHighLevel, увеличенное на T3TC - TimeSetHighLevel.
Вот так и возрастает подсчитанная загрузкаsm.gif
whiteTigr
Цитата(AHTOXA @ Aug 10 2011, 15:17) *
Ну вот смотрите:
Код
    TimeInHighLevel += T3TC - TimeSetHighLevel;

Здесь происходит считывание значения переменной TimeInHighLevel в регистр, прибавление к нему какого-то значения и запись значения этого регистра в переменную TimeInHighLevel.
Теперь представьте, что после считывания, но перед записью возникло прерывание. А там вот что:
Код
  TimeInHighLevel = 0;

Получается, что это обнуление пропадает, ибо в переменную TimeInHighLevel тут же запишется старое значение TimeInHighLevel, увеличенное на T3TC - TimeSetHighLevel.
Вот так и возрастает подсчитанная загрузкаsm.gif


Угук, спасибо за разъяснение. sm.gif А то у меня только на подсознательному уровне из книжек отложилось, что нежелательно так делать, а вот до осмысления так и не дошло.

А как запретить прерывания на время записи переменной? И будут ли обработаны пришедшие за это время прерывания?

Ошибка в показаниях нашлась. Таймер был настроен на отсчет по 100 мкс, и получалось так, что без оптимизации длительность сигнлов была больше этого времени, а с оптимизацией слишком сильно сократилась и перестала считаться.
AHTOXA
Цитата(whiteTigr @ Aug 10 2011, 18:02) *
А как запретить прерывания на время записи переменной? И будут ли обработаны пришедшие за это время прерывания?

Зависит от используемого компилятора. Что-то типа disable_irq()/enable_irq().
kan35
А не проще ли использовать RTOS, в них чаще всего определение загрузки процессора уже заложено, можно даже по отдельным задачам все проанализировать. Например:
http://www.freertos.org/rtos-run-time-stats.html
_Pasha
Цитата(kan35 @ Aug 10 2011, 22:37) *
А не проще ли использовать RTOS...

А не проще ли, там, где ртос не нада, ея не использовать? sm.gif
В общем случае, нужен один таймер для отсчета интервала времени и одна переменная для хранения кол-ва тактов, с атомарным доступом для записи.
Далее - в суперпуперлупе вызываете функцию, которая ничего не делает, кроме того, что прибавляет число тактов на выполнение самой себя (это - из профайлера узнаем) к счетчику циклов.
По прерыванию таймера - получаем число "полезных" тактов как разность между числом тактов в заданном интервале и содержимым счетчика.
Самое правильное здесь в том, что мы считаем так и прерывания/исключения без особого напряга
whiteTigr
Цитата(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) Потренироваться в программировании.

Завтра только допишу блокировку прерываний на время работы с переменной, и, я думаю, будет достаточно.
На последних тестах перед уходом домой показания осцилографа и модуля были примерно одинаковыми.
whiteTigr
Нашел только такой вариант отключение прерываний:
Код
    VICIntEnClr = INTTIM3;
    TimeSetHighLevel = T3TC;
    VICIntEnable += INTTIM3;

Код
    VICIntEnClr = INTTIM3;
    TimeInHighLevel += T3TC - TimeSetHighLevel;    
    VICIntEnable += INTTIM3;


Как такой код отреагирует, если прерывание придет во время записи в переменные? Выполнится после разрешения прерывания, или проигнорируется?
toweroff
после
Код
VICIntEnClr = INTTIM3;

прерывание от таймера НЕ произойдет, посему присвоение значения переменной выполнится атомарно
после этого прерывание снова разрешается, но мавр дело уже сделал sm.gif
Если флаг прерывания выставлен во время операции присвоения, то после разрешения прерывания автоматом попадаем в обработку прерывания
whiteTigr
Цитата(toweroff @ Aug 11 2011, 13:52) *
Если флаг прерывания выставлен во время операции присвоения, то после разрешения прерывания автоматом попадаем в обработку прерывания


Спасибо. sm.gif
Кажется, я стал немножко больше понимать механизмы работы arm'а.
А то, 2 недели назад, все это было темным лесом и непонятно было с какой стороны к этому зверю подойти. sm.gif
kan35
Цитата(whiteTigr @ Aug 11 2011, 00:18) *
Посмотрел на скриншот. ОС, судя по всему, откликается по веб-интерфейсу, которого в моей платке и в помине нет.

Идея то у меня банальная:
1) Обнаружить, если мы написали что-то страшное, и оно сожрало все процессорное время;
2) Потренироваться в программировании.

выводить можно хоть как, хоть в компорт, если почитаете, там просто текстовый массив заполняется.
В общем там идея подсчета загрузки примерно такая же как и вы предложили, просто все это твориться без вашего ручного участия. Поделюсь своим опытом, в 100% своих проектов на АРМах я ставлю Ось, по моему иначе такой сильной машиной не реально управлять оптимально, выжимать из нее всю мощность. Отклонился от темы я..
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.