Serial Wire Output (SWO) viewer for tracing purposes. Tested on F401 and ST-LINK Utility as well as for F103 and Segger J-Link SWO viewer.
Dependents: WiFi_Scanner mbed_nucleo_swo DISCO-F429ZI_LCDTS_demo_richard TEST_SM_SPEED
SWO.cpp@4:53de8ef789f3, 2017-08-24 (annotated)
- Committer:
- wim
- Date:
- Thu Aug 24 18:15:02 2017 +0000
- Revision:
- 4:53de8ef789f3
- Parent:
- 3:e5af2e131b95
Added stream claim for stdout, proposed by Pavel Sorejs.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
wim | 1:bae4cff278f6 | 1 | /* mbed SWO Library |
wim | 2:ef928f61a770 | 2 | * Copyright (c) 2014, v01: WH. Ported from Segger example (www.segger.com) |
wim | 3:e5af2e131b95 | 3 | * v02: WH. Added Class with Stream support |
wim | 4:53de8ef789f3 | 4 | * 2017, v03: WH,PS. Added stream claim for stdout, proposed by Pavel Sorejs |
wim | 1:bae4cff278f6 | 5 | * |
wim | 2:ef928f61a770 | 6 | * Simple implementation for tracing via Serial Wire Output(SWO) for Cortex-M processors. |
wim | 1:bae4cff278f6 | 7 | * It can be used with Host PC software such as ST-LINK Utility or Segger J-Link SWO viewer. |
wim | 1:bae4cff278f6 | 8 | * This sample implementation ensures that output via SWO is enabled in order to guarantee |
wim | 1:bae4cff278f6 | 9 | * that the application does not hang. |
wim | 1:bae4cff278f6 | 10 | * |
wim | 1:bae4cff278f6 | 11 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
wim | 1:bae4cff278f6 | 12 | * of this software and associated documentation files (the "Software"), to deal |
wim | 1:bae4cff278f6 | 13 | * in the Software without restriction, including without limitation the rights |
wim | 1:bae4cff278f6 | 14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
wim | 1:bae4cff278f6 | 15 | * copies of the Software, and to permit persons to whom the Software is |
wim | 1:bae4cff278f6 | 16 | * furnished to do so, subject to the following conditions: |
wim | 1:bae4cff278f6 | 17 | * |
wim | 1:bae4cff278f6 | 18 | * The above copyright notice and this permission notice shall be included in |
wim | 1:bae4cff278f6 | 19 | * all copies or substantial portions of the Software. |
wim | 1:bae4cff278f6 | 20 | * |
wim | 1:bae4cff278f6 | 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
wim | 1:bae4cff278f6 | 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
wim | 1:bae4cff278f6 | 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
wim | 1:bae4cff278f6 | 24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
wim | 1:bae4cff278f6 | 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
wim | 1:bae4cff278f6 | 26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
wim | 1:bae4cff278f6 | 27 | * THE SOFTWARE. |
wim | 1:bae4cff278f6 | 28 | */ |
wim | 2:ef928f61a770 | 29 | #include "mbed.h" |
wim | 0:0fd55660fc26 | 30 | #include "SWO.h" |
wim | 0:0fd55660fc26 | 31 | |
wim | 3:e5af2e131b95 | 32 | // |
wim | 3:e5af2e131b95 | 33 | // This the Class implementation |
wim | 3:e5af2e131b95 | 34 | // |
wim | 3:e5af2e131b95 | 35 | |
wim | 3:e5af2e131b95 | 36 | /** Create and SWO interface for debugging that supports Stream |
wim | 3:e5af2e131b95 | 37 | * @brief Currently works on nucleo ST-LINK using ST-Link Utility and other devices that support SWD/SWO using Segger SWO viewer |
wim | 3:e5af2e131b95 | 38 | */ |
wim | 4:53de8ef789f3 | 39 | SWO_Channel::SWO_Channel (const char *name) : Stream(name) { |
wim | 3:e5af2e131b95 | 40 | //May want to add initialisation stuff here |
wim | 3:e5af2e131b95 | 41 | } |
wim | 4:53de8ef789f3 | 42 | |
wim | 3:e5af2e131b95 | 43 | |
wim | 3:e5af2e131b95 | 44 | /** Write a single character (Stream implementation) |
wim | 3:e5af2e131b95 | 45 | * |
wim | 3:e5af2e131b95 | 46 | * @param value character to be displayed |
wim | 3:e5af2e131b95 | 47 | * @return value |
wim | 3:e5af2e131b95 | 48 | */ |
wim | 3:e5af2e131b95 | 49 | int SWO_Channel::_putc(int value) { |
wim | 3:e5af2e131b95 | 50 | |
wim | 3:e5af2e131b95 | 51 | //Use CMSIS_core_DebugFunctions. See core_cm3.h |
wim | 3:e5af2e131b95 | 52 | ITM_SendChar(value); |
wim | 3:e5af2e131b95 | 53 | |
wim | 3:e5af2e131b95 | 54 | return value; |
wim | 3:e5af2e131b95 | 55 | } |
wim | 3:e5af2e131b95 | 56 | |
wim | 3:e5af2e131b95 | 57 | /** Get a single character (Stream implementation) |
wim | 3:e5af2e131b95 | 58 | * @return -1 Not supported |
wim | 3:e5af2e131b95 | 59 | */ |
wim | 3:e5af2e131b95 | 60 | int SWO_Channel::_getc() { |
wim | 3:e5af2e131b95 | 61 | return -1; |
wim | 3:e5af2e131b95 | 62 | } |
wim | 3:e5af2e131b95 | 63 | |
wim | 4:53de8ef789f3 | 64 | /** |
wim | 4:53de8ef789f3 | 65 | * Claim and redirect a stream to this SWO object |
wim | 4:53de8ef789f3 | 66 | * Important: A name parameter must have been added when creating the SWO object. |
wim | 4:53de8ef789f3 | 67 | * |
wim | 4:53de8ef789f3 | 68 | * @param FILE *stream The stream to redirect (default = stdout) |
wim | 4:53de8ef789f3 | 69 | * @return true if succeeded, else false |
wim | 4:53de8ef789f3 | 70 | */ |
wim | 4:53de8ef789f3 | 71 | bool SWO_Channel::claim (FILE *stream) { |
wim | 4:53de8ef789f3 | 72 | if ( FileBase::getName() == NULL) { |
wim | 4:53de8ef789f3 | 73 | error("claim requires a name to be given in the instantiator of the SWO instance!\r\n"); |
wim | 4:53de8ef789f3 | 74 | } |
wim | 4:53de8ef789f3 | 75 | |
wim | 4:53de8ef789f3 | 76 | //Add '/' before name: |
wim | 4:53de8ef789f3 | 77 | char *path = new char[strlen(FileBase::getName()) + 2]; |
wim | 4:53de8ef789f3 | 78 | sprintf(path, "/%s", FileBase::getName()); |
wim | 4:53de8ef789f3 | 79 | |
wim | 4:53de8ef789f3 | 80 | if (freopen(path, "w", stream) == NULL) { |
wim | 4:53de8ef789f3 | 81 | // Failed, should not happen |
wim | 4:53de8ef789f3 | 82 | return false; |
wim | 4:53de8ef789f3 | 83 | } |
wim | 4:53de8ef789f3 | 84 | |
wim | 4:53de8ef789f3 | 85 | delete(path); |
wim | 4:53de8ef789f3 | 86 | |
wim | 4:53de8ef789f3 | 87 | //No buffering |
wim | 4:53de8ef789f3 | 88 | setvbuf(stream, NULL, _IONBF, 32); |
wim | 4:53de8ef789f3 | 89 | return true; |
wim | 4:53de8ef789f3 | 90 | } |
wim | 4:53de8ef789f3 | 91 | |
wim | 4:53de8ef789f3 | 92 | |
wim | 3:e5af2e131b95 | 93 | |
wim | 3:e5af2e131b95 | 94 | // |
wim | 3:e5af2e131b95 | 95 | //This is the classic implementation |
wim | 3:e5af2e131b95 | 96 | // |
wim | 3:e5af2e131b95 | 97 | |
wim | 1:bae4cff278f6 | 98 | /** |
wim | 1:bae4cff278f6 | 99 | * Defines for Cortex-M debug unit |
wim | 1:bae4cff278f6 | 100 | */ |
wim | 3:e5af2e131b95 | 101 | #define ITM_STIM_U32(n) (*(volatile unsigned int*) (0xE0000000+4*n)) // Stimulus Port n Register word access |
wim | 3:e5af2e131b95 | 102 | #define ITM_STIM_U8(n) (*(volatile unsigned char*)(0xE0000000+4*n)) // Stimulus Port n Register byte access |
wim | 0:0fd55660fc26 | 103 | //#define ITM_STIM_U32_0 (*(volatile unsigned int*)0xE0000000) // Stimulus Port 0 Register word access |
wim | 0:0fd55660fc26 | 104 | //#define ITM_STIM_U8_0 (*(volatile char*)0xE0000000) // Stimulus Port 0 Register byte access |
wim | 0:0fd55660fc26 | 105 | #define ITM_ENA (*(volatile unsigned int*)0xE0000E00) // Trace Enable Ports Register |
wim | 0:0fd55660fc26 | 106 | #define ITM_TCR (*(volatile unsigned int*)0xE0000E80) // Trace control register |
wim | 0:0fd55660fc26 | 107 | |
wim | 2:ef928f61a770 | 108 | #define ITM_STIM_FIFOREADY 0x00000001 // FIFO empty |
wim | 2:ef928f61a770 | 109 | |
wim | 2:ef928f61a770 | 110 | //Stuff below is for documentation and needs further testing |
wim | 2:ef928f61a770 | 111 | // It seems that the Segger SWO Viewer and the ST-Link Utility do most/all of these |
wim | 2:ef928f61a770 | 112 | // initialisations on the target before starting the session. This is probably not the case |
wim | 2:ef928f61a770 | 113 | // when using GDB/OpenOCD. |
wim | 2:ef928f61a770 | 114 | // |
wim | 2:ef928f61a770 | 115 | // |
wim | 2:ef928f61a770 | 116 | #if(0) |
wim | 2:ef928f61a770 | 117 | #include <libopencm3/stm32/rcc.h> |
wim | 2:ef928f61a770 | 118 | #include <libopencm3/stm32/gpio.h> |
wim | 2:ef928f61a770 | 119 | |
wim | 2:ef928f61a770 | 120 | #include <libopencm3/stm32/dbgmcu.h> |
wim | 2:ef928f61a770 | 121 | #include <libopencm3/cm3/scs.h> |
wim | 2:ef928f61a770 | 122 | #include <libopencm3/cm3/tpiu.h> |
wim | 2:ef928f61a770 | 123 | #include <libopencm3/cm3/itm.h> |
wim | 2:ef928f61a770 | 124 | |
wim | 1:bae4cff278f6 | 125 | /** |
wim | 2:ef928f61a770 | 126 | * SWO_Setup() Example |
wim | 1:bae4cff278f6 | 127 | * |
wim | 2:ef928f61a770 | 128 | * This file is part of the libopencm3 project. |
wim | 2:ef928f61a770 | 129 | * |
wim | 2:ef928f61a770 | 130 | * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de> |
wim | 2:ef928f61a770 | 131 | * https://github.com/1divf/libopenstm32/blob/master/examples/stm32/stm32-h103/traceswo/traceswo.c |
wim | 2:ef928f61a770 | 132 | * |
wim | 2:ef928f61a770 | 133 | */ |
wim | 2:ef928f61a770 | 134 | void SWO_Setup(void) { |
wim | 2:ef928f61a770 | 135 | /* Enable trace subsystem (we'll use ITM and TPIU) */ |
wim | 2:ef928f61a770 | 136 | SCS_DEMCR |= SCS_DEMCR_TRCENA; |
wim | 2:ef928f61a770 | 137 | |
wim | 2:ef928f61a770 | 138 | /* Use Manchester code for asynchronous transmission */ |
wim | 2:ef928f61a770 | 139 | TPIU_SPPR = TPIU_SPPR_ASYNC_MANCHESTER; |
wim | 2:ef928f61a770 | 140 | TPIU_ACPR = 7; |
wim | 2:ef928f61a770 | 141 | |
wim | 2:ef928f61a770 | 142 | /* Data width is 1 byte */ |
wim | 2:ef928f61a770 | 143 | TPIU_CSPSR = TPIU_CSPSR_BYTE; |
wim | 2:ef928f61a770 | 144 | |
wim | 2:ef928f61a770 | 145 | /* Formatter and flush control */ |
wim | 2:ef928f61a770 | 146 | TPIU_FFCR &= ~TPIU_FFCR_ENFCONT; |
wim | 2:ef928f61a770 | 147 | |
wim | 2:ef928f61a770 | 148 | /* Enable TRACESWO pin for async mode */ |
wim | 2:ef928f61a770 | 149 | DBGMCU_CR = DBGMCU_CR_TRACE_IOEN | DBGMCU_CR_TRACE_MODE_ASYNC; |
wim | 2:ef928f61a770 | 150 | |
wim | 2:ef928f61a770 | 151 | /* Unlock access to ITM registers */ |
wim | 2:ef928f61a770 | 152 | /* FIXME: Magic numbers... Is this Cortex-M3 generic? */ |
wim | 2:ef928f61a770 | 153 | *((volatile uint32_t*)0xE0000FB0) = 0xC5ACCE55; |
wim | 2:ef928f61a770 | 154 | |
wim | 2:ef928f61a770 | 155 | /* Enable ITM with ID = 1 */ |
wim | 2:ef928f61a770 | 156 | ITM_TCR = (1 << 16) | ITM_TCR_ITMENA; |
wim | 2:ef928f61a770 | 157 | /* Enable stimulus port 1 */ |
wim | 2:ef928f61a770 | 158 | ITM_TER[0] = 1; |
wim | 2:ef928f61a770 | 159 | } |
wim | 2:ef928f61a770 | 160 | |
wim | 2:ef928f61a770 | 161 | /** |
wim | 2:ef928f61a770 | 162 | * SWO_Setup() Example |
wim | 2:ef928f61a770 | 163 | * |
wim | 2:ef928f61a770 | 164 | * http://forum.segger.com/index.php?page=Thread&threadID=608 |
wim | 2:ef928f61a770 | 165 | * |
wim | 2:ef928f61a770 | 166 | */ |
wim | 2:ef928f61a770 | 167 | void SWO_Setup_1(void) { |
wim | 2:ef928f61a770 | 168 | U32 SWOPrescaler; |
wim | 2:ef928f61a770 | 169 | U32 SWOSpeed; |
wim | 2:ef928f61a770 | 170 | |
wim | 2:ef928f61a770 | 171 | //<Init PLL, set CPU clock to 72 MHz> // Optional, so I do not pos it here |
wim | 2:ef928f61a770 | 172 | |
wim | 2:ef928f61a770 | 173 | SWOSpeed = 6000000; |
wim | 2:ef928f61a770 | 174 | *((volatile unsigned *)0xE000EDFC) = 0x01000000; // "Debug Exception and Monitor Control Register (DEMCR)" |
wim | 2:ef928f61a770 | 175 | *((volatile unsigned *)0xE0042004) = 0x00000027; |
wim | 2:ef928f61a770 | 176 | *((volatile unsigned *)0xE00400F0) = 0x00000002; // "Selected PIN Protocol Register": Select which protocol to use for trace output (2: SWO) |
wim | 2:ef928f61a770 | 177 | SWOPrescaler = (72000000 / SWOSpeed) - 1; // SWOSpeed in Hz |
wim | 2:ef928f61a770 | 178 | *((volatile unsigned *)0xE0040010) = SWOPrescaler; // "Async Clock Prescaler Register". Scale the baud rate of the asynchronous output |
wim | 2:ef928f61a770 | 179 | *((volatile unsigned *)0xE0000FB0) = 0xC5ACCE55; // ITM Lock Access Register, C5ACCE55 enables more write access to Control Register 0xE00 :: 0xFFC |
wim | 2:ef928f61a770 | 180 | *((volatile unsigned *)0xE0000E80) = 0x0001000D; // ITM Trace Control Register |
wim | 2:ef928f61a770 | 181 | *((volatile unsigned *)0xE0000E40) = 0x0000000F; // ITM Trace Privilege Register |
wim | 2:ef928f61a770 | 182 | *((volatile unsigned *)0xE0000E00) = 0x00000001; // ITM Trace Enable Register. Enabled tracing on stimulus ports. One bit per stimulus port. |
wim | 2:ef928f61a770 | 183 | *((volatile unsigned *)0xE0001000) = 0x400003FE; // DWT_CTRL |
wim | 2:ef928f61a770 | 184 | *((volatile unsigned *)0xE0040304) = 0x00000100; // Formatter and Flush Control Register |
wim | 2:ef928f61a770 | 185 | } |
wim | 2:ef928f61a770 | 186 | #endif |
wim | 2:ef928f61a770 | 187 | |
wim | 2:ef928f61a770 | 188 | /** |
wim | 1:bae4cff278f6 | 189 | * SWO_PrintChar() |
wim | 1:bae4cff278f6 | 190 | * |
wim | 1:bae4cff278f6 | 191 | * @brief |
wim | 1:bae4cff278f6 | 192 | * Checks if SWO is set up. If it is not, return, |
wim | 1:bae4cff278f6 | 193 | * to avoid program hangs if no debugger is connected. |
wim | 1:bae4cff278f6 | 194 | * If it is set up, print a character to the ITM_STIM register |
wim | 1:bae4cff278f6 | 195 | * in order to provide data for SWO. |
wim | 1:bae4cff278f6 | 196 | * @param c The Character to be printed. |
wim | 1:bae4cff278f6 | 197 | * @notes Additional checks for device specific registers can be added. |
wim | 1:bae4cff278f6 | 198 | */ |
wim | 1:bae4cff278f6 | 199 | void SWO_PrintChar(char c) { |
wim | 0:0fd55660fc26 | 200 | |
wim | 2:ef928f61a770 | 201 | #if(1) |
wim | 2:ef928f61a770 | 202 | //Use CMSIS_core_DebugFunctions. See core_cm3.h |
wim | 2:ef928f61a770 | 203 | ITM_SendChar (c); |
wim | 2:ef928f61a770 | 204 | |
wim | 2:ef928f61a770 | 205 | #else |
wim | 2:ef928f61a770 | 206 | //Use Segger example. Basically same as CMSIS |
wim | 2:ef928f61a770 | 207 | |
wim | 0:0fd55660fc26 | 208 | // Check if ITM_TCR.ITMENA is set |
wim | 0:0fd55660fc26 | 209 | if ((ITM_TCR & 1) == 0) { |
wim | 0:0fd55660fc26 | 210 | return; |
wim | 0:0fd55660fc26 | 211 | } |
wim | 1:bae4cff278f6 | 212 | |
wim | 0:0fd55660fc26 | 213 | // Check if stimulus port is enabled |
wim | 0:0fd55660fc26 | 214 | if ((ITM_ENA & 1) == 0) { |
wim | 0:0fd55660fc26 | 215 | return; |
wim | 0:0fd55660fc26 | 216 | } |
wim | 0:0fd55660fc26 | 217 | |
wim | 1:bae4cff278f6 | 218 | // Wait until STIMx FIFO is ready, then send data |
wim | 2:ef928f61a770 | 219 | // while ((ITM_STIM_U8(0) & 1) == 0); |
wim | 2:ef928f61a770 | 220 | while (!(ITM_STIM_U8(0) & ITM_STIM_FIFOREADY)); |
wim | 1:bae4cff278f6 | 221 | ITM_STIM_U8(0) = c; |
wim | 2:ef928f61a770 | 222 | |
wim | 1:bae4cff278f6 | 223 | // while ((ITM_STIM_U32(0) & 1) == 0); |
wim | 1:bae4cff278f6 | 224 | // ITM_STIM_U32(0) = c; |
wim | 2:ef928f61a770 | 225 | |
wim | 2:ef928f61a770 | 226 | #endif |
wim | 0:0fd55660fc26 | 227 | } |
wim | 0:0fd55660fc26 | 228 | |
wim | 1:bae4cff278f6 | 229 | /** |
wim | 1:bae4cff278f6 | 230 | * SWO_PrintString() |
wim | 1:bae4cff278f6 | 231 | * |
wim | 1:bae4cff278f6 | 232 | * @brief Print a string via SWO. |
wim | 1:bae4cff278f6 | 233 | * @param *s The string to be printed. |
wim | 1:bae4cff278f6 | 234 | * |
wim | 1:bae4cff278f6 | 235 | */ |
wim | 0:0fd55660fc26 | 236 | void SWO_PrintString(const char *s) { |
wim | 1:bae4cff278f6 | 237 | |
wim | 1:bae4cff278f6 | 238 | // Print out characters until \0 |
wim | 0:0fd55660fc26 | 239 | while (*s) { |
wim | 0:0fd55660fc26 | 240 | SWO_PrintChar(*s++); |
wim | 0:0fd55660fc26 | 241 | } |
wim | 3:e5af2e131b95 | 242 | } |
wim | 3:e5af2e131b95 | 243 | |
wim | 3:e5af2e131b95 | 244 | |
wim | 3:e5af2e131b95 | 245 |