400 lines
6.9 KiB
C++
400 lines
6.9 KiB
C++
// This file is Copyright (c) 2015 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
// License: BSD
|
|
#include "Vdut.h"
|
|
#include "verilated.h"
|
|
#include "verilated_vcd_c.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <termios.h>
|
|
#include <sys/poll.h>
|
|
|
|
#include <linux/if.h>
|
|
#include <linux/if_tun.h>
|
|
|
|
/* ios */
|
|
|
|
#ifdef SERIAL_SOURCE_STB
|
|
#define WITH_SERIAL
|
|
#endif
|
|
|
|
#ifdef ETH_SOURCE_STB
|
|
#define WITH_ETH
|
|
#endif
|
|
|
|
#define MAX(a,b) (((a)>(b))?(a):(b))
|
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
|
|
|
int trace = 0;
|
|
|
|
vluint64_t main_time = 0;
|
|
double sc_time_stamp()
|
|
{
|
|
return main_time;
|
|
}
|
|
|
|
/* Sim struct */
|
|
struct sim {
|
|
bool run;
|
|
|
|
unsigned int tick;
|
|
clock_t start;
|
|
clock_t end;
|
|
float speed;
|
|
|
|
#ifdef WITH_SERIAL_PTY
|
|
char serial_dev[64];
|
|
int serial_fd;
|
|
unsigned char serial_rx_data;
|
|
unsigned char serial_tx_data;
|
|
#endif
|
|
#ifdef WITH_ETH
|
|
const char *eth_dev;
|
|
const char *eth_tap;
|
|
int eth_fd;
|
|
unsigned char eth_txbuffer[2048];
|
|
unsigned char eth_rxbuffer[2048];
|
|
int eth_txbuffer_len;
|
|
int eth_rxbuffer_len;
|
|
int eth_rxbuffer_pos;
|
|
int eth_last_source_stb;
|
|
#endif
|
|
};
|
|
|
|
/* Serial functions */
|
|
#ifndef WITH_SERIAL_PTY
|
|
struct termios orig_termios;
|
|
|
|
void reset_terminal_mode(void)
|
|
{
|
|
tcsetattr(0, TCSANOW, &orig_termios);
|
|
}
|
|
|
|
void set_conio_terminal_mode(void)
|
|
{
|
|
struct termios new_termios;
|
|
|
|
/* take two copies - one for now, one for later */
|
|
tcgetattr(0, &orig_termios);
|
|
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
|
|
|
|
/* register cleanup handler, and set the new terminal mode */
|
|
atexit(reset_terminal_mode);
|
|
cfmakeraw(&new_termios);
|
|
tcsetattr(0, TCSANOW, &new_termios);
|
|
}
|
|
|
|
int kbhit(void)
|
|
{
|
|
struct timeval tv = { 0L, 0L };
|
|
fd_set fds;
|
|
FD_ZERO(&fds);
|
|
FD_SET(0, &fds);
|
|
return select(1, &fds, NULL, NULL, &tv);
|
|
}
|
|
|
|
int getch(void)
|
|
{
|
|
int r;
|
|
unsigned char c;
|
|
if((r = read(0, &c, sizeof(c))) < 0) {
|
|
return r;
|
|
} else {
|
|
return c;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Ethernet functions */
|
|
/* create tap:
|
|
openvpn --mktun --dev tap0
|
|
ifconfig tap0 192.168.0.14 up
|
|
mknod /dev/net/tap0 c 10 200
|
|
delete tap:
|
|
openvpn --rmtun --dev tap0 */
|
|
#ifdef WITH_ETH
|
|
void eth_init(struct sim *s, const char *dev, const char*tap)
|
|
{
|
|
s->eth_txbuffer_len = 0;
|
|
s->eth_rxbuffer_len = 0;
|
|
s->eth_rxbuffer_pos = 0;
|
|
s->eth_last_source_stb = 0;
|
|
s->eth_dev = dev;
|
|
s->eth_tap = tap;
|
|
}
|
|
|
|
void eth_open(struct sim *s)
|
|
{
|
|
|
|
struct ifreq ifr;
|
|
s->eth_fd = open (s->eth_dev, O_RDWR);
|
|
if(s->eth_fd < 0) {
|
|
fprintf(stderr, " Could not open dev %s\n", s->eth_dev);
|
|
return;
|
|
}
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
|
strncpy(ifr.ifr_name, s->eth_tap, IFNAMSIZ);
|
|
|
|
if(ioctl(s->eth_fd, TUNSETIFF, (void *) &ifr) < 0) {
|
|
fprintf(stderr, " Could not set %s\n", s->eth_tap);
|
|
close(s->eth_fd);
|
|
}
|
|
return;
|
|
}
|
|
|
|
int eth_close(struct sim *s)
|
|
{
|
|
if(s->eth_fd < 0)
|
|
close(s->eth_fd);
|
|
}
|
|
|
|
void eth_write(struct sim *s, unsigned char *buf, int len)
|
|
{
|
|
write(s->eth_fd, buf, len);
|
|
}
|
|
|
|
int eth_read(struct sim *s, unsigned char *buf)
|
|
{
|
|
|
|
struct pollfd fds[1];
|
|
int n;
|
|
int len;
|
|
|
|
fds[0].fd = s->eth_fd;
|
|
fds[0].events = POLLIN;
|
|
|
|
n = poll(fds, 1, 0);
|
|
if((n > 0) && ((fds[0].revents & POLLIN) == POLLIN)) {
|
|
len = read(s->eth_fd, buf, 1532);
|
|
} else {
|
|
len = 0;
|
|
}
|
|
return len;
|
|
}
|
|
#endif
|
|
|
|
Vdut* dut;
|
|
VerilatedVcdC* tfp;
|
|
|
|
#ifndef WITH_SERIAL_PTY
|
|
int console_service(struct sim *s)
|
|
{
|
|
/* fpga --> console */
|
|
SERIAL_SOURCE_ACK = 1;
|
|
if(SERIAL_SOURCE_STB == 1) {
|
|
if(SERIAL_SOURCE_DATA == '\n')
|
|
putchar('\r');
|
|
putchar(SERIAL_SOURCE_DATA);
|
|
fflush(stdout);
|
|
}
|
|
|
|
/* console --> fpga */
|
|
SERIAL_SINK_STB = 0;
|
|
if(s->tick%(1000) == 0) {
|
|
if(kbhit()) {
|
|
char c = getch();
|
|
if(c == 27 && !kbhit()) {
|
|
printf("\r\n");
|
|
return -1;
|
|
} else {
|
|
SERIAL_SINK_STB = 1;
|
|
SERIAL_SINK_DATA = c;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#else
|
|
void console_init(struct sim *s)
|
|
{
|
|
FILE *f;
|
|
f = fopen("/tmp/simserial","r");
|
|
fscanf(f, "%[^\n]", s->serial_dev);
|
|
fclose(f);
|
|
return;
|
|
}
|
|
|
|
void console_open(struct sim *s)
|
|
{
|
|
s->serial_fd = open(s->serial_dev, O_RDWR);
|
|
if(s->serial_fd < 0) {
|
|
fprintf(stderr, " Could not open dev %s\n", s->serial_dev);
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
int console_close(struct sim *s)
|
|
{
|
|
if(s->serial_fd < 0)
|
|
close(s->serial_fd);
|
|
}
|
|
|
|
void console_write(struct sim *s, unsigned char *buf, int len)
|
|
{
|
|
write(s->serial_fd, buf, len);
|
|
}
|
|
|
|
int console_read(struct sim *s, unsigned char *buf)
|
|
{
|
|
struct pollfd fds[1];
|
|
int n;
|
|
int len;
|
|
|
|
fds[0].fd = s->serial_fd;
|
|
fds[0].events = POLLIN;
|
|
|
|
n = poll(fds, 1, 0);
|
|
if((n > 0) && ((fds[0].revents & POLLIN) == POLLIN)) {
|
|
len = read(s->serial_fd, buf, 1);
|
|
} else {
|
|
len = 0;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
int console_service(struct sim *s)
|
|
{
|
|
/* fpga --> console */
|
|
SERIAL_SOURCE_ACK = 1;
|
|
if(SERIAL_SOURCE_STB == 1) {
|
|
s->serial_tx_data = SERIAL_SOURCE_DATA;
|
|
console_write(s, &(s->serial_tx_data), 1);
|
|
}
|
|
|
|
/* console --> fpga */
|
|
SERIAL_SINK_STB = 0;
|
|
if(console_read(s, &(s->serial_rx_data)))
|
|
{
|
|
SERIAL_SINK_STB = 1;
|
|
SERIAL_SINK_DATA = s->serial_rx_data;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_ETH
|
|
int ethernet_service(struct sim *s) {
|
|
/* fpga --> tap */
|
|
ETH_SOURCE_ACK = 1;
|
|
if(ETH_SOURCE_STB == 1) {
|
|
s->eth_txbuffer[s->eth_txbuffer_len] = ETH_SOURCE_DATA;
|
|
s->eth_txbuffer_len++;
|
|
} else {
|
|
if(s->eth_last_source_stb) {
|
|
eth_write(s, s->eth_txbuffer, s->eth_txbuffer_len);
|
|
s->eth_txbuffer_len = 0;
|
|
}
|
|
}
|
|
s->eth_last_source_stb = ETH_SOURCE_STB;
|
|
|
|
/* tap --> fpga */
|
|
if(s->eth_rxbuffer_len == 0) {
|
|
ETH_SINK_STB = 0;
|
|
s->eth_rxbuffer_pos = 0;
|
|
s->eth_rxbuffer_len = eth_read(s, s->eth_rxbuffer);
|
|
} else {
|
|
if(s->eth_rxbuffer_pos < MAX(s->eth_rxbuffer_len, 60)) {
|
|
ETH_SINK_STB = 1;
|
|
ETH_SINK_DATA = s->eth_rxbuffer[s->eth_rxbuffer_pos];
|
|
s->eth_rxbuffer_pos++;
|
|
} else {
|
|
ETH_SINK_STB = 0;
|
|
s->eth_rxbuffer_len = 0;
|
|
memset(s->eth_rxbuffer, 0, 1532);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void sim_tick(struct sim *s)
|
|
{
|
|
SYS_CLK = s->tick%2;
|
|
dut->eval();
|
|
if(trace)
|
|
tfp->dump(s->tick);
|
|
s->tick++;
|
|
}
|
|
|
|
void sim_init(struct sim *s)
|
|
{
|
|
int i;
|
|
s->tick = 0;
|
|
#ifdef SYS_RST
|
|
SYS_RST = 1;
|
|
SYS_CLK = 0;
|
|
for (i=0; i<8; i++)
|
|
sim_tick(s);
|
|
SYS_RST = 0;
|
|
#endif
|
|
s->start = clock();
|
|
}
|
|
|
|
int main(int argc, char **argv, char **env)
|
|
{
|
|
float speed;
|
|
|
|
#ifndef WITH_SERIAL_PTY
|
|
set_conio_terminal_mode();
|
|
#endif
|
|
|
|
Verilated::commandArgs(argc, argv);
|
|
dut = new Vdut;
|
|
|
|
Verilated::traceEverOn(true);
|
|
tfp = new VerilatedVcdC;
|
|
dut->trace(tfp, 99);
|
|
tfp->open("dut.vcd");
|
|
|
|
struct sim s;
|
|
sim_init(&s);
|
|
|
|
#ifdef WITH_SERIAL_PTY
|
|
console_init(&s);
|
|
console_open(&s);
|
|
#endif
|
|
|
|
#ifdef WITH_ETH
|
|
eth_init(&s, "/dev/net/tap0", "tap0"); // XXX get this from /tmp/simethernet
|
|
eth_open(&s);
|
|
#endif
|
|
|
|
s.run = true;
|
|
while(s.run) {
|
|
sim_tick(&s);
|
|
if(SYS_CLK) {
|
|
#ifdef WITH_SERIAL
|
|
if(console_service(&s) != 0)
|
|
s.run = false;
|
|
#endif
|
|
#ifdef WITH_ETH
|
|
ethernet_service(&s);
|
|
#endif
|
|
}
|
|
}
|
|
s.end = clock();
|
|
|
|
speed = (s.tick/2)/((s.end-s.start)/CLOCKS_PER_SEC);
|
|
|
|
printf("average speed: %3.3f MHz\n\r", speed/1000000);
|
|
|
|
tfp->close();
|
|
|
|
|
|
#ifdef WITH_SERIAL_PTY
|
|
console_close(&s);
|
|
#endif
|
|
#ifdef WITH_ETH
|
|
eth_close(&s);
|
|
#endif
|
|
|
|
exit(0);
|
|
}
|