// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2022 Gwenhael Goavec-Merou */ #include "xvc_client.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "display.hpp" using namespace std; XVC_client::XVC_client(const std::string &ip_addr, int port, uint32_t clkHz, int8_t verbose): _verbose(verbose > 0), _xfer_buf(NULL), _tms(NULL), _tditdo(NULL), _num_bits(0), _last_tms(0), _last_tdi(0), _buffer_size(0), _sock(0), _port(port) { if (!open_connection(ip_addr)) throw std::runtime_error("connection failure"); uint8_t buffer[2048]; if (xfer_pkt("getinfo:", NULL, 0, buffer, 2048) <= 0) throw std::runtime_error("can't read info"); std::regex r("[_:]"); string rep((const char *)buffer); std::sregex_token_iterator start{ rep.begin(), rep.end(), r, -1 }, end; std::vector toto(start, end); if (toto.size() != 3) throw std::runtime_error("wrong getinfo: answer"); _server_name = std::move(toto[0]); _server_vers = std::move(toto[1]); _buffer_size = stoi(toto[2]) / 2; // buffer_size is for tms + tdi _xfer_buf = reinterpret_cast(malloc(sizeof(uint8_t) * ((2*_buffer_size) + 4))); _tms = reinterpret_cast(malloc(sizeof(uint8_t) * _buffer_size)); _tditdo = reinterpret_cast(malloc(sizeof(uint8_t) * _buffer_size)); if (!_xfer_buf || !_tms || !_tditdo) throw std::runtime_error("buffer allocation failure"); char disp[2048]; snprintf(disp, sizeof(disp), "detected %s version %s packet size %u", _server_name.c_str(), _server_vers.c_str(), _buffer_size); printInfo(disp); setClkFreq(clkHz); } XVC_client::~XVC_client() { // flush buffers before quit if (_num_bits != 0) flush(); // cleanup if (_xfer_buf) free(_xfer_buf); if (_tms) free(_tms); if (_tditdo) free(_tditdo); // close socket close(_sock); } int XVC_client::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 == _buffer_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) _tditdo[(_num_bits >> 3)] |= (1 << (_num_bits & 0x07)); else _tditdo[(_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 == _buffer_size * 8) return flush(); return len; } int XVC_client::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 = _buffer_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(_tditdo, 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 XVC_client::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(_tditdo, curr_tdi, _buffer_size); memset(_tms, curr_tms, _buffer_size); do { _num_bits = _buffer_size * 8; if (len < _num_bits) _num_bits = len; len -= _num_bits; ll_write(NULL); } while (len > 0); return clk_len; } int XVC_client::flush() { return ll_write(NULL); } int XVC_client::setClkFreq(uint32_t clkHz) { double clk_periodf = (1e9 / clkHz); uint32_t clk_period = (uint32_t)floor(clk_periodf); _xfer_buf[0] = static_cast((clk_period >> 0) & 0xff); _xfer_buf[1] = static_cast((clk_period >> 8) & 0xff); _xfer_buf[2] = static_cast((clk_period >> 16) & 0xff); _xfer_buf[3] = static_cast((clk_period >> 24) & 0xff); if (xfer_pkt("settck:", _xfer_buf, 4, _xfer_buf, 4) <= 0) { printError("setClkFreq: fail to configure frequency"); return -EXIT_FAILURE; } printf("freq %d %lf %d %d\n", clkHz, clk_periodf, clk_period, atoi((const char *)_xfer_buf)); printf("%x %x %x %x\n", _xfer_buf[0], _xfer_buf[1], _xfer_buf[2], _xfer_buf[3]); _clkHZ = clkHz; return _clkHZ; } bool XVC_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; } return true; } ssize_t XVC_client::xfer_pkt(const string &instr, const uint8_t *tx, uint32_t tx_size, uint8_t *rx, uint32_t rx_size) { ssize_t len = tx_size; /* 1. instruction */ if (send(_sock, instr.c_str(), instr.size(), 0) == -1) { printError("Send instruction failed"); return -1; } if (tx) { if (send(_sock, tx, tx_size, 0) == -1) { printError("Send error"); return -1; } } if (rx) { len = recv(_sock, rx, rx_size, 0); if (len < 0) { printError("Receive error"); } else if (len == 0) { fprintf(stderr, "Client orderly shut down the connection.\n"); } rx[len] = '\0'; if (_verbose) { printInfo("received " + std::to_string(len) + " Bytes (" + std::to_string(len * 8) + ")"); printf("\t"); for (int i = 0; i < len; i++) printf("%02x ", rx[i]); printf("\n"); } } return len; } bool XVC_client::ll_write(uint8_t *tdo) { int ret; if (_num_bits == 0) return true; uint32_t numbytes = (_num_bits + 7) >> 3; _xfer_buf[0] = static_cast((_num_bits >> 0) & 0xff); _xfer_buf[1] = static_cast((_num_bits >> 8) & 0xff); _xfer_buf[2] = static_cast((_num_bits >> 16) & 0xff); _xfer_buf[3] = static_cast((_num_bits >> 24) & 0xff); memcpy(_xfer_buf + 4, _tms, numbytes); memcpy(_xfer_buf + 4 + numbytes, _tditdo, numbytes); if ((ret = xfer_pkt("shift:\0", _xfer_buf, (2 * numbytes) + 4, _tditdo, numbytes)) < 0) return false; _num_bits = 0; // clear counter if (tdo) memcpy(tdo, _tditdo, numbytes); return true; }