--- /dev/null
+-------------------------------------------------------------------------------
+ARM ETM/ITM Tracing
+-------------------------------------------------------------------------------
+
+This is an example capture from ARM Cortex-M3 ETM/ITM trace stream.
+
+The trace was captured from STM32F105 microcontroller while running
+this software (revision 5a1553c):
+https://github.com/PetteriAimonen/STM32_Trace_Example
+
+The example software outputs both ITM and ETM trace as follows:
+
+1) The main loop blinks PC8 approx every 10 ms.
+ On every blink it prints "On" and "Off" to ITM port 0.
+
+2) TIM2 interrupt blinks PC9 approx every 6 ms.
+ It is kept on for the duration of a bubble_sort() call.
+ ETM tracing is enabled for the duration of that subroutine.
+ Sort results are printed to ITM port 1.
+
+3) DWT is continuously reporting exception status and periodically
+ reporting PC values (these go to the ITM stream).
+
+Both ITM and ETM are mixed into the same stream by TPIU module
+ITM is configured as stream 1 and ETM as stream 2.
+
+Trace output is configured as 8 MHz UART protocol from TRACESWO pin.
+
+
+Logic analyzer setup
+--------------------
+
+The logic analyzer used was an FX2 based device (at 24MHz):
+
+ Probe STM32F105
+ ---------------------
+ 3 PC9
+ 5 SWO
+ 7 PC8
+
+
+Data
+----
+
+Capture command:
+
+ sigrok-cli -d fx2lafw -c samplerate=24MHz -C 4=SWO,2=PC9,6=PC8 -o trace_example.sr --time 50
+
+Decoding command for ITM output:
+
+ sigrok-cli -i trace_example.sr -P \
+ uart:rx=SWO:baudrate=8000000,arm_tpiu:stream=1,arm_itm:elffile=trace_example.elf
+
+Decoding command for ETM output:
+
+ sigrok-cli -i trace_example.sr -P \
+ uart:rx=SWO:baudrate=8000000,arm_tpiu:stream=2,arm_etmv3:elffile=trace_example.elf
+
--- /dev/null
+/* 2015 Petteri Aimonen <jpa@git.mail.kapsi.fi>
+ * Public domain
+ *
+ * This file is an example on how to configure the TPIU/DWT/ITM/ETM blocks
+ * on a Cortex-M3 microcontroller for tracing the execution. Trace data is
+ * output from the TRACESWO pin.
+ *
+ * Designed to run especially on STM32 Value Line discovery board, but should
+ * be easily adaptible to other boards also. Note that the STM32F100 chip on
+ * value line discovery does not have ETM feature.
+ *
+ * What this does:
+ * 1) Configures the trace pin to output TPIU formatted trace from both ITM and ETM.
+ * 2) Blinks a led, while monitored by ITM tracing.
+ * 3) Causes periodic interrupts, where it runs bubblesort while tracing it with ETM.
+ */
+
+#include "stm32f10x.h"
+#include "core_cm3.h"
+#include "arm_etm.h"
+
+int globalCounter; // For watchpoint example
+
+void hardfault_handler(void) { for(;;); }
+
+void configure_tracing()
+{
+ /* STM32 specific configuration to enable the TRACESWO IO pin */
+ RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
+ AFIO->MAPR |= (2 << 24); // Disable JTAG to release TRACESWO
+ DBGMCU->CR |= DBGMCU_CR_TRACE_IOEN; // Enable IO trace pins
+
+ if (!(DBGMCU->CR & DBGMCU_CR_TRACE_IOEN))
+ {
+ // Some (all?) STM32s don't allow writes to DBGMCU register until
+ // C_DEBUGEN in CoreDebug->DHCSR is set. This cannot be set by the
+ // CPU itself, so in practice you need to connect to the CPU with
+ // a debugger once before resetting it.
+ return;
+ }
+
+ /* Configure Trace Port Interface Unit */
+ CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // Enable access to registers
+ TPI->ACPR = 0; // Trace clock = HCLK/(x+1) = 8MHz
+ TPI->SPPR = 2; // Pin protocol = NRZ/USART
+ TPI->FFCR = 0x102; // TPIU packet framing enabled when bit 2 is set.
+ // You can use 0x100 if you only need DWT/ITM and not ETM.
+
+ /* Configure PC sampling and exception trace */
+ DWT->CTRL = (1 << DWT_CTRL_CYCTAP_Pos) // Prescaler for PC sampling
+ // 0 = x32, 1 = x512
+ | (0 << DWT_CTRL_POSTPRESET_Pos) // Postscaler for PC sampling
+ // Divider = value + 1
+ | (1 << DWT_CTRL_PCSAMPLENA_Pos) // Enable PC sampling
+ | (2 << DWT_CTRL_SYNCTAP_Pos) // Sync packet interval
+ // 0 = Off, 1 = Every 2^23 cycles,
+ // 2 = Every 2^25, 3 = Every 2^27
+ | (1 << DWT_CTRL_EXCTRCENA_Pos) // Enable exception trace
+ | (1 << DWT_CTRL_CYCCNTENA_Pos); // Enable cycle counter
+
+ /* Configure instrumentation trace macroblock */
+ ITM->LAR = 0xC5ACCE55;
+ ITM->TCR = (1 << ITM_TCR_TraceBusID_Pos) // Trace bus ID for TPIU
+ | (1 << ITM_TCR_DWTENA_Pos) // Enable events from DWT
+ | (1 << ITM_TCR_SYNCENA_Pos) // Enable sync packets
+ | (1 << ITM_TCR_ITMENA_Pos); // Main enable for ITM
+ ITM->TER = 0xFFFFFFFF; // Enable all stimulus ports
+
+ /* Configure embedded trace macroblock */
+ ETM->LAR = 0xC5ACCE55;
+ ETM_SetupMode();
+ ETM->CR = ETM_CR_ETMEN // Enable ETM output port
+ | ETM_CR_STALL_PROCESSOR // Stall processor when fifo is full
+ | ETM_CR_BRANCH_OUTPUT; // Report all branches
+ ETM->TRACEIDR = 2; // Trace bus ID for TPIU
+ ETM->TECR1 = ETM_TECR1_EXCLUDE; // Trace always enabled
+ ETM->FFRR = ETM_FFRR_EXCLUDE; // Stalling always enabled
+ ETM->FFLR = 24; // Stall when less than N bytes free in FIFO (range 1..24)
+ // Larger values mean less latency in trace, but more stalls.
+ // Note: we do not enable ETM trace yet, only for specific parts of code.
+}
+
+void configure_watchpoint()
+{
+ /* This is an example of how to configure DWT to monitor a watchpoint.
+ The data value is reported when the watchpoint is hit. */
+
+ /* Monitor all accesses to GPIOC (range length 32 bytes) */
+ DWT->COMP0 = (uint32_t)GPIOC;
+ DWT->MASK0 = 5;
+ DWT->FUNCTION0 = (2 << DWT_FUNCTION_FUNCTION_Pos) // Report data and addr on watchpoint hit
+ | (1 << DWT_FUNCTION_EMITRANGE_Pos);
+
+ /* Monitor all accesses to globalCounter (range length 4 bytes) */
+ DWT->COMP1 = (uint32_t)&globalCounter;
+ DWT->MASK1 = 2;
+ DWT->FUNCTION1 = (3 << DWT_FUNCTION_FUNCTION_Pos); // Report data and PC on watchpoint hit
+}
+
+// Print a given string to ITM.
+// This uses 8 bit writes, as that seems to be the most common way to write text
+// through ITM. Otherwise there is no good way for the PC software to know what
+// is text and what is some other data.
+void ITM_Print(int port, const char *p)
+{
+ if ((ITM->TCR & ITM_TCR_ITMENA_Msk) && (ITM->TER & (1UL << port)))
+ {
+ while (*p)
+ {
+ while (ITM->PORT[port].u32 == 0);
+ ITM->PORT[port].u8 = *p++;
+ }
+ }
+}
+
+// Write a 32-bit value to ITM.
+// This can be used as a fast way to log important values from code.
+void ITM_SendValue (int port, uint32_t value)
+{
+ if ((ITM->TCR & ITM_TCR_ITMENA_Msk) && (ITM->TER & (1UL << port)))
+ {
+ while (ITM->PORT[port].u32 == 0);
+ ITM->PORT[port].u32 = value;
+ }
+}
+
+void delay()
+{
+ for (int i = 0; i < 10000; i++)
+ asm("nop");
+}
+
+void bubble_sort (int *a, int n) {
+ int i, t, s = 1;
+ while (s) {
+ s = 0;
+ for (i = 1; i < n; i++) {
+ if (a[i] < a[i - 1]) {
+ t = a[i];
+ a[i] = a[i - 1];
+ a[i - 1] = t;
+ s = 1;
+ }
+ }
+ }
+}
+
+void TIM2_IRQ()
+{
+ int values[5] = {35,2,235,11,2};
+
+ // We are very interested in how the values get sorted,
+ // so we enable ETM tracing for it.
+ ETM_TraceMode();
+ GPIOC->BSRR = (1 << 9); // Toggle a led so that we can see the latency in ETM trace
+ bubble_sort(values, 5);
+ GPIOC->BRR = (1 << 9);
+ ETM_SetupMode();
+
+ // We can also use ITM to send the result of the sort
+ // Note that we use port 1 here so that output does not get mixed
+ // with port 0 output from main thread.
+ ITM_Print(1, "Sort");
+ for (int i = 0; i < 5; i++)
+ ITM_SendValue(1, values[i]);
+
+ TIM2->SR = 0; // Clear interrupt flag
+}
+
+int main(void)
+{
+ configure_tracing();
+ configure_watchpoint();
+
+ RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
+ GPIOC->CRH = 0x44444433; // GPIOC 8 and 9 as output (STM32F1 discovery leds)
+
+ RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
+ NVIC_EnableIRQ(TIM2_IRQn);
+ TIM2->ARR = 50000;
+ TIM2->DIER = 1;
+ TIM2->CR1 = 1;
+
+ ITM_Print(0, "Boot");
+
+ globalCounter = 0;
+
+ for (;;)
+ {
+ delay();
+ GPIOC->BSRR = (1 << 8);
+ ITM_Print(0, "On");
+
+ delay();
+ GPIOC->BRR = (1 << 8);
+ ITM_Print(0, "Off");
+
+ globalCounter++; // This will trigger the watchpoint
+ }
+}
+
+
+void* myvectors[]
+__attribute__ ((section("vectors")))= {
+ (void*)0x20002000, // Stack ptr
+ main, // Reset addr
+ hardfault_handler,
+ hardfault_handler,
+ [16 + TIM2_IRQn] = TIM2_IRQ
+};
+