diff --git a/pylink/jlink.py b/pylink/jlink.py index 36ea818..8c2f68c 100644 --- a/pylink/jlink.py +++ b/pylink/jlink.py @@ -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): + """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): diff --git a/pylink/structs.py b/pylink/structs.py index d1b0d71..5557d48 100644 --- a/pylink/structs.py +++ b/pylink/structs.py @@ -1468,3 +1468,54 @@ def __str__(self): String formatted instance. """ return '%s(SampleFreq=%uHz, MinDiv=%u)' % (self.__class__.__name__, self.BaseSampleFreq, self.MinDiv) + + +class JLinkJTAGDeviceInfo(ctypes.Structure): + """Structure representing the information of a device on the JTAG scan chain. + + Attributes: + sName: the name of the device. + IRLen: instruction register length. + IRPrint: instruction register print. + DeviceId: JTAG id. + """ + _fields_ = [ + ('sName', ctypes.c_char_p), + ('IRLen', ctypes.c_uint32), + ('IRPrint', ctypes.c_uint32), + ('DeviceId', ctypes.c_uint32) + ] + + def __repr__(self): + """Returns a representation of this instance. + + Args: + self (JLinkJTAGDeviceInfo): the ``JLinkJTAGDeviceInfo`` instance + + Returns: + Returns a string representation of the instance. + """ + return 'JLinkJTAGDeviceInfo(%s)' % self.__str__() + + def __str__(self): + """Returns a string representation of this instance. + + Args: + self (JLinkJTAGDeviceInfo): the ``JLinkJTAGDeviceInfo`` instance + + Returns: + Returns a string specifying the device name and ID. + """ + return '%s ' % (self.name, self.DeviceId) + + @property + def name(self): + """Returns the name of the JTAG device. + + Args: + self (JLinkJTAGDeviceInfo): the ``JLinkJTAGDeviceInfo`` instance + + Returns: + Device name. + """ + return ctypes.cast(self.sName, ctypes.c_char_p).value.decode() diff --git a/tests/unit/test_jlink.py b/tests/unit/test_jlink.py index 588aa45..7220d65 100644 --- a/tests/unit/test_jlink.py +++ b/tests/unit/test_jlink.py @@ -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) + + 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.