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

[ISSUE-214] Add Support for Power Tracing #215

Merged
merged 3 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from 2 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
7 changes: 5 additions & 2 deletions pylink/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
import argparse
import logging
import os
import six
import sys
try:
from six.moves import with_metaclass
except (AttributeError, ImportError):
from six import with_metaclass


class CommandMeta(type):
Expand Down Expand Up @@ -49,7 +52,7 @@ def __new__(cls, name, parents, dct):
return newClass


class Command(six.with_metaclass(CommandMeta)):
class Command(with_metaclass(CommandMeta)):
"""Base command-class.

All commands should inherit from this class.
Expand Down
25 changes: 25 additions & 0 deletions pylink/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -722,3 +722,28 @@ class JLinkRTTDirection(object):
"""RTT Direction."""
UP = 0
DOWN = 1


class JLinkPowerTraceCommand(object):
"""Power trace commands."""
SETUP = 0
START = 1
FLUSH = 2
STOP = 3
GET_CAPS = 4
GET_CHANNEL_CAPS = 5
GET_NUM_ITEMS = 6


class JLinkPowerTraceRef(object):
"""Reference values to store on power trace capture.

Attributes:
NONE: No reference value is stored.
BYTES: Number of bytes transferred via SWO is stored since capturing
started.
TIME: Number of milliseconds since capturing started.
"""
NONE = 0
BYTES = 1
TIME = 2
210 changes: 210 additions & 0 deletions pylink/jlink.py
Original file line number Diff line number Diff line change
Expand Up @@ -5250,3 +5250,213 @@ def cp15_register_write(self, cr_n, op_1, cr_m, op_2, value):
if res != 0:
raise errors.JLinkException(res)
return res

###############################################################################
#
# Power API
#
###############################################################################
@open_required
def power_trace_configure(self, channels, freq, ref, always):
"""Configures power tracing.

This method must be called before calling the other power trace APIs. It
is the responsibility of the calling application code to keep track of
which channels were enabled in order to determine which trace samples
correspond to which channels when read.

Args:
self (JLink): the ``JLink`` instance.
channels (list[int]): list specifying which channels to capture on (0-7).
freq (int): sampling frequency (in Hertz).
ref (JLinkPowerTraceRef): reference value to stored on capture.
always (bool): ``True`` to capture data even while CPU halted, otherwise ``False``.

Returns:
The sampling frequency (in Hz) for power sampling.

Raises:
JLinkException: on error
"""
if isinstance(channels, list):
channel_mask = 0x00
for channel in channels:
channel_mask |= (1 << channel)
else:
channel_mask = channels
Comment on lines +5282 to +5287

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we test the channel values are in the range of 0-7?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yah. Are you thinking of filtering out the values outside of the range, or raising a ValueError? I think either could work here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was thinking along the lines of a ValueError or just an assert.


setup = structs.JLinkPowerTraceSetup()
setup.ChannelMask = channel_mask
setup.SampleFreq = int(freq)
setup.RefSelect = int(ref)
setup.EnableCond = 0 if always else 1

res = self._dll.JLINK_POWERTRACE_Control(enums.JLinkPowerTraceCommand.SETUP, ctypes.byref(setup), 0)
if res < 0:
raise errors.JLinkException(res)
return res

@open_required
def power_trace_start(self):
"""Starts capturing data on the channels enabled via ``power_trace_configure()``.

Args:
self (JLink): the ``JLink`` instance.

Returns:
``None``

Raises:
JLinkException: on error
"""
res = self._dll.JLINK_POWERTRACE_Control(enums.JLinkPowerTraceCommand.START, 0, 0)
if res < 0:
raise errors.JLinkException(res)

@open_required
def power_trace_stop(self):
"""Stops a capture started by ``power_trace_start()``.

Args:
self (JLink): the ``JLink`` instance.

Returns:
``None``

Raises:
JLinkException: on error
"""
res = self._dll.JLINK_POWERTRACE_Control(enums.JLinkPowerTraceCommand.STOP, 0, 0)
if res < 0:
raise errors.JLinkException(res)

@open_required
def power_trace_flush(self):
"""Flushes all capture data.

Any data that has not been read by ``power_trace_read()`` is dropped.

Args:
self (JLink): the ``JLink`` instance.

Returns:
``None``

Raises:
JLinkException: on error
"""
res = self._dll.JLINK_POWERTRACE_Control(enums.JLinkPowerTraceCommand.FLUSH, 0, 0)
if res < 0:
raise errors.JLinkException(res)

@open_required
def power_trace_get_channels(self):
"""Returns a list of the available channels for power tracing.

This method returns a list of the available channels for power tracing.
The application code can use this to determine which channels to
enable for tracing.

Args:
self (JLink): the ``JLink`` instance.

Returns:
List of available channel identifiers.

Raises:
JLinkException: on error
"""
caps = structs.JLinkPowerTraceCaps()
res = self._dll.JLINK_POWERTRACE_Control(enums.JLinkPowerTraceCommand.GET_CAPS, 0, ctypes.byref(caps))
if res < 0:
raise errors.JLinkException(res)

return [i for i in range(0, 32) if (caps.ChannelMask >> i) & 0x1]

@open_required
def power_trace_get_channel_capabilities(self, channels):
"""Returns the capabilities for the specified channels.

Args:
self (JLink): the ``JLink`` instance.
channels (list[int]): list specifying which channels to get capabilities for.

Returns:
Channel capabilities.

Raises:
JLinkException: on error
"""
if isinstance(channels, list):
channel_mask = 0x00
for channel in channels:
channel_mask |= (1 << channel)
else:
channel_mask = channels

channel_caps = structs.JLinkPowerTraceChannelCaps()
caps = structs.JLinkPowerTraceCaps()
caps.ChannelMask = channel_mask

res = self._dll.JLINK_POWERTRACE_Control(enums.JLinkPowerTraceCommand.GET_CHANNEL_CAPS,
ctypes.byref(caps),
ctypes.byref(channel_caps))
if res < 0:
raise errors.JLinkException(res)

return channel_caps

@open_required
def power_trace_get_num_items(self):
"""Returns a count of the number of items in the power trace buffer.

Since each channel is sampled simulataneously, the count of number of
items per channel is the return value of this function divided by the
number of active channels.

Args:
self (JLink): the ``JLink`` instance.

Returns:
Number of items in the power trace buffer.

Raises:
JLinkException: on error
"""
res = self._dll.JLINK_POWERTRACE_Control(enums.JLinkPowerTraceCommand.GET_NUM_ITEMS, 0, 0)
if res < 0:
raise errors.JLinkException(res)
return res

@open_required
def power_trace_read(self, num_items=None):
"""Reads data from the power trace buffer.

Any read data is flushed from the power trace buffer.

Args:
self (JLink): the ``JLink`` instance.
num_items (int): the number of items to read (if not specified, reads all).

Returns:
List of ``JLinkPowerTraceItem``s.

Raises:
JLinkException: on error
"""
if num_items is None:
num_items = self.power_trace_get_num_items()

items = []
if num_items < 0:
raise ValueError("Invalid number of items requested, expected > 0, given %d" % num_items)
elif num_items > 0:
items = (structs.JLinkPowerTraceItem * num_items)()
res = self._dll.JLINK_POWERTRACE_Read(ctypes.byref(items), num_items)
if res < 0:
raise errors.JLinkException(res)

# Number of items may be less than the requested count, so clip the
# array here.
items = list(items)[:res]
return items
Loading
Loading