Forum: Mikrocontroller und Digitale Elektronik Cortex M7 und Cycle Counter (DWT)


von Falko J. (spacefrog)


Lesenswert?

Hallo zusammen,

ich versuche gerade die Zeit für die Ausführung von Funktionen auf einen 
Cortex M7 (STM32F746) zu messen. Das mache ich wie hier im Artikel (ganz 
unten) beschrieben:

STM32 für Einsteiger


Beim M4 hat das so funktioniert. Beim F7 habe ich 2 Probleme:

- Der Counter lässt sich mit
1
 DWT->CYCCNT = 0;
 nicht auf 0 zurücksetzen.

- Für ein einfaches inkrementieren einer int Variable werden lt. dem 
Zähler   12 Taktzyklen benötigt. Obwohl im Disasembly nur ein Befehl zu 
sehen ist. Beim M4 wurde bei gleichem code nur ein Zyklus angezeigt.

Ich benutze den IAR Compiler. Als Projekt Grundgerüst habe ich das 
Template aus dem Cube Ordner genutzt 
(..\STM32Cube_FW_F7_V1.1.0\Projects\STM32746G-Discovery\Templates\Src)
1
int main(void)
2
{
3
  /* This project template calls firstly two functions in order to configure MPU feature 
4
     and to enable the CPU Cache, respectively MPU_Config() and CPU_CACHE_Enable().
5
     These functions are provided as template implementation that User may integrate 
6
     in his application, to enhance the performance in case of use of AXI interface 
7
     with several masters. */ 
8
  
9
  /* Configure the MPU attributes as Write Through */
10
  MPU_Config();
11
12
  /* Enable the CPU Cache */
13
  CPU_CACHE_Enable();
14
15
  /* STM32F7xx HAL library initialization:
16
       - Configure the Flash ART accelerator on ITCM interface
17
       - Configure the Systick to generate an interrupt each 1 msec
18
       - Set NVIC Group Priority to 4
19
       - Low Level Initialization
20
     */
21
  HAL_Init();
22
23
  /* Configure the System clock to have a frequency of 216 MHz */
24
  SystemClock_Config();
25
26
27
  /* Add your application code here
28
     */
29
   
30
  int a = 0;
31
  
32
  DWT_Enable(); // DWT-Einheit aktivieren
33
  DWT_CycCounterEn (); // Zähler aktivieren
34
  DWT_CycCounterClear (); // Zähler löschen
35
  
36
  a++;
37
  a++;
38
  
39
  uint32_t iZ = DWT_CycCounterRead (); // Zähler auslesen
40
  
41
  /* Infinite loop */
42
  while (1)
43
  {
44
  }
45
}

Hat jemand eine Idee?

von Jim M. (turboj)


Lesenswert?

Falko J. schrieb:
> - Für ein einfaches inkrementieren einer int Variable werden lt. dem
> Zähler   12 Taktzyklen benötigt. Obwohl im Disasembly nur ein Befehl zu
> sehen ist. Beim M4 wurde bei gleichem code nur ein Zyklus angezeigt.

Dann zeig uns doch mal das Disassembly, inklusive des counter clear.

von Falko J. (spacefrog)


Lesenswert?

