Skip to content

Commit

Permalink
mibuild/sim: create server.py and server_tb (Proof of concept OK with…
Browse files Browse the repository at this point in the history
… 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.
enjoy-digital committed Mar 3, 2015
1 parent f154c2e commit 991572f
Showing 4 changed files with 313 additions and 9 deletions.
File renamed without changes.
103 changes: 103 additions & 0 deletions mibuild/sim/server.py
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)
202 changes: 202 additions & 0 deletions mibuild/sim/server_tb.cpp
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.

Copy link
@sbourdeauducq

sbourdeauducq Mar 3, 2015

Member

Yes, that's one of the reasons why UNIX sockets are nasty. You may want to put this one in /tmp.


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);
}
17 changes: 8 additions & 9 deletions mibuild/sim/verilator.py
Original file line number Diff line number Diff line change
@@ -49,24 +49,23 @@ def io_name(ressource, subsignal=None):
f.close()
tools.write_to_file("dut_tb.cpp", content)

def _build_sim(platform, build_name, include_paths, template_file, trace, verbose):
def _build_sim(platform, build_name, include_paths, sim_path, dut, verbose):
include = ""
for path in include_paths:
include += "-I"+path+" "

build_script_contents = """# Autogenerated by mibuild
rm -rf obj_dir/
verilator {disable_warnings} -O3 --cc dut.v --exe dut_tb.cpp {trace} {include}
verilator {disable_warnings} -O3 --cc dut.v --exe dut_tb.cpp -LDFLAGS "-lpthread" -trace {include}
make -j -C obj_dir/ -f Vdut.mk Vdut
""".format(
disable_warnings="-Wno-fatal",
trace="-trace" if trace else "",
include=include)
build_script_file = "build_" + build_name + ".sh"
tools.write_to_file(build_script_file, build_script_contents, force_unix=True)

_build_tb(platform, os.path.join("../", template_file)) # XXX
_build_tb(platform, os.path.join("../", sim_path, dut + ".cpp")) # XXX
if verbose:
r = subprocess.call(["bash", build_script_file])
else:
@@ -84,10 +83,10 @@ def _run_sim(build_name):
raise OSError("Subprocess failed")

class VerilatorPlatform(GenericPlatform):
# XXX fix template_file
def build(self, soc, build_dir="build", build_name="top", run=True, trace=True,
template_file="../migen/mibuild/sim/dut_tb.cpp",
verbose=False):
# XXX fir sim_path
def build(self, soc, build_dir="build", build_name="top",
sim_path="../migen/mibuild/sim/", dut="console_tb",
run=True, verbose=False):
tools.mkdir_noerror(build_dir)
os.chdir(build_dir)

@@ -106,7 +105,7 @@ def build(self, soc, build_dir="build", build_name="top", run=True, trace=True,
if path not in include_paths:
include_paths.append(path)
include_paths += self.verilog_include_paths
_build_sim(self, build_name, include_paths, template_file, trace, verbose)
_build_sim(self, build_name, include_paths, sim_path, dut, verbose)

if run:
_run_sim(build_name)

0 comments on commit 991572f

Please sign in to comment.