Skip to content

Commit

Permalink
add RTT logger
Browse files Browse the repository at this point in the history
  • Loading branch information
zjli-2019 committed Apr 4, 2023
1 parent f87b940 commit 49da490
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 1 deletion.
27 changes: 26 additions & 1 deletion tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,29 @@ nim -d:release c gen_files.nim
gen_files /path/to/sdk/bundles
```

Note: [Nim](https://nim-lang.org/) is required to build this tool.
Note: [Nim](https://nim-lang.org/) is required to build this tool.

## RTT Logger

_rtt_logger.py_ is based on [pyocd](https://pyocd.io/), and supports J-Link,
DAP-Link, etc.

Usage:

```shell
python rtt_logger.py -RTTSearchRanges "0x2000XXXX 0xYYYY" log_file
```

### Command line options

|Option | Explanation |
|:--------------------|:-----------------------------------------|
|-Speed `SpeedInKHZ` |Sets speed in kHz |
|-ID `id` | Connects to the probe with unique `id` |
|-RTTAddress `RTTAddress` | Sets RTT address to `RTTAddress` |
|-RTTSearchRanges "`Ranges`" |Sets RTT search ranges to `Ranges` |
|-RTTChannel `RTTChannel` | Sets RTT channel to `RTTChannel` |

`Ranges` is specified as `start` address and `size`, for example
"0x2000XXXX 0xYYYY" tells this tool to search for RTT in the memory range
from `0x2000XXXX` to `(0x2000XXXX + 0xYYYY)`.
1 change: 1 addition & 0 deletions tools/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ pytz==2022.7.1
pyusb==1.2.1
PyYAML==6.0
zope.interface==5.5.2
pyocd==0.34.3
238 changes: 238 additions & 0 deletions tools/rtt_logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import sys
import ctypes
import logging
import argparse
import time

import colorama
from colorama import (Fore, Style)
import logging
from shutil import get_terminal_size
from typing import (IO, Optional)

import pyocd.utility.color_log
from pyocd.core.helpers import ConnectHelper
from pyocd.core.memory_map import MemoryMap, MemoryRegion, MemoryType
from pyocd.core.soc_target import SoCTarget
from pyocd.utility.cmdline import convert_session_options, int_base_0
from pyocd.probe import aggregator
from pyocd.utility.kbhit import KBHit
from pyocd.utility.color_log import ColorFormatter

from ctypes import Structure, c_char, c_int32, c_uint32, sizeof

LOG = logging.getLogger(__name__)

RTT_MAGIC = 'SEGGER RTT'

class SEGGER_RTT_BUFFER_UP(ctypes.Structure):
_fields_ = [
('sName', ctypes.c_uint32),
('pBuffer', ctypes.c_uint32),
('SizeOfBuffer', ctypes.c_uint32),
('WrOff', ctypes.c_uint32),
('RdOff', ctypes.c_uint32),
('Flags', ctypes.c_uint32),
]

class SEGGER_RTT_BUFFER_DOWN(ctypes.Structure):
_fields_ = [
('sName', ctypes.c_uint32),
('pBuffer', ctypes.c_uint32),
('SizeOfBuffer', ctypes.c_uint32),
('WrOff', ctypes.c_uint32),
('RdOff', ctypes.c_uint32),
('Flags', ctypes.c_uint32),
]

class SEGGER_RTT_CB(ctypes.Structure): # Control Block
_fields_ = [
('acID', ctypes.c_char * 16),
('MaxNumUpBuffers', ctypes.c_uint32),
('MaxNumDownBuffers', ctypes.c_uint32),
]

def get_default_probe_id():
daplinks = aggregator.DebugProbeAggregator.get_all_connected_probes()

if len(daplinks) == 0:
LOG.error("NO DAP-Link")
exit(-1)

if len(daplinks) == 1:
return daplinks[0].unique_id

LOG.info("Multiple DAP-Link found. Use -Id .. to specify one:\n")
LOG.info(list(daplinks.keys()))

exit(-2)

def find_rtt_ctx(FLAGS, dap):
if FLAGS.RTTAddress > 0:
buff = dap.read_memory_block8(FLAGS.RTTAddress, 32)
buff = ''.join([chr(x) for x in buff])
if buff.find(RTT_MAGIC) == 0:
return FLAGS.RTTAddress
else:
raise Exception(f"SEGGER RTT not found at 0x{FLAGS.RTTAddress:0>8x}")

search = [int(x, 0) for x in FLAGS.RTTSearchRanges.split()]
if len(search) != 2:
raise Exception(f"Search range '{FLAGS.RTTSearchRanges}' is invalid")

start = search[0]
remain = search[1]
while remain > 0:
buff = dap.read_memory_block8(start, 512 + 16)
buff = ''.join([chr(x) for x in buff])
index = buff.find(RTT_MAGIC)
if index >= 0:
return start + index
start += 512
remain -= 512

raise Exception(f"SEGGER RTT not found in specified range")

def run(FLAGS):
pyocd.utility.color_log.build_color_logger(color_setting='auto')

if FLAGS.ID == '':
FLAGS.ID = get_default_probe_id()
LOG.setLevel(logging.DEBUG)
kb = None
try:
session = ConnectHelper.session_with_chosen_probe(
project_dir=None,
config_file=None,
user_script=None,
no_config=True,
pack=None,
unique_id=FLAGS.ID,
target_override=FLAGS.Device,
frequency=FLAGS.Speed * 1000,
blocking=False,
connect_mode=FLAGS.ConnectMode,
options={})

if session is None:
LOG.error("No target device available")
return 1

with session, open(FLAGS.LogFile, 'wb') as log_file:

target: SoCTarget = session.board.target

rtt_cb_addr = find_rtt_ctx(FLAGS, target)

data = target.read_memory_block8(rtt_cb_addr, sizeof(SEGGER_RTT_CB))
rtt_cb = SEGGER_RTT_CB.from_buffer(bytearray(data))
up_addr = rtt_cb_addr + sizeof(SEGGER_RTT_CB) + sizeof(SEGGER_RTT_BUFFER_UP) * FLAGS.RTTChannel

LOG.info(f"_SEGGER_RTT @ {rtt_cb_addr:#08x} with {rtt_cb.MaxNumUpBuffers} aUp and {rtt_cb.MaxNumDownBuffers} aDown")

target.resume()

# set up terminal input
kb = KBHit()

LOG.info("start logging...")
total_size = 0
block_size = 0
last_time = time.time()

while True:
# read data from up buffers (target -> host)
data = target.read_memory_block8(up_addr, sizeof(SEGGER_RTT_BUFFER_UP))
up = SEGGER_RTT_BUFFER_UP.from_buffer(bytearray(data))

if up.WrOff == up.RdOff:
if kb.kbhit():
LOG.info("Abort...")
break

if up.WrOff > up.RdOff:
"""
|oooooo|xxxxxxxxxxxx|oooooo|
0 rdOff WrOff SizeOfBuffer
"""
data = target.read_memory_block8(up.pBuffer + up.RdOff, up.WrOff - up.RdOff)
target.write_memory(up_addr + SEGGER_RTT_BUFFER_UP.RdOff.offset, up.WrOff)

elif up.WrOff < up.RdOff:
"""
|xxxxxx|oooooooooooo|xxxxxx|
0 WrOff RdOff SizeOfBuffer
"""
data = target.read_memory_block8(up.pBuffer + up.RdOff, up.SizeOfBuffer - up.RdOff)
data += target.read_memory_block8(up.pBuffer, up.WrOff)
target.write_memory(up_addr + SEGGER_RTT_BUFFER_UP.RdOff.offset, up.WrOff)

log_file.write(bytes(data))

s = len(data)
block_size += s
total_size += s
diff = time.time() - last_time
if diff > 1.0:
print(f"Transfer rate: {block_size / 1000:.1f} KByte/s; Bytes written: {total_size / 1000:.0f} KByte", end="\r")
block_size = 0
last_time = time.time()

except KeyboardInterrupt:
pass

finally:
if session:
session.close()
if kb:
kb.set_normal_term()


if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
'-If',
type=str,
default='SWD',
help='interface')
parser.add_argument(
'-Device',
type=str,
default='cortex_m',
help='device')
parser.add_argument(
'-Speed',
type=int,
default=8000,
help='speed')
parser.add_argument(
'-RTTChannel',
type=int,
default=0,
help='RTT channel')
parser.add_argument(
'-RTTSearchRanges',
type=str,
default="",
help='RTT search ranges')
parser.add_argument(
'-RTTAddress',
type=lambda x: int(x, 0),
default=-1,
help='RTT search ranges')
parser.add_argument(
'-ID',
type=str,
default="",
help='Probe unique ID')
parser.add_argument(
'-ConnectMode',
type=str,
default="attach",
help='Select connect mode from one of (halt, pre-reset, under-reset, attach)')
parser.add_argument(
'LogFile',
type=str,
help='Log file name')
FLAGS, unparsed = parser.parse_known_args()
run(FLAGS)

0 comments on commit 49da490

Please sign in to comment.