Klar..
1
int main(void)
2
{
3
main:
4
    0x8000cc6: 0xb510         PUSH      {R4, LR}
5
  MPU_Config();
6
    0x8000cc8: 0xf000 0xf84e  BL        MPU_Config              ; 0x8000d68
7
  CPU_CACHE_Enable();
8
    0x8000ccc: 0xf000 0xf888  BL        CPU_CACHE_Enable        ; 0x8000de0
9
  HAL_Init();
10
    0x8000cd0: 0xf7ff 0xfeb2  BL        HAL_Init                ; 0x8000a38
11
  SystemClock_Config();
12
    0x8000cd4: 0xf000 0xf80d  BL        SystemClock_Config      ; 0x8000cf2
13
  int a = 0;
14
    0x8000cd8: 0x2400         MOVS      R4, #0
15
  __disable_irq();
16
    0x8000cda: 0xb672         CPSID     i
17
  DWT_Enable(); // DWT-Einheit aktivieren
18
    0x8000cdc: 0xf000 0xf886  BL        DWT_Enable              ; 0x8000dec
19
  DWT_CycCounterEn(); // Zähler aktivieren
20
    0x8000ce0: 0xf000 0xf88e  BL        DWT_CycCounterEn        ; 0x8000e00
21
  DWT_CycCounterClear(); // Zähler löschen
22
    0x8000ce4: 0xf000 0xf896  BL        DWT_CycCounterClear     ; 0x8000e14
23
  a++;
24
    0x8000ce8: 0x1c64         ADDS      R4, R4, #1
25
  a++;
26
    0x8000cea: 0x1c64         ADDS      R4, R4, #1
27
  uint32_t iZ = DWT_CycCounterRead (); // Zähler auslesen
28
    0x8000cec: 0xf000 0xf898  BL        DWT_CycCounterRead      ; 0x8000e20
29
  while (1)
30
    0x8000cf0: 0xe7fe         B.N       0x8000cf0
31
static void SystemClock_Config(void)
32
{
33
SystemClock_Config:
34
    0x8000cf2: 0xb500         PUSH      {LR}
35
    0x8000cf4: 0xb091         SUB       SP, SP, #0x44
36
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
37
    0x8000cf6: 0x2001         MOVS      R0, #1
38
    0x8000cf8: 0x9005         STR       R0, [SP, #0x14]
1
  CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
2
DWT_Enable:
3
    0x8000dec: 0x4803         LDR.N     R0, [PC, #0xc]          ; [0x8000dfc] DEMCR
4
    0x8000dee: 0x6800         LDR       R0, [R0]
5
    0x8000df0: 0xf050 0x7080  ORRS.W    R0, R0, #16777216       ; 0x1000000
6
    0x8000df4: 0x4901         LDR.N     R1, [PC, #0x4]          ; [0x8000dfc] DEMCR
7
    0x8000df6: 0x6008         STR       R0, [R1]
8
}
9
    0x8000df8: 0x4770         BX        LR
10
    0x8000dfa: 0xbf00         NOP
11
    0x8000dfc: 0xe000edfc     DC32      DEMCR
12
  DWT->CTRL = DWT->CTRL | 1;
13
DWT_CycCounterEn:
14
    0x8000e00: 0x4803         LDR.N     R0, [PC, #0xc]          ; [0x8000e10] 0xe0001000 (-536866816)
15
    0x8000e02: 0x6800         LDR       R0, [R0]
16
    0x8000e04: 0xf050 0x0001  ORRS.W    R0, R0, #1
17
    0x8000e08: 0x4901         LDR.N     R1, [PC, #0x4]          ; [0x8000e10] 0xe0001000 (-536866816)
18
    0x8000e0a: 0x6008         STR       R0, [R1]
19
}
20
    0x8000e0c: 0x4770         BX        LR
21
    0x8000e0e: 0xbf00         NOP
22
    0x8000e10: 0xe0001000     DC32      -536866816              ; '...à'
23
  DWT->CYCCNT = 0;
24
DWT_CycCounterClear:
25
    0x8000e14: 0x2000         MOVS      R0, #0
26
    0x8000e16: 0x4901         LDR.N     R1, [PC, #0x4]          ; [0x8000e1c] DWT_CYCCNT
27
    0x8000e18: 0x6008         STR       R0, [R1]
28
}
29
    0x8000e1a: 0x4770         BX        LR
30
    0x8000e1c: 0xe0001004     DC32      DWT_CYCCNT
31
  return DWT->CYCCNT;
32
DWT_CycCounterRead:
33
    0x8000e20: 0x4801         LDR.N     R0, [PC, #0x4]          ; [0x8000e28] DWT_CYCCNT
34
    0x8000e22: 0x6800         LDR       R0, [R0]
35
    0x8000e24: 0x4770         BX        LR
36
    0x8000e26: 0xbf00         NOP
37
    0x8000e28: 0xe0001004     DC32      DWT_CYCCNT

Optimierung ist übrigens aus...

Gruß
Falko

: Bearbeitet durch User
von Jim M. (turboj)


Lesenswert?

Falko J. schrieb:
> Optimierung ist übrigens aus...

Das dürfte dein Problem sein. Die Funktionsaufrufe kosten Zeit.

von Stefan K. (stefan64)


Lesenswert?

Lass Dein Programm einmal mit nur einem
  a++;
laufen und danach mit 11:
  a++;
  a++;
  a++;
  a++;
  a++;
  a++;
  a++;
  a++;
  a++;
  a++;

Die Differenz beider Laufzeiten geteilt durch 10 ist die 
Ausführungsdauer von:
  a++;
bzw.
  0x8000ce8: 0x1c64         ADDS      R4, R4, #1

Gruß, Stefan

von Falko J. (spacefrog)


Lesenswert?

Aber mit Optimierung hat sich nichts geändert. Das inkrementieren der 
Variable ist aber auch ohne Optimierung nur ein assembler Befehl. 
Trotzdem erhöht sich der Cycle counter um 11, wenn in Debugger genau 
diesen einen Schritt weiter gehe.

von Falko J. (spacefrog)


Lesenswert?

Hallo Stefan,
das klingt logisch, aber warum kann ich nicht einfach den counter Wert 
nehmen? wird der counter schnelle getaktet als der Systemtakt?

: Bearbeitet durch User
von Stefan K. (stefan64)


Lesenswert?

Weil diese Befehle dann mit im Counter eingerechnet sind:

DWT_CycCounterClear:
    0x8000e14: 0x2000         MOVS      R0, #0
    0x8000e16: 0x4901         LDR.N     R1, [PC, #0x4]          ; 
[0x8000e1c] DWT_CYCCNT
    0x8000e18: 0x6008         STR       R0, [R1]
}
    0x8000e1a: 0x4770         BX        LR
    0x8000e1c: 0xe0001004     DC32      DWT_CYCCNT
  return DWT->CYCCNT;

DWT_CycCounterRead:
    0x8000e20: 0x4801         LDR.N     R0, [PC, #0x4]          ; 
[0x8000e28] DWT_CYCCNT
    0x8000e22: 0x6800         LDR       R0, [R0]
    0x8000e24: 0x4770         BX        LR
    0x8000e26: 0xbf00         NOP
    0x8000e28: 0xe0001004     DC32      DWT_CYCCNT

Ev. wurde bei Deinen Messungen früher der Counter inline ausgelesen.
Du kannst auch DWT_CycCounterClear() und DWT_CycCounterRead() direkt 
hintereinander aufrufen und den ausgelesenen Wert bei "richtigen" 
Messungen als Offset abziehen.

Gruß, Stefan

: Bearbeitet durch User
von Falko J. (spacefrog)


Angehängte Dateien:

Lesenswert?

Mhh das kann's nicht sein, auch wenn ich mit dem Debugger den cycle 
counter lese (also direkt das Register), werden viel zu viele Schritte 
angezeigt.

Anbei 2 Bilder (vor und nach der Inkrementierung)

von Jim M. (turboj)


Lesenswert?

In die 22 Takte würde ein (fast)leerer Interrupt Handler passen, hattest 
Du nicht oben den Systick eingeschaltet...?

Ich würde es mit __disable_fault_irq(); vorher probieren.

: Bearbeitet durch User
von Falko J. (spacefrog)


Lesenswert?

Hallo Jim,

gute Idee, aber das war es auch nicht. Denke ich zumindest...hab alle 
Interrupts abgeschaltet, die ich gefunden habe (systick war an).
Zusätzlich das __disable_fault_irq(); von dir ausprobiert und 
__disable_irq();

Keine Änderung :-( ... so langsam denk ich der M7 will mich mobben...

von (prx) A. K. (prx)


Lesenswert?

Steht irgendwo in Stein gemeisselt, dass sich dieser Zähler bei den 
Aktivitäten des Debuggers nur im Rahmen eines einzelnen Befehlsschritts 
des Anwenderprogramms bewegt? Ich kann mir nämlich schon vorstellen, 
dass die Ausführung eines einzelnen Befehls im Debugger auf der 
Zielhardware deutlich mehr macht, als nur diesen einen Befehl 
auszuführen.

Zyklenmessung erfolgt üblicherweise anders. Zähler auslesen, 
Messprogramm laufen lassen, Zähler nochmal auslesen, Differenz ausgeben 
- und die Differenz bei leerem Messprogramm vorher abziehen. Dabei aber 
aufpassen, dass der Optimizer des Compiler keinen Strich durch die 
Rechnung macht und die Reihenfolge ändert. Und das alles ohne 
Debugger.

: Bearbeitet durch User
von Falko J. (spacefrog)


Lesenswert?

A. K. schrieb:
> Steht irgendwo in Stein gemeisselt, dass sich dieser Zähler bei den
> Aktivitäten des Debuggers nur im Rahmen eines einzelnen Befehlsschritts
> des Anwenderprogramms bewegt

Mhhh, wahrscheinlich hast du Recht, vielleicht könnte es trotzdem mal 
noch jemand anderes mit einem M7 Board und anderen compiler/Debugger 
probieren....
LG
Falko

von Stefan (Gast)


Lesenswert?

Ich habe das Phänomen mit Crossworks und einem STM32F746VG auch 
beobachtet. Debug HW ist ein Nucleo Board. Viel getestet habe ich aber 
noch nicht da die Platine noch nicht vollständig bestückt ist. Heute 
Abend löte ich weiter.

Als nächstes wollte ich es dann ohne den Cache probieren. Und auch 
schauen ob es nur bei einem Singlestep auftritt oder auch bei einer 
längeren Befehlssequenz wenn die CPU per BP gestoppt wird.
Zurücksetzen kann ich den Counter wobei ich mir das Register selbst 
nicht angesehen habe. In der IDE gibt es einen Cycle Counter in der 
Statusleiste. Mag sein daß dort nur ein relativer Wert angezeigt wird.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Hast du die FlashLatency gesetzt?
1
FLASH_SetLatency(FLASH_Latency_5); // für 180 MHz @2,7..3,6V

Das ist jetzt nicht die Einstellung für den STM32F7, aber so in etwa 
könnte das aussehen.

Wenn das Flash zu langsam parametriert ist macht die CPU Waitstates.


Auch "SystemClock_Config();" nimmt erst mal an dass der Quarz 25MHz 
hätte und stellt die CPU Clock auf eine Sichere Betriebsart ein, aber 
das muss man anpassen.
Beim STM32F4xx ist es hier:
stm32f4xx.h:
1
/**
2
 * @brief In the following line adjust the value of External High Speed oscillator (HSE)
3
   used in your application 
4
   
5
   Tip: To avoid modifying this file each time you need to use different HSE, you
6
        can define the HSE value in your toolchain compiler preprocessor.
7
  */           
8
9
#if !defined  (HSE_VALUE) 
10
  #define HSE_VALUE    ((uint32_t)12000000) /*!< Value of the External oscillator in Hz */
11
  
12
#endif /* HSE_VALUE */

: Bearbeitet durch User
von Falko J. (spacefrog)


Lesenswert?

Hi,

FLASH_LATENCY habe ich in der SystemClockConfig gefunden und ist auf 7 
eingestellt.
1
/**
2
  * @brief  System Clock Configuration
3
  *         The system Clock is configured as follow : 
4
  *            System Clock source            = PLL (HSE)
5
  *            SYSCLK(Hz)                     = 216000000
6
  *            HCLK(Hz)                       = 216000000
7
  *            AHB Prescaler                  = 1
8
  *            APB1 Prescaler                 = 4
9
  *            APB2 Prescaler                 = 2
10
  *            HSE Frequency(Hz)              = 25000000
11
  *            PLL_M                          = 25
12
  *            PLL_N                          = 432
13
  *            PLL_P                          = 2
14
  *            PLL_Q                          = 9
15
  *            VDD(V)                         = 3.3
16
  *            Main regulator output voltage  = Scale1 mode
17
  *            Flash Latency(WS)              = 7
18
  * @param  None
19
  * @retval None
20
  */
21
static void SystemClock_Config(void)
22
{
23
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
24
  RCC_OscInitTypeDef RCC_OscInitStruct;
25
26
  /* Enable HSE Oscillator and activate PLL with HSE as source */
27
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
28
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
29
  RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
30
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
31
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
32
  RCC_OscInitStruct.PLL.PLLM = 25;
33
  RCC_OscInitStruct.PLL.PLLN = 432;  
34
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
35
  RCC_OscInitStruct.PLL.PLLQ = 9;
36
  if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
37
  {
38
    Error_Handler();
39
  }
40
41
  /* activate the OverDrive to reach the 216 Mhz Frequency */
42
  if(HAL_PWREx_EnableOverDrive() != HAL_OK)
43
  {
44
    Error_Handler();
45
  }
46
  
47
  /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 
48
     clocks dividers */
49
  RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
50
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
51
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
52
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;  
53
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;  
54
  if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK)
55
  {
56
    Error_Handler();
57
  }
58
}

ich muss zugeben das ich das mit den Flash Waitstates noch nicht ganz 
verstanden habe. Bedeutet die 7, dass sieben Takte benötigt werden um 
den nächsten Befehl aus dem Flash zu lesen?

Aber oben in der main werden noch verschieden cache's eingeschaltet
CPU_CACHE_Enable(); Die Funktion sieht so aus:
1
/**
2
  * @brief  CPU L1-Cache enable.
3
  * @param  None
4
  * @retval None
5
  */
6
static void CPU_CACHE_Enable(void)
7
{
8
  /* Enable I-Cache */
9
  SCB_EnableICache();
10
11
  /* Enable D-Cache */
12
  SCB_EnableDCache();
13
}

Wie gesagt, das ist der ST Beispiel Template aus dem cube Ordner für das 
discovery board.

Der Systemtakt stimmt.

Gruß
Falko

: Bearbeitet durch User
von Falko J. (spacefrog)


Lesenswert?

PS:
Verdoppeln der Flash Latenzy brachte keine Änderung. Aber 
auskommentieren von  CPU_Cache_Enable(); hat die Zeiten noch ein gutes 
Stück erhöht.

von Stefan (Gast)


Lesenswert?

So, die Platine ist fast komplett und ich habe noch etwas gespielt.

Den Cache Test habe ich mir gespart, das hat Falko ja bereits gemacht.
Also nun die Codeausführung "am Stück" mit BPs.
1
// Start der "Messung"
2
__GPIOE_CLK_ENABLE();
3
__GPIOC_CLK_ENABLE();
4
__GPIOH_CLK_ENABLE();
5
__GPIOA_CLK_ENABLE();
6
__GPIOB_CLK_ENABLE();
7
__GPIOD_CLK_ENABLE();
8
// Breakpoint

Den ASM Code lasse ich lieber weg. Ist ziemlich länglich (auch wegen 
-O0) aber linear.
Mit Singlestep komme ich auf 789 Zyklen. Lasse ich den Code durchlaufen 
und per BP stoppen sind es nur noch 140 Zyklen. Das ergibt ein 
Verhältnis von 5.6:1

Weiter geht's mit einer Schleife. Die sollte nach einem Durchlauf in 
einem der Caches liegen.
1
volatile int x;
2
3
int main()
4
{
5
   initHardware();
6
7
// Start
8
   for (x = 0; x < 20; x++);
9
// BP
10
...
11
}

Hier sind es mit Singlestep 3412 Zyklen (immer noch -O0), mit BP dagegen 
309 Zyklen. Das Verhältnis ist nun 11:1.

Es scheint daß der Cycle Counter zumindest zur Ermittlung der Laufzeit 
einer Code Abschnitts (Funktion) zu gebrauchen ist.

von Programmierer (Gast)


Lesenswert?

Kleiner Hinweis: Auch Breakpoints können als Instruktionen zählen, denn 
die Debugger fügen BKPT-Anweisungen in den Code im Flash ein, wenn die 
Hardware-Breakpoints aufgebraucht sind. Dies passiert auch beim 
Single-Stepping, denn der Prozessor weiß ja nicht wo eine Zeile aufhört, 
und der Debugger fügt einen temporären Breakpoint nach der Zeile ein. 
Könnte eventuell die Unterschiede erklären?

von Stefan (Gast)


Lesenswert?

Wenn es immer so wäre vielleicht. Aber die CM-0/3/4 haben diese 
Eigenheit ja nicht. Und die "Flash-BPs" sind soweit ich weiß eine 
Spezialität von Segger.

Haben die Cortex-A Kerne auch einen Cycle Counter? Falls ja, wäre ich 
nicht überrascht wenn die sich ebenso verhalten.

von A. L. (rel)


Lesenswert?

Hallo zusammen! Erster Post hier be mikocontroller.net; ich hatte schon 
oft gute Infos im Forum gefunden, und bin nun hier auf diesen älteren 
Thread gestossen... und dachte nun ich muss mich hier endlich mal 
anmelden.

Mit dem Problem oben habe ich grad zu kämpfen.

1) Obwohl der Cycle-Counter (DWT->CYCCNT) in GDB ausgelesen UND 
geschrieben werden kann (auf einem STM32F746ZGT6), ist es aus 
irgendwelchen Gründen nicht möglich den Register-Inhalt auf 0 
zurückzusetzen, wenn ich dieselbe Funktion in C schreibe. Echt 
verwirrend.

2) Auch steigen die Werte viel zu schnell an, wie oben beschrieben.



1) Im Detail:
Der Cycle-Counter kann in GDB-Konsole manuell auf 0 zurückgesetzt 
werden. Alles funktioniert, und man kann einzelne 
Single-(Instruction)-Steps "messen", was ja recht praktisch wär.
1
# DWT_Enable
2
set *( (uint32_t *)( 0xE000EDF0UL + 0x00C ) ) |= ( 1 << 24 )
3
4
# DWT_CycCounterEnable
5
set *( (uint32_t *)( 0xE0001000UL + 0x000 ) ) |= 1
6
7
# DWT_CycCounterDisable
8
set *( (uint32_t *)( 0xE0001000UL + 0x000 ) ) &= ~1
9
10
# DWT_CycCounterRead
11
p/t *( (uint32_t *)( 0xE0001000UL + 0x004 ) )
12
13
# DWT_CycCounterClear
14
set *( (uint32_t *)( 0xE0001000UL + 0x004 ) ) = 0


Doch die Werte steigen zu schnell. Einzelne simple Instruktionen 
brauchen > 10 Zyklen...


Und wenn ich folgendes kompiliere und einen Breakpoint da drunter setze 
ist aus irgendwelchen Gründen das Register nie gleich 0:
1
    DWT_CycCounterClear();
2
    DWT->CYCCNT = 0U;
3
    *( (uint32_t *)0xE0001004 ) = 0;
4
    *( (uint32_t *)0xE0001004 ) = 0U;
5
    *( (uint32_t *)( 0xE0001000UL + 0x004UL ) ) = 0U;
6
    *( (uint32_t *)( 0xE0001000UL + 0x004UL ) ) = 0;
7
    *( (uint32_t *)0xE0001004 ) ^= *( (uint32_t *)0xE0001004 );


Wenn ich test code wie hier ausführ'...
1
    LOG_STR( "Cycle counter test\r\n" );
2
    
3
    uint32_t cycles, a, b, t0, t1, t2;
4
    
5
    DWT_Enable(); // DWT-Einheit aktivieren
6
    DWT_CycCounterClear();
7
    
8
    DWT->CYCCNT = 0U;
9
    *( (uint32_t *)0xE0001004 ) = 0U;
10
    
11
    DWT_CycCounterEnable(); // Zähler aktivieren
12
    
13
    cycles = *( (uint32_t *)( 0xE0001004UL ) );
14
    LOG_INT( cycles );
15
    
16
    
17
    const uint32_t cyc_start = DWT_CycCounterRead();
18
    LOG_INT( cyc_start );
19
    
20
#define CYC ( *( (uint32_t *)( 0xE0001004UL ) ) - cyc_start )
21
    
22
    a = 12;
23
    b = 2345;
24
    a += b;
25
    
26
    t0 = CYC;
27
    a = 5;
28
    
29
    t1 = CYC;
30
    b = 1;
31
    
32
    t2 = CYC;
33
    
34
    LOG_INT( a ); // drinlassen, dass nicht ausoptimiert wird
35
    LOG_INT( b );
36
    
37
    LOG( "cycles: %u, %u, %u\r\n", t0, t1, t2 );

...bekomm ich Resultate wie die hier (-O0 gcc switch):
1
cycles: 2438761695
2
cyc_start: 2439138883
3
a: 5
4
b: 1
5
cycles: 432981, 432987, 433003


Wenn hingegen der Wert in GDB manipuliert werd, geht das gut, doch wie 
ich nehm nicht an, dass dies wirkliche der Anzahl Zyklen entspricht für 
die paar Instruktionen die da ausgeführt werden:
1
(gdb) p/x *( (uint32_t *)( 0xE0001000UL + 0x004 ) )
2
$16 = 0x6f55d32
3
(gdb) set *( (uint32_t *)( 0xE0001000UL + 0x004 ) ) = 0
4
(gdb) p/x *( (uint32_t *)( 0xE0001000UL + 0x004 ) )
5
$17 = 0x0
6
(gdb) si
7
(gdb) p/x *( (uint32_t *)( 0xE0001000UL + 0x004 ) )
8
$18 = 0xc
9
(gdb) si
10
(gdb) p/x *( (uint32_t *)( 0xE0001000UL + 0x004 ) )
11
$19 = 0x18
12
(gdb) si
13
(gdb) p/x *( (uint32_t *)( 0xE0001000UL + 0x004 ) )
14
$20 = 0x24
15
(gdb) si

Hab noch wenig Ahnung in Sache ARM und Embedded... und bin erst am 
Anfang bei den STM32ern. Wär froh bald mal eine genauere Delay-Funktion 
und eben solchen simplen Cycle-Counter zum Laufen zu bringen....

von A. L. (rel)


Lesenswert?

Korrektur: Die Zahlen stimmen, die Anzahl Cycles stimmt. Hab's 
verglichen mit einem alten Resultat, das noch mit dem Sys-Tick (1 ms) 
gemessen wurde. Problem 2) ist gegessen...

Aber warum dieses CYCCNT register nicht geresettet werden kann ist mir 
nachwievor ein Rätsel...

von rel (Gast)


Lesenswert?

Hab eine Lösung gefunden...

Die magische Zahl ist 0xC5ACCE55! :) Das Lock Access Register (DWT->LAR) 
muss beschrieben/unlocked werden, bevor man DWT und den cycle counter 
benutzen kann.

http://stackoverflow.com/questions/36378280/stm32-how-to-enable-dwt-cycle-counter

http://stackoverflow.com/questions/38355831/measuring-clock-cycle-count-on-cortex-m7#38360668

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0489c/BABJFFGJ.html

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.