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.
--- /dev/null
+// 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);
+}
+++ /dev/null
-// 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);
-}
--- /dev/null
+# 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)
--- /dev/null
+// 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);
+}
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:
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)
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)