openFPGALoader/src/remoteBitbang_client.cpp

243 lines
5.2 KiB
C++

// 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(const uint8_t *tms, uint32_t len,
bool flush_buffer, __attribute__((unused)) const uint8_t tdi)
{
// 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(const 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;
}