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

Resource: add Segger J-Link support #1175

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

PaulVittorino
Copy link
Contributor

@PaulVittorino PaulVittorino commented May 8, 2023

Description
Add a resource for the Segger J-Link debug probe.
Export the J-Link resource over IP using the J-Link Remote Server.
Adding support for J-Link over IP combined with pylink provides a Python interface for the J-Link over IP.

Verified by acquiring a place via the Python API and using pylink to establish an IP connection.

resource = target.get_resource(labgrid.resource.remote.NetworkJLinkDevice)
host, port = labgrid.util.proxy.proxymanager.get_host_and_port(resource)
jlink = pylink.JLink()
jlink.open(ip_addr=f"{ip_addr}:{port}")

Supported hardware includes Segger J-Link JTAG probes. Tested with J-Link Ultra+ Version 5.0 hardware.

Checklist

  • Documentation for the feature
  • Tests for the feature
  • The arguments and description in doc/configuration.rst have been updated
  • Add a section on how to use the feature to doc/usage.rst (N/A)
  • Add a section on how to use the feature to doc/development.rst (N/A)
  • PR has been tested
  • Man pages have been regenerated (N/A)

@codecov
Copy link

codecov bot commented May 16, 2023

Codecov Report

Attention: 5 lines in your changes are missing coverage. Please review.

Comparison is base (2906645) 63.0% compared to head (53cdba7) 63.4%.
Report is 74 commits behind head on master.

Files Patch % Lines
labgrid/remote/exporter.py 95.5% 2 Missing ⚠️
labgrid/resource/udev.py 80.0% 2 Missing ⚠️
labgrid/resource/suggest.py 0.0% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##           master   #1175     +/-   ##
========================================
+ Coverage    63.0%   63.4%   +0.4%     
========================================
  Files         160     161      +1     
  Lines       11865   11959     +94     
========================================
+ Hits         7480    7588    +108     
+ Misses       4385    4371     -14     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@PaulVittorino
Copy link
Contributor Author

I added testing of USBJLinkExport to try and meet the code coverage goal.

Copy link
Member

@jluebbe jluebbe left a comment

Choose a reason for hiding this comment

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

For new resources, there should also be a corresponding driver. As there is already a Python module which provides access, only a very simple driver is needed to load it and wrap the open method. Take a look at these:

@PaulVittorino
Copy link
Contributor Author

For new resources, there should also be a corresponding driver. As there is already a Python module which provides access, only a very simple driver is needed to load it and wrap the open method. Take a look at these:

* https://github.com/labgrid-project/labgrid/blob/master/labgrid/driver/modbusdriver.py

* https://github.com/labgrid-project/labgrid/blob/master/labgrid/driver/modbusrtudriver.py

@jluebbe driver added with 100% code coverage.

@PaulVittorino
Copy link
Contributor Author

@jluebbe does my driver meet your expectations?

Comment on lines 564 to 576
child = self.child
self.child = None
port = self.port
self.port = None
child.terminate()
try:
child.wait(2.0) # JLinkRemoteServer takes about a second to react
except subprocess.TimeoutExpired:
self.logger.warning("JLinkRemoteServer for %s still running after SIGTERM", self.local.serial)
log_subprocess_kernel_stack(self.logger, child)
child.kill()
child.wait(1.0)
self.logger.info("stopped JLinkRemoteServer for %s on port %d", self.local.serial, port)
Copy link
Member

Choose a reason for hiding this comment

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

