Skip to content

Commit

Permalink
probe: jlink: add accelerated memory interface for jlink AP 0 (#1618)
Browse files Browse the repository at this point in the history
* add memory interface for jlink ap 0

---------

Co-authored-by: marian <[email protected]>
  • Loading branch information
2 people authored and flit committed Aug 30, 2023
1 parent c947c04 commit 6d42caa
Showing 1 changed file with 123 additions and 1 deletion.
124 changes: 123 additions & 1 deletion pyocd/probe/jlink_probe.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# pyOCD debugger
# Copyright (c) 2020 Arm Limited
# Copyright (c) 2021-2022 Chris Reed
# Copyright (c) 2023 Marian Muller Rebeyrol
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -21,12 +22,14 @@
import pylink
from pylink.enums import JLinkInterfaces
from pylink.errors import (JLinkException, JLinkWriteException, JLinkReadException)
from typing import (TYPE_CHECKING, Optional, Tuple)
from typing import (TYPE_CHECKING, Optional, Tuple, Any, Sequence, Union, Callable)

from .debug_probe import DebugProbe
from ..core.memory_interface import MemoryInterface
from ..core import exceptions
from ..core.plugin import Plugin
from ..core.options import OptionInfo
from ..utility import conversion

if TYPE_CHECKING:
from pylink.structs import JLinkHardwareStatus
Expand Down Expand Up @@ -126,6 +129,7 @@ def __init__(self, serial_number):
self._default_protocol = None
self._is_open = False
self._product_name = six.ensure_str(info.acProduct)
self._memory_interfaces = {}

@property
def description(self):
Expand Down Expand Up @@ -211,6 +215,7 @@ def close(self):
try:
self._link.close()
self._is_open = False
self._memory_interfaces = {}
except JLinkException as exc:
raise self._convert_exception(exc) from exc

Expand Down Expand Up @@ -450,6 +455,19 @@ def write_ap_multiple(self, addr, values):
for v in values:
self.write_ap(addr, v)

def get_memory_interface_for_ap(self, ap_address):
assert self._is_open
# JLink memory access commands only support AP 0
if ap_address.apsel != 0:
return None
# JLink memory access commands require to be conneected to the target
if not self._link.target_connected():
return None
apsel = ap_address.apsel
if apsel not in self._memory_interfaces:
self._memory_interfaces[apsel] = JLinkMemoryInterface(self._link, apsel)
return self._memory_interfaces[apsel]

def swo_start(self, baudrate):
try:
self._link.swo_start(int(baudrate))
Expand Down Expand Up @@ -483,6 +501,110 @@ def _convert_exception(exc):
else:
return exc

class JLinkMemoryInterface(MemoryInterface):
"""@brief Concrete memory interface for a single AP."""

def __init__(self, link, apsel):
self._link = link
self._apsel = apsel

def write_memory(self, addr: int, data: int, transfer_size: int=32, **attrs: Any) -> None:
"""@brief Write a single memory location.
By default the transfer size is a word.
"""
assert transfer_size in (8, 16, 32)
addr &= 0xffffffff
if transfer_size == 32:
self._link.memory_write32(addr, [data])
elif transfer_size == 16:
self._link.memory_write16(addr, [data])
elif transfer_size == 8:
self._link.memory_write8(addr, [data])

def read_memory(self, addr: int, transfer_size: int=32, now: bool=True, **attrs: Any) \
-> Union[int, Callable[[], int]]:
"""@brief Read a memory location.
By default, a word will be read.
"""
assert transfer_size in (8, 16, 32)
addr &= 0xffffffff
if transfer_size == 32:
result = self._link.memory_read32(addr, 1)[0]
elif transfer_size == 16:
result = self._link.memory_read16(addr, 1)[0]
elif transfer_size == 8:
result = self._link.memory_read8(addr, 1)[0]

def read_callback():
return result
return result if now else read_callback

def write_memory_block32(self, addr: int, data: Sequence[int], **attrs: Any) -> None:
addr &= 0xffffffff
self._link.memory_write32(addr, data)

def read_memory_block32(self, addr: int, size: int, **attrs: Any) -> Sequence[int]:
addr &= 0xffffffff
return self._link.memory_read32(addr, size)

def read_memory_block8(self, addr: int, size: int, **attrs: Any) -> Sequence[int]:
addr &= 0xffffffff
res = []

# Transfers are handled in 3 phases:
# 1. read 8-bit chunks until the first aligned address is reached,
# 2. read 32-bit chunks from all aligned addresses,
# 3. read 8-bit chunks from the remaining unaligned addresses.
# If the requested size is so small that phase-1 would not even reach
# aligned address, go straight to phase-3.

# 1. read leading unaligned bytes
unaligned_count = 3 & (4 - addr)
if (size > unaligned_count > 0):
res += self._link.memory_read8(addr, unaligned_count)
size -= unaligned_count
addr += unaligned_count

# 2. read aligned block of 32 bits
if (size >= 4):
aligned_size = size & ~3
res += conversion.u32le_list_to_byte_list(self._link.memory_read32(addr, aligned_size//4))
size -= aligned_size
addr += aligned_size

# 3. read trailing unaligned bytes
if (size > 0):
res += self._link.memory_read8(addr, size)

return res

def write_memory_block8(self, addr: int, data: Sequence[int], **attrs: Any) -> None:
addr &= 0xffffffff
size = len(data)
idx = 0

# write leading unaligned bytes
unaligned_count = 3 & (4 - addr)
if (size > unaligned_count > 0):
self._link.memory_write8(addr, data[:unaligned_count])
size -= unaligned_count
addr += unaligned_count
idx += unaligned_count

# write aligned block of 32 bits
if (size >= 4):
aligned_size = size & ~3
self._link.memory_write32(addr, conversion.byte_list_to_u32le_list(data[idx:idx + aligned_size]))
size -= aligned_size
addr += aligned_size
idx += aligned_size

# write trailing unaligned bytes
if (size > 0):
self._link.memory_write8(addr, data[idx:])

class JLinkProbePlugin(Plugin):
"""@brief Plugin class for JLinkProbe."""

Expand Down

0 comments on commit 6d42caa

Please sign in to comment.