From 991572f4fe836a17a771b5b0a415d947b9e64c12 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 3 Mar 2015 17:35:52 +0100 Subject: [PATCH] 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. --- mibuild/sim/{dut_tb.cpp => console_tb.cpp} | 0 mibuild/sim/server.py | 103 +++++++++++ mibuild/sim/server_tb.cpp | 202 +++++++++++++++++++++ mibuild/sim/verilator.py | 17 +- 4 files changed, 313 insertions(+), 9 deletions(-) rename mibuild/sim/{dut_tb.cpp => console_tb.cpp} (100%) create mode 100644 mibuild/sim/server.py create mode 100644 mibuild/sim/server_tb.cpp diff --git a/mibuild/sim/dut_tb.cpp b/mibuild/sim/console_tb.cpp similarity index 100% rename from mibuild/sim/dut_tb.cpp rename to mibuild/sim/console_tb.cpp diff --git a/mibuild/sim/server.py b/mibuild/sim/server.py new file mode 100644 index 000000000..99627a90c --- /dev/null +++ b/mibuild/sim/server.py @@ -0,0 +1,103 @@ +# This file is Copyright (c) 2015 Florent Kermarrec +# 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) diff --git a/mibuild/sim/server_tb.cpp b/mibuild/sim/server_tb.cpp new file mode 100644 index 000000000..977206f98 --- /dev/null +++ b/mibuild/sim/server_tb.cpp @@ -0,0 +1,202 @@ +// This file is Copyright (c) 2015 Florent Kermarrec +// License: BSD + +#include + +#include "Vdut.h" +#include "verilated.h" +#include "verilated_vcd_c.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; irx_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 + + 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); +} diff --git a/mibuild/sim/verilator.py b/mibuild/sim/verilator.py index 20afa8deb..f72e8db30 100644 --- a/mibuild/sim/verilator.py +++ b/mibuild/sim/verilator.py @@ -49,24 +49,23 @@ def _build_tb(platform, template): 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 @@ class VerilatorPlatform(GenericPlatform): 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)