Skip to content

Commit 1904f31

Browse files
committed
ISSUE-232: Add JTAG APIs
This patch adds JTAG APIs for executing instructions and sending data directly to devices over the JTAG scan chain.
1 parent 6a00172 commit 1904f31

File tree

3 files changed

+346
-0
lines changed

3 files changed

+346
-0
lines changed

pylink/jlink.py

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2629,6 +2629,196 @@ 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, num_bits):
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+
num_bits (int): Number of bits in the given instruction.
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), num_bits)
2655+
2656+
@interface_required(enums.JLinkInterfaces.JTAG)
2657+
@open_required
2658+
def jtag_store_data(self, data, num_bits):
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+
num_bits (int): number of bits to to transfer per integer in ``data``.
2672+
2673+
Returns:
2674+
Bit position in input buffer after instruction transmission.
2675+
"""
2676+
buf = data
2677+
if isinstance(buf, list):
2678+
buf = bytes(buf)
2679+
2680+
return self._dll.JLINKARM_JTAG_StoreData(buf, len(data) * num_bits)
2681+
2682+
@interface_required(enums.JLinkInterfaces.JTAG)
2683+
@open_required
2684+
def jtag_get_device_info(self, index=0):
2685+
"""Retrieves the JTAG related information for the JTAG device on the scan chain.
2686+
2687+
Args:
2688+
self (JLink): the ``JLink`` instance.
2689+
index (int): index of the device on the scan chain.
2690+
2691+
Returns:
2692+
A ``JLinkJTAGDeviceInfo`` describing the requested device.
2693+
2694+
Raises:
2695+
ValueError: if index is less than 0 or >= number of devices on the scan chain.
2696+
"""
2697+
if index < 0:
2698+
raise ValueError('Invalid index provided, must be > 0.')
2699+
2700+
info = structs.JLinkJTAGDeviceInfo()
2701+
res = self._dll.JLINKARM_JTAG_GetDeviceInfo(index, ctypes.byref(info))
2702+
if res == -1:
2703+
raise ValueError('Invalid index provided, no device found.')
2704+
2705+
info.DeviceId = self._dll.JLINKARM_JTAG_GetDeviceId(index)
2706+
return info
2707+
2708+
@interface_required(enums.JLinkInterfaces.JTAG)
2709+
@open_required
2710+
def jtag_read(self, offset, num_bits):
2711+
"""Reads the specified number of bits from the JTAG input buffer.
2712+
2713+
Note:
2714+
If there is data in the output buffer, then ``num_bits`` of data will
2715+
be transmitted.
2716+
2717+
Args:
2718+
self (JLink): the ``JLink`` instance.
2719+
offset (int): bit position within the input buffer to read from.
2720+
num_bits (int): total number of bits to read.
2721+
2722+
Returns:
2723+
List of bytes containing the TDO data. This function may return more
2724+
bytes than expected due to no context around the data size. The
2725+
caller should pull bits as appopriate starting from the first returned
2726+
byte.
2727+
"""
2728+
# The smallest data length is 4 bits, so we use that as a divider. If
2729+
# the data length is actually 7 and the user specifies 7, we will
2730+
# return two integers, but that is fine, so the caller ultimately knows
2731+
# the data length they need.
2732+
buf_size = num_bits // 4
2733+
if (buf_size % 4) > 0:
2734+
buf_size += 1
2735+
buf = (ctypes.c_uint8 * buf_size)()
2736+
self._dll.JLINKARM_JTAG_GetData(ctypes.byref(buf), offset, num_bits)
2737+
return list(buf)
2738+
2739+
@interface_required(enums.JLinkInterfaces.JTAG)
2740+
@open_required
2741+
def jtag_read8(self, offset):
2742+
"""Reads a 8-bit integer from the JTAG input buffer.
2743+
2744+
Note:
2745+
If there is data in the output buffer, this function will force a
2746+
transmission.
2747+
2748+
Args:
2749+
self (JLink): the ``JLink`` instance.
2750+
offset (int): bit position within the input buffer to read from.
2751+
2752+
Returns:
2753+
The read 8-bit integer from the input buffer.
2754+
"""
2755+
return self._dll.JLINKARM_JTAG_GetU8(offset)
2756+
2757+
@interface_required(enums.JLinkInterfaces.JTAG)
2758+
@open_required
2759+
def jtag_read16(self, offset):
2760+
"""Reads a 16-bit integer from the JTAG input buffer.
2761+
2762+
Note:
2763+
If there is data in the output buffer, this function will force a
2764+
transmission.
2765+
2766+
Args:
2767+
self (JLink): the ``JLink`` instance.
2768+
offset (int): bit position within the input buffer to read from.
2769+
2770+
Returns:
2771+
The read 16-bit integer from the input buffer.
2772+
"""
2773+
return self._dll.JLINKARM_JTAG_GetU16(offset)
2774+
2775+
@interface_required(enums.JLinkInterfaces.JTAG)
2776+
@open_required
2777+
def jtag_read32(self, offset):
2778+
"""Reads a 32-bit integer from the JTAG input buffer.
2779+
2780+
Note:
2781+
If there is data in the output buffer, this function will force a
2782+
transmission.
2783+
2784+
Args:
2785+
self (JLink): the ``JLink`` instance.
2786+
offset (int): bit position within the input buffer to read from.
2787+
2788+
Returns:
2789+
The read 32-bit integer from the input buffer.
2790+
"""
2791+
return self._dll.JLINKARM_JTAG_GetU32(offset)
2792+
2793+
@interface_required(enums.JLinkInterfaces.JTAG)
2794+
@open_required
2795+
def jtag_sync_bits(self):
2796+
"""Flushes the internal output buffer to the JTAG device.
2797+
2798+
Args:
2799+
self (JLink): the ``JLink`` instance.
2800+
2801+
Returns:
2802+
``None``
2803+
"""
2804+
self._dll.JLINKARM_JTAG_SyncBits()
2805+
2806+
@interface_required(enums.JLinkInterfaces.JTAG)
2807+
@open_required
2808+
def jtag_sync_bytes(self):
2809+
"""Flushes the data content in the internal output buffer to the JTAG device.
2810+
2811+
This function will add the necessary bits to ensure the transmitted
2812+
data is byte-aligned.
2813+
2814+
Args:
2815+
self (JLink): the ``JLink`` instance.
2816+
2817+
Returns:
2818+
``None``
2819+
"""
2820+
self._dll.JLINKARM_JTAG_SyncBytes()
2821+
26322822
@interface_required(enums.JLinkInterfaces.SWD)
26332823
@connection_required
26342824
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: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3297,6 +3297,111 @@ def test_jlink_jtag_flush(self):
32973297
self.jlink.jtag_flush()
32983298
self.dll.JLINKARM_WriteBits.assert_called_once()
32993299

3300+
def test_jlijnk_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+
self.assertEqual(10, num_bits)
3332+
self.assertEqual(b'\x0A\x03', bytearray(buf))
3333+
3334+
def test_jlink_jtag_get_device_info(self):
3335+
"""Tests the J-Link JTAG method for retrieving JTAG device information.
3336+
3337+
Args:
3338+
self (TestJLink): the ``TestJLink`` instance
3339+
3340+
Return:
3341+
``None``
3342+
"""
3343+
with self.assertRaises(ValueError):
3344+
_ = self.jlink.jtag_get_device_info(-1)
3345+
3346+
self.dll.JLINKARM_JTAG_GetDeviceInfo.return_value = -1
3347+
with self.assertRaises(ValueError):
3348+
_ = self.jlink.jtag_get_device_info(0)
3349+
3350+
def _get_device_info(index, info):
3351+
c_info = ctypes.cast(info, ctypes.POINTER(structs.JLinkJTAGDeviceInfo)).contents
3352+
c_info.IRLen = 0x1
3353+
c_info.IRPrint = 0x2
3354+
c_info.DeviceId = 0x1337
3355+
name = b"Silk Song"
3356+
c_info.sName = ctypes.cast(name, ctypes.c_char_p)
3357+
return 0
3358+
3359+
self.dll.JLINKARM_JTAG_GetDeviceInfo = _get_device_info
3360+
self.dll.JLINKARM_JTAG_GetDeviceId.return_value = 0x1337
3361+
3362+
info = self.jlink.jtag_get_device_info(0)
3363+
self.assertEqual(0x1337, info.DeviceId)
3364+
self.assertEqual(0x1, info.IRLen)
3365+
self.assertEqual(0x2, info.IRPrint)
3366+
self.assertEqual("Silk Song", info.name)
3367+
3368+
def test_jlink_jtag_read(self):
3369+
"""Tests the J-Link JTAG read methods.
3370+
3371+
Args:
3372+
self (TestJLink): the ``TestJLink`` instance
3373+
3374+
Returns:
3375+
``None``
3376+
"""
3377+
self.jlink._tif = enums.JLinkInterfaces.JTAG
3378+
3379+
val = 0x12345678
3380+
self.dll.JLINKARM_JTAG_GetU8.return_value = val & 0xFF
3381+
self.dll.JLINKARM_JTAG_GetU16.return_value = val & 0xFFFF
3382+
self.dll.JLINKARM_JTAG_GetU32.return_value = val & 0xFFFFFFFF
3383+
3384+
self.assertEqual(0x78, self.jlink.jtag_read8(0))
3385+
self.assertEqual(0x5678, self.jlink.jtag_read16(0))
3386+
self.assertEqual(0x12345678, self.jlink.jtag_read32(0))
3387+
3388+
def _get_data(buf, offset, num_bits):
3389+
c_buf = ctypes.cast(buf, ctypes.POINTER(ctypes.c_uint8))
3390+
buf_index = 0
3391+
bit_index = 0
3392+
while num_bits:
3393+
rd_size = min(num_bits, 4)
3394+
num_bits -= rd_size
3395+
b = 0
3396+
for i in range(0, rd_size):
3397+
b |= ((val & (0x1 << bit_index)) >> bit_index) << i
3398+
bit_index += 1
3399+
c_buf[buf_index] = b
3400+
buf_index += 1
3401+
3402+
self.dll.JLINKARM_JTAG_GetData = _get_data
3403+
self.assertEqual([0x8, 0x7, 0x6, 0x5], self.jlink.jtag_read(0, 16))
3404+
33003405
def test_jlink_swd_read8(self):
33013406
"""Tests the J-Link ``swd_read8()`` method.
33023407

0 commit comments

Comments
 (0)