xvc_server: first draft
This commit is contained in:
parent
e91ae5e65b
commit
34ff4f4020
|
|
@ -106,6 +106,7 @@ set(OPENFPGALOADER_SOURCE
|
||||||
src/xilinxMapParser.cpp
|
src/xilinxMapParser.cpp
|
||||||
src/colognechip.cpp
|
src/colognechip.cpp
|
||||||
src/colognechipCfgParser.cpp
|
src/colognechipCfgParser.cpp
|
||||||
|
src/xvc_server.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(OPENFPGALOADER_HEADERS
|
set(OPENFPGALOADER_HEADERS
|
||||||
|
|
@ -156,6 +157,7 @@ set(OPENFPGALOADER_HEADERS
|
||||||
src/xilinxMapParser.hpp
|
src/xilinxMapParser.hpp
|
||||||
src/colognechip.hpp
|
src/colognechip.hpp
|
||||||
src/colognechipCfgParser.hpp
|
src/colognechipCfgParser.hpp
|
||||||
|
src/xvc_server.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
link_directories(
|
link_directories(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,351 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "xvc_server.hpp"
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "ftdiJtagMPSSE.hpp"
|
||||||
|
#include "cable.hpp"
|
||||||
|
#include "display.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
XVC_server::XVC_server(int port, const cable_t & cable,
|
||||||
|
const jtag_pins_conf_t * pin_conf, string dev,
|
||||||
|
const string & serial, uint32_t clkHZ, int8_t verbose,
|
||||||
|
const string & ip_adr, const bool invert_read_edge,
|
||||||
|
const string & firmware_path):_verbose(verbose > 1),
|
||||||
|
_jtag(NULL), _port(port), _sock(-1),
|
||||||
|
_is_stopped(false), _must_stop(false),
|
||||||
|
_buffer_size(2048)
|
||||||
|
{
|
||||||
|
(void)pin_conf;
|
||||||
|
(void)ip_adr;
|
||||||
|
(void)firmware_path;
|
||||||
|
switch (cable.type) {
|
||||||
|
case MODE_FTDI_SERIAL:
|
||||||
|
_jtag = new FtdiJtagMPSSE(cable.config, dev, serial, clkHZ,
|
||||||
|
invert_read_edge, _verbose);
|
||||||
|
break;
|
||||||
|
#if 0
|
||||||
|
case MODE_ANLOGICCABLE:
|
||||||
|
_jtag = new AnlogicCable(clkHZ);
|
||||||
|
break;
|
||||||
|
case MODE_FTDI_BITBANG:
|
||||||
|
if (pin_conf == NULL)
|
||||||
|
throw std::exception();
|
||||||
|
_jtag =
|
||||||
|
new FtdiJtagBitBang(cable.config, pin_conf, dev, serial,
|
||||||
|
clkHZ, _verbose);
|
||||||
|
break;
|
||||||
|
case MODE_CH552_JTAG:
|
||||||
|
_jtag =
|
||||||
|
new CH552_jtag(cable.config, dev, serial, clkHZ, _verbose);
|
||||||
|
break;
|
||||||
|
case MODE_DIRTYJTAG:
|
||||||
|
_jtag = new DirtyJtag(clkHZ, _verbose);
|
||||||
|
break;
|
||||||
|
case MODE_JLINK:
|
||||||
|
_jtag = new Jlink(clkHZ, _verbose);
|
||||||
|
break;
|
||||||
|
case MODE_USBBLASTER:
|
||||||
|
_jtag = new UsbBlaster(cable.config.vid, cable.config.pid,
|
||||||
|
firmware_path, _verbose);
|
||||||
|
break;
|
||||||
|
#ifdef ENABLE_CMSISDAP
|
||||||
|
case MODE_CMSISDAP:
|
||||||
|
_jtag =
|
||||||
|
new CmsisDAP(cable.config.vid, cable.config.pid, _verbose);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
std::cerr << "Jtag: unknown cable type" << std::endl;
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
_tmstdi = (unsigned char *)malloc(sizeof(unsigned char) * _buffer_size);
|
||||||
|
_result = (unsigned char *)malloc(sizeof(unsigned char) * (_buffer_size / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
XVC_server::~XVC_server()
|
||||||
|
{
|
||||||
|
close_connection();
|
||||||
|
if (_jtag)
|
||||||
|
delete _jtag;
|
||||||
|
free(_tmstdi);
|
||||||
|
free(_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XVC_server::open_connection()
|
||||||
|
{
|
||||||
|
char hostname[256];
|
||||||
|
memset(&_sock_addr, '\0', sizeof(_sock_addr));
|
||||||
|
_sock_addr.sin_family = AF_INET;
|
||||||
|
_sock_addr.sin_port = htons(_port);
|
||||||
|
_sock_addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
|
||||||
|
_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (_sock < 0) {
|
||||||
|
printError("Socket creation error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof i);
|
||||||
|
|
||||||
|
if (::bind(_sock, (struct sockaddr*) &_sock_addr, sizeof(_sock_addr)) < 0) {
|
||||||
|
printError("Socket bind error");
|
||||||
|
close(_sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(_sock, 1) < 0) {
|
||||||
|
printError("Socket listen error");
|
||||||
|
close(_sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gethostname(hostname, sizeof(hostname)) != 0) {
|
||||||
|
printError("hostname lookup");
|
||||||
|
close(_sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char mess[256];
|
||||||
|
snprintf(mess, sizeof(mess),
|
||||||
|
"INFO: To connect to this xvcServer instance, use: TCP:%s:%d\n\n",
|
||||||
|
hostname, _port);
|
||||||
|
printInfo(mess);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XVC_server::close_connection()
|
||||||
|
{
|
||||||
|
if (_sock != -1)
|
||||||
|
close(_sock);
|
||||||
|
_sock = -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XVC_server::thread_listen()
|
||||||
|
{
|
||||||
|
fd_set conn;
|
||||||
|
int maxfd = 0;
|
||||||
|
|
||||||
|
FD_ZERO(&conn);
|
||||||
|
FD_SET(_sock, &conn);
|
||||||
|
|
||||||
|
maxfd = _sock;
|
||||||
|
|
||||||
|
while (!_must_stop) {
|
||||||
|
fd_set read = conn, except = conn;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = 1;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
if (select(maxfd + 1, &read, 0, &except, &tv) < 0) {
|
||||||
|
printError("select");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (fd = 0; fd <= maxfd; ++fd) {
|
||||||
|
if (FD_ISSET(fd, &read)) {
|
||||||
|
if (fd == _sock) {
|
||||||
|
int newfd;
|
||||||
|
socklen_t nsize = sizeof(_sock_addr);
|
||||||
|
|
||||||
|
newfd = accept(_sock, (struct sockaddr*) &_sock_addr,
|
||||||
|
&nsize);
|
||||||
|
|
||||||
|
printf("connection accepted - fd %d\n", newfd);
|
||||||
|
if (newfd < 0) {
|
||||||
|
throw std::runtime_error("accept");
|
||||||
|
} else {
|
||||||
|
printInfo("setting TCP_NODELAY to 1\n");
|
||||||
|
int flag = 1;
|
||||||
|
int optResult = setsockopt(newfd, IPPROTO_TCP,
|
||||||
|
TCP_NODELAY, (char *)&flag, sizeof(int));
|
||||||
|
if (optResult < 0)
|
||||||
|
throw std::runtime_error("TCP_NODELAY error");
|
||||||
|
if (newfd > maxfd) {
|
||||||
|
maxfd = newfd;
|
||||||
|
}
|
||||||
|
FD_SET(newfd, &conn);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int ret = handle_data(fd);
|
||||||
|
printInfo("connection closed - fd " + std::to_string(fd));
|
||||||
|
close(fd);
|
||||||
|
FD_CLR(fd, &conn);
|
||||||
|
if (ret == 1)
|
||||||
|
throw std::runtime_error("communication failure");
|
||||||
|
}
|
||||||
|
} else if (FD_ISSET(fd, &except)) {
|
||||||
|
printWarn("connection aborted - fd " + std::to_string(fd));
|
||||||
|
close(fd);
|
||||||
|
FD_CLR(fd, &conn);
|
||||||
|
if (fd == _sock)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_is_stopped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XVC_server::listen_loop()
|
||||||
|
{
|
||||||
|
_is_stopped = false;
|
||||||
|
_must_stop = false;
|
||||||
|
_thread = new std::thread(&XVC_server::thread_listen, this);
|
||||||
|
printInfo("Press to quit");
|
||||||
|
getchar();
|
||||||
|
_must_stop = true;
|
||||||
|
close_connection();
|
||||||
|
while (!_is_stopped){}
|
||||||
|
delete _thread;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int XVC_server::sread(int fd, void *target, int len)
|
||||||
|
{
|
||||||
|
unsigned char *t = (unsigned char *)target;
|
||||||
|
while (len) {
|
||||||
|
int r = read(fd, t, len);
|
||||||
|
if (r <= 0)
|
||||||
|
return r;
|
||||||
|
t += r;
|
||||||
|
len -= r;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int XVC_server::handle_data(int fd)
|
||||||
|
{
|
||||||
|
char xvcInfo[32];
|
||||||
|
|
||||||
|
do {
|
||||||
|
char cmd[16];
|
||||||
|
memset(cmd, 0, 16);
|
||||||
|
|
||||||
|
if (sread(fd, cmd, 2) != 1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* getinfo */
|
||||||
|
if (memcmp(cmd, "ge", 2) == 0) {
|
||||||
|
if (sread(fd, cmd, 6) != 1)
|
||||||
|
return 1;
|
||||||
|
snprintf(xvcInfo, sizeof(xvcInfo),
|
||||||
|
"xvcServer_v1.0:%u\n", _buffer_size);
|
||||||
|
if (send(fd, xvcInfo, strlen(xvcInfo), 0) !=
|
||||||
|
(ssize_t) strlen(xvcInfo)) {
|
||||||
|
perror("write");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (_verbose) {
|
||||||
|
printInfo(std::to_string((int)time(NULL)) +
|
||||||
|
" : Received command: 'getinfo'");
|
||||||
|
printInfo("\t Replied with " + string(xvcInfo));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* settck */
|
||||||
|
} else if (memcmp(cmd, "se", 2) == 0) {
|
||||||
|
if (sread(fd, cmd, 9) != 1)
|
||||||
|
return 1;
|
||||||
|
memcpy(_result, cmd + 5, 4);
|
||||||
|
if (write(fd, _result, 4) != 4) {
|
||||||
|
printError("write");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
uint32_t clk_period =
|
||||||
|
(static_cast<uint32_t>(_result[0]) << 0) |
|
||||||
|
(static_cast<uint32_t>(_result[1]) << 8) |
|
||||||
|
(static_cast<uint32_t>(_result[2]) << 16) |
|
||||||
|
(static_cast<uint32_t>(_result[3]) << 24);
|
||||||
|
|
||||||
|
_jtag->setClkFreq(static_cast<uint32_t>(1e9/clk_period));
|
||||||
|
|
||||||
|
if (_verbose) {
|
||||||
|
printInfo(std::to_string((int)time(NULL)) +
|
||||||
|
" : Received command: 'settck'");
|
||||||
|
printf("\t Replied with '%.*s'\n\n", 4,
|
||||||
|
cmd + 5);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else if (memcmp(cmd, "de", 2) == 0) { // DEBUG CODE
|
||||||
|
if (sread(fd, cmd, 3) != 1)
|
||||||
|
return 1;
|
||||||
|
printf("%u : Received command: 'debug'\n",
|
||||||
|
(int)time(NULL));
|
||||||
|
break;
|
||||||
|
} else if (memcmp(cmd, "of", 2) == 0) { // DEBUG CODE
|
||||||
|
if (sread(fd, cmd, 1) != 1)
|
||||||
|
return 1;
|
||||||
|
printf("%u : Received command: 'off'\n",
|
||||||
|
(int)time(NULL));
|
||||||
|
break;
|
||||||
|
} else if (memcmp(cmd, "sh", 2) == 0) {
|
||||||
|
if (sread(fd, cmd, 4) != 1)
|
||||||
|
return 1;
|
||||||
|
if (_verbose) {
|
||||||
|
printInfo(std::to_string((int)time(NULL)) +
|
||||||
|
" : Received command: 'shift'");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printError("invalid cmd '" + string(cmd) + "'");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handling for -> "shift:<num bits><tms vector><tdi vector>" */
|
||||||
|
uint32_t len, nr_bytes;
|
||||||
|
/* 1. len */
|
||||||
|
if (sread(fd, &len, 4) != 1) {
|
||||||
|
printError("reading length failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. convert len (in bits) to nr_bytes (in bytes) */
|
||||||
|
nr_bytes = (len + 7) / 8;
|
||||||
|
/* check buffer size */
|
||||||
|
if (nr_bytes * 2 > _buffer_size) {
|
||||||
|
printError("buffer size exceeded");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. receiv 2 x nr_bytes (TMS + TDI) */
|
||||||
|
memset(_tmstdi, 0, _buffer_size);
|
||||||
|
if (sread(fd, _tmstdi, nr_bytes * 2) != 1) {
|
||||||
|
printError("reading data failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
memset(_result, 0, _buffer_size/2);
|
||||||
|
|
||||||
|
if (_verbose) {
|
||||||
|
printInfo("\tNumber of Bits : " + std::to_string(len));
|
||||||
|
printInfo("\tNumber of Bytes : " + std::to_string(nr_bytes));
|
||||||
|
printInfo("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_jtag)
|
||||||
|
_jtag->writeTMSTDI(_tmstdi, _tmstdi + nr_bytes, _result, len);
|
||||||
|
|
||||||
|
/* send received TDO sequence */
|
||||||
|
if (send(fd, _result, nr_bytes, 0) != nr_bytes) {
|
||||||
|
printError("write");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||||
|
* based on:
|
||||||
|
* Taisen proposition
|
||||||
|
* https://github.com/tom01h/xvc-pico
|
||||||
|
* https://github.com/Xilinx/XilinxVirtualCable
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRC_XVC_SERVER_HPP_
|
||||||
|
#define SRC_XVC_SERVER_HPP_
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "jtag.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class XVC_server {
|
||||||
|
public:
|
||||||
|
XVC_server(int port, const cable_t &cable, const jtag_pins_conf_t *pin_conf,
|
||||||
|
string dev, const string &serial, uint32_t clkHZ, int8_t verbose,
|
||||||
|
const string &ip_adr,
|
||||||
|
const bool invert_read_edge, const string &firmware_path);
|
||||||
|
~XVC_server();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief open a server socket
|
||||||
|
* \return true when success, false otherwise
|
||||||
|
*/
|
||||||
|
bool open_connection();
|
||||||
|
/*!
|
||||||
|
* \brief close server socket
|
||||||
|
* \return true when success, false otherwise
|
||||||
|
*/
|
||||||
|
bool close_connection();
|
||||||
|
/*!
|
||||||
|
* \brief start server loop
|
||||||
|
* \return true when success, false otherwise
|
||||||
|
*/
|
||||||
|
bool listen_loop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*!
|
||||||
|
* \brief thread dedicated for wait for incoming connection
|
||||||
|
*/
|
||||||
|
void thread_listen();
|
||||||
|
/*!
|
||||||
|
* \brief parser and dispatcher for XVC transactions
|
||||||
|
* \return 1 when transactions fails, 0 otherwise
|
||||||
|
*/
|
||||||
|
int handle_data(int fd);
|
||||||
|
/*!
|
||||||
|
* \brief read len bytes from client
|
||||||
|
* \param fd: socket descriptor
|
||||||
|
* \param target: buffer
|
||||||
|
* \param len: number of bytes to read
|
||||||
|
* \return <= 0 when failure, 1 otherwise
|
||||||
|
*/
|
||||||
|
int sread(int fd, void *target, int len);
|
||||||
|
int _verbose; /*!< verbose level */
|
||||||
|
JtagInterface *_jtag; /*!< jtag interface */
|
||||||
|
int _port; /*!< network port */
|
||||||
|
int _sock; /*!< server socket descriptor */
|
||||||
|
struct sockaddr_in _sock_addr;
|
||||||
|
std::thread *_thread; /*!< connection thread */
|
||||||
|
bool _is_stopped; /*!< true when thread is stopped */
|
||||||
|
bool _must_stop; /*!< true to stop thread */
|
||||||
|
uint32_t _buffer_size; /*!< buffer max capacity TDI+TMS */
|
||||||
|
uint8_t *_tmstdi; /*!< TDI/TMS from client */
|
||||||
|
uint8_t *_result; /*!< buffer for server -> client */
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SRC_XVC_SERVER_HPP_
|
||||||
Loading…
Reference in New Issue