Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add memory interface for jlink AP 0 #1618

Merged
merged 2 commits into from
Aug 30, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading