remoteBitbang_client: adding new protocol (client side)

This commit is contained in:
Gwenhael Goavec-Merou 2023-02-26 20:39:12 +01:00
parent 567d854edf
commit fd97c244d9
6 changed files with 387 additions and 2 deletions

View File

@ -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})

View File

@ -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

View File

@ -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_

View File

@ -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;

View File

@ -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;
}

View File

@ -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_