-
Notifications
You must be signed in to change notification settings - Fork 147
[ISSUE-232] Add JTAG APIs #245
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2629,6 +2629,201 @@ def jtag_flush(self): | |
| """ | ||
| self._dll.JLINKARM_WriteBits() | ||
|
|
||
| @interface_required(enums.JLinkInterfaces.JTAG) | ||
| @open_required | ||
| def jtag_store_instruction(self, instr, ir_len): | ||
| """Stores the specified JTAG instruction in the internal output buffer to | ||
| be written to the instruction register of the JTAG device. | ||
|
|
||
| The necessary bits to place the TAP controller into the Shift-IR state | ||
| are automatically added in order to form the complete command | ||
| sequence for the given instruction. | ||
|
|
||
| Data in the output buffer is not flushed until TDO data is required, or | ||
| ``jtag_sync_bits()`` or ``jtag_sync_bytes()`` is called. | ||
|
|
||
| Args: | ||
| self (JLink): the ``JLink`` instance. | ||
| instr (int): JTAG protocol command bits. | ||
| ir_len (int): instruction register length. | ||
|
|
||
| Returns: | ||
| Bit position in input buffer after instruction transmission. | ||
| """ | ||
| buf = ctypes.c_uint8(instr) | ||
| return self._dll.JLINKARM_JTAG_StoreInst(ctypes.byref(buf), ir_len) | ||
|
|
||
| @interface_required(enums.JLinkInterfaces.JTAG) | ||
| @open_required | ||
| def jtag_store_data(self, data, dr_len): | ||
| """Stores the specified JTAG data in the internal output buffer to be | ||
| written to the data register of the JTAG device. | ||
|
|
||
| The necessary bits to place the TAP controller into the Shift-DR state | ||
| are automatically added in order to form a complete data transmission. | ||
|
|
||
| Data in the output buffer is not flushed until TDO data is required, or | ||
| ``jtag_sync_bits()`` or ``jtag_sync_bytes()`` is called. | ||
|
|
||
| Args: | ||
| self (JLink): the ``JLink`` instance. | ||
| data (list): list of bits to transfer. | ||
| dr_len (int): data register length. | ||
|
|
||
| Returns: | ||
| Bit position in input buffer after instruction transmission. | ||
|
|
||
| Raises: | ||
| TypeError: If passed data is not bytes or a list of integers. | ||
| """ | ||
| buf = data | ||
| if isinstance(buf, list): | ||
| buf = bytes(buf) | ||
| elif not any(isinstance(buf, t) for t in [bytes, bytearray]): | ||
| raise TypeError('Expected to be given bytes / list: given %s' % type(buf)) | ||
|
|
||
| return self._dll.JLINKARM_JTAG_StoreData(buf, len(data) * dr_len) | ||
|
|
||
| @interface_required(enums.JLinkInterfaces.JTAG) | ||
| @connection_required | ||
| def jtag_get_device_info(self, index=0): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function takes index as an input to determine which TAP on the JTAG chain to query - this makes perfect sense to me. How do the jtag_store_instruction and jtag_store_data functions determine which JTAG TAP to access? I.e. if I have a JTAG chain with, say, three taps on it how do I specify which tap to access?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You would have to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK - fantastic - that makes sense. Thanks |
||
| """Retrieves the JTAG related information for the JTAG device on the scan chain. | ||
|
|
||
| Args: | ||
| self (JLink): the ``JLink`` instance. | ||
| index (int): index of the device on the scan chain. | ||
|
|
||
| Returns: | ||
| A ``JLinkJTAGDeviceInfo`` describing the requested device. | ||
|
|
||
| Raises: | ||
| ValueError: if index is less than 0 or >= number of devices on the scan chain. | ||
| """ | ||
| if index < 0: | ||
| raise ValueError('Invalid index provided, must be > 0.') | ||
|
|
||
| info = structs.JLinkJTAGDeviceInfo() | ||
| res = self._dll.JLINKARM_JTAG_GetDeviceInfo(index, ctypes.byref(info)) | ||
| if res == -1: | ||
| raise ValueError('Invalid index provided, no device found.') | ||
|
|
||
| info.DeviceId = self._dll.JLINKARM_JTAG_GetDeviceId(index) | ||
| return info | ||
|
|
||
| @interface_required(enums.JLinkInterfaces.JTAG) | ||
| @open_required | ||
| def jtag_read(self, offset, num_bits): | ||
| """Reads the specified number of bits from the JTAG input buffer. | ||
|
|
||
| Note: | ||
| If there is data in the output buffer, then ``num_bits`` of data will | ||
| be transmitted. | ||
|
|
||
| Args: | ||
| self (JLink): the ``JLink`` instance. | ||
| offset (int): bit position within the input buffer to read from. | ||
| num_bits (int): total number of bits to read. | ||
|
|
||
| Returns: | ||
| List of bytes containing the TDO data. This function may return more | ||
| bytes than expected due to no context around the data size. The | ||
| caller should pull bits as appopriate starting from the first returned | ||
| byte. | ||
| """ | ||
| # The smallest data length is 4 bits, so we use that as a divider. If | ||
| # the data length is actually 7 and the user specifies 7, we will | ||
| # return two integers, but that is fine, so the caller ultimately knows | ||
| # the data length they need. | ||
| buf_size = num_bits // 4 | ||
| if (num_bits % 4) > 0: | ||
| buf_size += 1 | ||
| buf = (ctypes.c_uint8 * buf_size)() | ||
| self._dll.JLINKARM_JTAG_GetData(ctypes.byref(buf), offset, num_bits) | ||
| return list(buf) | ||
|
|
||
| @interface_required(enums.JLinkInterfaces.JTAG) | ||
| @open_required | ||
| def jtag_read8(self, offset): | ||
| """Reads a 8-bit integer from the JTAG input buffer. | ||
|
|
||
| Note: | ||
| If there is data in the output buffer, this function will force a | ||
| transmission. | ||
|
|
||
| Args: | ||
| self (JLink): the ``JLink`` instance. | ||
| offset (int): bit position within the input buffer to read from. | ||
|
|
||
| Returns: | ||
| The read 8-bit integer from the input buffer. | ||
| """ | ||
| return self._dll.JLINKARM_JTAG_GetU8(offset) | ||
|
|
||
| @interface_required(enums.JLinkInterfaces.JTAG) | ||
| @open_required | ||
| def jtag_read16(self, offset): | ||
| """Reads a 16-bit integer from the JTAG input buffer. | ||
|
|
||
| Note: | ||
| If there is data in the output buffer, this function will force a | ||
| transmission. | ||
|
|
||
| Args: | ||
| self (JLink): the ``JLink`` instance. | ||
| offset (int): bit position within the input buffer to read from. | ||
|
|
||
| Returns: | ||
| The read 16-bit integer from the input buffer. | ||
| """ | ||
| return self._dll.JLINKARM_JTAG_GetU16(offset) | ||
|
|
||
| @interface_required(enums.JLinkInterfaces.JTAG) | ||
| @open_required | ||
| def jtag_read32(self, offset): | ||
| """Reads a 32-bit integer from the JTAG input buffer. | ||
|
|
||
| Note: | ||
| If there is data in the output buffer, this function will force a | ||
| transmission. | ||
|
|
||
| Args: | ||
| self (JLink): the ``JLink`` instance. | ||
| offset (int): bit position within the input buffer to read from. | ||
|
|
||
| Returns: | ||
| The read 32-bit integer from the input buffer. | ||
| """ | ||
| return self._dll.JLINKARM_JTAG_GetU32(offset) | ||
|
|
||
| @interface_required(enums.JLinkInterfaces.JTAG) | ||
| @open_required | ||
| def jtag_sync_bits(self): | ||
| """Flushes the internal output buffer to the JTAG device. | ||
|
|
||
| Args: | ||
| self (JLink): the ``JLink`` instance. | ||
|
|
||
| Returns: | ||
| ``None`` | ||
| """ | ||
| self._dll.JLINKARM_JTAG_SyncBits() | ||
|
|
||
| @interface_required(enums.JLinkInterfaces.JTAG) | ||
| @open_required | ||
| def jtag_sync_bytes(self): | ||
| """Flushes the data content in the internal output buffer to the JTAG device. | ||
|
|
||
| This function will add the necessary bits to ensure the transmitted | ||
| data is byte-aligned. | ||
|
|
||
| Args: | ||
| self (JLink): the ``JLink`` instance. | ||
|
|
||
| Returns: | ||
| ``None`` | ||
| """ | ||
| self._dll.JLINKARM_JTAG_SyncBytes() | ||
|
|
||
| @interface_required(enums.JLinkInterfaces.SWD) | ||
| @connection_required | ||
| def swd_read8(self, offset): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3297,6 +3297,112 @@ def test_jlink_jtag_flush(self): | |
| self.jlink.jtag_flush() | ||
| self.dll.JLINKARM_WriteBits.assert_called_once() | ||
|
|
||
| def test_jlink_jtag_store_instruction(self): | ||
| """Tests the J-Link JTAG method for storing a JTAG instruction. | ||
|
|
||
| Args: | ||
| self (TestJLink): the ``TestJLink`` instance | ||
|
|
||
| Returns: | ||
| ``None`` | ||
| """ | ||
| cmd = 0xE | ||
| self.jlink.jtag_store_instruction(cmd, 4) | ||
|
|
||
| c_byte, num_bits = self.dll.JLINKARM_JTAG_StoreInst.call_args[0] | ||
| self.assertEqual(4, num_bits) | ||
|
|
||
| c_uint = ctypes.cast(c_byte, ctypes.POINTER(ctypes.c_uint8)).contents | ||
| self.assertEqual(cmd, c_uint.value) | ||
|
|
||
| def test_jlink_jtag_store_data(self): | ||
| """Tests the J-Link JTAG method for storing TDI. | ||
|
|
||
| Args: | ||
| self (TestJLink): the ``TestJLink`` instance | ||
|
|
||
| Returns: | ||
| ``None`` | ||
| """ | ||
| tdi = [0xA, 0x3] | ||
| self.jlink.jtag_store_data(tdi, 5) | ||
|
|
||
| buf, num_bits = self.dll.JLINKARM_JTAG_StoreData.call_args[0] | ||
| expected_num_bits = len(tdi) * 5 | ||
| self.assertEqual(expected_num_bits, num_bits) | ||
| self.assertEqual(b'\x0A\x03', bytearray(buf)) | ||
|
|
||
| def test_jlink_jtag_get_device_info(self): | ||
| """Tests the J-Link JTAG method for retrieving JTAG device information. | ||
|
|
||
| Args: | ||
| self (TestJLink): the ``TestJLink`` instance | ||
|
|
||
| Return: | ||
| ``None`` | ||
| """ | ||
| with self.assertRaises(ValueError): | ||
| _ = self.jlink.jtag_get_device_info(-1) | ||
|
|
||
| self.dll.JLINKARM_JTAG_GetDeviceInfo.return_value = -1 | ||
| with self.assertRaises(ValueError): | ||
| _ = self.jlink.jtag_get_device_info(0) | ||
|
|
||
| def _get_device_info(index, info): | ||
| c_info = ctypes.cast(info, ctypes.POINTER(structs.JLinkJTAGDeviceInfo)).contents | ||
| c_info.IRLen = 0x1 | ||
| c_info.IRPrint = 0x2 | ||
| c_info.DeviceId = 0x1337 | ||
| name = b"Silk Song" | ||
| c_info.sName = ctypes.cast(name, ctypes.c_char_p) | ||
| return 0 | ||
|
|
||
| self.dll.JLINKARM_JTAG_GetDeviceInfo = _get_device_info | ||
| self.dll.JLINKARM_JTAG_GetDeviceId.return_value = 0x1337 | ||
|
|
||
| info = self.jlink.jtag_get_device_info(0) | ||
| self.assertEqual(0x1337, info.DeviceId) | ||
| self.assertEqual(0x1, info.IRLen) | ||
| self.assertEqual(0x2, info.IRPrint) | ||
| self.assertEqual("Silk Song", info.name) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should info.name actually be info.sName? @hkpeprah
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh yes - got it. Thanks |
||
|
|
||
| def test_jlink_jtag_read(self): | ||
| """Tests the J-Link JTAG read methods. | ||
|
|
||
| Args: | ||
| self (TestJLink): the ``TestJLink`` instance | ||
|
|
||
| Returns: | ||
| ``None`` | ||
| """ | ||
| self.jlink._tif = enums.JLinkInterfaces.JTAG | ||
|
|
||
| val = 0x12345678 | ||
| self.dll.JLINKARM_JTAG_GetU8.return_value = val & 0xFF | ||
| self.dll.JLINKARM_JTAG_GetU16.return_value = val & 0xFFFF | ||
| self.dll.JLINKARM_JTAG_GetU32.return_value = val & 0xFFFFFFFF | ||
|
|
||
| self.assertEqual(0x78, self.jlink.jtag_read8(0)) | ||
| self.assertEqual(0x5678, self.jlink.jtag_read16(0)) | ||
| self.assertEqual(0x12345678, self.jlink.jtag_read32(0)) | ||
|
|
||
| def _get_data(buf, offset, num_bits): | ||
| c_buf = ctypes.cast(buf, ctypes.POINTER(ctypes.c_uint8)) | ||
| buf_index = 0 | ||
| bit_index = 0 | ||
| while num_bits: | ||
| rd_size = min(num_bits, 4) | ||
| num_bits -= rd_size | ||
| b = 0 | ||
| for i in range(0, rd_size): | ||
| b |= ((val & (0x1 << bit_index)) >> bit_index) << i | ||
| bit_index += 1 | ||
| c_buf[buf_index] = b | ||
| buf_index += 1 | ||
|
|
||
| self.dll.JLINKARM_JTAG_GetData = _get_data | ||
| self.assertEqual([0x8, 0x7, 0x6, 0x5], self.jlink.jtag_read(0, 16)) | ||
|
|
||
| def test_jlink_swd_read8(self): | ||
| """Tests the J-Link ``swd_read8()`` method. | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If someone didn't read the doctstring and simply passed in an int for data then this if statement wouldn't execute and then the subsequent call to self._dll.JLINKARM_JTAG_StoreData would fail because you can't call len() on an int.
Is it worth adding an else to the if isinstance(buf, list): to raise if buf is not a list?