]> sigrok.org Git - sigrok-dumps.git/blame - arm_trace/stm32f105/trace_example.c
arm_trace: Add ARM ETMv3/ITM trace dumps.
[sigrok-dumps.git] / arm_trace / stm32f105 / trace_example.c
CommitLineData
82da1f4d
PA
1/* 2015 Petteri Aimonen <jpa@git.mail.kapsi.fi>
2 * Public domain
3 *
4 * This file is an example on how to configure the TPIU/DWT/ITM/ETM blocks
5 * on a Cortex-M3 microcontroller for tracing the execution. Trace data is
6 * output from the TRACESWO pin.
7 *
8 * Designed to run especially on STM32 Value Line discovery board, but should
9 * be easily adaptible to other boards also. Note that the STM32F100 chip on
10 * value line discovery does not have ETM feature.
11 *
12 * What this does:
13 * 1) Configures the trace pin to output TPIU formatted trace from both ITM and ETM.
14 * 2) Blinks a led, while monitored by ITM tracing.
15 * 3) Causes periodic interrupts, where it runs bubblesort while tracing it with ETM.
16 */
17
18#include "stm32f10x.h"
19#include "core_cm3.h"
20#include "arm_etm.h"
21
22int globalCounter; // For watchpoint example
23
24void hardfault_handler(void) { for(;;); }
25
26void configure_tracing()
27{
28 /* STM32 specific configuration to enable the TRACESWO IO pin */
29 RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
30 AFIO->MAPR |= (2 << 24); // Disable JTAG to release TRACESWO
31 DBGMCU->CR |= DBGMCU_CR_TRACE_IOEN; // Enable IO trace pins
32
33 if (!(DBGMCU->CR & DBGMCU_CR_TRACE_IOEN))
34 {
35 // Some (all?) STM32s don't allow writes to DBGMCU register until
36 // C_DEBUGEN in CoreDebug->DHCSR is set. This cannot be set by the
37 // CPU itself, so in practice you need to connect to the CPU with
38 // a debugger once before resetting it.
39 return;
40 }
41
42 /* Configure Trace Port Interface Unit */
43 CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // Enable access to registers
44 TPI->ACPR = 0; // Trace clock = HCLK/(x+1) = 8MHz
45 TPI->SPPR = 2; // Pin protocol = NRZ/USART
46 TPI->FFCR = 0x102; // TPIU packet framing enabled when bit 2 is set.
47 // You can use 0x100 if you only need DWT/ITM and not ETM.
48
49 /* Configure PC sampling and exception trace */
50 DWT->CTRL = (1 << DWT_CTRL_CYCTAP_Pos) // Prescaler for PC sampling
51 // 0 = x32, 1 = x512
52 | (0 << DWT_CTRL_POSTPRESET_Pos) // Postscaler for PC sampling
53 // Divider = value + 1
54 | (1 << DWT_CTRL_PCSAMPLENA_Pos) // Enable PC sampling
55 | (2 << DWT_CTRL_SYNCTAP_Pos) // Sync packet interval
56 // 0 = Off, 1 = Every 2^23 cycles,
57 // 2 = Every 2^25, 3 = Every 2^27
58 | (1 << DWT_CTRL_EXCTRCENA_Pos) // Enable exception trace
59 | (1 << DWT_CTRL_CYCCNTENA_Pos); // Enable cycle counter
60
61 /* Configure instrumentation trace macroblock */
62 ITM->LAR = 0xC5ACCE55;
63 ITM->TCR = (1 << ITM_TCR_TraceBusID_Pos) // Trace bus ID for TPIU
64 | (1 << ITM_TCR_DWTENA_Pos) // Enable events from DWT
65 | (1 << ITM_TCR_SYNCENA_Pos) // Enable sync packets
66 | (1 << ITM_TCR_ITMENA_Pos); // Main enable for ITM
67 ITM->TER = 0xFFFFFFFF; // Enable all stimulus ports
68
69 /* Configure embedded trace macroblock */
70 ETM->LAR = 0xC5ACCE55;
71 ETM_SetupMode();
72 ETM->CR = ETM_CR_ETMEN // Enable ETM output port
73 | ETM_CR_STALL_PROCESSOR // Stall processor when fifo is full
74 | ETM_CR_BRANCH_OUTPUT; // Report all branches
75 ETM->TRACEIDR = 2; // Trace bus ID for TPIU
76 ETM->TECR1 = ETM_TECR1_EXCLUDE; // Trace always enabled
77 ETM->FFRR = ETM_FFRR_EXCLUDE; // Stalling always enabled
78 ETM->FFLR = 24; // Stall when less than N bytes free in FIFO (range 1..24)
79 // Larger values mean less latency in trace, but more stalls.
80 // Note: we do not enable ETM trace yet, only for specific parts of code.
81}
82
83void configure_watchpoint()
84{
85 /* This is an example of how to configure DWT to monitor a watchpoint.
86 The data value is reported when the watchpoint is hit. */
87
88 /* Monitor all accesses to GPIOC (range length 32 bytes) */
89 DWT->COMP0 = (uint32_t)GPIOC;
90 DWT->MASK0 = 5;
91 DWT->FUNCTION0 = (2 << DWT_FUNCTION_FUNCTION_Pos) // Report data and addr on watchpoint hit
92 | (1 << DWT_FUNCTION_EMITRANGE_Pos);
93
94 /* Monitor all accesses to globalCounter (range length 4 bytes) */
95 DWT->COMP1 = (uint32_t)&globalCounter;
96 DWT->MASK1 = 2;
97 DWT->FUNCTION1 = (3 << DWT_FUNCTION_FUNCTION_Pos); // Report data and PC on watchpoint hit
98}
99
100// Print a given string to ITM.
101// This uses 8 bit writes, as that seems to be the most common way to write text
102// through ITM. Otherwise there is no good way for the PC software to know what
103// is text and what is some other data.
104void ITM_Print(int port, const char *p)
105{
106 if ((ITM->TCR & ITM_TCR_ITMENA_Msk) && (ITM->TER & (1UL << port)))
107 {
108 while (*p)
109 {
110 while (ITM->PORT[port].u32 == 0);
111 ITM->PORT[port].u8 = *p++;
112 }
113 }
114}
115
116// Write a 32-bit value to ITM.
117// This can be used as a fast way to log important values from code.
118void ITM_SendValue (int port, uint32_t value)
119{
120 if ((ITM->TCR & ITM_TCR_ITMENA_Msk) && (ITM->TER & (1UL << port)))
121 {
122 while (ITM->PORT[port].u32 == 0);
123 ITM->PORT[port].u32 = value;
124 }
125}
126
127void delay()
128{
129 for (int i = 0; i < 10000; i++)
130 asm("nop");
131}
132
133void bubble_sort (int *a, int n) {
134 int i, t, s = 1;
135 while (s) {
136 s = 0;
137 for (i = 1; i < n; i++) {
138 if (a[i] < a[i - 1]) {
139 t = a[i];
140 a[i] = a[i - 1];
141 a[i - 1] = t;
142 s = 1;
143 }
144 }
145 }
146}
147
148void TIM2_IRQ()
149{
150 int values[5] = {35,2,235,11,2};
151
152 // We are very interested in how the values get sorted,
153 // so we enable ETM tracing for it.
154 ETM_TraceMode();
155 GPIOC->BSRR = (1 << 9); // Toggle a led so that we can see the latency in ETM trace
156 bubble_sort(values, 5);
157 GPIOC->BRR = (1 << 9);
158 ETM_SetupMode();
159
160 // We can also use ITM to send the result of the sort
161 // Note that we use port 1 here so that output does not get mixed
162 // with port 0 output from main thread.
163 ITM_Print(1, "Sort");
164 for (int i = 0; i < 5; i++)
165 ITM_SendValue(1, values[i]);
166
167 TIM2->SR = 0; // Clear interrupt flag
168}
169
170int main(void)
171{
172 configure_tracing();
173 configure_watchpoint();
174
175 RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
176 GPIOC->CRH = 0x44444433; // GPIOC 8 and 9 as output (STM32F1 discovery leds)
177
178 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
179 NVIC_EnableIRQ(TIM2_IRQn);
180 TIM2->ARR = 50000;
181 TIM2->DIER = 1;
182 TIM2->CR1 = 1;
183
184 ITM_Print(0, "Boot");
185
186 globalCounter = 0;
187
188 for (;;)
189 {
190 delay();
191 GPIOC->BSRR = (1 << 8);
192 ITM_Print(0, "On");
193
194 delay();
195 GPIOC->BRR = (1 << 8);
196 ITM_Print(0, "Off");
197
198 globalCounter++; // This will trigger the watchpoint
199 }
200}
201
202
203void* myvectors[]
204__attribute__ ((section("vectors")))= {
205 (void*)0x20002000, // Stack ptr
206 main, // Reset addr
207 hardfault_handler,
208 hardfault_handler,
209 [16 + TIM2_IRQn] = TIM2_IRQ
210};
211