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

Feature: Jlink plugin #150

Closed
Closed
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
mbed SDK
Copyright (c) 2011-2015 ARM Limited
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you mind updating the copyright year here?

Suggested change
Copyright (c) 2011-2015 ARM Limited
Copyright (c) 2019 ARM Limited

Copy link
Contributor

Choose a reason for hiding this comment

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

Also line 17 to be removed. Plus files should have SPDX identifier (any new file should).


Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Author: Przemyslaw Wirkus <[email protected]>
"""

from mbed_os_tools.test.host_tests_plugins.module_copy_jlink import (
HostTestPluginCopyMethod_Jlink,
load_plugin,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
mbed SDK
Copyright (c) 2011-2015 ARM Limited
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you mind updating the copyright year here?

Suggested change
Copyright (c) 2011-2015 ARM Limited
Copyright (c) 2019 ARM Limited


Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Author: Przemyslaw Wirkus <[email protected]>
"""

from mbed_os_tools.test.host_tests_plugins.module_reset_jlink import (
HostTestPluginResetMethod_Jlink,
load_plugin,
)
4 changes: 4 additions & 0 deletions src/mbed_os_tools/test/host_tests_plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
# Additional, non standard platforms
from . import module_copy_silabs
from . import module_reset_silabs
from . import module_copy_jlink
from . import module_reset_jlink
from . import module_copy_stlink
from . import module_reset_stlink
from . import module_copy_ublox
Expand All @@ -56,6 +58,8 @@
# Extra platforms support
HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_copy_mps2.load_plugin())
HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_reset_mps2.load_plugin())
HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_copy_jlink.load_plugin())
HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_reset_jlink.load_plugin())
HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_copy_silabs.load_plugin())
HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_reset_silabs.load_plugin())
HOST_TEST_PLUGIN_REGISTRY.register_plugin(module_copy_stlink.load_plugin())
Expand Down
113 changes: 113 additions & 0 deletions src/mbed_os_tools/test/host_tests_plugins/module_copy_jlink.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Copyright (c) 2018, Arm Limited and affiliates.
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you mind updating the copyright year here?

Suggested change
# Copyright (c) 2018, Arm Limited and affiliates.
# Copyright (c) 2019, Arm Limited and affiliates.

# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import tempfile

from .host_test_plugins import HostTestPluginBase


COPY_FILE = "copy.jlink"

class HostTestPluginCopyMethod_Jlink(HostTestPluginBase):

# Plugin interface
name = 'HostTestPluginCopyMethod_Jlink'
type = 'CopyMethod'
capabilities = ['JTAG', 'SWD']
Copy link
Contributor

Choose a reason for hiding this comment

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

Since this plugin is specific to JLink, could you prefix these as follows? We also stick with lower case capabilities across the plugins:

Suggested change
capabilities = ['JTAG', 'SWD']
capabilities = ['jlink_jtag', 'jlink_swd']

required_parameters = ['image_path', 'mcu']

def is_os_supported(self, os_name=None):
"""! In this implementation this plugin only is supporeted under Windows machines
"""
# If no OS name provided use host OS name
if not os_name:
os_name = self.mbed_os_support()

# This plugin only works on Windows
if os_name and os_name.startswith('Windows'):
return True
return False

def setup(self, *args, **kwargs):
"""! Configure plugin, this function should be called before plugin execute() method is used.
"""
#Note you need to have jlink.exe on your system path!
self.JLINK = 'JLink.exe'
return True

def build_jlink_script(self, path, image_path):
"""! write a jlink commander file that
erase and flash the target

@param path the file path of the script we are writing in the function
@param image_path path to the binary file to flash
"""
with open(path, "w") as jlink_script:
jlink_script.write("connect\n")
jlink_script.write("erase\n")
jlink_script.write("loadbin {0} 0x08000000\n".format(image_path))
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the 0x08000000 address here representing?

Copy link

Choose a reason for hiding this comment

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

I believe it is the internal flash address of STM32 microcontrollers. However, there are microcontrollers with different flash addresses.

Copy link
Author

Choose a reason for hiding this comment

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

You are right. This plugin is what we use internally, and we only use ST. We definitely need feedback from non-ST future users of the plugin before we can precede.

Copy link

Choose a reason for hiding this comment

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

You should check the file format of the file under image_path. This line simply assumes target that internal flash starts at 0x08000000 and the image format is .bin. The .bin format is quite problematic as it doesn't include the loading address information - if the .bin format is preferred, then the elf->bin converter should somehow provide the loading address to be used here.

