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)