Forum: Mikrocontroller und Digitale Elektronik ARM-Assembler: Takte zählen


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

ich mache mich in meinen ersten Gehversuche mit Assembler. Ich habe eine 
kleine ISR geschrieben und wundere mich, wie sich die Ausfuehrungsdauer 
unterscheidet.

Die Funktion sieht so aus:
1
.syntax unified
2
.cpu cortex-m3
3
.thumb
4
5
#include "hw_config.h"
6
#include "io_pins.h"
7
.include "../../src_library/src_asm/stm32f4xx.inc"
8
.include "../../src_library/src_asm/stm32f4xx_gpio.inc"
9
.include "../../src_library/src_asm/stm32f4xx_tim.inc"
10
11
@ Zyklenzaehler fuer Profiling
12
DWT_CYCCNT = 0xE0001004
13
14
.align 4
15
.bss
16
.global DdaState
17
DdaState:
18
akku:
19
    .space 4
20
cnt:
21
    .space 4
22
v:
23
    .space 4
24
25
.data
26
min:
27
    .word 0xFFFFFFFF
28
max:
29
    .word 0
30
31
.align 4
32
.text
33
.type TIM6_DAC_IRQHandler, %function
34
.global TIM6_DAC_IRQHandler
35
TIM6_DAC_IRQHandler:
36
37
    @ Profiling 1
38
    push { r4-r5 }
39
2    ldr r4, =DWT_CYCCNT
40
2    ldr r5, [r4]
41
42
43
    @ IT pending Bit loeschen TIMx->SR = (uint16_t)~TIM_IT;
44
2    ldr r0, =TIM6_BASE
45
2    ldr r1, =~TIM_IT_Update
46
2    str r1, [r0, #TIMx_SR]
47
48
    @ DDA: akku inkrementieren, rueckschreiben, Ueberlauf pruefen
49
2    ldr r0, =DdaState
50
2    ldr r1, [r0, #(akku-DdaState)]
51
2    ldr r2, [r0, #(v-DdaState)]
52
53
1    adds r1, r1, r2
54
2    str r1, [r0, #(akku-DdaState)]      @ Neuen Akku speichern
55
1+P  bcc 1f                              @ Kein Overflow -> Ende
56
57
    @ Zaehler-Update (re-use r2)
58
2    ldr r2, [r0, #(cnt-DdaState)]
59
1    add r2, #1
60
2    str r2, [r0, #(cnt-DdaState)]
61
62
    @ GPIO-Pin wackeln
63
2    ldr r1, =GPIO_PORT( PULSEGEN_PULSE0_PIN )  @ GPIOA
64
2    ldrh r3, =GPIO_PIN( PULSEGEN_PULSE0_PIN )  @ 1<<10
65
66
1    tst r2, #0x1
67
?    ite eq
68
2    strheq r3, [r1, #GPIOx_BSRRL]
69
2    strhne r3, [r1, #GPIOx_BSRRH]
70
71
1:
72
    @ Profiling 2
73
2   ldr r4, [r4]
74
    sub r4, r5 @ Ueberlauf richtig?
75
    ldr r0, =min
76
    ldr r1, [r0]
77
    cmp r1, r4
78
    it hs
79
    movhs r1, r4
80
    str r1, [r0]
81
82
    ldr r0, =max
83
    ldr r1, [r0]
84
    cmp r1, r4
85
    it ls
86
    movls r1, r4
87
    str r1, [r0]
88
    pop { r4-r5 }
89
90
    bx lr
91
    .ltorg
Man sieht: Die Funktion ist extrem kurz und ist eine Timer-ISR. Jetzt 
der merkwürdige Teil: Ist der Wert "v" = 0, variiert die Ausführungszeit 
(der Wert in "min" und "max") schon um 17...22 Takte. Das finde ich 
schon sehr merkwürdig, weil in diesem Fall nichts bedingt ausgeführt 
wird.

Sobald "v" größer als Null ist, wird die Bandbreite noch größer mit 
einem Maximum von 36 Takten.

Ich habe mal versucht, die Takte zu zählen (wie in 
https://developer.arm.com/documentation/ddi0439/b/CHDDIGAC ). Ich komme 
auf 12+P Takte. Also 5 bis 10 Takte für den Pipeline-Refill nach dem 
Branch? (In der Doku steht "3-stage-Pipeline", also hätte ich mit 3 oder 
4 Takten gerechnet.)

14 Takte innerhalb [bcc 1f...1:] scheint ja zu passen (wobei da der 
Pipeline-Flush fehlt).

Kurzum: Wo verzähle ich mich?

: Bearbeitet durch User
von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

Du hast glaube ich vergessen des der Einsprung und der Aussprung auch 
etliche Tackte benötigt.

https://interrupt.memfault.com/blog/arm-cortex-m-exceptions-and-nvic#tail-chaining

Dazu kommt noch der Zeitpunkt wo das Interrupt auftritt und was die CPU 
gerade macht und wann sie beschließt auf dem Interrupt zu reagieren usw.

Meistens ist das mit dem Tackte zählen bei den derzeit gängigen 
Prozessoren nicht mehr wirklich in allen Fällen sauber möglich.
Da kommen einfach zu viele Faktoren mit rein die man gar nicht mehr 
Tackt genau berechnen kann.
- Die Piplines und wenn welche Stufe davon gültig bleiben oder nicht
- Die Caches was diese beinhalten bzw. wann das Ding meint diese seinen 
ungültig
- Zugriff auf den Speicher mit Wartezyklen (die aber teilweise durch 
abwechselnden Zugriff auf zwei bänke kaschiert werden) und auch 
Wartezeiten, weil gerade irgend ein anderer Hardwareteil den Bus belegt 
usw.
- und noch einiges mehr...

von Walter T. (nicolas)


Lesenswert?

Irgend W. schrieb:
> Du hast glaube ich vergessen des der Einsprung und der Aussprung auch
> etliche Tackte benötigt.

Der Teil ist ja alles außerhalb dessen was im Profiling berücksicht 
wird. Leider wird es auch wohl mit meinem Werkzeug nicht möglich sein, 
ein- und Aussprung aus ISRs zu berücksichtigen.

Berücksichtigt ist ja nur der Bereich zwischen den Zeilen 40 und 72, 
wobei der Bereich der Zeilen 56 bis 69 nicht durchlaufen wird. Und da 
finde ich nichts, was eine unterschiedliche Anzahl an Prozessorzyklen 
verbrauchen könnte.

: Bearbeitet durch User
von foobar (Gast)


Lesenswert?

Bei diesen CPUs bringt Taktzählen nicht mehr viel.  Jenachdem in welchem 
Speicher das Programm liegt (CCM, RAM, Flash), variiert die Laufzeit. 
Wenn Zugriffe (auch I/O-Zugriffe) dann noch über die Busmatrix gehen 
oder gar auf asynchronen Bussen (z.B. APB) landen, kannst du gleich 
Würfeln ...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.