Skip to content

Commit c8810ec

Browse files
authored
[ISSUE-232] Add JTAG APIs (#245)
This patch adds JTAG APIs for executing instructions and sending data directly to devices over the JTAG scan chain.
1 parent 6a00172 commit c8810ec

File tree

3 files changed

+352
-0
lines changed

3 files changed

+352
-0
lines changed

pylink/jlink.py

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2629,6 +2629,201 @@ def jtag_flush(self):
26292629
"""
26302630
self._dll.JLINKARM_WriteBits()
26312631

2632+
@interface_required(enums.JLinkInterfaces.JTAG)
2633+
@open_required
2634+
def jtag_store_instruction(self, instr, ir_len):
2635+
"""Stores the specified JTAG instruction in the internal output buffer to
2636+
be written to the instruction register of the JTAG device.
2637+
2638+
The necessary bits to place the TAP controller into the Shift-IR state
2639+
are automatically added in order to form the complete command
2640+
sequence for the given instruction.
2641+
2642+
Data in the output buffer is not flushed until TDO data is required, or
2643+
``jtag_sync_bits()`` or ``jtag_sync_bytes()`` is called.
2644+
2645+
Args:
2646+
self (JLink): the ``JLink`` instance.
2647+
instr (int): JTAG protocol command bits.
2648+
ir_len (int): instruction register length.
2649+
2650+
Returns:
2651+
Bit position in input buffer after instruction transmission.
2652+
"""
2653+
buf = ctypes.c_uint8(instr)
2654+
return self._dll.JLINKARM_JTAG_StoreInst(ctypes.byref(buf), ir_len)
2655+
2656+
@interface_required(enums.JLinkInterfaces.JTAG)
2657+
@open_required
2658+
def jtag_store_data(self, data, dr_len):
2659+
"""Stores the specified JTAG data in the internal output buffer to be
2660+
written to the data register of the JTAG device.
2661+
2662+
The necessary bits to place the TAP controller into the Shift-DR state
2663+
are automatically added in order to form a complete data transmission.
2664+
2665+
Data in the output buffer is not flushed until TDO data is required, or
2666+
``jtag_sync_bits()`` or ``jtag_sync_bytes()`` is called.
2667+
2668+
Args:
2669+
self (JLink): the ``JLink`` instance.
2670+
data (list): list of bits to transfer.
2671+
dr_len (int): data register length.
2672+
2673+
Returns:
2674+
Bit position in input buffer after instruction transmission.
2675+
2676+
Raises:
2677+
TypeError: If passed data is not bytes or a list of integers.
2678+
"""
2679+
buf = data
2680+
if isinstance(buf, list):
2681+
buf = bytes(buf)
2682+
elif not any(isinstance(buf, t) for t in [bytes, bytearray]):
2683+
raise TypeError('Expected to be given bytes / list: given %s' % type(buf))
2684+
2685+
return self._dll.JLINKARM_JTAG_StoreData(buf, len(data) * dr_len)
2686+
2687+
@interface_required(enums.JLinkInterfaces.JTAG)
2688+
@connection_required
2689+
def jtag_get_device_info(self, index=0):
2690+
"""Retrieves the JTAG related information for the JTAG device on the scan chain.
2691+
2692+
Args:
2693+
self (JLink): the ``JLink`` instance.
2694+
index (int): index of the device on the scan chain.
2695+
2696+
Returns:
2697+
A ``JLinkJTAGDeviceInfo`` describing the requested device.
2698+
2699+
Raises:
2700+
ValueError: if index is less than 0 or >= number of devices on the scan chain.
2701+
"""
2702+
if index < 0:
2703+
raise ValueError('Invalid index provided, must be > 0.')
2704+
2705+
info = structs.JLinkJTAGDeviceInfo()
2706+
res = self._dll.JLINKARM_JTAG_GetDeviceInfo(index, ctypes.byref(info))
2707+
if res == -1:
2708+
raise ValueError('Invalid index provided, no device found.')
2709+
2710+
info.DeviceId = self._dll.JLINKARM_JTAG_GetDeviceId(index)
2711+
return info
2712+
2713+
@interface_required(enums.JLinkInterfaces.JTAG)
2714+
@open_required
2715+
def jtag_read(self, offset, num_bits):
2716+
"""Reads the specified number of bits from the JTAG input buffer.
2717+
2718+
Note:
2719+
If there is data in the output buffer, then ``num_bits`` of data will
2720+
be transmitted.
2721+
2722+
Args:
2723+
self (JLink): the ``JLink`` instance.
2724+
offset (int): bit position within the input buffer to read from.
2725+
num_bits (int): total number of bits to read.
2726+
2727+
Returns:
2728+
List of bytes containing the TDO data. This function may return more
2729+
bytes than expected due to no context around the data size. The
2730+
caller should pull bits as appopriate starting from the first returned
2731+
byte.
2732+
"""
2733+
# The smallest data length is 4 bits, so we use that as a divider. If
2734+
# the data length is actually 7 and the user specifies 7, we will
2735+
# return two integers, but that is fine, so the caller ultimately knows
2736+
# the data length they need.
2737+
buf_size = num_bits // 4
2738+
if (num_bits % 4) > 0:
2739+
buf_size += 1
2740+
buf = (ctypes.c_uint8 * buf_size)()
2741+
self._dll.JLINKARM_JTAG_GetData(ctypes.byref(buf), offset, num_bits)
2742+
return list(buf)
2743+
2744+
@interface_required(enums.JLinkInterfaces.JTAG)
2745+
@open_required
2746+
def jtag_read8(self, offset):
2747+
"""Reads a 8-bit integer from the JTAG input buffer.
2748+
2749+
Note:
2750+
If there is data in the output buffer, this function will force a
2751+
transmission.
2752+
2753+
Args:
2754+
self (JLink): the ``JLink`` instance.
2755+
offset (int): bit position within the input buffer to read from.
2756+
2757+
Returns:
2758+
The read 8-bit integer from the input buffer.
2759+
"""
2760+
return self._dll.JLINKARM_JTAG_GetU8(offset)
2761+
2762+
@interface_required(enums.JLinkInterfaces.JTAG)
2763+
@open_required
2764+
def jtag_read16(self, offset):
2765+
"""Reads a 16-bit integer from the JTAG input buffer.
2766+
2767+
Note:
2768+
If there is data in the output buffer, this function will force a
2769+
transmission.
2770+
2771+
Args:
2772+
self (JLink): the ``JLink`` instance.
2773+
offset (int): bit position within the input buffer to read from.
2774+
2775+
Returns:
2776+
The read 16-bit integer from the input buffer.
2777+
"""
2778+
return self._dll.JLINKARM_JTAG_GetU16(offset)
2779+
2780+
@interface_required(enums.JLinkInterfaces.JTAG)
2781+
@open_required
2782+
def jtag_read32(self, offset):
2783+
"""Reads a 32-bit integer from the JTAG input buffer.
2784+
2785+
Note:
2786+
If there is data in the output buffer, this function will force a
2787+
transmission.
2788+
2789+
Args:
2790+
self (JLink): the ``JLink`` instance.
2791+
offset (int): bit position within the input buffer to read from.
2792+
2793+
Returns:
2794+
The read 32-bit integer from the input buffer.
2795+
"""
2796+
return self._dll.JLINKARM_JTAG_GetU32(offset)
2797+
2798+
@interface_required(enums.JLinkInterfaces.JTAG)
2799+
@open_required
2800+
def jtag_sync_bits(self):
2801+
"""Flushes the internal output buffer to the JTAG device.
2802+
2803+
Args:
2804+
self (JLink): the ``JLink`` instance.
2805+
2806+
Returns:
2807+
``None``
2808+
"""
2809+
self._dll.JLINKARM_JTAG_SyncBits()
2810+
2811+
@interface_required(enums.JLinkInterfaces.JTAG)
2812+
@open_required
2813+
def jtag_sync_bytes(self):
2814+
"""Flushes the data content in the internal output buffer to the JTAG device.
2815+
2816+
This function will add the necessary bits to ensure the transmitted
2817+
data is byte-aligned.
2818+
2819+
Args:
2820+
self (JLink): the ``JLink`` instance.
2821+
2822+
Returns:
2823+
``None``
2824+
"""
2825+
self._dll.JLINKARM_JTAG_SyncBytes()
2826+
26322827
@interface_required(enums.JLinkInterfaces.SWD)
26332828
@connection_required
26342829
def swd_read8(self, offset):

pylink/structs.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,3 +1468,54 @@ def __str__(self):
14681468
String formatted instance.
14691469
"""
14701470
return '%s(SampleFreq=%uHz, MinDiv=%u)' % (self.__class__.__name__, self.BaseSampleFreq, self.MinDiv)
1471+
1472+
1473+
class JLinkJTAGDeviceInfo(ctypes.Structure):
1474+
"""Structure representing the information of a device on the JTAG scan chain.
1475+
1476+
Attributes:
1477+
sName: the name of the device.
1478+
IRLen: instruction register length.
1479+
IRPrint: instruction register print.
1480+
DeviceId: JTAG id.
1481+
"""
1482+
_fields_ = [
1483+
('sName', ctypes.c_char_p),
1484+
('IRLen', ctypes.c_uint32),
1485+
('IRPrint', ctypes.c_uint32),
1486+
('DeviceId', ctypes.c_uint32)
1487+
]
1488+
1489+
def __repr__(self):
1490+
"""Returns a representation of this instance.
1491+
1492+
Args:
1493+
self (JLinkJTAGDeviceInfo): the ``JLinkJTAGDeviceInfo`` instance
1494+
1495+
Returns:
1496+
Returns a string representation of the instance.
1497+
"""
1498+
return 'JLinkJTAGDeviceInfo(%s)' % self.__str__()
1499+
1500+
def __str__(self):
1501+
"""Returns a string representation of this instance.
1502+
1503+
Args:
1504+
self (JLinkJTAGDeviceInfo): the ``JLinkJTAGDeviceInfo`` instance
1505+
1506+
Returns:
1507+
Returns a string specifying the device name and ID.
1508+
"""
1509+
return '%s <Device Id. %s>' % (self.name, self.DeviceId)
1510+
1511+
@property
1512+
def name(self):
1513+
"""Returns the name of the JTAG device.
1514+
1515+
Args:
1516+
self (JLinkJTAGDeviceInfo): the ``JLinkJTAGDeviceInfo`` instance
1517+
1518+
Returns:
1519+
Device name.
1520+
"""
1521+
return ctypes.cast(self.sName, ctypes.c_char_p).value.decode()

tests/unit/test_jlink.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3297,6 +3297,112 @@ def test_jlink_jtag_flush(self):
32973297
self.jlink.jtag_flush()
32983298
self.dll.JLINKARM_WriteBits.assert_called_once()
32993299

3300+
def test_jlink_jtag_store_instruction(self):
3301+
"""Tests the J-Link JTAG method for storing a JTAG instruction.
3302+
3303+
Args:
3304+
self (TestJLink): the ``TestJLink`` instance
3305+
3306+
Returns:
3307+
``None``
3308+
"""
3309+
cmd = 0xE
3310+
self.jlink.jtag_store_instruction(cmd, 4)
3311+
3312+
c_byte, num_bits = self.dll.JLINKARM_JTAG_StoreInst.call_args[0]
3313+
self.assertEqual(4, num_bits)
3314+
3315+
c_uint = ctypes.cast(c_byte, ctypes.POINTER(ctypes.c_uint8)).contents
3316+
self.assertEqual(cmd, c_uint.value)
3317+
3318+
def test_jlink_jtag_store_data(self):
3319+
"""Tests the J-Link JTAG method for storing TDI.
3320+
3321+
Args:
3322+
self (TestJLink): the ``TestJLink`` instance
3323+
3324+
Returns:
3325+
``None``
3326+
"""
3327+
tdi = [0xA, 0x3]
3328+
self.jlink.jtag_store_data(tdi, 5)
3329+
3330+
buf, num_bits = self.dll.JLINKARM_JTAG_StoreData.call_args[0]
3331+
expected_num_bits = len(tdi) * 5
3332+
self.assertEqual(expected_num_bits, num_bits)
3333+
self.assertEqual(b'\x0A\x03', bytearray(buf))
3334+
3335+
def test_jlink_jtag_get_device_info(self):
3336+
"""Tests the J-Link JTAG method for retrieving JTAG device information.
3337+
3338+
Args:
3339+
self (TestJLink): the ``TestJLink`` instance
3340+
3341+
Return:
3342+
``None``
3343+
"""
3344+
with self.assertRaises(ValueError):
3345+
_ = self.jlink.jtag_get_device_info(-1)
3346+
3347+
self.dll.JLINKARM_JTAG_GetDeviceInfo.return_value = -1
3348+
with self.assertRaises(ValueError):
3349+
_ = self.jlink.jtag_get_device_info(0)
3350+
3351+
def _get_device_info(index, info):
3352+
c_info = ctypes.cast(info, ctypes.POINTER(structs.JLinkJTAGDeviceInfo)).contents
3353+
c_info.IRLen = 0x1
3354+
c_info.IRPrint = 0x2
3355+
c_info.DeviceId = 0x1337
3356+
name = b"Silk Song"
3357+
c_info.sName = ctypes.cast(name, ctypes.c_char_p)
3358+
return 0
3359+
3360+
self.dll.JLINKARM_JTAG_GetDeviceInfo = _get_device_info
3361+
self.dll.JLINKARM_JTAG_GetDeviceId.return_value = 0x1337
3362+
3363+
info = self.jlink.jtag_get_device_info(0)
3364+
self.assertEqual(0x1337, info.DeviceId)
3365+
self.assertEqual(0x1, info.IRLen)
3366+
self.assertEqual(0x2, info.IRPrint)
3367+
self.assertEqual("Silk Song", info.name)
3368+
3369+
def test_jlink_jtag_read(self):
3370+
"""Tests the J-Link JTAG read methods.
3371+
3372+
Args:
3373+
self (TestJLink): the ``TestJLink`` instance
3374+
3375+
Returns:
3376+
``None``
3377+
"""
3378+
self.jlink._tif = enums.JLinkInterfaces.JTAG
3379+
3380+
val = 0x12345678
3381+
self.dll.JLINKARM_JTAG_GetU8.return_value = val & 0xFF
3382+
self.dll.JLINKARM_JTAG_GetU16.return_value = val & 0xFFFF
3383+
self.dll.JLINKARM_JTAG_GetU32.return_value = val & 0xFFFFFFFF
3384+
3385+
self.assertEqual(0x78, self.jlink.jtag_read8(0))
3386+
self.assertEqual(0x5678, self.jlink.jtag_read16(0))
3387+
self.assertEqual(0x12345678, self.jlink.jtag_read32(0))
3388+
3389+
def _get_data(buf, offset, num_bits):
3390+
c_buf = ctypes.cast(buf, ctypes.POINTER(ctypes.c_uint8))
3391+
buf_index = 0
3392+
bit_index = 0
3393+
while num_bits:
3394+
rd_size = min(num_bits, 4)
3395+
num_bits -= rd_size
3396+
b = 0
3397+
for i in range(0, rd_size):
3398+
b |= ((val & (0x1 << bit_index)) >> bit_index) << i
3399+
bit_index += 1
3400+
c_buf[buf_index] = b
3401+
buf_index += 1
3402+
3403+
self.dll.JLINKARM_JTAG_GetData = _get_data
3404+
self.assertEqual([0x8, 0x7, 0x6, 0x5], self.jlink.jtag_read(0, 16))
3405+
33003406
def test_jlink_swd_read8(self):
33013407
"""Tests the J-Link ``swd_read8()`` method.
33023408

0 commit comments

Comments
 (0)