From 49da490417cdf87c249114a2746132c363846744 Mon Sep 17 00:00:00 2001 From: zjli2019 Date: Tue, 4 Apr 2023 10:35:02 +0800 Subject: [PATCH] add RTT logger --- tools/README.md | 27 ++++- tools/requirements.txt | 1 + tools/rtt_logger.py | 238 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 tools/rtt_logger.py diff --git a/tools/README.md b/tools/README.md index cf2357038..d774046b7 100644 --- a/tools/README.md +++ b/tools/README.md @@ -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. \ No newline at end of file +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)`. \ No newline at end of file diff --git a/tools/requirements.txt b/tools/requirements.txt index 219afa3f9..2a21aa462 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -7,3 +7,4 @@ pytz==2022.7.1 pyusb==1.2.1 PyYAML==6.0 zope.interface==5.5.2 +pyocd==0.34.3 diff --git a/tools/rtt_logger.py b/tools/rtt_logger.py new file mode 100644 index 000000000..875621bc1 --- /dev/null +++ b/tools/rtt_logger.py @@ -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)