openFPGALoader/src/jlink.hpp

313 lines
9.4 KiB
C++
Raw Normal View History

2022-03-12 18:46:23 +01:00
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2021 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*/
#ifndef SRC_JLINK_HPP_
#define SRC_JLINK_HPP_
#include <libusb.h>
#include <string>
#include <vector>
#include "jtagInterface.hpp"
/*!
* \brief Segger JLink probe driver
*/
class Jlink: public JtagInterface {
public:
/*!
2022-12-18 23:29:21 +01:00
* \brief constructor: open device
2022-03-12 18:46:23 +01:00
* \param[in] clkHz: output clock frequency
* \param[in] verbose: verbose level -1 quiet, 0 normal,
* 1 verbose, 2 debug
*/
Jlink(uint32_t clkHz, int8_t verbose);
~Jlink();
2022-12-18 23:29:21 +01:00
// jtag Interface requirement
2022-03-12 18:46:23 +01:00
/*!
* \brief configure probe clk frequency
* \param[in] clkHZ: frequency in Hertz
* \return <= 0 if something wrong, clkHZ otherwise
*/
int setClkFreq(uint32_t clkHZ) override;
/*!
* \brief store a len tms bits in a buffer. send is only done if
* flush_buffer
* \param[in] tms: serie of tms state
* \param[in] len: number of tms bits
* \param[in] flush_buffer: force buffer to be send or not
* \return <= 0 if something wrong, len otherwise
*/
int writeTMS(uint8_t *tms, uint32_t len, bool flush_buffer) override;
/*!
* \brief write and read len bits with optional tms set to 1 if end
* \param[in] tx: serie of tdi state to send
* \param[out] rx: buffer to store tdo bits from device
* \param[in] len: number of bit to read/write
* \param[in] end: if true tms is set to one with the last tdi bit
* \return <= 0 if something wrong, len otherwise
*/
int writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end) override;
/*!
* \brief access ll_write outer this class / directly receives
2022-12-18 23:29:21 +01:00
* fully filled tms, tdi buffers, and optionally tdo
* \param[in] tms: tms buffer
* \param[in] tdi: tdi buffer
* \param[out] tdo: tdo buffer
* \param[in] numbits: tms/tdi/tdo buffer size (in bit)
*/
virtual bool writeTMSTDI(const uint8_t *tms, const uint8_t *tdi,
uint8_t *tdo, uint32_t numbits) override;
2022-03-12 18:46:23 +01:00
/*!
* \brief send a serie of clock cycle with constant TMS and TDI
* \param[in] tms: tms state
* \param[in] tdi: tdi state
* \param[in] clk_len: number of clock cycle
* \return <= 0 if something wrong, clk_len otherwise
*/
int toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len) override;
/*!
* \brief flush internal buffer
* \return <=0 if something fail, > 0 otherwise
*/
int flush() override;
/*
* unused
*/
int get_buffer_size() override { return 2048;}
bool isFull() override { return false;}
// JLINK specifics methods
std::string get_version();
void get_speeds();
bool set_speed(uint16_t freqHz);
bool get_caps();
bool set_ks_power(bool val);
void read_config();
int get_hw_version();
bool get_result();
bool max_mem_block(uint32_t *max_mem);
private:
// Jlink EMU_CMD code
enum {
EMU_CMD_VERSION = 0x01,
EMU_CMD_SET_SPEED = 0x05,
EMU_CMD_SET_KS_POWER = 0x08,
EMU_CMD_GET_SPEEDS = 0xC0,
EMU_CMD_GET_MAX_MEM_BLOCK = 0xD4,
EMU_CMD_HW_JTAG_GET_RESULT = 0xD6,
EMU_CMD_SELECT_IF = 0xC7,
EMU_CMD_HW_JTAG2 = 0xCE,
EMU_CMD_HW_JTAG3 = 0xCF,
EMU_CMD_GET_CAPS = 0xE8,
EMU_CMD_GET_HW_VERSION = 0xF0,
EMU_CMD_READ_CONFIG = 0xF2,
EMU_CMD_WRITE_CONFIG = 0xF3
};
// JLink hardware type
const std::string jlink_hw_type[4] = {
"J-Link",
"J-Trace",
"Flasher",
"J-Link Pro"
};
// Jlink configuration structure
struct jlink_cfg_t {
uint8_t usb_adr;
uint8_t reserved1[3]; // 0x01 - 0x03: 0xff
uint32_t kickstart; // Kickstart power on JTAG-pin 19
uint8_t reserved2[24]; // 0x08 - 0x1F: 0xff
uint32_t ip_address; // IP-Address (Only for J-Link Pro)
uint32_t subnetmask; // subnetmask (Only for J-Link Pro)
uint8_t reserved3[8]; // 0x08 - 0x1F: 0xff
uint8_t mackaddr[6]; // MAC-Address (Only for J-Link Pro)
uint8_t reserved[202]; // MAC-Address (Only for J-Link Pro)
} __attribute__((__packed__));
typedef jlink_cfg_t jlink_cfg;
// JLink caps code
typedef enum {
EMU_CAP_GET_HW_VERSION = (1 << 1),
EMU_CAP_READ_CONFIG = (1 << 4),
EMU_CAP_WRITE_CONFIG = (1 << 5),
EMU_CAP_SPEED_INFO = (1 << 9),
EMU_CAP_GET_HW_INFO = (1 << 12),
EMU_CAP_SELECT_IF = (1 << 17),
EMU_CAP_GET_CPU_CAPS = (1 << 21)
} emu_caps_t;
// JLink caps code -> string
const std::string jlink_caps_flags[32] {
"EMU_CAP_RESERVED",
"EMU_CAP_GET_HW_VERSION",
"EMU_CAP_WRITE_DCC",
"EMU_CAP_ADAPTIVE_CLOCKING",
"EMU_CAP_READ_CONFIG",
"EMU_CAP_WRITE_CONFIG",
"EMU_CAP_TRACE",
"EMU_CAP_WRITE_MEM",
"EMU_CAP_READ_MEM",
"EMU_CAP_SPEED_INFO",
"EMU_CAP_EXEC_CODE",
"EMU_CAP_GET_MAX_BLOCK_SIZE",
"EMU_CAP_GET_HW_INFO",
"EMU_CAP_SET_KS_POWER",
"EMU_CAP_RESET_STOP_TIMED",
"Reserved",
"EMU_CAP_MEASURE_RTCK_REACT",
"EMU_CAP_SELECT_IF", // 17
"EMU_CAP_RW_MEM_ARM79",
"EMU_CAP_GET_COUNTERS",
"EMU_CAP_READ_DCC", // 20
"EMU_CAP_GET_CPU_CAPS",
"EMU_CAP_EXEC_CPU_CMD",
"EMU_CAP_SWO",
"EMU_CAP_WRITE_DCC_EX",
"EMU_CAP_UPDATE_FIRMWARE_EX", // 25
"EMU_CAP_FILE_IO",
"EMU_CAP_REGISTER",
"EMU_CAP_INDICATORS",
"EMU_CAP_TEST_NET_SPEED",
"EMU_CAP_RAWTRACE",
"Reserved"
};
/*!
* \brief lowlevel write: EMU_CMD_HW_JTAGx implementation
* \param[out]: tdo: TDO read buffer (may be null)
* \return false when failure
*/
bool ll_write(uint8_t *tdo);
/*!
* \brief read size Bytes using read endpoint
* \param[in] cmd: Jlink cmd
* \param[out] val: received Bytes
* \param[in] size: number of Bytes to read
* \return false when transaction failure, true otherwise
*/
bool cmd_read(uint8_t cmd, uint8_t *val, int size);
/*!
* \brief read one short using read endpoint
* \param[in] cmd: Jlink cmd
* \param[out] val: received short
* \return false when transaction failure, true otherwise
*/
bool cmd_read(uint8_t cmd, uint16_t *val);
/*!
* \brief read one word using read endpoint
* \param[in] cmd: Jlink cmd
* \param[out] val: received word
* \return false when transaction failure, true otherwise
*/
bool cmd_read(uint8_t cmd, uint32_t *val);
/*!
* \brief write one short using write endpoint
* \param[in] cmd: Jlink cmd
* \param[in] val: value to send
* \return false when transaction failure, true otherwise
*/
bool cmd_write(uint8_t cmd, uint16_t param);
/*!
* \brief write one Byte using write endpoint into EMU_CMD_X register
* \param[in] cmd: Jlink cmd
* \param[in] val: value to send
* \return false when transaction failure, true otherwise
*/
bool cmd_write(uint8_t cmd, uint8_t param);
/*!
* \brief lowlevel method to read using libusb_bulk_transfer
* with read endpoint. If required do OByte read
* \param[out] buf: buffer used to store read Bytes
* \param[in] size: buffer size
* \return number of Byte reads or libusb error code
*/
int read_device(uint8_t *buf, uint32_t size);
/*!
* \brief lowlevel method to write using libusb_bulk_transfer
* with write endpoint.
* \param[in] buf: Bytes to send
* \param[in] size: buffer size
* \return false when failure, true otherwise
*/
bool write_device(const uint8_t *buf, uint32_t size);
/*!
* \brief configure interface (JTAG/SWD)
* \param[in] interface: 0 -> JTAG, 1 -> SWD
*/
bool set_interface(uint8_t interface);
/*!
* \brief analyze one USB peripheral to search if compatible and
* and for interface/config/alt IDs
* \param[in] dev: USB device
* \param[in] desc: libusb_device_descriptor
* \param[out] interface_idx: interface ID
* \param[out] config_idx: configuration descriptor ID
* \return false if failure or no interface found
*/
bool jlink_search_interface(libusb_device *dev,
libusb_device_descriptor *desc,
int *interface_idx, int *config_idx, int *alt_idx);
/*!
* \brief iterate on all USB peripheral to find one JLink
* \return false when failure, unable to open or no device found
*/
bool jlink_scan_usb();
typedef struct {
libusb_device *usb_dev;
int if_idx;
int cfg_idx;
int alt_idx;
} jlink_devices_t;
uint32_t _base_freq; /*!< JLink interface frequency */
uint16_t _min_div; /*!> dividor applied to base freq */
int jlink_write_ep; /*!< jlink write endpoint */
int jlink_read_ep; /*!< jlink read endpoint */
int jlink_interface; /*!< jlink usb interface */
libusb_device_handle *jlink_handle;
libusb_context *jlink_ctx;
std::vector<jlink_devices_t> device_available; /*!< list of compatible devices */
bool _verbose; /*!< display informations */
bool _debug; /*!< display debug messages */
bool _quiet; /*!< no messages */
// buffers for xfer, tdi and tdo
// each jlink's buffer have 2K Byte
// enough to send full jtag write
2022-12-18 23:29:21 +01:00
// buffers must be independent
2022-03-12 18:46:23 +01:00
uint8_t _xfer_buf[(2048*2) + 4]; /*!> internal buffer */
uint8_t _tms[2048]; /*!< TMS buffer */
uint8_t _tdi[2048]; /*!< TDI buffer */
uint32_t _num_bits; /*!< number of bits stored */
uint32_t _last_tms; /*!< last known TMS state */
uint32_t _last_tdi; /*!< last known TDI state */
uint32_t _caps; /*!< current probe capacity */
uint8_t _hw_type;
uint8_t _major; /*!< major Jlink probe release number */
uint8_t _minor; /*!< minor Jlink probe release number */
uint8_t _revision; /*!< revision number */
};
#endif // SRC_JLINK_HPP_