]>
Commit | Line | Data |
---|---|---|
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 | ||
22 | int globalCounter; // For watchpoint example | |
23 | ||
24 | void hardfault_handler(void) { for(;;); } | |
25 | ||
26 | void 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 | ||
83 | void 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. | |
104 | void 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. | |
118 | void 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 | ||
127 | void delay() | |
128 | { | |
129 | for (int i = 0; i < 10000; i++) | |
130 | asm("nop"); | |
131 | } | |
132 | ||
133 | void 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 | ||
148 | void 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 | ||
170 | int 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 | ||
203 | void* 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 |