Another issue with .bin format is that it doesn't really support disjoint flash sections. For example, if user provides his own linker file which includes the QSPI section on STM32F746 target, then the resulting bin file is over 2 gigabytes (MCU flash startings at 0x08000000 while the QSPI starts at 0x90000000).

The much better alternative in such situations is .hex. See ARMmbed/mbed-os#9789 (comment) for example.

If the image format is .hex, you should use jlink_script.write("loadfile {}\n".format(image_path)) instead. .hex format includes all the addresses, so there's no need for build_jlink_script() to know them.

If the .hex file contains QSPI flash, it might be necessary to enable flashing external memories before calling loadfile/loadbin. You can do so using exec EnableEraseAllFlashBanks.

Below is fully working jlink script I use to program 32F746GDISCOVERY board (onboard ST-Link upgraded to JLink using https://www.segger.com/products/debug-probes/j-link/models/other-j-links/st-link-on-board/) with the .hex file that contains both internal flash and QSPI flash data.

device STM32F746NG
speed auto
si 1
exec EnableEraseAllFlashBanks
r
st
loadfile target.hex
r
g
qc

Note that the r and st commands after exec EnableEraseAllFlashBanks are needed. If you call loadfile directly after exec EnableEraseAllFlashBanks, then SEGGER J-Link Commander V6.42b (Compiled Feb 5 2019 17:33:07) fails to flash the QSPI memory on 32F746GDISCOVERY discovery board. I didn't find any mention of that in the JLink documentation, I discovered it by trial and error.

Copy link
Author

Choose a reason for hiding this comment

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

Some questions:

  • If I want to support .hex files, do I need to pass some configuration to the build system - to output hex files? Should the plugin somehow require this from the mbed_base.py? Is that even part of htrun?

  • If I want to support .bin files, I need to get the start address of the internal flash from the MCU. Is there any such mapping? I don't want to use mbed-ls here only to detect the target information. Is there a simple way to do this?

Copy link

@desowin desowin Mar 28, 2019

Choose a reason for hiding this comment

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

In my opinion .hex files are really simple to support here (because .hex files contain the loading address information), while .bin are pretty hard. I guess the reason why .bin is commonly used in mbed, is that the mbed drive does accept it (and it doesn't like .hex). The whole burden of knowing the load address is shifted onto the mbed drive.

There isn't any simple way to determine the load address of .bin file after it is generated. The addresses and what should go into .bin and/or .hex file is essentially determined by the linker script. Finding and parsing the linker script in htrun is in my opinion completely out of question.

To generate .hex file, the target should contain "OUTPUT_EXT": "hex". It is trivial for the user to create custom target based on any currently supported target and change the output format to .hex. Example custom_targets.json that changes the output format to .hex:

{
    "CUSTOM_DISCO_F746NG": {
        "inherits": [
            "DISCO_F746NG"
        ],
        "OUTPUT_EXT": "hex"
    }
}

You can check which targets do output .hex by default by looking at targets.json. One such example is: LPC55S69_NS.

A bit different approach to the problem, would be to pass the .elf file to tests plugin, and the plugin could then parse the elf header and generate .bin and/or .hex files as needed. Whoever generates the .bin file out of .elf, knows the correct loading address.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi everyone, good discussion going on here!

Addressing your questions @embeddedteam103:

If I want to support .hex files, do I need to pass some configuration to the build system - to output hex files? Should the plugin somehow require this from the mbed_base.py? Is that even part of htrun?

Htrun doesn't know anything about the build system really, it just knows about targets and binaries. I think just checking the file extension of image_path would be sufficient to make the behavior conditional (as @desowin suggested).

If I want to support .bin files, I need to get the start address of the internal flash from the MCU. Is there any such mapping? I don't want to use mbed-ls here only to detect the target information. Is there a simple way to do this?

I don't really have much of an idea here unfortunately. Htrun doesn't have access to the start address of ROM at the moment. It only initially supported flashing via the Mbed drive (USB MSD) as @desowin mentioned, so this information was never integrated into this tool.

One idea we've had for the future is to consolidate around pyocd as our flashing and reset mechanism. It has growing target support. At the moment I believe it supports CMSIS-DAP and ST-Link debuggers. Still no mainline support for JLink, but I think that's being worked on.

jlink_script.write("r\n")
jlink_script.write("go\n")
jlink_script.write("exit\n")


def execute(self, capability, *args, **kwargs):
"""! Executes capability by name

@param capability Capability name
@param args Additional arguments
@param kwargs Additional arguments

@details Each capability e.g. may directly just call some command line program or execute building pythonic function

@return Capability call return value
"""
result = False
if self.check_parameters(capability, *args, **kwargs) is True:

mcu = os.path.normpath(kwargs['mcu'])
image_path = os.path.normpath(kwargs['image_path'])

jlink_file_path = os.path.join(tempfile.gettempdir(), COPY_FILE)
Copy link
Contributor

Choose a reason for hiding this comment

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

If there are multiple processes running at once, I believe this it is possible for multiple processes trying to write and read to the same file. I think an alternative to this would be to use tempfile.mkstemp() instead.

Copy link
Author

Choose a reason for hiding this comment

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

Sure. We used the same tempfile methods in the ST-LINK plugin bugfix. Should I open a separate PR for changing to the STLINK plugin to use mkstemp as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

That would be great, thanks!


self.build_jlink_script(jlink_file_path, image_path)

if capability == 'JTAG':
cmd = [self.JLINK,
"-device", mcu,
"-if", "JTAG",
"-jtagconf", "-1,-1",
"-speed", "1000",
"-CommanderScript", jlink_file_path]

elif capability == 'SWD':
cmd = [self.JLINK,
"-device", mcu,
"-if", "SWD",
"-speed", "1000",
"-CommanderScript", jlink_file_path]
else:
self.print_plugin_error("Unsupported capability")

result = self.run_command(cmd)

return result


def load_plugin():
""" Returns plugin available in this module
"""
return HostTestPluginCopyMethod_Jlink()
108 changes: 108 additions & 0 deletions src/mbed_os_tools/test/host_tests_plugins/module_reset_jlink.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Copyright (c) 2018, Arm Limited and affiliates.
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you mind updating the copyright year here?

Suggested change
# Copyright (c) 2018, Arm Limited and affiliates.
# Copyright (c) 2019, Arm Limited and affiliates.

# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import tempfile

from .host_test_plugins import HostTestPluginBase


RESET_FILE = "reset.jlink"

class HostTestPluginResetMethod_Jlink(HostTestPluginBase):

# Plugin interface
name = 'HostTestPluginResetMethod_Jlink'
type = 'ResetMethod'
capabilities = ['JTAG', 'SWD']
Copy link
Contributor

Choose a reason for hiding this comment

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

Since this plugin is specific to JLink, could you prefix these as follows? We also stick with lower case capabilities across the plugins:

Suggested change
capabilities = ['JTAG', 'SWD']
capabilities = ['jlink_jtag', 'jlink_swd']

required_parameters = ['mcu']

def is_os_supported(self, os_name=None):
"""! In this implementation this plugin only is supporeted under Windows machines
Copy link
Contributor

Choose a reason for hiding this comment

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

Little spelling error here:

Suggested change
"""! In this implementation this plugin only is supporeted under Windows machines
"""! In this implementation this plugin only is supported under Windows machines

"""
# If no OS name provided use host OS name
if not os_name:
os_name = self.mbed_os_support()

# This plugin only works on Windows
if os_name and os_name.startswith('Windows'):
return True
return False

def setup(self, *args, **kwargs):
"""! Configure plugin, this function should be called before plugin execute() method is used.
"""
#Note you need to have jlink.exe on your system path!
self.JLINK = 'JLink.exe'
return True

def build_jlink_script(self, path):
"""! write a jlink commander file that
reset the target

@param path the file path of the script we are writing in the function
"""
with open(path, "w") as jlink_script:
jlink_script.write("r\n")
jlink_script.write("go\n")
jlink_script.write("exit\n")


def execute(self, capability, *args, **kwargs):
"""! Executes capability by name

@param capability Capability name
@param args Additional arguments
@param kwargs Additional arguments

@details Each capability e.g. may directly just call some command line program or execute building pythonic function

@return Capability call return value
"""
result = False
if self.check_parameters(capability, *args, **kwargs) is True:

mcu = os.path.normpath(kwargs['mcu'])

jlink_file_path = os.path.join(tempfile.gettempdir(), RESET_FILE)
Copy link
Contributor

Choose a reason for hiding this comment

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

If there are multiple processes running at once, I believe this has the opportunity for multiple processes trying to write and read to the same file. I think an alternative to this would be to use tempfile.mkstemp() instead.


self.build_jlink_script(jlink_file_path)

if capability == 'JTAG':
cmd = [self.JLINK,
"-device", mcu,
"-if", "JTAG",
"-jtagconf", "-1,-1",
"-speed", "1000",
"-CommanderScript", jlink_file_path]

elif capability == 'SWD':
cmd = [self.JLINK,
"-device", mcu,
"-if", "SWD",
"-speed", "1000",
"-CommanderScript", jlink_file_path]
else:
self.print_plugin_error("Unsupported capability")

result = self.run_command(cmd)

return result


def load_plugin():
""" Returns plugin available in this module
"""
return HostTestPluginResetMethod_Jlink()