When operating with pipes (stdout=subprocess.PIPE), they should be closed explicitly when the
subprocess has terminated (see also #1018).

"""

def __attrs_post_init__(self):
self.match["ID_VENDOR_ID"] = "1366"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since USBDebugger now covers the same vendor & model ID as JLinkDevice, do I need to refactor to use USBDebugger instead?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, please do so.

Add a driver and resource for the Segger J-Link debug probe.
Export the J-Link resource over IP using the J-Link Remote Server.

Signed-off-by: Paul Vittorino <paul.vittorino@garmin.com>
Comment on lines -450 to +451

Copy link
Member

Choose a reason for hiding this comment

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

Seems unrelated.

- `JLinkDriver`_

NetworkJLinkDevice
~~~~~~~~~~~~~~~~~~~~~
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~

Comment on lines +29 to +32
# Workaround for Debian's /etc/hosts entry
# https://www.debian.org/doc/manuals/debian-reference/ch05.en.html#_the_hostname_resolution
if ip_addr == "127.0.1.1":
ip_addr = "127.0.0.1"
Copy link
Member

Choose a reason for hiding this comment

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

Why can't we use 127.0.1.1 here?

"""

def __attrs_post_init__(self):
self.match["ID_VENDOR_ID"] = "1366"
Copy link
Member

Choose a reason for hiding this comment

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

Yes, please do so.

Comment on lines +505 to +578
class USBJLinkExport(USBGenericExport):
"""Export J-Link device using the J-Link Remote Server"""

def __attrs_post_init__(self):
super().__attrs_post_init__()
self.child = None
self.port = None
self.tool = '/opt/SEGGER/JLink/JLinkRemoteServer'

def _get_params(self):
"""Helper function to return parameters"""
return {
'host': self.host,
'port': self.port,
'busnum': self.local.busnum,
'devnum': self.local.devnum,
'path': self.local.path,
'vendor_id': self.local.vendor_id,
'model_id': self.local.model_id,
}

def __del__(self):
if self.child is not None:
self.stop()

def _start(self, start_params):
"""Start ``JLinkRemoteServer`` subprocess"""
assert self.local.avail
assert self.child is None
self.port = get_free_port()

cmd = [
self.tool,
"-Port",
f"{self.port}",
"-select",
f"USB={self.local.serial}",
]
self.logger.info("Starting JLinkRemoteServer with: %s", " ".join(cmd))
self.child = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True)

# Wait for the server to be ready for incoming connections
# Waiting to open the socket with Python does not work
timeout = Timeout(10.0)
while not timeout.expired:
line = self.child.stdout.readline().rstrip()
self.logger.debug(line)
if "Waiting for client connections..." in line:
break

if timeout.expired:
raise TIMEOUT(
f"Timeout of {timeout.timeout} seconds exceeded during waiting for J-Link Remote Server startup"
)
self.logger.info("started JLinkRemoteServer for %s on port %d", self.local.serial, self.port)

def _stop(self, start_params):
"""Stop ``JLinkRemoteServer`` subprocess"""
assert self.child
child = self.child
self.child = None
port = self.port
self.port = None
child.terminate()
try:
child.communicate(2.0) # JLinkRemoteServer takes about a second to react
except subprocess.TimeoutExpired:
self.logger.warning("JLinkRemoteServer for %s still running after SIGTERM", self.local.serial)
log_subprocess_kernel_stack(self.logger, child)
child.kill()
child.communicate(1.0)
self.logger.info("stopped JLinkRemoteServer for %s on port %d", self.local.serial, port)


Copy link
Member

Choose a reason for hiding this comment

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

This should not be required. The driver should start the remote server on activate and stop it on deactivate.


def update(self):
super().update()
self.serial = self.device.properties.get('ID_SERIAL_SHORT')
Copy link
Member

Choose a reason for hiding this comment

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

self.device might be None here. Maybe make it a @property?

@target_factory.reg_driver
@attr.s(eq=False)
class JLinkDriver(Driver):
bindings = {"jlink_device": {"JLinkDevice", "NetworkJLinkDevice"}, }
Copy link
Member

Choose a reason for hiding this comment

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

Maybe s/jlink_device/device/? Since we know that we are in the jlink context anyways.

@@ -1789,6 +1809,21 @@ Arguments:
- board_config (str): optional, board config in the ``openocd/scripts/board/`` directory
- load_commands (list of str): optional, load commands to use instead of ``init``, ``bootstrap {filename}``, ``shutdown``

JLinkDriver
~~~~~~~~~~~~~
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
~~~~~~~~~~~~~
~~~~~~~~~~~

@Bastian-Krause Bastian-Krause added needs rebase Needs a rebase onto the master branch, maintainter could probably not push to submitter branch. and removed work in progress labels Dec 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement needs rebase Needs a rebase onto the master branch, maintainter could probably not push to submitter branch.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants