tillitis-key/hw/application_fpga/tb/application_fpga_verilator.cc
2022-10-21 14:10:41 +02:00

343 lines
5.8 KiB
C++

//======================================================================
//
// application_fpga_verilator.cc
// -----------------------------
// Wrapper to allow simulation of the application_fpga using Verilator.
//
//
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================
#include <pty.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include "Vapplication_fpga.h"
#include "verilated.h"
// Clock: 18 MHz, 62500 bps
// Divisor = 18E6 / 62500 = 288
#define BIT_DIV 288
struct uart {
int bit_div;
unsigned int ts;
unsigned int tx_next_ts;
unsigned int rx_next_ts;
int rx_state; /* 0, (idle), 1 (start), 2..9 (data), 10 (stop), 11 (stop end) */
int tx_state; /* 0, (idle), 1..8 (data), 9 (stop), 10 (stop end) */
uint8_t tx_data;
uint8_t rx_data;
int rx_has_data;
int tx_has_data;
uint8_t *tx;
uint8_t *rx;
};
void uart_init(struct uart *u, uint8_t *tx, uint8_t *rx, int bit_div);
void uart_tick(struct uart *u);
int uart_can_send(struct uart *u);
int uart_send(struct uart *u, uint8_t data);
int uart_recv(struct uart *u, uint8_t *data);
void uart_init(struct uart *u, uint8_t *tx, uint8_t *rx, int bit_div)
{
u->bit_div = bit_div;
u->ts = 0;
u->tx_next_ts = 0;
u->rx_next_ts = 0;
u->rx_state = 0;
u->tx_state = 0;
u->tx_data = 0;
u->rx_data = 0;
u->rx_has_data = 0;
u->tx_has_data = 0;
u->tx = tx;
*u->tx = 1;
u->rx = rx;
}
void uart_rx_tick(struct uart *u)
{
if (u->rx_state == 0) {
// Idle
if (!u->rx_has_data && !*u->rx) { // Active low
u->rx_next_ts = u->ts + u->bit_div / 2; // sample mid-point
u->rx_state = 1;
}
return;
}
if (u->rx_state == 1) {
// Start
if (u->ts < u->rx_next_ts)
return;
if (*u->rx) {
u->rx_state = 0; // Back to idle, shouldn't happen
return;
}
u->rx_next_ts += u->bit_div;
u->rx_data = 0;
u->rx_state = 2;
return;
}
if (u->rx_state > 1 && u->rx_state < 10) {
// Data
if (u->ts < u->rx_next_ts)
return;
u->rx_next_ts += u->bit_div;
u->rx_data |= (!!*u->rx) << (u->rx_state - 2);
u->rx_state++;
return;
}
if (u->rx_state == 10) {
// Stop
if (u->ts < u->rx_next_ts)
return;
if (!*u->rx) {
u->rx_state = 0; // Back to dle, shouldn't happen
return;
}
u->rx_next_ts += u->bit_div/2;
u->rx_has_data = 1;
u->rx_state = 11;
return;
}
if (u->rx_state == 11) {
if (u->ts < u->rx_next_ts)
return;
u->rx_state = 0;
return;
}
}
int uart_recv(struct uart *u, uint8_t *data)
{
if (u->rx_has_data && (u->rx_state == 0 || u->rx_state == 11)) {
*data = u->rx_data;
u->rx_has_data = 0;
return 1;
}
return 0;
}
void uart_tx_tick(struct uart *u)
{
if (u->tx_state == 0) {
// Idle
if (u->tx_has_data) {
u->tx_next_ts = u->ts + u->bit_div;
*u->tx = 0; // Start
u->tx_state = 1;
}
return;
}
if (u->tx_state > 0 && u->tx_state < 9) {
// Data
if (u->ts < u->tx_next_ts)
return;
u->tx_next_ts += u->bit_div;
*u->tx = (u->tx_data >> (u->tx_state - 1)) & 1;
u->tx_state++;
return;
}
if (u->tx_state == 9) {
// Stop
if (u->ts < u->tx_next_ts)
return;
u->tx_next_ts += u->bit_div;
*u->tx = 1; // Stop
u->tx_has_data = 0;
u->tx_state = 10;
return;
}
if (u->tx_state == 10) {
if (u->ts < u->tx_next_ts)
return;
u->tx_state = 0;
return;
}
}
int uart_can_send(struct uart *u)
{
return !u->tx_has_data;
}
int uart_send(struct uart *u, uint8_t data)
{
if (!uart_can_send(u))
return 0;
u->tx_has_data = 1;
u->tx_data = data;
return 1;
}
void uart_tick(struct uart *u)
{
u->ts++;
uart_rx_tick(u);
uart_tx_tick(u);
}
struct pty {
int amaster;
int aslave;
char slave[32];
};
int pty_init(struct pty *p);
int pty_can_recv(struct pty *p);
int pty_recv(struct pty *p, uint8_t *data);
void pty_send(struct pty *p, uint8_t data);
int pty_init(struct pty *p)
{
struct termios tty;
int flags;
memset(p, 0, sizeof(*p));
if (openpty(&p->amaster, &p->aslave, p->slave, NULL, NULL) < 0)
return -1;
if (tcgetattr(p->aslave, &tty) < 0)
return -1;
cfmakeraw(&tty);
if (tcsetattr(p->aslave, TCSAFLUSH, &tty) < 0)
return -1;
if ((flags = fcntl(p->amaster, F_GETFL, 0) < 0))
return -1;
flags |= O_NONBLOCK;
if (fcntl(p->amaster, F_SETFL, flags) < 0)
return -1;
printf("pty: %s\n", p->slave);
return 0;
}
int pty_can_recv(struct pty *p)
{
struct pollfd fds = {p->amaster, POLLIN, 0};
char c;
return poll(&fds, 1, 0) == 1;
}
int pty_recv(struct pty *p, uint8_t *data)
{
return read(p->amaster, data, 1) == 1;
}
void pty_send(struct pty *p, uint8_t data)
{
ssize_t i __attribute__((unused));
i = write(p->amaster, &data, 1);
}
volatile int touch_cyc = 0;
void sighandler(int)
{
printf("touched!\n");
touch_cyc = 1000;
}
void touch(uint8_t *touch_event)
{
if (touch_cyc > 0) {
touch_cyc--;
*touch_event = 1;
} else {
*touch_event = 0;
}
}
vluint64_t main_time = 0;
double sc_time_stamp()
{
return main_time;
}
int main(int argc, char **argv, char **env)
{
Verilated::commandArgs(argc, argv);
int r = 0, g = 0, b = 0;
Vapplication_fpga top;
struct uart u;
struct pty p;
int err;
if (signal(SIGUSR1, sighandler) == SIG_ERR)
return -1;
printf("generate touch event: \"$ kill -USR1 %d\"\n", (int)getpid());
err = pty_init(&p);
if (err)
return -1;
uart_init(&u, &top.interface_tx, &top.interface_rx, BIT_DIV);
top.clk = 0;
while (!Verilated::gotFinish()) {
uint8_t to_host = 0;
top.clk = !top.clk;
if (main_time < 10)
goto skip;
if (!top.clk) {
touch(&top.touch_event);
uart_tick(&u);
}
if (pty_can_recv(&p) && uart_can_send(&u)) {
uint8_t from_host = 0;
pty_recv(&p, &from_host);
uart_send(&u, from_host);
}
if (uart_recv(&u, &to_host) == 1) {
pty_send(&p, to_host);
}
skip:
main_time++;
top.eval();
}
}