]> sigrok.org Git - sigrok-dumps.git/blob - arm_trace/stm32f105/trace_example.c
arm_trace: Add ARM ETMv3/ITM trace dumps.
[sigrok-dumps.git] / arm_trace / stm32f105 / trace_example.c
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