clash-vexriscv-0.1: data/vexriscv/ffi/impl.cpp
// SPDX-FileCopyrightText: 2022 Google LLC
//
// SPDX-License-Identifier: Apache-2.0
#include "interface.h"
#include <arpa/inet.h>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
typedef struct {
int server_socket, client_handle;
struct sockaddr_in server_addr;
struct sockaddr_storage server_storage;
uint32_t timer;
socklen_t addr_size;
uint32_t self_sleep;
uint32_t check_new_connections_timer;
uint8_t rx_buffer[100];
int32_t rx_buffer_size;
int32_t rx_buffer_remaining;
JTAG_INPUT prev_input;
} vexr_jtag_bridge_data;
extern "C" {
vexr_jtag_bridge_data *vexr_jtag_bridge_init(uint16_t port);
void vexr_jtag_bridge_step(vexr_jtag_bridge_data *d, const JTAG_OUTPUT *output,
JTAG_INPUT *input);
void vexr_jtag_bridge_shutdown(vexr_jtag_bridge_data *bridge_data);
}
static bool set_socket_blocking_enabled(int fd, bool blocking);
static void connection_reset(vexr_jtag_bridge_data *bridge_data);
vexr_jtag_bridge_data *vexr_jtag_bridge_init(uint16_t port) {
vexr_jtag_bridge_data *d = new vexr_jtag_bridge_data;
d->prev_input = {0, 0, 0};
d->timer = 0;
d->self_sleep = 0;
d->check_new_connections_timer = 0;
d->rx_buffer_size = 0;
d->rx_buffer_remaining = 0;
d->server_socket = socket(PF_INET, SOCK_STREAM, 0);
assert(d->server_socket != -1);
int flag = 1;
setsockopt(d->server_socket, /* socket affected */
IPPROTO_TCP, /* set option at TCP level */
TCP_NODELAY, /* name of option */
(char *)&flag, /* the cast is historical cruft */
sizeof(int) /* length of option value */
);
set_socket_blocking_enabled(d->server_socket, 0);
d->server_addr.sin_family = AF_INET;
d->server_addr.sin_port = htons(port);
d->server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(d->server_addr.sin_zero, '\0', sizeof(d->server_addr.sin_zero));
bind(d->server_socket, (struct sockaddr *)&d->server_addr,
sizeof(d->server_addr));
listen(d->server_socket, 1);
d->client_handle = -1;
d->addr_size = sizeof(d->server_storage);
return d;
}
void vexr_jtag_bridge_step(vexr_jtag_bridge_data *d, const JTAG_OUTPUT *output,
JTAG_INPUT *input) {
*input = d->prev_input;
if (d->timer != 0) {
d->timer -= 1;
return;
}
d->check_new_connections_timer++;
if (d->check_new_connections_timer == 200) {
d->check_new_connections_timer = 0;
int new_client_handle = accept(
d->server_socket, (struct sockaddr *)&d->server_storage, &d->addr_size);
if (new_client_handle != -1) {
if (d->client_handle != -1) {
connection_reset(d);
}
d->client_handle = new_client_handle;
} else if (d->client_handle == -1) {
d->self_sleep = 200;
}
}
if (d->self_sleep) {
d->self_sleep--;
return;
}
if (d->client_handle != -1) {
int n;
if (d->rx_buffer_remaining == 0) {
if (ioctl(d->client_handle, FIONREAD, &n) != 0) {
connection_reset(d);
return;
}
if (n >= 1) {
d->rx_buffer_size =
read(d->client_handle, &d->rx_buffer, sizeof(d->rx_buffer));
if (d->rx_buffer_size < 0) {
connection_reset(d);
return;
}
d->rx_buffer_remaining = d->rx_buffer_size;
} else {
d->self_sleep = 30;
return;
}
}
if (d->rx_buffer_remaining != 0) {
char command =
d->rx_buffer[d->rx_buffer_size - (d->rx_buffer_remaining--)];
switch (command) {
case 'R': { // Read request
char tdo_value = (output->tdo != 0) ? '1' : '0';
if (send(d->client_handle, &tdo_value, 1, 0) == -1) {
printf("[REMOTE_BITBANG] send failed\n");
connection_reset(d);
}
break;
}
case '0' ... '7': { // Write tck, tms, tdi
uint8_t value = command - '0';
input->tck = (value & 4) != 0;
input->tms = (value & 2) != 0;
input->tdi = (value & 1) != 0;
d->prev_input = *input;
break;
}
case 'Q': { // Quit
printf("[REMOTE_BITBANG] Received `Q`, closing socket.\n");
connection_reset(d);
// Consider terminating the simulation here
printf("[REMOTE_BITBANG] Simulation will not be terminated.\n");
break;
}
case 'B': { // Blink on
// Do not print anything, it will flood the output
break;
}
case 'b': { // Blink off
// Do not print anything, it will flood the output
break;
}
case 'r': { // Reset 0 0
printf("[REMOTE_BITBANG] Command 'r': Reset 0 0 not implemented\n");
break;
}
case 's': { // Reset 0 1
printf("[REMOTE_BITBANG] Command 's': Reset 0 1 not implemented\n");
break;
}
case 't': { // Reset 1 1
printf("[REMOTE_BITBANG] Command 't': Reset 1 1 not implemented\n");
break;
}
case 'u': { // Reset 1 0
printf("[REMOTE_BITBANG] Command 'u': Reset 1 0 not implemented\n");
break;
}
default: {
printf("[REMOTE_BITBANG] Unknown command: %c\n", command);
connection_reset(d);
break;
}
}
}
}
d->timer = 3;
}
void vexr_jtag_bridge_shutdown(vexr_jtag_bridge_data *bridge_data) {
if (bridge_data->client_handle != -1) {
shutdown(bridge_data->client_handle, SHUT_RDWR);
usleep(100);
}
if (bridge_data->server_socket != -1) {
close(bridge_data->server_socket);
usleep(100);
}
}
/** Returns true on success, or false if there was an error */
static bool set_socket_blocking_enabled(int fd, bool blocking) {
if (fd < 0)
return false;
#ifdef WIN32
unsigned long mode = blocking ? 0 : 1;
return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? true : false;
#else
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0)
return false;
flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
return (fcntl(fd, F_SETFL, flags) == 0) ? true : false;
#endif
}
static void connection_reset(vexr_jtag_bridge_data *bridge_data) {
printf("[JTAG BRIDGE] closed connection\n");
shutdown(bridge_data->client_handle, SHUT_RDWR);
bridge_data->client_handle = -1;
}