Монітор тактових циклів для коду на arduino / AVR?


11

Чи можливо відстежувати блок коду та визначати кількість циклів тактових процесорів, які код прийняв на процесор Arduino та / або AVR atmel? або, чи варто мені відстежувати мікросекунди, що пройшли до та після запуску коду? Примітка. Я не переймаюся реальним часом (наприклад, скільки реальних секунд пройшло), скільки я перебуваю в "скільки годинних циклів потрібен цей код від центрального процесора".

Поточне рішення, яке я можу придумати, - це time.c:

#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )

wiring.c додає:

#define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() )

За допомогою цього рахунку я міг обчислити цикли годин, пройдені моніторингом минулих мікросекунд, а потім передати їх мікросекундамToClockCycles (). Моє запитання: чи є кращий спосіб?

Sidenote: чи є хороші ресурси для моніторингу продуктивності AVR. Пошуки lmgtfy.com та різних форумів не дають явних результатів, окрім дослідження таймерів

Спасибі

Відповіді:


6

Найпростіший метод - змусити ваш код підтягнути деяку шпильку до того, як він виконає код, який ви хочете вчасно, і витягніть його низько після того, як він закінчить робити все, що завгодно. Потім зробіть цикл коду (або використовуйте цифровий осцилоскоп із пам'яттю в режимі одиночного зйомки) і просто обведіть, а потім приклейте. Довжина імпульсу говорить вам про те, скільки часу знадобилося виконати фрагмент коду плюс один тактовий цикл від зміни стану шпильки (я думаю, це займає один цикл, не впевнений на 100%).


Спасибі. Так, я можу бачити, що це, мабуть, найбільш точне рішення. Я все ще чіпляюся за кодом, який би дав мені принаймні навіть загальний аналіз використання циклу всередині коду. Я буду використовувати це для створення деяких інструментів тестування, і було б непогано встановити мої верхні межі для таких параметрів, як максимальний дозволений час виконання, виходячи з того, наскільки ефективно код + все, що з ним пов'язано, працює на поточному процесорі Atmel у використання
cyphunk

4

Що ви маєте на увазі під «монітором»?

Не повинно бути важким підрахунок тактових циклів для AVR для невеликих шматочків асемблерного коду.

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

І ви також можете прочитати час із швидкодіючого таймера, як ви кажете.


Під монітором я маю на увазі визначити кількість циклів, що використовуються кодом. щось на зразок (зверніть увагу, форматування коду, ймовірно, буде вирівняно механізмом коментарів): clock = startCountingAtmegaClocks (); for ... {for ... {digitalRead ...}} Serial.print ("кількість використаних циклів:"); Serial.print (currentCountingAtmegaClocks () - годинник, DEC);
cyphunk

Але так, ваша відповідь - це те, що я припустив, що є мої варіанти. Я здогадуюсь, я припускаю, що якщо я можу обчислити цикли годин, асемблер взяв би вручну, що, можливо, хтось уже написав якийсь приємний код, щоб це зробити програмно
cyphunk

3

Це приклад для Arduino за допомогою функції clockCyclesPerMicrosecond () для обчислення пройдених годин. Цей код буде чекати 4 секунди, після чого надрукувати час, що минув з моменту запуску програми. Значення зліва 3 - це загальний час (мікросекунди, мілісекунди, загальний тактовий цикл), а більшість правильних - 3 рази:

Вихід:

clocks for 1us:16
runtime us, ms, ck :: elapsed tme us, ms ck
4003236 4002	64051776	::	4003236	4002	64051760
8006668 8006	128106688	::	4003432	4004	64054912
12010508    12010	192168128	::	4003840	4004	64061440
16014348    16014	256229568	::	4003840	4004	64061440
20018188    20018	320291008	::	4003840	4004	64061440
24022028    24022	384352448	::	4003840	4004	64061440
28026892    28026	448430272	::	4004864	4004	64077824
32030732    32030	512491712	::	4003840	4004	64061440
36034572    36034	576553152	::	4003840	4004	64061440
40038412    40038	640614592	::	4003840	4004	64061440
44042252    44042	704676032	::	4003840	4004	64061440
48046092    48046	768737472	::	4003840	4004	64061440
52050956    52050	832815296	::	4004864	4004	64077824

Я впевнений, що є розумне пояснення, чому у перших занадто циклів були коротші минулі цикли годин, ніж у більшості, і чому всі інші петлі перемикаються між двома довжинами тактових циклів.

Код:

unsigned long us, ms, ck;
unsigned long _us, _ms, _ck;
unsigned long __us, __ms, __ck;
void setup() {
        Serial.begin(9600);
}
boolean firstloop=1;
void loop() { 
        delay(4000);

        if (firstloop) {
                Serial.print("clocks for 1us:");
                ck=microsecondsToClockCycles(1);
                Serial.println(ck,DEC);
                firstloop--;
                Serial.println("runtime us, ms, ck :: elapsed tme us, ms ck");
        }

        _us=us;
        _ms=ms;
        _ck=ck;

        us=micros(); // us since program start
        ms=millis();
        //ms=us/1000;
        ck=microsecondsToClockCycles(us);
        Serial.print(us,DEC);
        Serial.print("\t");
        Serial.print(ms,DEC);
        Serial.print("\t");
        Serial.print(ck,DEC);     
        Serial.print("\t::\t");

        __us = us - _us;
        __ms = ms - _ms;
        __ck = ck - _ck;
        Serial.print(__us,DEC);
        Serial.print("\t");
        Serial.print(__ms,DEC);
        Serial.print("\t");
        Serial.println(__ck,DEC);     

}

Сторінка: якщо ви знімете затримку на 4 секунди, ви почнете бачити ефекти Serial.print () набагато чіткіше. Зауважте, тут порівнюються 2 прогони. Я включив лише 4 зразки один біля одного з відповідних журналів.

Виконати 1:

5000604 5000	80009664	::	2516	2	40256
6001424 6001	96022784	::	2520	3	40320
7002184 7002	112034944	::	2600	3	41600
8001292 8001	128020672	::	2600	3	41600

Виконати 2:

5002460 5002	80039360	::	2524	3	40384
6000728 6000	96011648	::	2520	2	40320
7001452 7001	112023232	::	2600	3	41600
8000552 8000	128008832	::	2604	3	41664

Час, що минув, збільшується за загальний час виконання. Після закінчення секунди годинник збільшується в середньому з 40 к 44 к. Це трапляється послідовно через кілька мілісекунд через 1 секунду, а минулі годинники залишаються приблизно 44 к принаймні наступні 10 секунд (я не перевіряв це далі). Ось чому моніторинг корисний або потрібен. Можливо, зниження ефективності пов'язане з конфігурацією або помилками в серійному? Або, можливо, код не використовує належну пам’ять і має витік, що впливає на продуктивність тощо.


через багато років мені все одно хотілося б чогось, що більш точно показує годинник з кодом (як призначений для осцилографа). Я намагаюся визначити кількість тактових циклів, необхідних для цифрового запису (), як в 16 МГц, так і в 8 МГц. У 16 МГц я отримую 8us / 64clk. Але в 8 МГц я отримую 0us / 0clk.
cyphunk

1

Оскільки кожен рядок коду, доданий до вашого джерела, матиме вплив на продуктивність і може змінити застосовані оптимізації. Зміни повинні бути мінімальними, необхідними для виконання завдання.

Щойно я знайшов плагін Atmel Studio під назвою "Анотований відладчик файлу збірки". http://www.atmel.com/webdoc/aafdebugger/pr01.html Здається, що переходити через фактично створену мову складання, хоча, ймовірно, нудно буде показувати вам саме те, що відбувається. Вам, можливо, доведеться розшифрувати, скільки циклів потрібно для кожної інструкції, але це буде набагато ближче, ніж деякі інші розміщені параметри.

Для тих, хто не знає у папці Output вашого проекту, це файл із розширенням LSS. Цей файл містить увесь ваш вихідний вихідний код у вигляді коментарів, а під кожним рядком - мова складання, створена на основі цього рядка коду. Генерування файлу LSS можна вимкнути, тому перевірте наступне налаштування.

Властивості проекту | Ланцюжок інструментів | AVR / GNU Загальні | Вихідніфайли

Поставити прапорець ".lss (Створити файл lss)


1

Ви можете використовувати один із вбудованих таймерів. Отримайте все, налаштоване для prescaller = 1 і TCNT = 0 перед блоком. Потім увімкніть таймер на лінії перед блоком і відключіть його на лінії після блоку. Тепер TCNT утримуватиме кількість циклів, які взяв блок, за вирахуванням фіксованих циклів для коду включення та відключення.

Зауважте, що TNCT переповниться після 65535 тактових циклів на 16-бітовому таймері. Ви можете використовувати прапор переповнення, щоб подвоїти час виконання. Якщо вам все-таки потрібно довше, ви можете скористатися докашлером, але отримаєте меншу роздільну здатність.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.