cable: new SEGGER J-Link support (PoC)
This commit is contained in:
parent
15fb5ac591
commit
18100ec0f3
|
|
@ -95,6 +95,7 @@ set(OPENFPGALOADER_SOURCE
|
||||||
src/latticeBitParser.cpp
|
src/latticeBitParser.cpp
|
||||||
src/gowin.cpp
|
src/gowin.cpp
|
||||||
src/device.cpp
|
src/device.cpp
|
||||||
|
src/jlink.cpp
|
||||||
src/lattice.cpp
|
src/lattice.cpp
|
||||||
src/progressBar.cpp
|
src/progressBar.cpp
|
||||||
src/fsparser.cpp
|
src/fsparser.cpp
|
||||||
|
|
@ -129,6 +130,7 @@ set(OPENFPGALOADER_HEADERS
|
||||||
src/bitparser.hpp
|
src/bitparser.hpp
|
||||||
src/ftdiJtagBitbang.hpp
|
src/ftdiJtagBitbang.hpp
|
||||||
src/ftdiJtagMPSSE.hpp
|
src/ftdiJtagMPSSE.hpp
|
||||||
|
src/jlink.hpp
|
||||||
src/jtag.hpp
|
src/jtag.hpp
|
||||||
src/jtagInterface.hpp
|
src/jtagInterface.hpp
|
||||||
src/fsparser.hpp
|
src/fsparser.hpp
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,13 @@ ecpix5-debug:
|
||||||
URL: https://shop.lambdaconcept.com/home/46-ecpix-5.html
|
URL: https://shop.lambdaconcept.com/home/46-ecpix-5.html
|
||||||
|
|
||||||
|
|
||||||
|
jlink:
|
||||||
|
|
||||||
|
- Name: jlink
|
||||||
|
Description: SEGGER J-Link Debug Probes
|
||||||
|
URL: https://www.segger.com/products/debug-probes/j-link
|
||||||
|
|
||||||
|
|
||||||
jtag-smt2-nc:
|
jtag-smt2-nc:
|
||||||
|
|
||||||
- Name: jtag-smt2-nc
|
- Name: jtag-smt2-nc
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ enum communication_type {
|
||||||
MODE_CH552_JTAG, /*! ch552_jtag firmware */
|
MODE_CH552_JTAG, /*! ch552_jtag firmware */
|
||||||
MODE_FTDI_BITBANG, /*! used with ft232RL/ft231x */
|
MODE_FTDI_BITBANG, /*! used with ft232RL/ft231x */
|
||||||
MODE_FTDI_SERIAL, /*! ft2232, ft232H */
|
MODE_FTDI_SERIAL, /*! ft2232, ft232H */
|
||||||
|
MODE_JLINK, /*! ft2232, ft232H */
|
||||||
MODE_DIRTYJTAG, /*! JTAG probe firmware for STM32F1 */
|
MODE_DIRTYJTAG, /*! JTAG probe firmware for STM32F1 */
|
||||||
MODE_USBBLASTER, /*! JTAG probe firmware for USBBLASTER */
|
MODE_USBBLASTER, /*! JTAG probe firmware for USBBLASTER */
|
||||||
MODE_CMSISDAP, /*! CMSIS-DAP JTAG probe */
|
MODE_CMSISDAP, /*! CMSIS-DAP JTAG probe */
|
||||||
|
|
@ -59,6 +60,7 @@ static std::map <std::string, cable_t> cable_list = {
|
||||||
{"ft232RL", {MODE_FTDI_BITBANG, {0x0403, 0x6001, INTERFACE_A, 0x08, 0x0B, 0x08, 0x0B}}},
|
{"ft232RL", {MODE_FTDI_BITBANG, {0x0403, 0x6001, INTERFACE_A, 0x08, 0x0B, 0x08, 0x0B}}},
|
||||||
{"ft4232", {MODE_FTDI_SERIAL, {0x0403, 0x6011, INTERFACE_A, 0x08, 0x0B, 0x08, 0x0B}}},
|
{"ft4232", {MODE_FTDI_SERIAL, {0x0403, 0x6011, INTERFACE_A, 0x08, 0x0B, 0x08, 0x0B}}},
|
||||||
{"ecpix5-debug", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0xF8, 0xFB, 0xFF, 0xFF}}},
|
{"ecpix5-debug", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0xF8, 0xFB, 0xFF, 0xFF}}},
|
||||||
|
{"jlink", {MODE_JLINK, {0x1366, 0x0105, 0, 0, 0, 0, 0 }}},
|
||||||
{"jtag-smt2-nc", {MODE_FTDI_SERIAL, {0x0403, 0x6014, INTERFACE_A, 0xe8, 0xeb, 0x00, 0x60}}},
|
{"jtag-smt2-nc", {MODE_FTDI_SERIAL, {0x0403, 0x6014, INTERFACE_A, 0xe8, 0xeb, 0x00, 0x60}}},
|
||||||
{"orbtrace", {MODE_CMSISDAP, {0x1209, 0x3443, 0, 0, 0, 0, 0 }}},
|
{"orbtrace", {MODE_CMSISDAP, {0x1209, 0x3443, 0, 0, 0, 0, 0 }}},
|
||||||
{"tigard", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_B, 0x08, 0x3B, 0x00, 0x00}}},
|
{"tigard", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_B, 0x08, 0x3B, 0x00, 0x00}}},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,730 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "jlink.hpp"
|
||||||
|
|
||||||
|
#include <libusb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "display.hpp"
|
||||||
|
|
||||||
|
#define VID 0x1366
|
||||||
|
#define PID 0x0105
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// convert 2Byte to 1 short
|
||||||
|
#define CONV_16B(_val) ((((uint16_t) _val[0]) << 0) | \
|
||||||
|
(((uint16_t) _val[1]) << 8))
|
||||||
|
// convert 4Byte to 1 word
|
||||||
|
#define CONV_32B(_val) ((((uint32_t) _val[0]) << 0) | \
|
||||||
|
(((uint32_t) _val[1]) << 8) | \
|
||||||
|
(((uint32_t) _val[2]) << 16) | \
|
||||||
|
(((uint32_t) _val[3]) << 24))
|
||||||
|
// a 0-byte is introduce when reading a packet with
|
||||||
|
// size multiple of 64 Byte but < 0x8000
|
||||||
|
#define HAS_0BYTE(_len) ((_len != 0) && (_len % 64 == 0) && (_len != 0x8000))
|
||||||
|
|
||||||
|
// buffer capacity
|
||||||
|
#define BUF_SIZE 2048
|
||||||
|
|
||||||
|
Jlink::Jlink(uint32_t clkHz, int8_t verbose):_base_freq(0), _min_div(0),
|
||||||
|
jlink_write_ep(-1), jlink_read_ep(-1), jlink_interface(-1),
|
||||||
|
_verbose(verbose > 0), _debug(verbose > 1), _quiet(verbose < 0),
|
||||||
|
_num_bits(0), _last_tms(0), _last_tdi(0),
|
||||||
|
_hw_type(0), _major(0), _minor(0), _revision(0)
|
||||||
|
{
|
||||||
|
// init libusb context
|
||||||
|
if (libusb_init(&jlink_ctx) < 0)
|
||||||
|
throw std::runtime_error("libusb init failed\n");
|
||||||
|
|
||||||
|
// search for all compatible devices
|
||||||
|
if (!jlink_scan_usb())
|
||||||
|
throw std::runtime_error("can't find compatible device");
|
||||||
|
|
||||||
|
// get device capacity
|
||||||
|
if (!get_caps())
|
||||||
|
throw std::runtime_error("can't read device CAPS");
|
||||||
|
|
||||||
|
// get hw version
|
||||||
|
if (get_hw_version() < 0)
|
||||||
|
throw std::runtime_error("can't read hw version");
|
||||||
|
|
||||||
|
get_speeds();
|
||||||
|
|
||||||
|
// configure device in JTAG mode
|
||||||
|
set_interface(0);
|
||||||
|
|
||||||
|
// configure JTAG TCK frequency
|
||||||
|
setClkFreq(clkHz);
|
||||||
|
|
||||||
|
if (!set_ks_power(true))
|
||||||
|
throw std::runtime_error("can't set KS power");
|
||||||
|
}
|
||||||
|
|
||||||
|
Jlink::~Jlink()
|
||||||
|
{
|
||||||
|
// flush buffers before quit
|
||||||
|
if (_num_bits != 0)
|
||||||
|
flush();
|
||||||
|
// release interface
|
||||||
|
libusb_release_interface(jlink_handle, jlink_interface);
|
||||||
|
// close device
|
||||||
|
libusb_close(jlink_handle);
|
||||||
|
// context cleanup
|
||||||
|
libusb_exit(jlink_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Jlink::writeTMS(uint8_t *tms, uint32_t len, bool flush_buffer)
|
||||||
|
{
|
||||||
|
// empty buffer
|
||||||
|
// if asked flush
|
||||||
|
if (len == 0)
|
||||||
|
return ((flush_buffer) ? flush() : 0);
|
||||||
|
|
||||||
|
for (uint32_t pos = 0; pos < len; pos++) {
|
||||||
|
// buffer full -> write
|
||||||
|
if (_num_bits == BUF_SIZE * 8) {
|
||||||
|
// write
|
||||||
|
ll_write(NULL);
|
||||||
|
_num_bits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_last_tms = (tms[pos >> 3] & (1 << (pos & 0x07))) ? 1 : 0;
|
||||||
|
|
||||||
|
if (_last_tms)
|
||||||
|
_tms[(_num_bits >> 3)] |= (1 << (_num_bits & 0x07));
|
||||||
|
else
|
||||||
|
_tms[(_num_bits >> 3)] &= ~(1 << (_num_bits & 0x07));
|
||||||
|
if (_last_tdi)
|
||||||
|
_tdi[(_num_bits >> 3)] |= (1 << (_num_bits & 0x07));
|
||||||
|
else
|
||||||
|
_tdi[(_num_bits >> 3)] &= ~(1 << (_num_bits & 0x07));
|
||||||
|
_num_bits++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush where it's asked or if the buffer is full
|
||||||
|
if (flush_buffer || _num_bits == BUF_SIZE * 8)
|
||||||
|
return flush();
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Jlink::writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end)
|
||||||
|
{
|
||||||
|
if (len == 0) // nothing to do
|
||||||
|
return 0;
|
||||||
|
if (_num_bits != 0) // flush buffer to simplify next step
|
||||||
|
flush();
|
||||||
|
|
||||||
|
uint32_t xfer_len = BUF_SIZE * 8; // default to buffer capacity
|
||||||
|
uint8_t tms = (_last_tms) ? 0xff : 0x00; // set tms byte
|
||||||
|
uint8_t *tx_ptr = tx, *rx_ptr = rx; // use pointer to simplify algo
|
||||||
|
|
||||||
|
/* write by burst */
|
||||||
|
for (uint32_t rest = 0; rest < len; rest += xfer_len) {
|
||||||
|
if ((xfer_len + rest) > len) // len < buffer size
|
||||||
|
xfer_len = len - rest; // reduce xfer len
|
||||||
|
uint16_t tt = (xfer_len + 7) >> 3; // convert to Byte
|
||||||
|
memset(_tms, tms, tt); // fill tms buffer
|
||||||
|
memcpy(_tdi, tx_ptr, tt); // fill tdi buffer
|
||||||
|
_num_bits = xfer_len; // set buffer size in bit
|
||||||
|
if (end && xfer_len + rest == len) { // last sequence: set tms 1
|
||||||
|
_last_tms = 1;
|
||||||
|
uint16_t idx = _num_bits - 1;
|
||||||
|
_tms[(idx >> 3)] |= (1 << (idx & 0x07));
|
||||||
|
}
|
||||||
|
ll_write((rx) ? rx_ptr : NULL); // write
|
||||||
|
|
||||||
|
tx_ptr += tt;
|
||||||
|
if (rx)
|
||||||
|
rx_ptr += tt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// toggle clk with constant TDI and TMS. More or less same idea as writeTDI
|
||||||
|
int Jlink::toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len)
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
if (clk_len == 0)
|
||||||
|
return 0;
|
||||||
|
if (_num_bits != 0)
|
||||||
|
flush();
|
||||||
|
|
||||||
|
_last_tms = tms;
|
||||||
|
_last_tdi = tdi;
|
||||||
|
uint8_t curr_tms = (tms) ? 0xff: 0x00;
|
||||||
|
uint8_t curr_tdi = (tdi) ? 0xff: 0x00;
|
||||||
|
|
||||||
|
uint32_t len = clk_len;
|
||||||
|
|
||||||
|
// flush buffer before starting
|
||||||
|
if (_num_bits != 0)
|
||||||
|
flush();
|
||||||
|
|
||||||
|
memset(_tdi, curr_tdi, BUF_SIZE);
|
||||||
|
memset(_tms, curr_tms, BUF_SIZE);
|
||||||
|
do {
|
||||||
|
_num_bits = BUF_SIZE * 8;
|
||||||
|
if (len < _num_bits)
|
||||||
|
_num_bits = len;
|
||||||
|
len -= _num_bits;
|
||||||
|
ll_write(NULL);
|
||||||
|
} while (len > 0);
|
||||||
|
|
||||||
|
return clk_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Jlink::flush()
|
||||||
|
{
|
||||||
|
return ll_write(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::ll_write(uint8_t *tdo)
|
||||||
|
{
|
||||||
|
if (_num_bits == 0)
|
||||||
|
return true;
|
||||||
|
uint32_t numbytes = (_num_bits + 7) >> 3;
|
||||||
|
uint8_t rx_buf[numbytes+2];
|
||||||
|
uint8_t status;
|
||||||
|
// 1. cmd + dummy + numbits + tms + tdi
|
||||||
|
_xfer_buf[0] = EMU_CMD_HW_JTAG3;
|
||||||
|
_xfer_buf[1] = 0; // dummy
|
||||||
|
_xfer_buf[2] = static_cast<uint8_t>((_num_bits >> 0) & 0xff);
|
||||||
|
_xfer_buf[3] = static_cast<uint8_t>((_num_bits >> 8) & 0xff);
|
||||||
|
memcpy(_xfer_buf + 4, _tms, numbytes);
|
||||||
|
memcpy(_xfer_buf + 4 + numbytes, _tdi, numbytes);
|
||||||
|
|
||||||
|
if (_debug) {
|
||||||
|
printf("Out : %u\n", numbytes);
|
||||||
|
printf("cmd : %02x\n", _xfer_buf[0]);
|
||||||
|
printf("dummy : %02x\n", _xfer_buf[1]);
|
||||||
|
printf("bitlength : %02x %02x (%u)\n", _xfer_buf[2], _xfer_buf[3], _num_bits);
|
||||||
|
printf("tms : ");
|
||||||
|
if (numbytes > 16) {
|
||||||
|
printf("snip");
|
||||||
|
} else {
|
||||||
|
for (uint32_t i = 0; i < numbytes; i++)
|
||||||
|
printf("%02x ", _xfer_buf[i+4]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
printf("tdi : ");
|
||||||
|
if (numbytes > 16) {
|
||||||
|
printf("snip");
|
||||||
|
} else {
|
||||||
|
for (uint32_t i = 0; i < numbytes; i++)
|
||||||
|
printf("%02x ", _xfer_buf[i+4+numbytes]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
printf("buffer : ");
|
||||||
|
for (uint32_t i = 0; i < 4 + (2 * numbytes); i++)
|
||||||
|
printf("%02x ", _xfer_buf[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!write_device(_xfer_buf, 4 + (2 * numbytes))) {
|
||||||
|
printError("fails to send buffer");
|
||||||
|
throw std::runtime_error("fails to send buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. read tdo + status
|
||||||
|
int ret = read_device(rx_buf, numbytes+1);
|
||||||
|
if (ret < 0) {
|
||||||
|
printError("fails to read tdo");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. read status
|
||||||
|
if ((uint32_t)ret == numbytes) {
|
||||||
|
printError("read status");
|
||||||
|
if (!read_device(&status, 1)) {
|
||||||
|
printError("fails to read status\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
status = rx_buf[numbytes];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tdo) {
|
||||||
|
memcpy(tdo, rx_buf, numbytes);
|
||||||
|
|
||||||
|
if (_debug) {
|
||||||
|
printf("tdo : ");
|
||||||
|
for (uint32_t i = 0; i < numbytes; i+=16) {
|
||||||
|
for (int ii = 0; ii < 16 && ((ii + i) < numbytes); ii++)
|
||||||
|
printf("%02x ", tdo[i+ii]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_debug)
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
_num_bits = 0; // clear counter
|
||||||
|
|
||||||
|
return status == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::cmd_read(uint8_t cmd, uint8_t *val, int size)
|
||||||
|
{
|
||||||
|
int actual_length;
|
||||||
|
int ret = libusb_bulk_transfer(jlink_handle, jlink_write_ep,
|
||||||
|
&cmd, 1, &actual_length, 5000);
|
||||||
|
if (ret < 0) {
|
||||||
|
printf("Error write cmd_read %d %s %s\n", ret,
|
||||||
|
libusb_error_name(ret), libusb_strerror(ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (size == read_device(val, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::cmd_read(uint8_t cmd, uint16_t *val)
|
||||||
|
{
|
||||||
|
if (!cmd_read(cmd, _xfer_buf, 2))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*val = CONV_16B(_xfer_buf);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::cmd_read(uint8_t cmd, uint32_t *val)
|
||||||
|
{
|
||||||
|
if (!cmd_read(cmd, _xfer_buf, 4))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*val = CONV_32B(_xfer_buf);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::cmd_write(uint8_t cmd, uint16_t param)
|
||||||
|
{
|
||||||
|
uint8_t tx_buf[3] = {cmd,
|
||||||
|
static_cast<uint8_t>((param >> 0) & 0xff),
|
||||||
|
static_cast<uint8_t>((param >> 8) & 0xff)};
|
||||||
|
|
||||||
|
int actual_length;
|
||||||
|
int ret = libusb_bulk_transfer(jlink_handle, jlink_write_ep,
|
||||||
|
tx_buf, 3, &actual_length, 5000);
|
||||||
|
if (ret < 0) {
|
||||||
|
printf("Error write cmd_write %d\n", ret);
|
||||||
|
printf("%s %s\n", libusb_error_name(ret), libusb_strerror(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::cmd_write(uint8_t cmd, uint8_t param)
|
||||||
|
{
|
||||||
|
uint8_t tx_buf[2] = {cmd, param};
|
||||||
|
|
||||||
|
int actual_length;
|
||||||
|
int ret = libusb_bulk_transfer(jlink_handle, jlink_write_ep,
|
||||||
|
tx_buf, 2, &actual_length, 5000);
|
||||||
|
if (ret < 0) {
|
||||||
|
printf("Error write cmd_write %d\n", ret);
|
||||||
|
printf("%s %s\n", libusb_error_name(ret), libusb_strerror(ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Jlink::read_device(uint8_t *buf, uint32_t size)
|
||||||
|
{
|
||||||
|
int actual_length, tries = 3;
|
||||||
|
uint32_t recv = 0, rest = size;
|
||||||
|
uint8_t *rx_ptr = buf;
|
||||||
|
|
||||||
|
do {
|
||||||
|
int ret = libusb_bulk_transfer(jlink_handle, jlink_read_ep,
|
||||||
|
rx_ptr, rest, &actual_length, 1000);
|
||||||
|
if (ret == 0) {
|
||||||
|
rx_ptr += actual_length;
|
||||||
|
recv += actual_length;
|
||||||
|
rest -= actual_length;
|
||||||
|
} else if (ret == LIBUSB_ERROR_TIMEOUT) {
|
||||||
|
tries--;
|
||||||
|
} else {
|
||||||
|
char toto[256];
|
||||||
|
snprintf(toto, sizeof(toto), "Error read length %d %d %u %s %s\n",
|
||||||
|
ret, actual_length, size, libusb_error_name(ret),
|
||||||
|
libusb_strerror(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} while (recv < size && tries != 0);
|
||||||
|
|
||||||
|
if (tries == 0)
|
||||||
|
printError("fail");
|
||||||
|
|
||||||
|
return recv;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::write_device(const uint8_t *buf, uint32_t size)
|
||||||
|
{
|
||||||
|
int actual_length, tries = 4;
|
||||||
|
int rest_size = size, recv = 0;
|
||||||
|
uint8_t *buf_ptr = (uint8_t*)buf;
|
||||||
|
|
||||||
|
do {
|
||||||
|
int ret = libusb_bulk_transfer(jlink_handle, jlink_write_ep,
|
||||||
|
(uint8_t *)buf_ptr, rest_size, &actual_length,
|
||||||
|
1000);
|
||||||
|
if (ret == 0) {
|
||||||
|
rest_size -= actual_length;
|
||||||
|
buf_ptr += actual_length;
|
||||||
|
recv += actual_length;
|
||||||
|
} else if (ret == LIBUSB_ERROR_TIMEOUT) {
|
||||||
|
tries--;
|
||||||
|
} else {
|
||||||
|
printf("Error write %d\n", ret);
|
||||||
|
printf("%s %s\n", libusb_error_name(ret),
|
||||||
|
libusb_strerror(ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} while (tries > 0 && rest_size > 0);
|
||||||
|
|
||||||
|
if (tries == 0 && rest_size != 0) {
|
||||||
|
printf("error\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((uint32_t)recv == size);
|
||||||
|
}
|
||||||
|
|
||||||
|
string Jlink::get_version()
|
||||||
|
{
|
||||||
|
uint16_t length;
|
||||||
|
cmd_read(EMU_CMD_VERSION, &length);
|
||||||
|
uint8_t version[length];
|
||||||
|
read_device(version, length);
|
||||||
|
return string(reinterpret_cast<char*>(version));
|
||||||
|
}
|
||||||
|
|
||||||
|
int Jlink::get_hw_version()
|
||||||
|
{
|
||||||
|
if (!(_caps & EMU_CAP_GET_HW_VERSION)) {
|
||||||
|
printf("get hw version is not supported\n");
|
||||||
|
printf("%u\n", _caps & EMU_CAP_GET_HW_VERSION);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint32_t version;
|
||||||
|
if (!cmd_read(EMU_CMD_GET_HW_VERSION, &version))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
_hw_type = (version / 1000000) % 100;
|
||||||
|
_major = (version / 10000) % 100;
|
||||||
|
_minor = (version / 100) % 100;
|
||||||
|
_revision = version % 100;
|
||||||
|
|
||||||
|
if (_debug)
|
||||||
|
printf("%08x ", version);
|
||||||
|
if (!_quiet) {
|
||||||
|
printInfo("device type: " + jlink_hw_type[_hw_type] +
|
||||||
|
" major: " + std::to_string(_major) +
|
||||||
|
" minor: " + std::to_string(_minor) +
|
||||||
|
" revision: " + std::to_string(_revision));
|
||||||
|
}
|
||||||
|
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Jlink::get_speeds()
|
||||||
|
{
|
||||||
|
cmd_read(EMU_CMD_GET_SPEEDS, _xfer_buf, 6);
|
||||||
|
_base_freq = CONV_32B(_xfer_buf);
|
||||||
|
_min_div = CONV_16B((&_xfer_buf[4]));
|
||||||
|
|
||||||
|
if (_debug) {
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
printf("%02x ", _xfer_buf[i]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("%02x %04x\n", _base_freq, _min_div);
|
||||||
|
printf("%u %u\n", _base_freq, _min_div);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Jlink::setClkFreq(uint32_t clkHz)
|
||||||
|
{
|
||||||
|
uint32_t max_freq = _base_freq / _min_div;
|
||||||
|
|
||||||
|
if (clkHz > max_freq) {
|
||||||
|
printWarn("Jlink probe limited to " +
|
||||||
|
std::to_string(max_freq/1000) + "kHz");
|
||||||
|
clkHz = max_freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cmd_write(EMU_CMD_SET_SPEED, static_cast<uint16_t>(clkHz / 1000))) {
|
||||||
|
printError("setClkFreq: fail to configure frequency");
|
||||||
|
return -EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
_clkHZ = clkHz;
|
||||||
|
return _clkHZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::set_speed(uint16_t freqHz)
|
||||||
|
{
|
||||||
|
uint16_t freqKHz = freqHz / 1000;
|
||||||
|
uint16_t max_speed = _base_freq / _min_div;
|
||||||
|
|
||||||
|
if (freqKHz > max_speed) {
|
||||||
|
printf("max freq limited to %d\n", max_speed * 1000);
|
||||||
|
freqKHz = max_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd_write(EMU_CMD_SET_SPEED, freqKHz);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::get_caps()
|
||||||
|
{
|
||||||
|
if (!cmd_read(EMU_CMD_GET_CAPS, &_caps))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (_verbose) {
|
||||||
|
printf("%04x\n", _caps);
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if ((_caps >> i) & 0x01)
|
||||||
|
printf("%2d %s\n", i, jlink_caps_flags[i].c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::get_result()
|
||||||
|
{
|
||||||
|
uint8_t error_bit;
|
||||||
|
if (cmd_read(EMU_CMD_HW_JTAG_GET_RESULT, &error_bit, 1) != 1) {
|
||||||
|
printError("get result failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printInfo("get_result " + std::to_string(error_bit));
|
||||||
|
if (error_bit != 0)
|
||||||
|
printError("pas bon");
|
||||||
|
return error_bit == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::set_ks_power(bool val)
|
||||||
|
{
|
||||||
|
if (!cmd_write(EMU_CMD_SET_KS_POWER, static_cast<uint8_t>((val) ? 1 : 0)))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::max_mem_block(uint32_t *max_mem)
|
||||||
|
{
|
||||||
|
if (!cmd_read(EMU_CMD_GET_MAX_MEM_BLOCK, max_mem))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is a typo in RM08001:
|
||||||
|
// select interface is 0 for JTAG and 1 for SWD
|
||||||
|
// so SubCmd must be 0..31 instead of 1..31
|
||||||
|
bool Jlink::set_interface(uint8_t interface)
|
||||||
|
{
|
||||||
|
uint8_t buf[2] = {EMU_CMD_SELECT_IF, interface};
|
||||||
|
uint8_t res[4];
|
||||||
|
write_device(buf, 2);
|
||||||
|
read_device(res, 4);
|
||||||
|
if (_debug) {
|
||||||
|
printf("set interface: ");
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
printf("%02x ", res[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Jlink::read_config()
|
||||||
|
{
|
||||||
|
jlink_cfg_t cfg;
|
||||||
|
cmd_read(EMU_CMD_READ_CONFIG, reinterpret_cast<uint8_t*>(&cfg), 256);
|
||||||
|
|
||||||
|
if (_verbose) {
|
||||||
|
printf("usb_adr : %02x\n", cfg.usb_adr);
|
||||||
|
printf("kickstart : %08x\n", cfg.kickstart);
|
||||||
|
printf("ip_address: %08x\n", cfg.ip_address);
|
||||||
|
printf("subnetmask: %08x\n", cfg.subnetmask);
|
||||||
|
printf("mac addr : ");
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
printf("%02x", (uint8_t)cfg.mackaddr[i]);
|
||||||
|
if (i < 5)
|
||||||
|
printf(":");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::write_data(const uint8_t *tms, const uint8_t *tdi, uint8_t *tdo,
|
||||||
|
uint16_t numbits)
|
||||||
|
{
|
||||||
|
if (numbits > (BUF_SIZE * 8))
|
||||||
|
numbits = BUF_SIZE * 8;
|
||||||
|
uint16_t numbytes = (numbits +7) >> 8;
|
||||||
|
|
||||||
|
memcpy(_tms, tms, numbytes);
|
||||||
|
memcpy(_tdi, tdi, numbytes);
|
||||||
|
_num_bits = numbits;
|
||||||
|
return ll_write(tdo);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::jlink_search_interface(libusb_device *dev,
|
||||||
|
libusb_device_descriptor *desc, int *interface_idx,
|
||||||
|
int *config_idx, int *alt_idx)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
*interface_idx = -1;
|
||||||
|
*config_idx = -1;
|
||||||
|
/* 1. iterate on all interface */
|
||||||
|
for (int cfg_idx = 0; cfg_idx < desc->bNumConfigurations; cfg_idx++) {
|
||||||
|
struct libusb_config_descriptor *cfg;
|
||||||
|
int ret = libusb_get_config_descriptor(dev, cfg_idx, &cfg);
|
||||||
|
if (ret != 0) {
|
||||||
|
printf("Fail to retrieve config_descriptor \n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int if_idx=0; if_idx < cfg->bNumInterfaces; if_idx++) {
|
||||||
|
const struct libusb_interface *uif = &cfg->interface[if_idx];
|
||||||
|
for (int intf_idx = 0; intf_idx < uif->num_altsetting; intf_idx++) {
|
||||||
|
const struct libusb_interface_descriptor *intf = &uif->altsetting[intf_idx];
|
||||||
|
uint8_t intfClass = intf->bInterfaceClass;
|
||||||
|
uint8_t intfSubClass = intf->bInterfaceSubClass;
|
||||||
|
if (_debug)
|
||||||
|
printf("intfClass: %x intfSubClass: %x\n", intfClass, intfSubClass);
|
||||||
|
if (intfClass == 0xff && intfSubClass == 0xff) {
|
||||||
|
if (found) {
|
||||||
|
printError("too many compatible interface");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
*interface_idx = if_idx;
|
||||||
|
*config_idx = cfg_idx;
|
||||||
|
*alt_idx = intf_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_debug)
|
||||||
|
printf("%d\n", if_idx);
|
||||||
|
}
|
||||||
|
libusb_free_config_descriptor(cfg);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Jlink::jlink_scan_usb()
|
||||||
|
{
|
||||||
|
libusb_device **dev_list;
|
||||||
|
libusb_device *usb_dev;
|
||||||
|
libusb_device_handle *handle;
|
||||||
|
ssize_t list_size = libusb_get_device_list(jlink_ctx, &dev_list);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (list_size == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while ((usb_dev = dev_list[i++]) != NULL) {
|
||||||
|
struct libusb_device_descriptor desc;
|
||||||
|
if (libusb_get_device_descriptor(usb_dev, &desc) != 0) {
|
||||||
|
printError("Unable to get device descriptor");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desc.idProduct != PID || desc.idVendor != VID)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (_verbose)
|
||||||
|
printf("%04x:%04x (bus %d, device %2d)\n",
|
||||||
|
desc.idVendor, desc.idProduct,
|
||||||
|
libusb_get_bus_number(usb_dev),
|
||||||
|
libusb_get_device_address(usb_dev));
|
||||||
|
/* try to open device to search for interface */
|
||||||
|
int ret = libusb_open(usb_dev, &handle);
|
||||||
|
if (ret != 0)
|
||||||
|
return false;
|
||||||
|
int if_idx, cfg_idx, alt_idx;
|
||||||
|
if (jlink_search_interface(usb_dev, &desc, &if_idx,
|
||||||
|
&cfg_idx, &alt_idx)) {
|
||||||
|
jlink_devices_t dev;
|
||||||
|
dev.usb_dev = usb_dev;
|
||||||
|
dev.alt_idx = alt_idx;
|
||||||
|
dev.if_idx = if_idx;
|
||||||
|
dev.cfg_idx = cfg_idx;
|
||||||
|
device_available.push_back(dev);
|
||||||
|
}
|
||||||
|
libusb_close(handle);
|
||||||
|
}
|
||||||
|
libusb_free_device_list(dev_list, 1);
|
||||||
|
|
||||||
|
// no JLINK probes found
|
||||||
|
if (device_available.size() == 0) {
|
||||||
|
printError("Error: no device found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_debug) {
|
||||||
|
for (size_t d = 0; d < device_available.size(); d++)
|
||||||
|
printf("%x %x\n", device_available[d].if_idx,
|
||||||
|
device_available[d].cfg_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// more than one device plugged: TODO how to deal with that ?
|
||||||
|
if (device_available.size() > 1) {
|
||||||
|
printError("Error: to many devices");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to open JLINK device
|
||||||
|
int ret = libusb_open(device_available[0].usb_dev, &jlink_handle);
|
||||||
|
if (ret != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// request interface
|
||||||
|
jlink_interface = device_available[0].if_idx;
|
||||||
|
int cfg_idx = device_available[0].cfg_idx;
|
||||||
|
libusb_claim_interface(jlink_handle, jlink_interface);
|
||||||
|
|
||||||
|
// search for IN and OUT endpoint
|
||||||
|
struct libusb_config_descriptor *cfg;
|
||||||
|
ret = libusb_get_config_descriptor(device_available[0].usb_dev, cfg_idx, &cfg);
|
||||||
|
if (ret != 0) {
|
||||||
|
printError("Can't get config descriptor");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const struct libusb_interface *uif = &cfg->interface[jlink_interface];
|
||||||
|
const struct libusb_interface_descriptor *intf = &uif->altsetting[cfg_idx];
|
||||||
|
for (int i = 0; i < intf->bNumEndpoints; i++) {
|
||||||
|
struct libusb_endpoint_descriptor endpoint = intf->endpoint[i];
|
||||||
|
if ((endpoint.bEndpointAddress & 0x80)) {
|
||||||
|
jlink_read_ep = endpoint.bEndpointAddress;
|
||||||
|
} else {
|
||||||
|
jlink_write_ep = endpoint.bEndpointAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_free_config_descriptor(cfg);
|
||||||
|
|
||||||
|
if (jlink_write_ep == -1 || jlink_read_ep == -1 || jlink_interface == -1) {
|
||||||
|
printError("error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,311 @@
|
||||||
|
// 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:
|
||||||
|
/*!
|
||||||
|
* \brief contructor: open device
|
||||||
|
* \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();
|
||||||
|
|
||||||
|
// jtagInterface requirement
|
||||||
|
/*!
|
||||||
|
* \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 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);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief test method to access ll_write outer this class
|
||||||
|
* \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)
|
||||||
|
*/
|
||||||
|
bool write_data(const uint8_t *tms, const uint8_t *tdi, uint8_t *tdo,
|
||||||
|
uint16_t numbits);
|
||||||
|
|
||||||
|
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
|
||||||
|
// buffers must be independant
|
||||||
|
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_
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
#include "ftdipp_mpsse.hpp"
|
#include "ftdipp_mpsse.hpp"
|
||||||
#include "ftdiJtagBitbang.hpp"
|
#include "ftdiJtagBitbang.hpp"
|
||||||
#include "ftdiJtagMPSSE.hpp"
|
#include "ftdiJtagMPSSE.hpp"
|
||||||
|
#include "jlink.hpp"
|
||||||
#ifdef ENABLE_CMSISDAP
|
#ifdef ENABLE_CMSISDAP
|
||||||
#include "cmsisDAP.hpp"
|
#include "cmsisDAP.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -103,6 +104,9 @@ void Jtag::init_internal(cable_t &cable, const string &dev, const string &serial
|
||||||
case MODE_DIRTYJTAG:
|
case MODE_DIRTYJTAG:
|
||||||
_jtag = new DirtyJtag(clkHZ, _verbose);
|
_jtag = new DirtyJtag(clkHZ, _verbose);
|
||||||
break;
|
break;
|
||||||
|
case MODE_JLINK:
|
||||||
|
_jtag = new Jlink(clkHZ, _verbose);
|
||||||
|
break;
|
||||||
case MODE_USBBLASTER:
|
case MODE_USBBLASTER:
|
||||||
_jtag = new UsbBlaster(cable.config.vid, cable.config.pid,
|
_jtag = new UsbBlaster(cable.config.vid, cable.config.pid,
|
||||||
firmware_path, _verbose);
|
firmware_path, _verbose);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue