mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2024-10-01 01:45:38 -04:00
343 lines
5.8 KiB
C++
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"
|
||
|
|
||
|
// Joachim says:
|
||
|
// Clock: 12 MHz, 38400 bps
|
||
|
// Divisor = 12*10E6 / 38400 = 312
|
||
|
#define BIT_DIV 312
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
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();
|
||
|
|
||
|
}
|
||
|
}
|