remoteBitbang_client: adding new protocol (client side)
This commit is contained in:
parent
567d854edf
commit
fd97c244d9
|
|
@ -13,8 +13,10 @@ endif()
|
|||
option(ENABLE_CMSISDAP "enable cmsis DAP interface (requires hidapi)" ON)
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
option(ENABLE_LIBGPIOD "enable libgpiod bitbang driver (requires libgpiod)" ON)
|
||||
option(ENABLE_REMOTEBITBANG "enable remote bitbang driver" ON)
|
||||
else()
|
||||
set(ENABLE_LIBGPIOD OFF)
|
||||
set(ENABLE_REMOTEBITBANG OFF)
|
||||
endif()
|
||||
option(USE_PKGCONFIG "Use pkgconfig to find libraries" ON)
|
||||
option(LINK_CMAKE_THREADS "Use CMake find_package to link the threading library" OFF)
|
||||
|
|
@ -254,13 +256,22 @@ endif(ENABLE_CMSISDAP)
|
|||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
add_definitions(-DENABLE_XVC=1)
|
||||
target_sources(openFPGALoader PRIVATE src/xvc_client.cpp src/xvc_server.cpp)
|
||||
list (APPEND OPENFPGALOADER_HEADERS src/xvc_client.hpp src/xvc_server.cpp)
|
||||
list (APPEND OPENFPGALOADER_HEADERS src/xvc_client.hpp src/xvc_server.hpp)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-pthread ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
message("Xilinx Virtual Server support enabled")
|
||||
else()
|
||||
message("Xilinx Virtual Server support disabled")
|
||||
endif()
|
||||
|
||||
if (ENABLE_REMOTEBITBANG)
|
||||
add_definitions(-DENABLE_REMOTEBITBANG=1)
|
||||
target_sources(openFPGALoader PRIVATE src/remoteBitbang_client.cpp)
|
||||
list (APPEND OPENFPGALOADER_HEADERS src/remoteBitbang_client.hpp)
|
||||
message("Remote bitbang client support enabled")
|
||||
else()
|
||||
message("Remote bitbang client support disabled")
|
||||
endif()
|
||||
|
||||
if (ZLIB_FOUND)
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(openFPGALoader ${ZLIB_LIBRARIES})
|
||||
|
|
|
|||
|
|
@ -218,6 +218,13 @@ steppenprobe:
|
|||
URL: https://github.com/diegoherranz/steppenprobe
|
||||
|
||||
|
||||
remote-bitgang:
|
||||
|
||||
- Name: OpenOCD remote bitbang
|
||||
Description: The remote_bitbang JTAG driver is used to drive JTAG from a remote (TCP) process
|
||||
URL: https://github.com/openocd-org/openocd/blob/master/doc/manual/jtag/drivers/remote_bitbang.txt
|
||||
|
||||
|
||||
tigard:
|
||||
|
||||
- Name: tigard
|
||||
|
|
@ -262,4 +269,4 @@ jetson-nano-gpio:
|
|||
|
||||
- Name: Bitbang GPIO
|
||||
Description: Bitbang GPIO pins on Jetson Nano Linux host. Use /dev/mem to have a faster clock.
|
||||
URL: https://github.com/jwatte/jetson-gpio-example
|
||||
URL: https://github.com/jwatte/jetson-gpio-example
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ enum communication_type {
|
|||
MODE_XVC_CLIENT, /*! Xilinx Virtual Cable client */
|
||||
MODE_LIBGPIOD_BITBANG, /*! Bitbang gpio pins */
|
||||
MODE_JETSONNANO_BITBANG, /*! Bitbang gpio pins */
|
||||
MODE_REMOTEBITBANG, /*! Remote Bitbang mode */
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
@ -119,6 +120,9 @@ static std::map <std::string, cable_t> cable_list = {
|
|||
#ifdef ENABLE_JETSONNANOGPIO
|
||||
{"jetson-nano-gpio", {MODE_JETSONNANO_BITBANG, {}}},
|
||||
#endif
|
||||
#ifdef ENABLE_REMOTEBITBANG
|
||||
{"remote-bitbang", CABLE_DEF(MODE_REMOTEBITBANG, 0x0000, 0x0000 )},
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // SRC_CABLE_HPP_
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@
|
|||
#endif
|
||||
#include "dirtyJtag.hpp"
|
||||
#include "part.hpp"
|
||||
#ifdef ENABLE_REMOTEBITBANG
|
||||
#include "remoteBitbang_client.hpp"
|
||||
#endif
|
||||
#include "usbBlaster.hpp"
|
||||
#ifdef ENABLE_XVC
|
||||
#include "xvc_client.hpp"
|
||||
|
|
@ -141,6 +144,11 @@ void Jtag::init_internal(const cable_t &cable, const string &dev, const string &
|
|||
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;
|
||||
#endif
|
||||
default:
|
||||
std::cerr << "Jtag: unknown cable type" << std::endl;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,242 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
* Copyright (C) 2023 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||
*/
|
||||
|
||||
#include "remoteBitbang_client.hpp"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "display.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define TCK_OFFSET 2
|
||||
#define TMS_OFFSET 1
|
||||
#define TDI_OFFSET 0
|
||||
#define TCK_BIT (1 << TCK_OFFSET)
|
||||
#define TMS_BIT (1 << TMS_OFFSET)
|
||||
#define TDI_BIT (1 << TDI_OFFSET)
|
||||
|
||||
RemoteBitbang_client::RemoteBitbang_client(const std::string &ip_addr, int port,
|
||||
int8_t verbose):
|
||||
_xfer_buf(NULL), _num_bytes(0), _last_tms(TMS_BIT),
|
||||
_last_tdi(0), _buffer_size(2048), _sock(0), _port(port)
|
||||
{
|
||||
(void) verbose;
|
||||
/* create client to server */
|
||||
if (!open_connection(ip_addr))
|
||||
throw std::runtime_error("connection failure");
|
||||
|
||||
/* set led to low */
|
||||
if (xfer_pkt('b', NULL) < 0)
|
||||
throw std::runtime_error("can't set led low");
|
||||
|
||||
_xfer_buf = reinterpret_cast<uint8_t *>(malloc(sizeof(uint8_t)
|
||||
* _buffer_size));
|
||||
if (!_xfer_buf)
|
||||
throw std::runtime_error("can't allocate internal buffer");
|
||||
}
|
||||
|
||||
RemoteBitbang_client::~RemoteBitbang_client()
|
||||
{
|
||||
// flush buffers before quit
|
||||
if (_num_bytes != 0)
|
||||
flush();
|
||||
|
||||
// set led high
|
||||
if (xfer_pkt('B', NULL) < 0)
|
||||
printf("can't set led high");
|
||||
|
||||
// send close request
|
||||
if (xfer_pkt('Q', NULL) < 0)
|
||||
printf("can't send close request");
|
||||
|
||||
// cleanup
|
||||
if (_xfer_buf)
|
||||
free(_xfer_buf);
|
||||
// close socket
|
||||
close(_sock);
|
||||
}
|
||||
|
||||
int RemoteBitbang_client::writeTMS(uint8_t *tms, uint32_t len,
|
||||
bool flush_buffer)
|
||||
{
|
||||
// empty buffer
|
||||
// if asked flush
|
||||
if (len == 0)
|
||||
return ((flush_buffer) ? flush() : 0);
|
||||
|
||||
uint8_t base_v = '0' + _last_tdi;
|
||||
for (uint32_t pos = 0; pos < len; pos++) {
|
||||
// buffer full -> write
|
||||
if (_num_bytes == _buffer_size)
|
||||
ll_write(NULL);
|
||||
_last_tms = (tms[pos >> 3] & (1 << (pos & 0x07))) ? TMS_BIT : 0;
|
||||
_xfer_buf[_num_bytes++] = base_v + _last_tms;
|
||||
_xfer_buf[_num_bytes++] = base_v + _last_tms + TCK_BIT;
|
||||
}
|
||||
|
||||
// flush where it's asked or if the buffer is full
|
||||
if (flush_buffer || _num_bytes == _buffer_size * 8)
|
||||
return flush();
|
||||
return len;
|
||||
}
|
||||
|
||||
int RemoteBitbang_client::writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len,
|
||||
bool end)
|
||||
{
|
||||
if (len == 0) // nothing to do
|
||||
return 0;
|
||||
|
||||
uint8_t base_v = '0' + _last_tms;
|
||||
for (uint32_t pos = 0; pos < len; pos++) {
|
||||
if (_num_bytes == _buffer_size)
|
||||
ll_write(NULL); // NULL because _num_bytes is always 0 when read
|
||||
_last_tdi = (tx[pos >> 3] & (1 << (pos & 0x07))) ? TDI_BIT : 0;
|
||||
if (end && pos == len - 1) {
|
||||
_last_tms = TMS_BIT;
|
||||
base_v = '0' + _last_tms;
|
||||
}
|
||||
_xfer_buf[_num_bytes++] = base_v + _last_tdi;
|
||||
_xfer_buf[_num_bytes++] = base_v + _last_tdi + TCK_BIT;
|
||||
if (rx) {
|
||||
uint8_t tdo;
|
||||
ll_write(&tdo);
|
||||
if (tdo == '1')
|
||||
rx[pos >> 3] |= (1 << (pos & 0x07));
|
||||
else
|
||||
rx[pos >> 3] &= ~(1 << (pos & 0x07));
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// toggle clk with constant TDI and TMS.
|
||||
int RemoteBitbang_client::toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len)
|
||||
{
|
||||
// nothing to do
|
||||
if (clk_len == 0)
|
||||
return 0;
|
||||
if (_num_bytes != 0)
|
||||
flush();
|
||||
|
||||
_last_tms = tms;
|
||||
_last_tdi = tdi;
|
||||
uint8_t val = (_last_tms | _last_tdi);
|
||||
|
||||
// flush buffer before starting
|
||||
if (_num_bytes != 0)
|
||||
flush();
|
||||
|
||||
for (uint32_t len = 0; len < clk_len; len++) {
|
||||
if (len == _num_bytes)
|
||||
ll_write(NULL);
|
||||
_xfer_buf[_num_bytes++] = '0' + val;
|
||||
_xfer_buf[_num_bytes++] = '0' + (val | TCK_BIT);
|
||||
}
|
||||
|
||||
ll_write(NULL);
|
||||
|
||||
return clk_len;
|
||||
}
|
||||
|
||||
int RemoteBitbang_client::flush()
|
||||
{
|
||||
return ll_write(NULL);
|
||||
}
|
||||
|
||||
int RemoteBitbang_client::setClkFreq(uint32_t clkHz)
|
||||
{
|
||||
printWarn("clock speed is not configurable");
|
||||
return clkHz;
|
||||
}
|
||||
|
||||
bool RemoteBitbang_client::open_connection(const string &ip_addr)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(_port);
|
||||
addr.sin_addr.s_addr = inet_addr(ip_addr.c_str());
|
||||
|
||||
_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (_sock == -1) {
|
||||
printError("Socket creation error");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (connect(_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||
printError("Connection error");
|
||||
close(_sock);
|
||||
return false;
|
||||
}
|
||||
|
||||
int one = 1;
|
||||
setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&one,
|
||||
sizeof(one));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t RemoteBitbang_client::xfer_pkt(uint8_t instr, uint8_t *rx)
|
||||
{
|
||||
ssize_t len;
|
||||
// 1. instruction
|
||||
if ((len = write(_sock, &instr, 1)) == -1) {
|
||||
printError("Send instruction failed with error " +
|
||||
std::to_string(len));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rx) {
|
||||
len = recv(_sock, rx, 1, 0);
|
||||
if (len < 0) {
|
||||
printError("Receive error");
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
return (rx) ? 1 : 0;
|
||||
}
|
||||
|
||||
bool RemoteBitbang_client::ll_write(uint8_t *tdo)
|
||||
{
|
||||
if (_num_bytes == 0)
|
||||
return true;
|
||||
|
||||
ssize_t len;
|
||||
// write current buffer
|
||||
if ((len = write(_sock, _xfer_buf, _num_bytes)) == -1) {
|
||||
printError("Send error error: " + std::to_string(len));
|
||||
return false;
|
||||
}
|
||||
_num_bytes = 0;
|
||||
|
||||
// read only one char (if tdo is not null
|
||||
if (tdo) {
|
||||
if (xfer_pkt('R', tdo) < 0) {
|
||||
printError("read request error");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
* Copyright (C) 2023 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||
*/
|
||||
|
||||
#ifndef SRC_REMOTEBITBANG_CLIENT_HPP_
|
||||
#define SRC_REMOTEBITBANG_CLIENT_HPP_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "jtagInterface.hpp"
|
||||
|
||||
/*!
|
||||
* \brief Remote Bitbang Protocol client side driver
|
||||
*/
|
||||
class RemoteBitbang_client: public JtagInterface {
|
||||
public:
|
||||
/*!
|
||||
* \brief constructor: open device
|
||||
* \param[in] ip_addr: server IP addr
|
||||
* \param[in] port : server port
|
||||
* \param[in] verbose: verbose level -1 quiet, 0 normal,
|
||||
* 1 verbose, 2 debug
|
||||
*/
|
||||
RemoteBitbang_client(const std::string &ip_addr, int port,
|
||||
int8_t verbose);
|
||||
|
||||
~RemoteBitbang_client();
|
||||
|
||||
// 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 _buffer_size;}
|
||||
bool isFull() override { return _buffer_size == _num_bytes;}
|
||||
|
||||
private:
|
||||
/*!
|
||||
* \brief create a TCP socket and connect to
|
||||
* server at ip_addr
|
||||
* \param[in] ip_addr: server IP
|
||||
* \return false if socket creation or connection fails
|
||||
*/
|
||||
bool open_connection(const std::string &ip_addr);
|
||||
|
||||
/*!
|
||||
* \brief sent one instruction (ASCII format) and read when requested
|
||||
* \param[in] instr: ascii instruction
|
||||
* \param[in] rx: server answer buffer (may be NULL)
|
||||
* \return -1 when error, 0 when disconnected or tx_size/rx_size
|
||||
*/
|
||||
ssize_t xfer_pkt(uint8_t instr, uint8_t *rx);
|
||||
|
||||
/*!
|
||||
* \brief lowlevel write: write internal buffer (ASCII format)
|
||||
* and read one char when tdo is not NULL.
|
||||
* \param[out]: tdo: TDO read pointer (may be null)
|
||||
* \return false when failure
|
||||
*/
|
||||
bool ll_write(uint8_t *tdo);
|
||||
|
||||
uint8_t *_xfer_buf; /*!< tx buffer */
|
||||
uint32_t _num_bytes; /*!< number of bits stored */
|
||||
uint32_t _last_tms; /*!< last known TMS state */
|
||||
uint32_t _last_tdi; /*!< last known TDI state */
|
||||
|
||||
uint32_t _buffer_size; /*!< buffer max capacity */
|
||||
int _sock; /*!< socket */
|
||||
int _port; /*!< target port */
|
||||
};
|
||||
#endif // SRC_REMOTEBITBANG_CLIENT_HPP_
|
||||
Loading…
Reference in New Issue