mibuild/sim: create server.py and server_tb (Proof of concept OK with flterm)
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Tue, 3 Mar 2015 16:35:52 +0000 (17:35 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Tue, 3 Mar 2015 16:38:22 +0000 (17:38 +0100)
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/console_tb.cpp [new file with mode: 0644]
mibuild/sim/dut_tb.cpp [deleted file]
mibuild/sim/server.py [new file with mode: 0644]
mibuild/sim/server_tb.cpp [new file with mode: 0644]
mibuild/sim/verilator.py

diff --git a/mibuild/sim/console_tb.cpp b/mibuild/sim/console_tb.cpp
new file mode 100644 (file)
index 0000000..02e875d
--- /dev/null
@@ -0,0 +1,162 @@
+// 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/select.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <termios.h>
+
+int trace = 0;
+
+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;
+       }
+}
+
+vluint64_t main_time = 0;
+double sc_time_stamp()
+{
+       return main_time;
+}
+
+Vdut* dut;
+VerilatedVcdC* tfp;
+unsigned int tick;
+
+/* ios */
+
+int console_service()
+{
+       /* 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 (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;
+}
+
+void sim_tick()
+{
+       SYS_CLK = tick%2;
+       dut->eval();
+       if (trace)
+               tfp->dump(tick);
+       tick++;
+}
+
+void sim_init()
+{
+       int i;
+       tick = 0;
+#ifdef SYS_RST
+       SYS_RST = 1;
+       SYS_CLK = 0;
+       for (i=0; i<8; i++)
+               sim_tick();
+       SYS_RST = 0;
+#endif
+}
+
+int main(int argc, char **argv, char **env)
+{
+
+       clock_t start;
+       clock_t end;
+       float speed;
+
+       set_conio_terminal_mode();
+
+       Verilated::commandArgs(argc, argv);
+       dut = new Vdut;
+
+       Verilated::traceEverOn(true);
+       tfp = new VerilatedVcdC;
+       dut->trace(tfp, 99);
+       tfp->open("dut.vcd");
+
+       start = clock();
+       sim_init();
+       bool run = true;
+       while(run) {
+               sim_tick();
+               if (SYS_CLK) {
+                       if (console_service() != 0)
+                               run = false;
+               }
+       }
+       end = clock();
+
+       speed = (tick/2)/((end-start)/CLOCKS_PER_SEC);
+
+       printf("average speed: %3.3f MHz\n\r", speed/1000000);
+
+       tfp->close();
+
+       exit(0);
+}
diff --git a/mibuild/sim/dut_tb.cpp b/mibuild/sim/dut_tb.cpp
deleted file mode 100644 (file)
index 02e875d..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-// 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/select.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <termios.h>
-
-int trace = 0;
-
-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;
-       }
-}
-
-vluint64_t main_time = 0;
-double sc_time_stamp()
-{
-       return main_time;
-}
-
-Vdut* dut;
-VerilatedVcdC* tfp;
-unsigned int tick;
-
-/* ios */
-
-int console_service()
-{
-       /* 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 (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;
-}
-
-void sim_tick()
-{
-       SYS_CLK = tick%2;
-       dut->eval();
-       if (trace)
-               tfp->dump(tick);
-       tick++;
-}
-
-void sim_init()
-{
-       int i;
-       tick = 0;
-#ifdef SYS_RST
-       SYS_RST = 1;
-       SYS_CLK = 0;
-       for (i=0; i<8; i++)
-               sim_tick();
-       SYS_RST = 0;
-#endif
-}
-
-int main(int argc, char **argv, char **env)
-{
-
-       clock_t start;
-       clock_t end;
-       float speed;
-
-       set_conio_terminal_mode();
-
-       Verilated::commandArgs(argc, argv);
-       dut = new Vdut;
-
-       Verilated::traceEverOn(true);
-       tfp = new VerilatedVcdC;
-       dut->trace(tfp, 99);
-       tfp->open("dut.vcd");
-
-       start = clock();
-       sim_init();
-       bool run = true;
-       while(run) {
-               sim_tick();
-               if (SYS_CLK) {
-                       if (console_service() != 0)
-                               run = false;
-               }
-       }
-       end = clock();
-
-       speed = (tick/2)/((end-start)/CLOCKS_PER_SEC);
-
-       printf("average speed: %3.3f MHz\n\r", speed/1000000);
-
-       tfp->close();
-
-       exit(0);
-}
diff --git a/mibuild/sim/server.py b/mibuild/sim/server.py
new file mode 100644 (file)
index 0000000..99627a9
--- /dev/null
@@ -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)
diff --git a/mibuild/sim/server_tb.cpp b/mibuild/sim/server_tb.cpp
new file mode 100644 (file)
index 0000000..977206f
--- /dev/null
@@ -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
+
+       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);
+}
index 20afa8deb47f09001b3e5c5c00278ff8b0ef4002..f72e8db3048034a0379ad1e30ef7c967f58e5507 100644 (file)
@@ -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)