openFPGALoader/src/jtag.cpp

681 lines
15 KiB
C++
Raw Normal View History

2021-06-26 15:24:07 +02:00
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2020 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*/
#include <cstring>
#include <iomanip>
2019-09-26 18:29:20 +02:00
#include <iostream>
#include <map>
#include <sstream>
#include <unistd.h>
2019-09-26 18:29:20 +02:00
#include <vector>
#include <string>
#include "anlogicCable.hpp"
#include "ch552_jtag.hpp"
#include "display.hpp"
#include "jtag.hpp"
#include "ftdiJtagBitbang.hpp"
#include "ftdiJtagMPSSE.hpp"
#ifdef ENABLE_LIBGPIOD
#include "libgpiodJtagBitbang.hpp"
#endif
2022-10-14 11:15:02 +02:00
#ifdef ENABLE_JETSONNANOGPIO
#include "jetsonNanoJtagBitbang.hpp"
#endif
2022-03-12 18:46:23 +01:00
#include "jlink.hpp"
2021-06-19 17:28:48 +02:00
#ifdef ENABLE_CMSISDAP
#include "cmsisDAP.hpp"
#endif
2020-06-14 15:41:00 +02:00
#include "dirtyJtag.hpp"
#include "ch347jtag.hpp"
#include "part.hpp"
#ifdef ENABLE_REMOTEBITBANG
#include "remoteBitbang_client.hpp"
#endif
#include "usbBlaster.hpp"
2022-07-06 21:14:25 +02:00
#ifdef ENABLE_XVC
#include "xvc_client.hpp"
#endif
2019-09-26 18:29:20 +02:00
using namespace std;
2019-12-20 07:54:31 +01:00
#define DEBUG 0
2019-09-26 18:29:20 +02:00
#if DEBUG
2019-09-26 18:29:20 +02:00
#define display(...) \
do { if (_verbose) fprintf(stdout, __VA_ARGS__);}while(0)
#else
#define display(...) do {}while(0)
#endif
/*
* AD0 -> TCK
* AD1 -> TDI
* AD2 -> TD0
* AD3 -> TMS
*/
/* Rmq:
* pour TMS: l'envoi de n necessite de mettre n-1 comme longueur
* mais le bit n+1 est utilise pour l'etat suivant le dernier
* front. Donc il faut envoyer 6bits ([5:0]) pertinents pour
* utiliser le bit 6 comme etat apres la commande,
* le bit 7 corresponds a l'etat de TDI (donc si on fait 7 cycles
* l'etat de TDI va donner l'etat de TMS...)
* transfert/lecture: le dernier bit de IR ou DR doit etre envoye en
* meme temps que le TMS qui fait sortir de l'etat donc il faut
* pour n bits a transferer :
* - envoyer 8bits * (n/8)-1
* - envoyer les 7 bits du dernier octet;
* - envoyer le dernier avec 0x4B ou 0x6B
*/
2023-04-15 08:20:05 +02:00
Jtag::Jtag(const cable_t &cable, const jtag_pins_conf_t *pin_conf,
const string &dev,
const string &serial, uint32_t clkHZ, int8_t verbose,
2022-10-06 07:07:17 +02:00
const string &ip_adr, int port,
const bool invert_read_edge, const string &firmware_path):
_verbose(verbose > 1),
_state(RUN_TEST_IDLE),
_tms_buffer_size(128), _num_tms(0),
_board_name("nope"), device_index(0)
{
init_internal(cable, dev, serial, pin_conf, clkHZ, firmware_path,
2022-10-06 07:07:17 +02:00
invert_read_edge, ip_adr, port);
detectChain(5);
2019-09-26 18:29:20 +02:00
}
Jtag::~Jtag()
2019-09-26 18:29:20 +02:00
{
free(_tms_buffer);
delete _jtag;
2019-09-26 18:29:20 +02:00
}
void Jtag::init_internal(const cable_t &cable, const string &dev, const string &serial,
const jtag_pins_conf_t *pin_conf, uint32_t clkHZ, const string &firmware_path,
2022-10-06 07:07:17 +02:00
const bool invert_read_edge, const string &ip_adr, int port)
{
2020-06-14 15:41:00 +02:00
switch (cable.type) {
case MODE_ANLOGICCABLE:
_jtag = new AnlogicCable(clkHZ);
break;
2020-06-14 15:41:00 +02:00
case MODE_FTDI_BITBANG:
if (pin_conf == NULL)
throw std::exception();
_jtag = new FtdiJtagBitBang(cable, pin_conf, dev, serial, clkHZ, _verbose);
2020-06-14 15:41:00 +02:00
break;
case MODE_FTDI_SERIAL:
_jtag = new FtdiJtagMPSSE(cable, dev, serial, clkHZ,
invert_read_edge, _verbose);
2020-06-14 15:41:00 +02:00
break;
case MODE_CH552_JTAG:
_jtag = new CH552_jtag(cable, dev, serial, clkHZ, _verbose);
break;
case MODE_CH347:
_jtag = new CH347Jtag(clkHZ, _verbose);
break;
2020-06-14 15:41:00 +02:00
case MODE_DIRTYJTAG:
_jtag = new DirtyJtag(clkHZ, _verbose);
break;
2022-03-12 18:46:23 +01:00
case MODE_JLINK:
2023-04-17 18:15:04 +02:00
_jtag = new Jlink(clkHZ, _verbose, cable.vid, cable.pid);
2022-03-12 18:46:23 +01:00
break;
case MODE_USBBLASTER:
_jtag = new UsbBlaster(cable, firmware_path, _verbose);
break;
2021-06-19 17:28:48 +02:00
case MODE_CMSISDAP:
#ifdef ENABLE_CMSISDAP
_jtag = new CmsisDAP(cable, cable.config.index, _verbose);
2021-06-19 17:28:48 +02:00
break;
#else
std::cerr << "Jtag: support for cmsisdap was not enabled at compile time" << std::endl;
throw std::exception();
#endif
case MODE_XVC_CLIENT:
2022-07-06 21:14:25 +02:00
#ifdef ENABLE_XVC
2022-10-06 07:07:17 +02:00
_jtag = new XVC_client(ip_adr, port, clkHZ, _verbose);
break;
#else
std::cerr << "Jtag: support for xvc-client was not enabled at compile time" << std::endl;
throw std::exception();
#endif
#ifdef ENABLE_LIBGPIOD
case MODE_LIBGPIOD_BITBANG:
_jtag = new LibgpiodJtagBitbang(pin_conf, dev, clkHZ, _verbose);
break;
2022-10-14 11:15:02 +02:00
#endif
#ifdef ENABLE_JETSONNANOGPIO
case MODE_JETSONNANO_BITBANG:
_jtag = new JetsonNanoJtagBitbang(pin_conf, dev, clkHZ, _verbose);
break;
#endif
#ifdef ENABLE_REMOTEBITBANG
case MODE_REMOTEBITBANG:
_jtag = new RemoteBitbang_client(ip_adr, port, _verbose);
break;
2021-06-19 17:28:48 +02:00
#endif
2020-06-14 15:41:00 +02:00
default:
std::cerr << "Jtag: unknown cable type" << std::endl;
throw std::exception();
}
_tms_buffer = (unsigned char *)malloc(sizeof(unsigned char) * _tms_buffer_size);
memset(_tms_buffer, 0, _tms_buffer_size);
}
int Jtag::detectChain(int max_dev)
2019-09-26 18:29:20 +02:00
{
char message[256];
2019-09-26 18:29:20 +02:00
unsigned char rx_buff[4];
/* WA for CH552/tangNano: write is always mandatory */
unsigned char tx_buff[4] = {0xff, 0xff, 0xff, 0xff};
2019-09-26 18:29:20 +02:00
unsigned int tmp;
/* cleanup */
_devices_list.clear();
_irlength_list.clear();
2019-09-26 18:29:20 +02:00
go_test_logic_reset();
set_state(SHIFT_DR);
if (_verbose)
printInfo("Raw IDCODE:");
2019-09-26 18:29:20 +02:00
for (int i = 0; i < max_dev; i++) {
read_write(tx_buff, rx_buff, 32, (i == max_dev-1)?1:0);
2019-09-26 18:29:20 +02:00
tmp = 0;
for (int ii=0; ii < 4; ii++)
tmp |= (rx_buff[ii] << (8*ii));
if (_verbose) {
snprintf(message, sizeof(message), "- %d -> 0x%08x", i, tmp);
printInfo(message);
}
/* search IDCODE in fpga_list and misc_dev_list
* since most device have idcode with high nibble masked
* we start to search sub IDCODE
* if IDCODE has no match: try the same with version unmasked
*/
if (tmp != 0 && tmp != 0xffffffff) {
bool found = false;
/* ckeck highest nibble to prevent confusion between Cologne Chip
* GateMate and Efinix Trion T4/T8 devices
*/
if (tmp != 0x20000001)
found = search_and_insert_device_with_idcode(tmp & 0x0fffffff);
if (!found) /* if masked not found -> search for full */
found = search_and_insert_device_with_idcode(tmp);
if (!found) {
uint16_t mfg = IDCODE2MANUFACTURERID(tmp);
uint8_t part = IDCODE2PART(tmp);
uint8_t vers = IDCODE2VERS(tmp);
char error[1024];
snprintf(error, sizeof(error),
"Unknown device with IDCODE: 0x%08x"
" (manufacturer: 0x%03x (%s),"
" part: 0x%02x vers: 0x%x", tmp,
mfg, list_manufacturer[mfg].c_str(), part, vers);
throw std::runtime_error(error);
}
}
2019-09-26 18:29:20 +02:00
}
go_test_logic_reset();
2020-10-09 21:28:00 +02:00
flushTMS(true);
return _devices_list.size();
}
bool Jtag::search_and_insert_device_with_idcode(uint32_t idcode)
{
int irlength = -1;
auto dev = fpga_list.find(idcode);
if (dev != fpga_list.end())
irlength = dev->second.irlength;
if (irlength == -1) {
auto misc = misc_dev_list.find(idcode);
if (misc != misc_dev_list.end())
irlength = misc->second.irlength;
}
if (irlength == -1)
return false;
return insert_first(idcode, irlength);
}
bool Jtag::insert_first(uint32_t device_id, uint16_t irlength)
{
_devices_list.insert(_devices_list.begin(), device_id);
_irlength_list.insert(_irlength_list.begin(), irlength);
return true;
}
uint16_t Jtag::device_select(uint16_t index)
{
if (index > (uint16_t) _devices_list.size())
return -1;
device_index = index;
return device_index;
2019-09-26 18:29:20 +02:00
}
void Jtag::setTMS(unsigned char tms)
2019-09-26 18:29:20 +02:00
{
display("%s %x %d %d\n", __func__, tms, _num_tms, (_num_tms >> 3));
2019-09-26 18:29:20 +02:00
if (_num_tms+1 == _tms_buffer_size * 8)
2020-10-09 21:28:00 +02:00
flushTMS(false);
2019-09-26 18:29:20 +02:00
if (tms != 0)
_tms_buffer[_num_tms>>3] |= (0x1) << (_num_tms & 0x7);
_num_tms++;
}
/* reconstruct byte sent to TMS pins
* - use up to 6 bits
* -since next bit after length is use to
* fix TMS state after sent we copy last bit
* to bit after next
* -bit 7 is TDI state for each clk cycles
*/
int Jtag::flushTMS(bool flush_buffer)
2019-09-26 18:29:20 +02:00
{
int ret = 0;
if (_num_tms != 0) {
display("%s: %d %x\n", __func__, _num_tms, _tms_buffer[0]);
2019-09-26 18:29:20 +02:00
ret = _jtag->writeTMS(_tms_buffer, _num_tms, flush_buffer);
/* reset buffer and number of bits */
memset(_tms_buffer, 0, _tms_buffer_size);
_num_tms = 0;
} else if (flush_buffer) {
_jtag->flush();
}
return ret;
2019-09-26 18:29:20 +02:00
}
void Jtag::go_test_logic_reset()
2019-09-26 18:29:20 +02:00
{
2022-12-18 23:29:21 +01:00
/* independently to current state 5 clk with TMS high is enough */
2019-09-26 18:29:20 +02:00
for (int i = 0; i < 6; i++)
setTMS(0x01);
2020-10-09 21:28:00 +02:00
flushTMS(false);
2019-09-26 18:29:20 +02:00
_state = TEST_LOGIC_RESET;
}
int Jtag::read_write(unsigned char *tdi, unsigned char *tdo, int len, char last)
2019-09-26 18:29:20 +02:00
{
2020-10-09 21:28:00 +02:00
flushTMS(false);
_jtag->writeTDI(tdi, tdo, len, last);
if (last == 1)
2019-09-26 18:29:20 +02:00
_state = (_state == SHIFT_DR) ? EXIT1_DR : EXIT1_IR;
return 0;
}
void Jtag::toggleClk(int nb)
2019-09-26 18:29:20 +02:00
{
unsigned char c = (TEST_LOGIC_RESET == _state) ? 1 : 0;
2020-10-09 21:28:00 +02:00
flushTMS(false);
if (_jtag->toggleClk(c, 0, nb) >= 0)
return;
throw std::exception();
return;
2019-09-26 18:29:20 +02:00
}
int Jtag::shiftDR(unsigned char *tdi, unsigned char *tdo, int drlen, int end_state)
2019-09-26 18:29:20 +02:00
{
/* get number of devices in the JTAG chain
* after the selected one
*/
int bits_after = device_index;
/* if current state not shift DR
* move to this state
*/
if (_state != SHIFT_DR) {
set_state(SHIFT_DR);
flushTMS(false); // force transmit tms state
/* get number of devices, in the JTAG chain,
* before the selected one
*/
int bits_before = _devices_list.size() - device_index - 1;
/* if not 0 send enough bits
*/
if (bits_before > 0) {
int n = (bits_before + 7) / 8;
uint8_t tx[n];
memset(tx, 0xff, n);
read_write(tx, NULL, bits_before, 0);
}
}
/* write tdi (and read tdo) to the selected device
* end (ie TMS high) is used only when current device
* is the last of the chain and a state change must
* be done
*/
read_write(tdi, tdo, drlen, bits_after == 0 && end_state != SHIFT_DR);
2019-09-26 18:29:20 +02:00
/* if it's asked to move in FSM */
if (end_state != SHIFT_DR) {
/* if current device is not the last */
if (bits_after > 0) {
int n = (bits_after + 7) / 8;
uint8_t tx[n];
memset(tx, 0xff, n);
read_write(tx, NULL, bits_after, 1); // its the last force
// tms high with last bit
}
/* move to end_state */
set_state(end_state);
}
2019-09-26 18:29:20 +02:00
return 0;
}
int Jtag::shiftIR(unsigned char tdi, int irlen, int end_state)
{
if (irlen > 8) {
cerr << "Error: this method this direct char don't support more than 1 byte" << endl;
return -1;
}
return shiftIR(&tdi, NULL, irlen, end_state);
}
int Jtag::shiftIR(unsigned char *tdi, unsigned char *tdo, int irlen, int end_state)
2019-09-26 18:29:20 +02:00
{
display("%s: avant shiftIR\n", __func__);
int bypass_after = 0;
if (end_state != SHIFT_IR) {
/* when the device is not alone and not
* the first a serie of bypass must be
* send to complete send ir sequence
*/
for (int i = 0; i < device_index; i++)
bypass_after += _irlength_list[i];
}
/* if not in SHIFT IR move to this state */
if (_state != SHIFT_IR) {
set_state(SHIFT_IR);
/* force flush */
flushTMS(false);
/* send serie of bypass instructions
* final size depends on number of device
* before targeted and irlength of each one
*/
int bypass_before = 0;
2021-06-26 08:47:37 +02:00
for (unsigned int i = device_index + 1; i < _devices_list.size(); i++)
bypass_before += _irlength_list[i];
/* if > 0 send bits */
if (bypass_before > 0) {
int n = (bypass_before + 7) / 8;
uint8_t tx[n];
memset(tx, 0xff, n);
read_write(tx, NULL, bypass_before, 0);
}
}
2019-09-26 18:29:20 +02:00
display("%s: envoi ircode\n", __func__);
/* write tdi (and read tdo) to the selected device
* end (ie TMS high) is used only when current device
* is the last of the chain and a state change must
* be done
*/
read_write(tdi, tdo, irlen, bypass_after == 0 && end_state != SHIFT_IR);
/* it's asked to move out of SHIFT IR state */
if (end_state != SHIFT_IR) {
/* again if devices after fill '1' */
if (bypass_after > 0) {
int n = (bypass_after + 7) / 8;
uint8_t tx[n];
memset(tx, 0xff, n);
read_write(tx, NULL, bypass_after, 1);
}
/* move to the requested state */
set_state(end_state);
}
2019-09-26 18:29:20 +02:00
return 0;
}
void Jtag::set_state(int newState)
2019-09-26 18:29:20 +02:00
{
unsigned char tms;
while (newState != _state) {
2019-11-08 11:30:10 +01:00
display("_state : %16s(%02d) -> %s(%02d) ",
2019-09-26 18:29:20 +02:00
getStateName((tapState_t)_state),
_state,
getStateName((tapState_t)newState), newState);
switch (_state) {
case TEST_LOGIC_RESET:
if (newState == TEST_LOGIC_RESET) {
tms = 1;
} else {
tms = 0;
_state = RUN_TEST_IDLE;
}
break;
case RUN_TEST_IDLE:
if (newState == RUN_TEST_IDLE) {
tms = 0;
} else {
tms = 1;
_state = SELECT_DR_SCAN;
}
break;
case SELECT_DR_SCAN:
switch (newState) {
case CAPTURE_DR:
case SHIFT_DR:
case EXIT1_DR:
case PAUSE_DR:
case EXIT2_DR:
case UPDATE_DR:
tms = 0;
_state = CAPTURE_DR;
break;
default:
tms = 1;
_state = SELECT_IR_SCAN;
}
break;
case SELECT_IR_SCAN:
switch (newState) {
case CAPTURE_IR:
case SHIFT_IR:
case EXIT1_IR:
case PAUSE_IR:
case EXIT2_IR:
case UPDATE_IR:
tms = 0;
_state = CAPTURE_IR;
break;
default:
tms = 1;
_state = TEST_LOGIC_RESET;
}
break;
/* DR column */
case CAPTURE_DR:
if (newState == SHIFT_DR) {
tms = 0;
_state = SHIFT_DR;
} else {
tms = 1;
_state = EXIT1_DR;
}
break;
case SHIFT_DR:
if (newState == SHIFT_DR) {
tms = 0;
} else {
tms = 1;
_state = EXIT1_DR;
}
break;
case EXIT1_DR:
switch (newState) {
case PAUSE_DR:
case EXIT2_DR:
case SHIFT_DR:
case EXIT1_DR:
tms = 0;
_state = PAUSE_DR;
break;
default:
tms = 1;
_state = UPDATE_DR;
}
break;
case PAUSE_DR:
if (newState == PAUSE_DR) {
tms = 0;
} else {
tms = 1;
_state = EXIT2_DR;
}
break;
case EXIT2_DR:
switch (newState) {
case SHIFT_DR:
case EXIT1_DR:
case PAUSE_DR:
tms = 0;
_state = SHIFT_DR;
break;
default:
tms = 1;
_state = UPDATE_DR;
}
break;
case UPDATE_DR:
if (newState == RUN_TEST_IDLE) {
tms = 0;
_state = RUN_TEST_IDLE;
} else {
tms = 1;
_state = SELECT_DR_SCAN;
}
break;
/* IR column */
case CAPTURE_IR:
if (newState == SHIFT_IR) {
tms = 0;
_state = SHIFT_IR;
} else {
tms = 1;
_state = EXIT1_IR;
}
break;
case SHIFT_IR:
if (newState == SHIFT_IR) {
tms = 0;
} else {
tms = 1;
_state = EXIT1_IR;
}
break;
case EXIT1_IR:
switch (newState) {
case PAUSE_IR:
case EXIT2_IR:
case SHIFT_IR:
case EXIT1_IR:
tms = 0;
_state = PAUSE_IR;
break;
default:
tms = 1;
_state = UPDATE_IR;
}
break;
case PAUSE_IR:
if (newState == PAUSE_IR) {
tms = 0;
} else {
tms = 1;
_state = EXIT2_IR;
}
break;
case EXIT2_IR:
switch (newState) {
case SHIFT_IR:
case EXIT1_IR:
case PAUSE_IR:
tms = 0;
_state = SHIFT_IR;
break;
default:
tms = 1;
_state = UPDATE_IR;
}
break;
case UPDATE_IR:
if (newState == RUN_TEST_IDLE) {
tms = 0;
_state = RUN_TEST_IDLE;
} else {
tms = 1;
_state = SELECT_DR_SCAN;
}
break;
}
setTMS(tms);
display("%d %d %d %x\n", tms, _num_tms-1, _state,
_tms_buffer[(_num_tms-1) / 8]);
2019-09-26 18:29:20 +02:00
}
/* force write buffer */
2020-10-09 21:28:00 +02:00
flushTMS(false);
2019-09-26 18:29:20 +02:00
}
const char *Jtag::getStateName(tapState_t s)
2019-09-26 18:29:20 +02:00
{
switch (s) {
case TEST_LOGIC_RESET:
return "TEST_LOGIC_RESET";
case RUN_TEST_IDLE:
return "RUN_TEST_IDLE";
case SELECT_DR_SCAN:
return "SELECT_DR_SCAN";
case CAPTURE_DR:
return "CAPTURE_DR";
case SHIFT_DR:
return "SHIFT_DR";
case EXIT1_DR:
return "EXIT1_DR";
case PAUSE_DR:
return "PAUSE_DR";
case EXIT2_DR:
return "EXIT2_DR";
case UPDATE_DR:
return "UPDATE_DR";
case SELECT_IR_SCAN:
return "SELECT_IR_SCAN";
case CAPTURE_IR:
return "CAPTURE_IR";
case SHIFT_IR:
return "SHIFT_IR";
case EXIT1_IR:
return "EXIT1_IR";
case PAUSE_IR:
return "PAUSE_IR";
case EXIT2_IR:
return "EXIT2_IR";
case UPDATE_IR:
return "UPDATE_IR";
default:
return "Unknown";
}
}