Pantau siklus jam untuk kode di Arduino / AVR?

11

Apakah mungkin untuk memantau blok kode dan menentukan jumlah siklus jam prosesor yang diambil oleh prosesor Arduino dan / atau prosesor atm AVR? atau, haruskah saya lebih suka memonitor mikrodetik yang dilewati sebelum dan sesudah kode dijalankan? Catatan: Saya tidak peduli dengan waktu nyata (seperti pada, berapa detik nyata berlalu) sebanyak saya di "berapa banyak siklus jam yang dibutuhkan kode ini dari CPU"

Solusi saat ini yang dapat saya buat adalah dari time.c:

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

wiring.c menambahkan:

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

Dengan akun ini saya bisa menghitung clock cyles yang dilewati dengan memonitor microsecond yang berlalu dan kemudian meneruskannya ke microsecondsToClockCycles (). Pertanyaan saya adalah, adakah cara yang lebih baik?

sidenote: apakah ada sumber daya yang baik untuk memantau kinerja AVR. lmgtfy.com dan berbagai pencarian forum tidak memberikan hasil yang jelas, selain eksplorasi timer

Terima kasih

cyphunk
sumber

Jawaban:

6

Metode paling sederhana adalah membuat kode Anda menarik pin sebelum mengeksekusi kode yang Anda inginkan, dan menariknya rendah setelah selesai melakukan apa pun. Kemudian buat pengulangan kode (atau gunakan osiloskop digital dengan memori dalam mode satu pemotretan) dan cukup ruang lingkup lalu pin. Panjang pulsa memberitahu Anda berapa lama waktu yang diperlukan untuk mengeksekusi potongan kode ditambah satu siklus clock dari mengubah keadaan pin (saya pikir itu membutuhkan satu siklus, tidak 100% yakin).

Dago
sumber
Terima kasih. Ya, saya bisa melihat ini mungkin solusi yang paling akurat. Saya masih membuang kode yang akan memberi saya setidaknya analisis siklus umum bahkan di dalam kode. Saya akan menggunakan ini untuk membangun beberapa alat pengujian dan akan lebih baik untuk mengatur batas atas saya untuk parameter seperti max run time yang diizinkan berdasarkan seberapa efisien kode + semua yang berhubungan dengannya berjalan pada CPU Atmel saat ini di use
cyphunk
4

Apa yang Anda maksud dengan "monitor"?

Seharusnya tidak sulit untuk menghitung siklus clock untuk AVR untuk potongan kecil kode perakitan.

Anda juga dapat mengatur porta sebelum kode dieksekusi dan meresetnya sesudahnya, dan memonitornya dengan penganalisis logika atau oszilloscope untuk mendapatkan waktunya.

Dan Anda juga bisa membaca waktu dari timer yang berjalan cepat, seperti yang Anda katakan.

starblue
sumber
Maksud saya monitor adalah menentukan jumlah siklus yang digunakan oleh kode. sesuatu seperti (catatan, pemformatan kode mungkin akan diratakan oleh mesin komentar): clocks = startCountingAtmegaClocks (); untuk ... {untuk ... {digitalRead ...}} Serial.print ("num siklus yang digunakan:"); Serial.print (currentCountingAtmegaClocks () - jam, DEC);
cyphunk
Tapi ya, respons Anda adalah apa yang saya anggap pilihan saya. Saya kira, saya berasumsi jika saya bisa menghitung siklus jam assembler akan mengambil dengan tangan bahwa seseorang telah mungkin sudah menulis beberapa kode yang bagus untuk melakukan ini secara programatik
cyphunk
3

Ini adalah contoh untuk Arduino menggunakan fungsi clockCyclesPerMicrosecond () untuk menghitung jam yang telah berlalu. Kode ini akan menunggu 4 detik, lalu mencetak waktu berlalu sejak awal program. Nilai 3 kiri adalah total waktu (mikrodetik, milidetik, siklus total jam) dan sebagian besar 3 adalah waktu yang telah berlalu:

Keluaran:

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

Saya yakin ada penjelasan yang masuk akal mengapa loop pertama juga memiliki siklus jam berlalu lebih pendek daripada kebanyakan dan mengapa semua loop lain beralih antara dua siklus clock panjang.

Kode:

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);     

}

Sidenote: jika Anda menghapus penundaan 4 detik Anda akan mulai melihat efek dari Serial.print () jauh lebih jelas. Catatan, di sini 2 run dibandingkan. Saya hanya memasukkan 4 sampel dekat satu sama lain dari log masing-masing.

Jalankan 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

Jalankan 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

Waktu yang berlalu meningkat dari total waktu berjalan. Setelah sedetik berlalu, jam meningkat rata-rata dari 40k menjadi 44k. Ini terjadi secara konsisten beberapa milidetik setelah 1 detik dan jam yang berlalu tetap sekitar 44r untuk setidaknya 10 detik berikutnya (saya belum mengujinya lebih lanjut). Inilah mengapa pemantauan bermanfaat atau dibutuhkan. Mungkin penurunan efisiensi berkaitan dengan konfigurasi atau bug dalam serial? Atau mungkin kodenya tidak menggunakan memori dengan benar dan memiliki kebocoran yang memengaruhi kinerja, dll.

cyphunk
sumber
bertahun-tahun kemudian, saya masih ingin sesuatu yang menunjukkan jam dengan lebih akurat dengan kode (seperti yang terlihat pada osiloskop). Pada Saya mencoba menentukan jumlah siklus jam yang diperlukan untuk digitalWrite () di 16MHZ dan 8MHZ. Dalam 16MHZ saya mendapatkan 8us / 64clk. Tetapi dalam 8MHZ saya mendapatkan 0us / 0clk.
cyphunk
1

Karena setiap baris kode yang ditambahkan ke sumber Anda akan berdampak pada kinerja dan dapat mengubah optimasi yang diterapkan. Perubahan harus menjadi minimum yang diperlukan untuk melakukan tugas.

Saya baru saja menemukan plugin Atmel Studio yang disebut "Debugger File Anotasi Majelis". http://www.atmel.com/webdoc/aafdebugger/pr01.html Sepertinya melangkah melalui bahasa assembly yang dihasilkan sementara mungkin membosankan akan menunjukkan kepada Anda apa yang terjadi. Anda mungkin masih harus men-decode berapa banyak siklus yang diperlukan untuk setiap instruksi tetapi itu akan jauh lebih dekat daripada beberapa opsi lain yang diposting.

Bagi mereka yang tidak tahu di folder Output proyek Anda adalah file dengan ekstensi LSS. File ini berisi semua kode sumber asli Anda sebagai komentar dan di bawah setiap baris adalah bahasa rakitan yang dihasilkan berdasarkan baris kode tersebut. Membuat file LSS dapat dimatikan, jadi periksa pengaturan berikut.

Properti Proyek | Toolchain | AVR / GNU Common | OutputFiles

Kotak centang ".lss (Hasilkan file lss)

James
sumber
1

Anda dapat menggunakan salah satu timer bawaan. Dapatkan semuanya diatur untuk prescaller = 1 dan TCNT = 0 sebelum blok. Kemudian aktifkan timer di telepon sebelum blok dan nonaktifkan di telepon setelah blok. TCNT sekarang akan menahan jumlah siklus yang diambil blok, dikurangi siklus tetap untuk kode aktifkan dan nonaktifkan.

Perhatikan bahwa TNCT akan meluap setelah 65535 clock cycle pada timer 16 bit. Anda dapat menggunakan flag overflow untuk menggandakan waktu lari. Jika Anda masih membutuhkan lebih lama, Anda dapat menggunakan prescaler, tetapi akan mendapatkan resolusi lebih sedikit.

bigjosh
sumber