├── find.syncframes.sh ├── captured-traces ├── winstlink-connect-again.pcapng ├── winstlink-connect-poweron.pcapng ├── win-stlink.pressing.run.target.pcapng ├── windows-stlink-dev72.startswo.stopswo.pcapng ├── windows-stlink-dev106.start.stop.start.stop.pcapng ├── winstlink-runcore.txt ├── winstlink-connect-again.txt ├── winstlink-startswv.txt └── winstlink-connect-poweron.txt ├── combiner2csv.py ├── README ├── magic.py ├── swodecoder.py ├── stlink2.ws.lua ├── itmdump.c └── hack.py /find.syncframes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | perl -ln0777e 'print unpack("H*",$1), "\n", pos() while /(.....\0\0\0\0\0\x80.....)/g' $1 3 | -------------------------------------------------------------------------------- /captured-traces/winstlink-connect-again.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karlp/swopy/HEAD/captured-traces/winstlink-connect-again.pcapng -------------------------------------------------------------------------------- /captured-traces/winstlink-connect-poweron.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karlp/swopy/HEAD/captured-traces/winstlink-connect-poweron.pcapng -------------------------------------------------------------------------------- /captured-traces/win-stlink.pressing.run.target.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karlp/swopy/HEAD/captured-traces/win-stlink.pressing.run.target.pcapng -------------------------------------------------------------------------------- /captured-traces/windows-stlink-dev72.startswo.stopswo.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karlp/swopy/HEAD/captured-traces/windows-stlink-dev72.startswo.stopswo.pcapng -------------------------------------------------------------------------------- /captured-traces/windows-stlink-dev106.start.stop.start.stop.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karlp/swopy/HEAD/captured-traces/windows-stlink-dev106.start.stop.start.stop.pcapng -------------------------------------------------------------------------------- /captured-traces/winstlink-runcore.txt: -------------------------------------------------------------------------------- 1 | 7553 72.831264000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe000edf0 => 2690580481 (0xa05f0001) 2 | 7557 72.835270000 host 122.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000edf0 => 0x01010001 3 | 7561 72.840257000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0x20000000 @ 4 => 0x016e3600 4 | magic_sync 5 | 7573 72.848288000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0x40023c1c @ 4 => 0x007800aa 6 | magic_sync 7 | 7581 72.856292000 host 122.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000edf0 => 0x01010001 8 | 9 | -------------------------------------------------------------------------------- /combiner2csv.py: -------------------------------------------------------------------------------- 1 | # Combine swodecoder file into a csv, 2 | # in this case, reading two lines at a time, and just keeping the _integer_ 3 | # portion. You NEED to know what your files look like before using this 4 | # AND you need to know how your ITM writes are scheduled! 5 | import sys 6 | 7 | def go(ifname): 8 | """ 9 | read from iff, write to stdout 10 | """ 11 | #off = open(ofname, "wb") 12 | with open(ifname) as iff: 13 | 14 | for i,l1 in enumerate(iff): 15 | l2 = iff.next() 16 | l1_vals = l1.split(":") 17 | #print("l1", l1_vals) 18 | l2_vals = l2.split(":") 19 | #print("l2", l2_vals) 20 | print("{},{},{}".format(i, l1_vals[4].strip(), l2_vals[4].strip())) 21 | 22 | 23 | if __name__ == "__main__": 24 | go(sys.argv[1]) 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | stlink swo usb capture tool 2 | 3 | heh ;) it works :) 4 | 5 | Dependencies 6 | ============ 7 | pyusb >= 1.x (Tested with 1.0.0-0.4.a2.fc18) 8 | (you have to work out udev rules yourself....) 9 | 10 | Basic Usage 11 | =========== 12 | 13 | python hack.py 14 | connect 15 | swo_file blahblahblah.log 16 | swo_start stimulusbitmask 17 | .... 18 | swo_start otherstimulusmask 19 | ... 20 | exit (or ^d) 21 | 22 | 23 | in another window: 24 | 25 | # to see everything... 26 | python swodecoder.py -f blahblahblah.log 27 | 28 | # to see just stimulus port 2 29 | python swodecoder.py -f blahblahblah.log -a 2 30 | 31 | 32 | Tips 33 | ==== 34 | You _probably_ will want to edit swodecoder.py, in the PacketParser's 35 | target parameter, if you want to change the output formatting. 36 | 37 | http://false.ekta.is/2013/12/using-swoswv-streaming-data-with-stlink-under-linux-part-2/ 38 | 39 | Bugs 40 | ==== 41 | The STLink firmware gets into some screwy states sometimes, you need to 42 | unplug/replug to get it back. Boo :( (This is a bug in swopy's USB handling) 43 | 44 | After streaming for a while, stably writing 40-60KB/sec, and seeing consistent 45 | 300-800bytes SWO data per frame, you'll see the STLink report that it has ~64KB 46 | to dump, and then it gets a USB Transfer error and locks up. Boo :( As best I 47 | can tell that's a firmware bug on the STLink, there's no field I can tell in 48 | the USB responses to indicate there's any sort of overflow) 49 | 50 | Sometimes the stimulus bits are weird. Writing to bits that _should_ turn on 51 | the channel you want doesn't. Boo :( No idea what's going on there. 52 | -------------------------------------------------------------------------------- /captured-traces/winstlink-connect-again.txt: -------------------------------------------------------------------------------- 1 | # had already connected once, tried to connect again 2 | GET VERSION (always first) 3 | GET MODE => 2 4 | GET VOLTAGE 5 | ENTER SWD 6 | 10 99.182781000 host 121.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe0042008 => 4096 (0x00001000) 7 | 12 99.186770000 host 121.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe0042004 => 263 (0x00000107) 8 | 14 99.190804000 host 121.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe000edf0 => 2690580483 (0xa05f0003) 9 | 16 99.194795000 host 121.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000edf0 = > 0x01030003 10 | 18 99.198768000 host 121.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe000edfc => 1 (0x00000001) 11 | 20 99.202787000 host 121.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe000ed0c => 100270084 (0x05fa0004) 12 | 22 99.206749000 host 121.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000ed0c => 0xfa050000 13 | 24 99.210763000 host 121.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000edf0 => 0x02030003 14 | 26 99.214807000 host 121.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000edf0 => 0x00030003 15 | 28 99.218762000 host 121.2 STLinkv2 80 DEBUG COMMAND - WRITE REG (r1 = 1?!) 16 | 30 99.222846000 host 121.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe000edfc => 0 (0000000000) 17 | 32 99.357854000 host 121.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0xe000ed00 @ 4 => 0x412fc230 18 | magic_sync 19 | 36 99.365815000 host 121.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0xe0042000 @ 4 => 0x10186416 20 | magic_sync 21 | 40 99.373786000 host 121.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0xe0042000 @ 4 => 0x10186416 22 | magic_sync 23 | 44 99.381766000 host 121.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0x1ff8004c @ 4 => 0x00000080 24 | magic_sync 25 | GET VOLTAGE 26 | -- exactly the same as the other one at this point, entirely. Just differences in exit dfu mode call. 27 | -------------------------------------------------------------------------------- /magic.py: -------------------------------------------------------------------------------- 1 | __author__ = 'karlp' 2 | # All magic numbers related to stlink, arm and stm32 3 | 4 | STLINK_CMD_SIZE_V2 = 16 5 | 6 | STLINK_GET_VERSION = 0xF1 7 | STLINK_DEBUG_COMMAND = 0xF2 8 | STLINK_DFU_COMMAND = 0xF3 9 | STLINK_SWIM_COMMAND = 0xF4 10 | STLINK_GET_CURRENT_MODE = 0xF5 11 | STLINK_GET_TARGET_VOLTAGE = 0xF7 12 | 13 | STLINK_MODE_DFU = 0 14 | STLINK_MODE_MASS = 1 15 | STLINK_MODE_DEBUG = 2 16 | STLINK_MODE_SWIM = 3 17 | STLINK_MODE_BOOTLOADER = 4 18 | 19 | STLINK_DFU_EXIT = 0x7 20 | 21 | STLINK_DEBUG_STATUS = 0x01 22 | STLINK_DEBUG_RESETSYS = 0x03 # apiv1 from stlink-texane?! 23 | STLINK_DEBUG_READMEM32 = 0x07 24 | STLINK_DEBUG_WRITEMEM32 = 0x08 25 | STLINK_DEBUG_RUNCORE = 0x09 26 | STLINK_DEBUG_ENTER_SWD = 0xa3 27 | STLINK_DEBUG_APIV2_ENTER = 0x30 28 | STLINK_DEBUG_APIV2_RESETSYS = 0x32 29 | STLINK_DEBUG_APIV2_READREG = 0x33 30 | STLINK_DEBUG_APIV2_WRITEREG = 0x34 31 | STLINK_DEBUG_APIV2_WRITEDEBUGREG = 0x35 32 | STLINK_DEBUG_APIV2_READDEBUGREG = 0x36 33 | STLINK_DEBUG_UNKNOWN_MAYBE_SYNC = 0x3e 34 | STLINK_DEBUG_EXIT = 0x21 35 | STLINK_DEBUG_APIV2_START_TRACE_RX = 0x40 36 | STLINK_DEBUG_APIV2_STOP_TRACE_RX = 0x41 37 | STLINK_DEBUG_APIV2_GET_TRACE_NB = 0x42 38 | 39 | 40 | STLINK_EP_TRACE = 0x83 41 | STLINK_EP_TX = 0x2 42 | STLINK_EP_RX = 0x81 43 | 44 | 45 | # ARM STUFF 46 | SCS_LAR_KEY = 0xC5ACCE55 47 | SCS_AIRCR = 0xe000ed0c 48 | SCS_AIRCR_KEY = (0x05fa << 16) 49 | SCS_AIRCR_VECTCLRACTIVE = (1<<1) 50 | 51 | DCB_DEMCR = 0xE000EDFC 52 | DCB_DEMCR_TRCENA = (1<<24) 53 | DCB_DEMCR_VC_CORERESET = (1<<0) # Enable Reset Vector Catch. This causes a Local reset to halt a running system. 54 | 55 | DCB_DHCSR = 0xE000EDF0 56 | DCB_DHCSR_DBGKEY = (0xA05F << 16) 57 | DCB_DHCSR_C_DEBUGEN = (1<<0) 58 | DCB_DHCSR_C_HALT = (1<<1) 59 | 60 | TPIU_CSPSR = 0xe0040004 61 | TPIU_ACPR = 0xE0040010 62 | TPIU_SPPR = 0xE00400F0 63 | TPIU_FFCR = 0xE0040304 64 | TPIU_SPPR_TXMODE_PARALELL = 0 65 | TPIU_SPPR_TXMODE_MANCHESTER = 1 66 | TPIU_SPPR_TXMODE_NRZ = 2 67 | 68 | ITM_LAR = 0xe0000fb0 69 | ITM_TER = 0xe0000e00 70 | ITM_TPR = 0xe0000e40 71 | ITM_TCR = 0xe0000e80 72 | ITM_TCR_SWOENA = (1 << 4) 73 | ITM_TCR_TXENA = (1 << 3) 74 | ITM_TCR_SYNCENA = (1 << 2) 75 | ITM_TCR_TSENA = (1 << 1) 76 | ITM_TCR_ITMENA = (1 << 0) 77 | 78 | DWT_CTRL = 0xE0001000 79 | 80 | # STM32 stuff 81 | DBGMCU_CR = 0xe0042004 82 | DBGMCU_CR_DEBUG_SLEEP = (1<<0) 83 | DBGMCU_CR_DEBUG_STOP = (1<<1) 84 | DBGMCU_CR_DEBUG_STANDBY = (1<<2) 85 | DBGMCU_CR_DEBUG_TRACE_IOEN = (1<<5) 86 | DBGMCU_CR_RESERVED_MAGIC_UNKNOWN = (1<<8) 87 | DBGMCU_APB1_FZ = 0xe0042008 88 | DBGMCU_APB1_FZ_DBG_IWDG_STOP = (1<<12) 89 | 90 | -------------------------------------------------------------------------------- /captured-traces/winstlink-startswv.txt: -------------------------------------------------------------------------------- 1 | 24meg, stimulus 0 2 | 3 | 48895 616.225850000 host 122.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000edf0 => 0x10180080 4 | 48899 616.246857000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0xe000edfc @ 4 => 0x01000000 | DEMCR = TRACE ENABLE 5 | magic_sync 6 | 48907 616.254888000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0x20000000 @ 4 => 0x016e3600 7 | magic_sync 8 | 48917 616.262839000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0xe0042004 @ 4 => 0x00000127 9 | magic_sync 10 | 48925 616.270883000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0xe0042004 @ 4 => 0x00000127 | DBGMCU_CR turn on debug in LP 11 | magic_sync 12 | 48933 616.278833000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0xe0040004 @ 4 => 0x00000001 | TPIU_CSPSR = 8bit wide trace 13 | magic_sync 14 | 48941 616.286855000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0xe0040010 @ 4 => 0x0000000b | TPIU_ACPR = async prescalar 15 | magic_sync 16 | STOP TRACE 17 | START TRACE 4096 @ 2000000 18 | 48959 616.302846000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0xe00400f0 @ 4 => 0x00000002 | TPIU_SPSR = NRZ 19 | magic_sync 20 | 48967 616.310909000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0xe0040304 @ 4 => 0 | TPIU_FFCR = no sync flush 21 | magic_sync 22 | 48975 616.318852000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0xe0000fb0 @ 4 => 0xc5acce55 | ITM_LAR = LAR_KEY 23 | magic_sync 24 | xxxxx 616.xxxxxxxxx host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0xe0000e80 @ 4 => 0x00010005 | ITM_TCR = chan1 + sync + itm_en 25 | magic_sync 26 | 48991 616.334865000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0xe0000e00 @ 4 => 0x00000001 | ITM_TER = 1 27 | magic_sync 28 | 49001 616.342854000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0xe0000e40 @ 4 => 0x00000001 | ITM_TPR = 1 29 | magic_sync 30 | 49009 616.350879000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0x20000000 @ 4 31 | magic_sync 32 | 49017 616.358832000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0xe0001000 @ 4 => 0x40000001 | DWT_CTRL 33 | magic_sync 34 | 49025 616.367844000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0xe0001000 @ 4 => 0x40000401 | DWT_CTRL |= sync tap = 1 35 | magic_sync 36 | 49033 616.376893000 host 122.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000edf0 => 0x01010001 37 | GET TRACE COUNT = 6 38 | SWO DATA 39 | 49043 616.437857000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0xe0042000 @ 4 => 0x10186416 40 | magic_sync 41 | GET TRACE COUNT = 0 42 | GET TRACE COUNT = 0 43 | GET TRACE COUNT = 2 44 | SWO DATA 45 | reread chipid 46 | magic_sync 47 | GET TRACE COUNT = 0 48 | GET TRACE COUNT = 0 49 | GET TRACE COUNT = 6 50 | SWO DATA 51 | read chipid 52 | magic_sync 53 | GET TRACE COUNT = 0 54 | GET TRACE COUNT = 0 55 | GET TRACE COUNT = 0 56 | GET TRACE COUNT = 0 57 | GET TRACE COUNT = 0 58 | read chipid 59 | magic_sync 60 | GET TRACEOUNT = 6 61 | SWO DATA 62 | GET TRACE COUNT = 2 63 | SWO DATA 64 | read chipid 65 | magic_sync 66 | GET TRACE COUNT 67 | blah blah blah 68 | STOP TRACE 69 | read chipid, | 70 | magic_sync |- * 5 71 | STOP TRACE 72 | more chipid read+ magic sync 73 | finally a 74 | 50951 631.101493000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe000edf0 => 2690580483 (0xa05f0003) 75 | 76 | -------------------------------------------------------------------------------- /captured-traces/winstlink-connect-poweron.txt: -------------------------------------------------------------------------------- 1 | ## When it was power cycled, came up in mode 1 2 | DFU EXIT 3 | GET VOLTAGE 4 | ENTER SWD 5 | 10 0.025010000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe0042008 => 4096 (0x00001000) 6 | 12 0.028957000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe0042004 => 263 (0x00000107) 7 | 14 0.032962000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe000edf0 => 2690580483 (0xa05f0003) 8 | 16 0.036953000 host 122.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000edf0 => 0x03030003 9 | 18 0.040999000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe000edf0 => 2690580483 (0xa05f0003) 10 | 20 0.046965000 host 122.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000edf0 => 0x00030003 11 | 22 0.052136000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe000edfc => 1 (0x00000001) 12 | 24 0.056007000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe000ed0c => 100270084 (0x05fa0004) 13 | 26 0.059945000 host 122.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000ed0c => 0xfa050000 14 | 28 0.064018000 host 122.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000edf0 => 0x02030003 15 | 30 0.067949000 host 122.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000edf0 => 0x00030003 16 | 32 0.071993000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITE REG (r1 = 1?!) 17 | 34 0.076803000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe000edfc => 0 (0000000000) 18 | 36 0.212811000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0xe000ed00 @ 4 => 0x412fc230 19 | magic_sync 20 | 40 0.220839000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0xe0042000 @ 4 => 0x10186416 21 | magic_sync 22 | 44 0.228835000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0xe0042000 @ 4 => 0x10186416 23 | magic_sync 24 | 48 0.236848000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0x1ff8004c @ 4 => 0x00000080 25 | magic_sync 26 | GET VOLTAGE 27 | 54 0.249786000 host 122.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000edf0 => 0x00030003 28 | 56 0.270792000 host 122.2 STLinkv2 80 DEBUG COMMAND - READ DEBUG REG 0xe000edf0 => 0x00030003 29 | 58 0.293805000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0x40003000 @ 4 => 0x0000aaaa 30 | magic_sync 31 | 62 0.301787000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0x40003000 @ 4 => 0x00005555 32 | magic_sync 33 | 66 0.310662000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITEMEM32 0x40003004 @ 2 => 0x00000006 34 | magic_sync 35 | 70 0.318994000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0x08000000 @ 4092 ## flash display pane in stlink 36 | magic_sync 37 | 75 through 85 is the same sequence of kick IWDG, unlock, set prescalar to max 38 | 87 0.437043000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0x08000ffc @ 4 => 0x99119e11 39 | magic_sync 40 | 91 throught 102 same sequence again 41 | 103 1.271006000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0xe0042000 @ 4 => 0x10186416 42 | magic sync 43 | 107 1.689998000 host 122.2 STLinkv2 80 DEBUG COMMAND - READMEM32 0xe0042000 @ 4 => 0x10186416 44 | magic_sync 45 | GET MODE == returns 2 46 | 113 2.112995000 host 122.2 STLinkv2 80 DEBUG COMMAND - WRITE DEBUG REG 0xe000edf0 => 2690580480 (0xa05f0000) 47 | 115 2.117013000 host 122.2 STLinkv2 80 DEBUG COMMAND - nil (0xf2 0x21 LEAVE DEBUG) 48 | 49 | -------------------------------------------------------------------------------- /swodecoder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | import argparse 4 | import time 5 | import io 6 | import os 7 | import sys 8 | import struct 9 | 10 | class ITMDWTPacket(): 11 | pass 12 | 13 | class SynchroPacket(ITMDWTPacket): 14 | def __repr__(self): 15 | return "SynchroPacket" 16 | 17 | class OverflowPacket(ITMDWTPacket): 18 | def __repr__(self): 19 | return "OverflowPacket" 20 | 21 | class SourcePacket(ITMDWTPacket): 22 | def __init__(self, address, source, size, data): 23 | self.address = address 24 | self.source = source 25 | self.size = size 26 | if size == 1: 27 | self.data = data[0] 28 | elif size == 2: 29 | d = bytearray(data) 30 | self.data = struct.unpack_from("> 3 109 | source = (b & 0x4) >> 2 110 | plen = b & 0x3 111 | rlen = zip([0, 1,2,3], [0, 1,2,4])[plen][1] # 1,2,4 byte mappings 112 | data = [] 113 | for x in range(rlen): 114 | b = yield 115 | data.append(b) 116 | ss = SourcePacket(address, source, rlen, data) 117 | target.send(ss) 118 | 119 | 120 | 121 | 122 | 123 | @coroutine 124 | def InsaneVerbosePacketReceiver(): 125 | """ A simple co-routine "sink" for receiving 126 | full frames. 127 | """ 128 | while True: 129 | frame = (yield) 130 | print("Got frame: %s" % frame) 131 | 132 | @coroutine 133 | def PacketReceiverConsolePrinter(valid_address=-1): 134 | while True: 135 | f = yield 136 | if not hasattr(f, "address"): 137 | # Skip things like synchro packets 138 | continue 139 | if f.address == valid_address or valid_address == -1: 140 | if (f.size == 1): 141 | print(chr(f.data), end='') 142 | else: 143 | print("Channel %d: %d byte value: %d : %#x : %d" % (f.address, f.size, f.data, f.data, f.sdata)) 144 | 145 | 146 | 147 | def demodemo(): 148 | data = """37 01 38 01 39 01 30 01 31 01 32 01 33 01 34 01 149 | 35 00 00 00 00 00 80 01 36 01 37 01 38 01 39 01 150 | 30 01 31 01 32 01 33 01 34 01 35 00 00 00 00 00 151 | 80 01 36 01 37 01 38 01 39 01 30 01 31 01 32 01""" 152 | data = data.split() 153 | dd = [int(x, base=16) for x in data] 154 | 155 | def chunker(ll, n): 156 | for i in range(0, len(ll), n): 157 | yield ll[i:i+n] 158 | 159 | chunks = [chunk for chunk in chunker(dd, 10)] 160 | 161 | parser = PacketParser(target=InsaneVerbosePacketReceiver()) 162 | 163 | for c in chunks: ## aka, while we read a file.... 164 | for b in c: 165 | parser.send(b) 166 | 167 | if __name__ == "__main__": 168 | ap = argparse.ArgumentParser() 169 | ap.add_argument('file', type=argparse.FileType('rb', 0), help="swo binary output file to parse", default="-") 170 | ap.add_argument("--address", "-a", type=int, default=-1, help="which channels to print, -1 for all") 171 | ap.add_argument("--follow", "-f", action="store_true", help="Seek to the 1024 bytes before the end of file first!", default=False) 172 | opts = ap.parse_args() 173 | 174 | parser = PacketParser(target=PacketReceiverConsolePrinter(opts.address)) 175 | 176 | with opts.file: 177 | #print("file pos = ", opts.file.tell()) 178 | if opts.follow: 179 | opts.file.seek(0, os.SEEK_END) 180 | size = opts.file.tell() 181 | if size > 1024: 182 | print("Jumping to the near the end") 183 | opts.file.seek(-1024, os.SEEK_END) 184 | else: 185 | opts.file.seek(0) 186 | bb = opts.file.read(1024) 187 | 188 | while True: 189 | #print("file pos = ", opts.file.tell()) 190 | if len(bb): 191 | [parser.send(ord(b)) for b in bb] 192 | else: 193 | if opts.follow: 194 | time.sleep(0.5) 195 | else: 196 | print("# All finished!") 197 | break 198 | 199 | bb = opts.file.read(1024) 200 | -------------------------------------------------------------------------------- /stlink2.ws.lua: -------------------------------------------------------------------------------- 1 | stlinkv2_proto = Proto("stlinkv2", "STLink/V2 api") 2 | 3 | local top_funcs = { 4 | [0xf1] = "GET VERSION", 5 | [0xf2] = "DEBUG COMMAND", 6 | [0xf3] = "DFU COMMAND", 7 | [0xf4] = "SWIM COMMAND", 8 | [0xf5] = "GET MODE", 9 | [0xf7] = "GET VOLTAGE" 10 | } 11 | 12 | local debug_command_funcs = { 13 | [0x7] = "READMEM32", 14 | [0x8] = "WRITEMEM32", 15 | [0x21] = "Exit Debug", 16 | [0x30] = "ENTER", 17 | [0x33] = "READ REG", 18 | [0x34] = "WRITE REG", 19 | [0x35] = "WRITE DEBUG REG", 20 | [0x36] = "READ DEBUG REG", 21 | [0x3a] = "Read all registers", 22 | [0x3e] = "UNKNOWN MAGIC SYNC", 23 | [0x40] = "Start Trace", 24 | [0x41] = "Stop Trace", 25 | [0x42] = "Get Trace Count" 26 | } 27 | 28 | local command_enter_funcs = { 29 | [0x00] = "Enter JTAG", 30 | [0xa3] = "Enter SWD" 31 | } 32 | 33 | local response_codes = { 34 | [0x80] = "OK" 35 | } 36 | 37 | local f = stlinkv2_proto.fields 38 | f.f_tfunc = ProtoField.uint8("stlinkv2.function", "Function", base.HEX, top_funcs) 39 | f.f_dfunc = ProtoField.uint8("stlinkv2.debug.command", "Debug Command", base.HEX, debug_command_funcs) 40 | f.f_dsubfunc = ProtoField.uint8("stlinkv2.debug.subcommand", "Debug subcommand", base.HEX, command_enter_funcs) 41 | f.f_addr = ProtoField.uint32("stlinkv2.addr", "Address", base.HEX) 42 | f.f_value = ProtoField.uint32("stlinkv2.value", "Value", base.HEX) 43 | f.f_length = ProtoField.uint16("stlinkv2.length", "Length", base.DEC) 44 | f.f_unknown = ProtoField.uint16("stlinkv2.unknown", "unknown", base.HEX) 45 | f.f_data = ProtoField.bytes("stlinkv2.data", "data") 46 | f.f_response_status = ProtoField.uint16("stlinkv2.response.status", "status", base.HEX, response_codes) 47 | f.f_trace_count = ProtoField.uint16("stlinkv2.trace.count", "available", base.DEC) 48 | f.f_trace_buff = ProtoField.uint16("stlinkv2.trace.buffsize", "buffsize", base.DEC) 49 | f.f_trace_hz = ProtoField.uint32("stlinkv2.trace.hz", "trace speed (hz)", base.DEC) 50 | 51 | local f_usb_ep_num = Field.new("usb.endpoint_number.endpoint") 52 | 53 | local function getstring(fi) 54 | local ok, val = pcall(tostring, fi) 55 | if not ok then val = "(unknown)" end 56 | return val 57 | end 58 | 59 | -- write32 doesn't have a response on the in endpoint, it tweaks decoding on the _out_ endpoint 60 | local responses = { 61 | NOTSET = 1, READMEM32 = 2, GENERIC = 3, READDEBUG = 4, 62 | WRITEMEM32 = 5, 63 | TRACECOUNT = 6 64 | } 65 | 66 | local expected = responses.NOTSET 67 | 68 | function stlinkv2_proto.dissector(buffer, pinfo, tree) 69 | pinfo.cols["protocol"] = "STLinkv2" 70 | 71 | --[[ 72 | -- This was very helpful for working out the field names I could use with Field.new() 73 | local fields = { all_field_infos() } 74 | for ix, finfo in ipairs(fields) do 75 | print(string.format("ix=%d, finfo.name = %s, finfo.value=%s", ix, finfo.name, getstring(finfo))) 76 | end 77 | ]]-- 78 | 79 | -- create protocol tree 80 | local t_stlinkv2 = tree:add(stlinkv2_proto, buffer()) 81 | local offset = 0 82 | 83 | local function response_header(res) 84 | t_stlinkv2:add_le(f.f_response_status, res) 85 | -- TODO - this should use the response_codes map I think?! 86 | if res:le_uint() == 0x80 then 87 | pinfo.cols["info"]:append(" OK") 88 | else 89 | pinfo.cols["info"]:append(" unknown?!" .. res.le_uint()) 90 | end 91 | end 92 | 93 | -- response data on general IN endpoint 94 | local ep = f_usb_ep_num() 95 | if (ep.value == 1) then 96 | pinfo.cols["info"] = "Response" 97 | if expected == responses.GENERIC then 98 | local res = buffer(offset, 2) 99 | offset = offset + 2 100 | response_header(res) 101 | elseif expected == responses.READDEBUG then 102 | local res = buffer(offset, 2) 103 | response_header(res) 104 | t_stlinkv2:add_le(f.f_unknown, buffer(offset + 2, 2)) 105 | local val = buffer(offset + 4, 4) 106 | t_stlinkv2:add_le(f.f_value, val) 107 | pinfo.cols["info"]:append(string.format(" ==> %#010x", val:le_uint())) 108 | offset = offset + 8 109 | elseif expected == responses.READMEM32 then 110 | -- FIXME - we only handle decoding single word reads :( 111 | -- would need to save the count from the request? 112 | local val = buffer(offset, 4) 113 | t_stlinkv2:add_le(f.f_value, val) 114 | offset = offset + 4 115 | pinfo.cols["info"]:append(string.format(" ==> %#010x", val:le_uint())) 116 | elseif expected == responses.TRACECOUNT then 117 | local val = buffer(offset, 2) 118 | t_stlinkv2:add_le(f.f_trace_count, val) 119 | val = val:le_uint() 120 | offset = offset + 2 121 | pinfo.cols["info"]:append(string.format(" ==> %d (%#x) bytes", val, val)) 122 | else 123 | t_stlinkv2:add(f.f_data, buffer(offset)) 124 | end 125 | expected = nil 126 | return 127 | end 128 | 129 | -- swo input data 130 | if (ep.value == 3) then 131 | pinfo.cols["info"] = "SWO/SWV data output" 132 | t_stlinkv2:add(f.f_data, buffer(offset)) 133 | return 134 | end 135 | 136 | if (expected == responses.WRITEMEM32) then 137 | assert(ep.value == 2) 138 | -- FIXME - only works for single word writes! 139 | local value = buffer(offset, 4) 140 | t_stlinkv2:add_le(f.f_value, value) 141 | value = value:le_uint() 142 | pinfo.cols["info"]= string.format("Write out ==> %d (%#010x)", value, value) 143 | expected = nil 144 | return 145 | end 146 | 147 | local func_code = buffer(offset, 1) 148 | t_stlinkv2:add(f.f_tfunc, func_code) 149 | func_code = func_code:uint() 150 | offset = offset + 1 151 | -- set info column to function name 152 | pinfo.cols["info"] = top_funcs[func_code] 153 | 154 | if func_code == 0xf2 then 155 | tfunc = buffer(offset, 1) 156 | t_stlinkv2:add(f.f_dfunc, tfunc) 157 | tfunc = tfunc:uint() 158 | pinfo.cols["info"]:append(" - " .. tostring(debug_command_funcs[tfunc])) 159 | offset = offset + 1 160 | if tfunc == 0x35 then -- write debug reg 161 | local addr = buffer(offset, 4) 162 | local value = buffer(offset + 4, 4) 163 | t_stlinkv2:add_le(f.f_addr, addr) 164 | t_stlinkv2:add_le(f.f_value, value) 165 | local extra = string.format(" %#010x => %d (%#010x)", addr:le_uint(), value:le_uint(), value:le_uint()) 166 | pinfo.cols["info"]:append(extra) 167 | expected = responses.GENERIC 168 | offset = offset + 8 169 | elseif tfunc == 0x36 then -- read debug reg 170 | local addr = buffer(offset, 4) 171 | t_stlinkv2:add_le(f.f_addr, addr) 172 | pinfo.cols["info"]:append(string.format(" %#010x", addr:le_uint())) 173 | offset = offset + 4 174 | expected = responses.READDEBUG 175 | elseif tfunc == 0x07 then -- readmem32 176 | local addr = buffer(offset, 4) 177 | local length = buffer(offset + 4, 2) 178 | t_stlinkv2:add_le(f.f_addr, addr) 179 | t_stlinkv2:add_le(f.f_length, length) 180 | pinfo.cols["info"]:append(string.format(" %#010x @ %d", addr:le_uint(), length:le_uint())) 181 | offset = offset + 6 182 | expected = responses.READMEM32 183 | elseif tfunc == 0x08 then -- writemem32 184 | local addr = buffer(offset, 4) 185 | local length = buffer(offset + 4, 2) 186 | t_stlinkv2:add_le(f.f_addr, addr) 187 | t_stlinkv2:add_le(f.f_length, length) 188 | pinfo.cols["info"]:append(string.format(" %#010x @ %d", addr:le_uint(), length:le_uint())) 189 | offset = offset + 6 190 | expected = responses.WRITEMEM32 191 | elseif tfunc == 0x40 then -- start trace 192 | local buffsize = buffer(offset, 2) 193 | local hz = buffer(offset + 2, 4) 194 | t_stlinkv2:add_le(f.f_trace_buff, buffsize) 195 | t_stlinkv2:add_le(f.f_trace_hz, hz) 196 | offset = offset + 6 197 | expected = responses.GENERIC 198 | elseif tfunc == 0x41 then -- stoptrace 199 | expected = responses.GENERIC 200 | elseif tfunc == 0x42 then -- get trace count 201 | expected = responses.TRACECOUNT 202 | elseif tfunc == 0x30 then -- enter subcommand 203 | subfunc = buffer(offset, 1) 204 | t_stlinkv2:add(f.f_dsubfunc, subfunc) 205 | expected = responses.GENERIC 206 | offset = offset + 1 207 | else 208 | expected = nil 209 | end 210 | 211 | end 212 | t_stlinkv2:add(f.f_data, buffer(offset)) 213 | end 214 | 215 | usb_table = DissectorTable.get("usb.bulk") 216 | -- this is the vendor specific class, which is how the usb.bulk table is arranged. 217 | usb_table:add(0xff, stlinkv2_proto) 218 | -- this is the unknown class, which seems to happen with oocd?! 219 | usb_table:add(0xffff, stlinkv2_proto) 220 | -------------------------------------------------------------------------------- /itmdump.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 by David Brownell 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see ;. 16 | */ 17 | 18 | /* 19 | * Simple utility to parse and dump ARM Cortex-M3 SWO trace output. Once the 20 | * mechanisms work right, this information can be used for various purposes 21 | * including profiling (particularly easy for flat PC-sample profiles) and 22 | * for debugging. 23 | * 24 | * SWO is the Single Wire Output found on some ARM cores, most notably on the 25 | * Cortex-M3. It combines data from several sources: 26 | * 27 | * - Software trace (ITM): so-called "printf-style" application messaging 28 | * using "ITM stimulus ports"; and differential timestamps. 29 | * - Hardware trace (DWT): for profiling counters and comparator matches. 30 | * - TPIU may issue sync packets. 31 | * 32 | * The trace data format is defined in Appendix E, "Debug ITM and DWT packet 33 | * protocol", of the ARMv7-M Architecture Reference Manual (DDI 0403C). It 34 | * is a superset of the ITM data format from the Coresight TRM. 35 | * 36 | * The trace data has two encodings. The working assumption is that data 37 | * gets into this program using the UART encoding. 38 | */ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | 48 | /* Example ITM trace word (0xWWXXYYZZ) parsing for task events, sent 49 | * on port 31. 50 | * WWXX: event code (0..3 pre-assigned, 4..15 reserved) 51 | * YY: task priority 52 | * ZZ: task number 53 | * 54 | * NOTE that this specific encoding could be space-optimized; and that 55 | * trace data streams could also be history-sensitive. 56 | */ 57 | static void show_task(int port, unsigned data) 58 | { 59 | unsigned code = data >> 16; 60 | char buf[16]; 61 | 62 | switch (code) { 63 | case 0: 64 | strcpy(buf, "run"); 65 | break; 66 | case 1: 67 | strcpy(buf, "block"); 68 | break; 69 | case 2: 70 | strcpy(buf, "create"); 71 | break; 72 | case 3: 73 | strcpy(buf, "destroy"); 74 | break; 75 | /* 4..15 reserved for other infrastructure ops */ 76 | default: 77 | sprintf(buf, "code %d", code); 78 | break; 79 | } 80 | printf("TASK %d, pri %d: %s", 81 | (data >> 0) & 0xff, 82 | (data >> 8) & 0xff, 83 | buf); 84 | } 85 | 86 | static void show_reserved(FILE *f, char *label, int c) 87 | { 88 | unsigned i; 89 | 90 | printf("%s - %#02x", label, c); 91 | 92 | for (i = 0; (c & 0x80) && i < 4; i++) { 93 | c = fgetc(f); 94 | if (c == EOF) { 95 | printf("(ERROR %d - %s) ", errno, strerror(errno)); 96 | break; 97 | } 98 | printf(" %#02x", c); 99 | } 100 | 101 | printf("\n"); 102 | } 103 | 104 | static bool read_varlen(FILE *f, int c, unsigned *value) 105 | { 106 | unsigned size; 107 | unsigned char buf[4]; 108 | unsigned i; 109 | 110 | *value = 0; 111 | 112 | switch (c & 3) { 113 | case 3: 114 | size = 4; 115 | break; 116 | case 2: 117 | size = 2; 118 | break; 119 | case 1: 120 | size = 1; 121 | break; 122 | default: 123 | printf("INVALID SIZE\n"); 124 | return false; 125 | } 126 | 127 | memset(buf, 0, sizeof buf); 128 | if (fread(buf, 1, size, f) != size) 129 | goto err; 130 | 131 | *value = (buf[3] << 24) 132 | + (buf[2] << 16) 133 | + (buf[1] << 8) 134 | + (buf[0] << 0); 135 | return true; 136 | 137 | err: 138 | printf("(ERROR %d - %s)\n", errno, strerror(errno)); 139 | return; 140 | } 141 | 142 | static void show_hard(FILE *f, int c) 143 | { 144 | unsigned type = c >> 3; 145 | unsigned value; 146 | unsigned size; 147 | char *label; 148 | 149 | printf("DWT - type: %d, ", type); 150 | 151 | if (!read_varlen(f, c, &value)) 152 | return; 153 | printf("rawval: %#x, ", value); 154 | 155 | switch (type) { 156 | case 0: /* event counter wrapping */ 157 | printf("overflow %s%s%s%s%s%s", 158 | (value & (1 << 5)) ? "cyc " : "", 159 | (value & (1 << 4)) ? "fold " : "", 160 | (value & (1 << 3)) ? "lsu " : "", 161 | (value & (1 << 2)) ? "slp " : "", 162 | (value & (1 << 1)) ? "exc " : "", 163 | (value & (1 << 0)) ? "cpi " : ""); 164 | break; 165 | case 1: /* exception tracing */ 166 | switch (value >> 12) { 167 | case 1: 168 | label = "entry to"; 169 | break; 170 | case 2: 171 | label = "exit from"; 172 | break; 173 | case 3: 174 | label = "return to"; 175 | break; 176 | default: 177 | label = "?"; 178 | break; 179 | } 180 | printf("%s exception %d", label, value & 0x1ff); 181 | break; 182 | case 2: /* PC sampling */ 183 | if (c == 0x15) 184 | printf("PC - sleep"); 185 | else 186 | printf("PC - %#08x", value); 187 | break; 188 | case 8: /* data tracing, pc value */ 189 | case 10: 190 | case 12: 191 | case 14: 192 | printf("Data trace %d, PC %#08x", (c >> 4) & 3, value); 193 | /* optionally followed by data value */ 194 | break; 195 | case 9: /* data tracing, address offset */ 196 | case 11: 197 | case 13: 198 | case 15: 199 | printf("Data trace %d, address offset %#04x", 200 | (c >> 4) & 3, value); 201 | /* always followed by data value */ 202 | break; 203 | case 16 ... 23: /* data tracing, data value */ 204 | printf("Data trace %d, ", (c >> 4) & 3); 205 | label = (c & 0x8) ? "write" : "read"; 206 | switch (c & 3) { 207 | case 3: 208 | printf("word %s, value %#08x", label, value); 209 | break; 210 | case 2: 211 | printf("halfword %s, value %#04x", label, value); 212 | break; 213 | case 1: 214 | printf("byte %s, value %#02x", label, value); 215 | break; 216 | } 217 | break; 218 | default: 219 | printf("UNDEFINED"); 220 | break; 221 | } 222 | 223 | printf("\n"); 224 | return; 225 | } 226 | 227 | /* 228 | * Table of SWIT (SoftWare InstrumentTation) message dump formats, for 229 | * ITM port 0..31 application data. 230 | * 231 | * Eventually this should be customizable; all usage is application defined. 232 | * 233 | * REVISIT there can be up to 256 trace ports, via "ITM Extension" packets 234 | */ 235 | struct { 236 | int port; 237 | void (*show)(int port, unsigned data); 238 | } format[] = { 239 | { .port = 31, .show = show_task, }, 240 | }; 241 | 242 | static void show_swit(FILE *f, int c) 243 | { 244 | unsigned size; 245 | unsigned port = c >> 3; 246 | unsigned char buf[4]; 247 | unsigned value = 0; 248 | unsigned i; 249 | 250 | printf("SWIT %u - ", port); 251 | 252 | if (!read_varlen(f, c, &value)) 253 | return; 254 | printf("%#08x", value); 255 | 256 | for (i = 0; i <= sizeof(format) / sizeof(format[0]); i++) { 257 | if (format[i].port == port) { 258 | printf(", "); 259 | format[i].show(port, value); 260 | break; 261 | } 262 | } 263 | 264 | printf("\n"); 265 | return; 266 | 267 | err: 268 | printf("(ERROR %d - %s)\n", errno, strerror(errno)); 269 | return; 270 | } 271 | 272 | static void show_timestamp(FILE *f, int c) 273 | { 274 | unsigned counter = 0; 275 | char *label = ""; 276 | bool delayed = false; 277 | 278 | printf("TIMESTAMP - "); 279 | 280 | /* Format 2: header only */ 281 | if (!(c & 0x80)) { 282 | switch (c) { 283 | case 0: /* sync packet -- coding error! */ 284 | case 0x70: /* overflow -- ditto! */ 285 | printf("ERROR - %#02x\n", c); 286 | break; 287 | default: 288 | /* synchronous to ITM */ 289 | counter = c >> 4; 290 | goto done; 291 | } 292 | return; 293 | } 294 | 295 | /* Format 1: one to four bytes of data too */ 296 | switch (c) { 297 | default: 298 | label = ", reserved control\n"; 299 | break; 300 | case 0xc: 301 | /* synchronous to ITM */ 302 | break; 303 | case 0xd: 304 | label = ", timestamp delayed"; 305 | delayed = true; 306 | break; 307 | case 0xe: 308 | label = ", packet delayed"; 309 | delayed = true; 310 | break; 311 | case 0xf: 312 | label = ", packet and timetamp delayed"; 313 | delayed = true; 314 | break; 315 | } 316 | 317 | c = fgetc(f); 318 | if (c == EOF) 319 | goto err; 320 | counter = c & 0x7f; 321 | if (!(c & 0x80)) 322 | goto done; 323 | 324 | c = fgetc(f); 325 | if (c == EOF) 326 | goto err; 327 | counter |= (c & 0x7f) << 7; 328 | if (!(c & 0x80)) 329 | goto done; 330 | 331 | c = fgetc(f); 332 | if (c == EOF) 333 | goto err; 334 | counter |= (c & 0x7f) << 14; 335 | if (!(c & 0x80)) 336 | goto done; 337 | 338 | c = fgetc(f); 339 | if (c == EOF) 340 | goto err; 341 | counter |= (c & 0x7f) << 21; 342 | 343 | done: 344 | /* REVISIT should we try to convert from delta values? */ 345 | printf("+%u%s\n", counter, label); 346 | return; 347 | 348 | err: 349 | printf("(ERROR %d - %s) ", errno, strerror(errno)); 350 | goto done; 351 | } 352 | 353 | int main(int argc, char **argv) 354 | { 355 | FILE *f = stdin; 356 | int c; 357 | 358 | /* parse arguments */ 359 | while ((c = getopt(argc, argv, "f:")) != EOF) { 360 | switch (c) { 361 | case 'f': 362 | /* e.g. from UART connected to /dev/ttyUSB0 */ 363 | f = fopen(optarg, "r"); 364 | if (!f) { 365 | perror(optarg); 366 | return 1; 367 | } 368 | break; 369 | default: 370 | usage: 371 | fprintf(stderr, "usage: %s [-f input]", 372 | basename(argv[0])); 373 | return 1; 374 | } 375 | } 376 | 377 | /* Parse data ... records have a header then data bytes. 378 | * NOTE: we assume getc() deals in 8-bit bytes. 379 | */ 380 | bool overflow = false; 381 | 382 | while ((c = getc(f)) != EOF) { 383 | 384 | /* Sync packet ... 7 zeroes, 0x80 */ 385 | if (c == 0) { 386 | int i; 387 | 388 | for (i = 0; i < 6; i++) { 389 | c = fgetc(f); 390 | if (c == EOF) 391 | break; 392 | if (c != 0) 393 | goto bad_sync; 394 | } 395 | c = fgetc(f); 396 | if (c == 0x80) { 397 | printf("SYNC\n"); 398 | continue; 399 | } 400 | bad_sync: 401 | printf("BAD SYNC\n"); 402 | continue; 403 | } 404 | 405 | /* Overflow packet */ 406 | if (c == 0x70) { 407 | /* REVISIT later, report just what overflowed! 408 | * Timestamp and SWIT can happen. Non-ITM too? 409 | */ 410 | overflow = true; 411 | printf("OVERFLOW ...\n"); 412 | continue; 413 | } 414 | overflow = false; 415 | 416 | switch (c & 0x0f) { 417 | case 0x00: /* Timestamp */ 418 | show_timestamp(f, c); 419 | break; 420 | case 0x04: /* "Reserved" */ 421 | /* karl - could be global timestamp too */ 422 | show_reserved(f, "RESERVED", c); 423 | break; 424 | case 0x08: /* ITM Extension */ 425 | /* FIXME someday, handle these ... */ 426 | show_reserved(f, "ITM EXT", c); 427 | break; 428 | case 0x0c: /* DWT Extension */ 429 | show_reserved(f, "DWT EXT", c); 430 | break; 431 | default: 432 | if (c & 4) 433 | show_hard(f, c); 434 | else 435 | show_swit(f, c); 436 | break; 437 | } 438 | 439 | #if DEBUGGING_CORRUPT_FILES 440 | long pos = ftell(f); 441 | printf("offset now: %d(%#x)\n", pos, pos); 442 | #endif 443 | } 444 | 445 | return 0; 446 | } 447 | -------------------------------------------------------------------------------- /hack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Karl Palsson, November 2013 3 | # This tool is for capturing SWO output from a STLinkv2 4 | # Released under your choice of the BSD 2 Clause, Apache 2.0, 5 | # MIT, or ISC Licenses. 6 | 7 | import cmd 8 | import logging 9 | import struct 10 | import sys 11 | import threading 12 | import time 13 | 14 | import usb.core 15 | import usb.util 16 | 17 | from magic import * 18 | 19 | #DEFAULT_CPU_HZ = 32000000 20 | DEFAULT_CPU_HZ = 24000000 21 | 22 | logging.basicConfig(level=logging.DEBUG) 23 | 24 | lame_py = None 25 | def _lame_py_buffer_required(inp): 26 | return buffer(inp) 27 | 28 | def _lame_py_buffer_not_required(inp): 29 | return inp 30 | 31 | try: 32 | blob = [1,2,3,4] 33 | struct.unpack_from("cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; 63 | # h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_STOP_TRACE_RX; 64 | # res = stlink_usb_xfer(handle, h->databuf, 2); 65 | 66 | class STLinkVersion(): 67 | def __init__(self, blob): 68 | # blob = [36, 0, 131, 4, 72, 55] 69 | # Info : STLINK v2 JTAG v16 API v2 SWIM v0 VID 0x0483 PID 0x3748 70 | # woo, different byte ordering! 71 | ver = struct.unpack_from(">H", lame_py(blob[:2]))[0] 72 | self.vid, self.pid = struct.unpack_from("> 12) & 0x0f 75 | self.jtag_ver = (ver >> 6) & 0x3f 76 | self.swim_ver = ver & 0x3f 77 | self.api_ver = 1 78 | if self.jtag_ver > 11: 79 | self.api_ver = 2 80 | 81 | def __repr__(self): 82 | return "STLINK v%d JTAG v%d API v%d SWIM v%d, VID %#x PID %#x" % ( 83 | self.major_ver, 84 | self.jtag_ver, 85 | self.api_ver, 86 | self.swim_ver, 87 | self.vid, 88 | self.pid 89 | ) 90 | 91 | def stlink_pad(cmd): 92 | # Both actually seem to work.... 93 | #return cmd 94 | return stlink_pad_real(cmd) 95 | 96 | def stlink_pad_real(cmd): 97 | """ 98 | make a zero buffer and fill the command in on top of it. not very pretty :( 99 | """ 100 | msg = [0 for x in range(STLINK_CMD_SIZE_V2)] 101 | for i,x in enumerate(cmd): 102 | msg[i] = x 103 | return msg 104 | 105 | def xfer_normal_input(dev, cmd, expected_response_size, verbose=False): 106 | msg = stlink_pad(cmd) 107 | if verbose: 108 | print("Sending msg: ", msg) 109 | count = dev.write(STLINK_EP_TX, msg, 0) 110 | assert count == len(msg), "Failed to write cmd to usb" 111 | if expected_response_size: 112 | res = dev.read(STLINK_EP_RX, expected_response_size, 0) 113 | if verbose: 114 | print("Received: ", res) 115 | return res 116 | 117 | def xfer_send_only_raw(dev, data): 118 | count = dev.write(STLINK_EP_TX, data, 0) 119 | assert count == len(data), "Failed to write data to usb" 120 | 121 | def xfer_write_debug(dev, reg_addr, val): 122 | cmd = [STLINK_DEBUG_COMMAND, STLINK_DEBUG_APIV2_WRITEDEBUGREG] 123 | args = [ord(q) for q in struct.pack(" %d (%#08x) (res=%s)", reg_addr, val, val, res) 127 | #assert res == 128 | # Sometimes this fails: 129 | """ 130 | (Cmd) raw_write_debug_reg 0x2000000 1 131 | DEBUG:root:WRITE DEBUG 0x2000000 ==> 1 (0x000001) (res=array('B', [25, 0])) 132 | ('write debug returned: ', None) 133 | (Cmd) raw_write_debug_reg 0x2000000 1 134 | DEBUG:root:WRITE DEBUG 0x2000000 ==> 1 (0x000001) (res=array('B', [17, 0])) 135 | ('write debug returned: ', None) 136 | (Cmd) raw_read_mem32 0x20000000 4 137 | DEBUG:root:READMEM32 0x20000000/4 returned: ['0x3'] 138 | """ 139 | 140 | def xfer_read_debug(dev, reg_addr): 141 | cmd = [STLINK_DEBUG_COMMAND, STLINK_DEBUG_APIV2_READDEBUGREG] 142 | args = [ord(q) for q in struct.pack(" %d (%#08x) status=%#x, unknown=%#x", reg_addr, val, val, status, unknown) 147 | #assert status == 0x80, "failed to read debug reg?!" 148 | # yuck, sometimes status is 0x15 or 0x25 and it shows garbage. not sure what it means though? 149 | # do I need to send the sync shit? 150 | return val 151 | 152 | 153 | def xfer_read32(dev, reg_addr, count): 154 | """ 155 | count is in bytes! 156 | """ 157 | cmd = [STLINK_DEBUG_COMMAND, STLINK_DEBUG_READMEM32] 158 | args = [ord(q) for q in struct.pack(" %s", reg_addr, dlen, [hex(i) for i in data]) 174 | 175 | unknown_noop = False 176 | def xfer_unknown_sync(dev): 177 | if unknown_noop: 178 | return 179 | cmd = [STLINK_DEBUG_COMMAND, STLINK_DEBUG_UNKNOWN_MAYBE_SYNC] 180 | res = xfer_normal_input(dev, cmd, 12) 181 | print("magic unknownn sync returned: ", res) 182 | return res 183 | 184 | def get_version(dev): 185 | res = xfer_normal_input(dev, [STLINK_GET_VERSION, 0x80], 6) 186 | v = STLinkVersion(res) 187 | return v 188 | 189 | def get_voltage(dev): 190 | res = xfer_normal_input(dev, [STLINK_GET_TARGET_VOLTAGE], 8) 191 | adc0, adc1 = struct.unpack_from(" mode 1 212 | elif current == STLINK_MODE_DEBUG: 213 | logging.debug("Leaving debug mode") 214 | cmd = [STLINK_DEBUG_COMMAND, STLINK_DEBUG_EXIT] 215 | dev._swopy_mode = 1 # exit debug goes from mode 2 -> mode 1 216 | 217 | if cmd: 218 | xfer_normal_input(dev, cmd, 0) 219 | else: 220 | logging.debug("Ignoring mode we don't know how to leave/or need to leave") 221 | 222 | def enter_state_debug(dev): 223 | cmd = [STLINK_DEBUG_COMMAND, STLINK_DEBUG_APIV2_ENTER, STLINK_DEBUG_ENTER_SWD] 224 | res = xfer_normal_input(dev, cmd, 2) 225 | logging.debug("enter debug state returned: %s", res) 226 | # res[0] should be 0x80 227 | assert res[0] == 0x80, "enter state failed :(" 228 | dev._swopy_mode = 2 229 | 230 | def reset(dev): 231 | cmd = [STLINK_DEBUG_COMMAND, STLINK_DEBUG_RESETSYS] 232 | res = xfer_normal_input(dev, cmd, 2) 233 | logging.debug("reset returned: %s", res) 234 | 235 | def run(dev): 236 | # This doesn't start it running :( 237 | cmd = [STLINK_DEBUG_COMMAND, STLINK_DEBUG_RUNCORE] 238 | res = xfer_normal_input(dev, cmd, 2) 239 | logging.debug("Run returned %s", res) 240 | return res 241 | 242 | def status(dev): 243 | cmd = [STLINK_DEBUG_COMMAND, STLINK_DEBUG_STATUS] 244 | res = xfer_normal_input(dev, cmd, 2) 245 | print("status returned", res) 246 | # THis is NOT correct. it shows "RUNNING" when the core is halted :( 247 | if res[0] == 0x80: 248 | return "RUNNING" 249 | elif res[0] == 0x81: 250 | return "HALTED" 251 | else: 252 | return "UNKNOWN" 253 | 254 | def run2(dev): 255 | #stlink_usb_write_debug_reg(handle, DCB_DHCSR, DBGKEY|C_DEBUGEN); 256 | # f2 35 f0 ed 00 e0 01 00 5f a0 00 00 00 00 00 00 257 | # pressing "run" in the target state pane in stlink sends this 258 | xfer_write_debug(dev, DCB_DHCSR, DCB_DHCSR_DBGKEY|DCB_DHCSR_C_DEBUGEN) 259 | # then just does a read of DCB_DHCSR, 260 | # then a read of 0x20000000 @ 4 261 | # then a read of x40023c1c @ 4 => 0x007800aa (FLASH_OBR, reeading option bytes and flash readout protection) 262 | # then one more read of DCB_DHCSR for good measure... 263 | 264 | def trace_off(dev): 265 | cmd = [STLINK_DEBUG_COMMAND, STLINK_DEBUG_APIV2_STOP_TRACE_RX] 266 | res = xfer_normal_input(dev, cmd, 2) 267 | logging.debug("STOP TRACE") 268 | 269 | def trace_on(dev, buff=4096, hz=2000000): 270 | cmd = [STLINK_DEBUG_COMMAND, STLINK_DEBUG_APIV2_START_TRACE_RX] 271 | args = [ord(q) for q in struct.pack(" 1 bit wide. 302 | # stm32 has traceclk directly to hclk, but swo clock can not be greater than 2Mhz 303 | # I tried 4 Mhz, and it's garbled, no real reason to believe it can do better 304 | prescaler = (cpu_hz / 2000000) - 1 305 | xfer_write32(dev, TPIU_ACPR, [prescaler]) # async prescalar 306 | trace_off(dev) 307 | trace_on(dev) 308 | xfer_write32(dev, TPIU_SPPR, [TPIU_SPPR_TXMODE_NRZ]) 309 | xfer_write32(dev, TPIU_FFCR, [0]) # Disable tpiu formatting 310 | xfer_write32(dev, ITM_LAR, [SCS_LAR_KEY]) 311 | xfer_write32(dev, ITM_TCR, [((1<<16) | ITM_TCR_SYNCENA | ITM_TCR_ITMENA)]) 312 | xfer_write32(dev, ITM_TER, [stim_bits]) 313 | # Not entirely convinced it's our place to edit the privilege register 314 | xfer_write32(dev, ITM_TPR, [stim_bits]) 315 | # weird read of SRAM here? 316 | set_dwt_sync_tap(dev, syncpackets) 317 | # READ DEBUG REG 0xe000edf0 => 0x01010001 318 | reg = xfer_read32(dev, DCB_DHCSR, 4)[0] 319 | print("DCB_DHCSR == %#x" % reg) 320 | # fixme - again, if this isn't ok, probably need to do something, like start it running or something.... 321 | 322 | 323 | 324 | def set_dwt_sync_tap(dev, syncbits): 325 | """ 326 | Selects the position of the synchronization packet counter tap 327 | on the CYCCNT counter. This determines the 328 | Synchronization packet rate: 329 | 00 = Disabled. No Synchronization packets. 330 | 01 = Synchronization counter tap at CYCCNT[24] 331 | 10 = Synchronization counter tap at CYCCNT[26] 332 | 11 = Synchronization counter tap at CYCCNT[28] 333 | For more information see The synchronization packet timer 334 | on page C1-874. 335 | 336 | To use synchronization (heartbeat and hot-connect synchronization), 337 | CYCCNTENA must be set to 1, SYNCTAP must be set to one of its values, and 338 | SYNCENA must be set to 1 339 | 340 | """ 341 | reg = xfer_read32(dev, DWT_CTRL, 4)[0] 342 | reg &= ~(3<<10) 343 | reg |= (syncbits << 10) | 1 # Must have cyccnt to have cyccnt tap! 344 | xfer_write32(dev, DWT_CTRL, [reg]) 345 | 346 | # Both of these return 0x80 347 | # winstlink sets up itm and tpiu unlock stuff too! 348 | 349 | def trace_bytes_available(dev): 350 | #logging.debug("checking for bytes to read") 351 | cmd = [STLINK_DEBUG_COMMAND, STLINK_DEBUG_APIV2_GET_TRACE_NB] 352 | res = xfer_normal_input(dev, cmd, 2, verbose=False) 353 | bytes = struct.unpack_from(" stimulus port 0 420 | """ 421 | stimbits = 1 422 | if args: 423 | try: 424 | x = int(args, base=0) 425 | stimbits = x 426 | except ValueError: 427 | print("Invalid stim bits: %s" % (args)) 428 | return 429 | if self.LOCK_DEV.acquire(1): 430 | enable_trace(self.dev, stimbits) 431 | self.LOCK_DEV.release() 432 | 433 | def do_swo_stop(self, args): 434 | if self.LOCK_DEV.acquire(1): 435 | trace_off(self.dev) 436 | self.LOCK_DEV.release() 437 | # TODO - possibly do a few extra SWO reads here? 438 | if self._swo_thread: 439 | print("Stopping worker thread") 440 | self._swo_thread.should_run = False 441 | self._swo_thread.join() 442 | 443 | def do_swo_file(self, args): 444 | """swo_file 445 | Start a background thread that continually reads the SWO data and writes 446 | it out to a file, as is 447 | """ 448 | self._swo_thread = SwopyDataWriter(self.dev, self.LOCK_DEV, args) 449 | self._swo_thread.start() 450 | 451 | 452 | 453 | def do_swo_read_raw(self, args): 454 | """ 455 | attempt to read from the swo endpoint, x times 456 | """ 457 | count = 1 458 | if args: 459 | try: 460 | x = int(args) 461 | count = x 462 | except ValueError: 463 | print("Ignoring invalid count of swo reads to attempt") 464 | 465 | for i in range(count): 466 | if self.LOCK_DEV.acquire(1): 467 | x = trace_bytes_available(self.dev) 468 | if x: 469 | qq = trace_read(self.dev, x) 470 | print("got trace bytes (raw)", qq) 471 | print("trace bytes as chars: ", [chr(x) for x in qq]) 472 | self.LOCK_DEV.release() 473 | 474 | 475 | def do_connect(self, args): 476 | """Connect to the target""" 477 | dev = self.dev 478 | if self.LOCK_DEV.acquire(1): 479 | get_mode(dev) 480 | leave_state(dev) 481 | v = get_version(dev) 482 | print(v) 483 | 484 | # OOCD claims this version, but no idea where it comes from, or how valid it is. 485 | if v.jtag_ver >= 13: 486 | volts = get_voltage(dev) 487 | print("Voltage: ", volts) 488 | 489 | enter_state_debug(dev) 490 | print("status is: ", status(dev)) 491 | self.LOCK_DEV.release() 492 | 493 | def do_run(self, args): 494 | """ Attempt to start the processor (THIS IS BSUTED?! 495 | Worked when it was halted after leaving oocd/gdb? 496 | """ 497 | run(self.dev) 498 | 499 | def do_enter_debug(self, args): 500 | if self.LOCK_DEV.acquire(1): 501 | enter_state_debug(self.dev) 502 | self.LOCK_DEV.release() 503 | 504 | def do_mode(self, args): 505 | if self.LOCK_DEV.acquire(1): 506 | s = get_mode(self.dev) 507 | self.LOCK_DEV.release() 508 | print("State is %#x" % s) 509 | 510 | def do_leave_state(self, args): 511 | if self.LOCK_DEV.acquire(1): 512 | leave_state(self.dev) 513 | self.LOCK_DEV.release() 514 | 515 | def do_version(self, args): 516 | if self.LOCK_DEV.acquire(1): 517 | v = get_version(self.dev) 518 | self.LOCK_DEV.release() 519 | print(v) 520 | 521 | def _argparse_two_ints(self, args): 522 | if not args: 523 | print("Parsing addr/count pair requires arguments!") 524 | return 525 | if args: 526 | aa = args.split() 527 | if len(aa) != 2: 528 | print("raw read mem32 requires 2 arguments: addr count") 529 | return 530 | try: 531 | addr = int(aa[0], base=0) 532 | count = int(aa[1], base=0) 533 | return (addr, count) 534 | except ValueError: 535 | print("addr and count both need to be integers, or convertible to integers") 536 | return 537 | 538 | 539 | def do_raw_read_debug_reg(self, args): 540 | reg = int(args, base=0) 541 | if self.LOCK_DEV.acquire(1): 542 | v = xfer_read_debug(self.dev, reg) 543 | self.LOCK_DEV.release() 544 | print("register %#x = %d (%#08x)" % (reg, v, v)) 545 | 546 | def do_raw_write_debug_reg(self, args): 547 | tup = self._argparse_two_ints(args) 548 | if tup: 549 | if self.LOCK_DEV.acquire(1): 550 | v = xfer_write_debug(self.dev, tup[0], tup[1]) 551 | self.LOCK_DEV.release() 552 | print("write debug returned: ", v) 553 | 554 | def do_raw_read_mem32(self, args): 555 | """raw_read_mem32
556 | Read bytecount bytes from address. 557 | bytecount probably has to be a multiple of 4, but hasn't been extensively tested. 558 | """ 559 | tup = self._argparse_two_ints(args) 560 | if tup: 561 | if self.LOCK_DEV.acquire(1): 562 | v = xfer_read32(self.dev, tup[0], tup[1]) 563 | self.LOCK_DEV.release() 564 | print("read32 returned: ", v) 565 | 566 | def do_raw_write_mem32(self, args): 567 | """uses write mem32 instead of write debug, but still only 1 word writes please!""" 568 | tup = self._argparse_two_ints(args) 569 | if tup: 570 | if self.LOCK_DEV.acquire(1): 571 | v = xfer_write32(self.dev, tup[0], [tup[1]]) 572 | self.LOCK_DEV.release() 573 | print("write32 returned", v) 574 | 575 | def do_magic_sync(self, args): 576 | """Send the unknown magic sync frame. helps for ???""" 577 | if self.LOCK_DEV.acquire(1): 578 | xfer_unknown_sync(self.dev) 579 | self.LOCK_DEV.release() 580 | print("unknown magic sync sent") 581 | 582 | 583 | def do_EOF(self, args): 584 | return self.do_exit(args) 585 | 586 | def do_exit(self, args): 587 | self.do_swo_stop(args) 588 | leave_state(self.dev) 589 | s = get_mode(self.dev) 590 | print("Disconnected with state in %d" % s) 591 | return True 592 | 593 | def do_voltage_trace(self, args): 594 | """request the voltage continuously and print to screen. (blocks swo)""" 595 | with self.LOCK_DEV: 596 | while True: 597 | volts = get_voltage(self.dev) 598 | print("Voltage: ", volts) 599 | 600 | 601 | def cmdloop(self): 602 | while True: 603 | try: 604 | # FIXME Or, you know, could I just lock here? 605 | # Might get in the way of the thread needing the lock to cleanup or anything? 606 | cmd.Cmd.cmdloop(self) 607 | # release here? 608 | except KeyboardInterrupt: 609 | print(' - interrupted') 610 | continue 611 | break 612 | 613 | 614 | if __name__ == "__main__": 615 | p = Swopy() 616 | p.cmdloop() 617 | 618 | 619 | 620 | --------------------------------------------------------------------------------