-
Notifications
You must be signed in to change notification settings - Fork 210
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
mibuild/sim: create server.py and server_tb (Proof of concept OK with…
… flterm) Using a server allow us to create a virtual UART (and ethernet TAP in the future). 1) start the server 2) start flterm on the virtual serial port created by the server 3) run the simulation This will enable us to do serialboot and netboot in simulation. This will also enable prototyping ethernet for ARTIQ in simulation.
1 parent
f154c2e
commit 991572f
Showing
4 changed files
with
313 additions
and
9 deletions.
There are no files selected for viewing
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
# This file is Copyright (c) 2015 Florent Kermarrec <florent@enjoy-digital.fr> | ||
# License: BSD | ||
|
||
import socket | ||
import os | ||
import pty | ||
import time | ||
import threading | ||
|
||
messages= { | ||
"EXIT": 0, | ||
"ACK": 1, | ||
"ERROR": 2, | ||
"UART": 3 | ||
} | ||
|
||
class PacketTooLarge(Exception): | ||
pass | ||
|
||
class VerilatorServer: | ||
def __init__(self, sockaddr="simsocket"): | ||
self.sockaddr = sockaddr | ||
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) | ||
self._cleanup_file() | ||
self.socket.bind(self.sockaddr) | ||
self.socket.listen(1) | ||
|
||
master, slave = pty.openpty() | ||
self.serial = master | ||
self.serial_name = os.ttyname(slave) | ||
|
||
self.ack = False | ||
|
||
self._print_banner() | ||
|
||
def _print_banner(self): | ||
print("Mibuild simulation server") | ||
print("sockaddr: {}".format(self.sockaddr)) | ||
print("serial: {}".format(self.serial_name)) | ||
|
||
def _cleanup_file(self): | ||
try: | ||
os.remove(self.sockaddr) | ||
except OSError: | ||
pass | ||
|
||
def accept(self): | ||
self.conn, addr = self.socket.accept() | ||
|
||
def send(self, packet): | ||
self.conn.send(packet) | ||
|
||
def recv(self): | ||
maxlen = 2048 | ||
packet = self.conn.recv(maxlen) | ||
if len(packet) < 1: | ||
return None | ||
if len(packet) >= maxlen: | ||
raise PacketTooLarge | ||
return packet | ||
|
||
def close(self): | ||
if hasattr(self, "conn"): | ||
self.conn.shutdown(socket.SHUT_RDWR) | ||
self.conn.close() | ||
if hasattr(self, "socket"): | ||
self.socket.shutdown(socket.SHUT_RDWR) | ||
self.socket.close() | ||
self._cleanup_file() | ||
|
||
# XXX proof of concept | ||
server = VerilatorServer() | ||
server.accept() | ||
print("Connection accepted") | ||
|
||
def read(): | ||
while True: | ||
packet = server.recv() | ||
if packet is not None: | ||
if packet[0] == messages["UART"]: | ||
c = bytes(chr(packet[1]).encode('utf-8')) | ||
os.write(server.serial, c) | ||
|
||
elif packet[0] == messages["ACK"]: | ||
server.ack = True | ||
|
||
def write(): | ||
while True: | ||
for c in list(os.read(server.serial, 100)): | ||
packet = [messages["UART"], c] | ||
server.send(bytes(packet)) | ||
while not server.ack: | ||
pass | ||
server.ack = False | ||
|
||
readthread = threading.Thread(target=read, daemon=True) | ||
readthread.start() | ||
|
||
writethread = threading.Thread(target=write, daemon=True) | ||
writethread.start() | ||
|
||
while True: | ||
time.sleep(1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
// This file is Copyright (c) 2015 Florent Kermarrec <florent@enjoy-digital.fr> | ||
// License: BSD | ||
|
||
#include <time.h> | ||
|
||
#include "Vdut.h" | ||
#include "verilated.h" | ||
#include "verilated_vcd_c.h" | ||
|
||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
#include <sys/types.h> | ||
#include <sys/select.h> | ||
#include <sys/socket.h> | ||
#include <netinet/in.h> | ||
#include <sys/un.h> | ||
#include <netdb.h> | ||
#include <pthread.h> | ||
|
||
int trace = 0; | ||
|
||
vluint64_t main_time = 0; | ||
double sc_time_stamp() | ||
{ | ||
return main_time; | ||
} | ||
|
||
Vdut* dut; | ||
VerilatedVcdC* tfp; | ||
|
||
/* ios */ | ||
|
||
#define MAX_LEN 2048 | ||
|
||
enum { | ||
MESSAGE_EXIT = 0, | ||
MESSAGE_ACK, | ||
MESSAGE_ERROR, | ||
MESSAGE_UART | ||
}; | ||
|
||
struct sim { | ||
int socket; | ||
bool run; | ||
|
||
unsigned int tick; | ||
clock_t start; | ||
clock_t end; | ||
float speed; | ||
|
||
char txbuffer[MAX_LEN]; | ||
char rxbuffer[MAX_LEN]; | ||
|
||
char rx_serial_stb; | ||
char rx_serial_data; | ||
char rx_serial_presented; | ||
}; | ||
|
||
int sim_connect(struct sim *s, const char *sockaddr) | ||
{ | ||
struct sockaddr_un addr; | ||
|
||
s->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0); | ||
if(s->socket < 0) { | ||
return -1; | ||
} | ||
|
||
addr.sun_family = AF_UNIX; | ||
strcpy(addr.sun_path, sockaddr); | ||
if(connect(s->socket, (struct sockaddr *)&addr, sizeof(addr)) != 0) { | ||
close(s->socket); | ||
return -1; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
int sim_send(struct sim *s, char *buffer, int len) | ||
{ | ||
send(s->socket, s->txbuffer, len, 0); | ||
return 0; | ||
} | ||
|
||
void sim_receive_process(struct sim *s, char * buffer, int len) { | ||
int i; | ||
switch(buffer[0]) { | ||
case MESSAGE_EXIT: | ||
s->run = false; | ||
break; | ||
case MESSAGE_UART: | ||
i = 0; | ||
for(i=0; i<len-1; i++) { | ||
s->rx_serial_stb = 1; | ||
s->rx_serial_data = buffer[i+1]; | ||
while (s->rx_serial_presented == 0); | ||
s->rx_serial_presented = 0; | ||
} | ||
s->rx_serial_stb = 0; | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
void *sim_receive(void *s_void) | ||
{ | ||
struct sim *s = (sim *) s_void; | ||
int rxlen; | ||
while(1) | ||
{ | ||
rxlen = recv(s->socket, s->rxbuffer, MAX_LEN, 0); | ||
if (rxlen > 0) | ||
sim_receive_process(s, s->rxbuffer, rxlen); | ||
s->txbuffer[0] = MESSAGE_ACK; | ||
sim_send(s, s->txbuffer, 1); | ||
} | ||
} | ||
|
||
void sim_destroy(struct sim *s) | ||
{ | ||
close(s->socket); | ||
free(s); | ||
} | ||
|
||
int console_service(struct sim *s) | ||
{ | ||
/* fpga --> console */ | ||
SERIAL_SOURCE_ACK = 1; | ||
if(SERIAL_SOURCE_STB == 1) { | ||
s->txbuffer[0] = MESSAGE_UART; | ||
s->txbuffer[1] = SERIAL_SOURCE_DATA; | ||
sim_send(s, s->txbuffer, 2); | ||
} | ||
|
||
/* console --> fpga */ | ||
SERIAL_SINK_STB = s->rx_serial_stb; | ||
SERIAL_SINK_DATA = s->rx_serial_data; | ||
if (s->rx_serial_stb) | ||
s->rx_serial_presented = 1; | ||
|
||
return 0; | ||
} | ||
|
||
void sim_tick(struct sim *s) | ||
{ | ||
SYS_CLK = s->tick%2; | ||
dut->eval(); | ||
if (trace) | ||
tfp->dump(s->tick); | ||
s->tick++; | ||
s->end = clock(); | ||
} | ||
|
||
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) | ||
{ | ||
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); | ||
sim_connect(&s, "../../migen/mibuild/sim/simsocket"); // XXX use args | ||
This comment has been minimized.
Sorry, something went wrong. |
||
|
||
pthread_t sim_receive_thread; | ||
|
||
pthread_create(&sim_receive_thread, NULL, sim_receive, &s); | ||
|
||
s.run = true; | ||
while(s.run) { | ||
sim_tick(&s); | ||
if (SYS_CLK) { | ||
if (console_service(&s) != 0) | ||
s.run = false; | ||
} | ||
} | ||
|
||
tfp->close(); | ||
pthread_cancel(sim_receive_thread); | ||
sim_destroy(&s); | ||
|
||
exit(0); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Yes, that's one of the reasons why UNIX sockets are nasty. You may want to put this one in
/tmp
.