mibuild: initial Verilator support
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Sun, 1 Mar 2015 17:27:46 +0000 (18:27 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Sun, 1 Mar 2015 17:27:46 +0000 (18:27 +0100)
mibuild/platforms/sim.py [new file with mode: 0644]
mibuild/sim/__init__.py [new file with mode: 0644]
mibuild/sim/dut_tb.cpp [new file with mode: 0644]
mibuild/sim/verilator.py [new file with mode: 0644]

diff --git a/mibuild/platforms/sim.py b/mibuild/platforms/sim.py
new file mode 100644 (file)
index 0000000..db48ec2
--- /dev/null
@@ -0,0 +1,31 @@
+from mibuild.generic_platform import *
+from mibuild.crg import SimpleCRG
+from mibuild.sim.verilator import VerilatorPlatform
+
+class SimPins(Pins):
+       def __init__(self, n):
+               Pins.__init__(self, "s "*n)
+
+_io = [
+       ("sys_clk", 0, SimPins(1)),
+       ("sys_rst", 0, SimPins(1)),
+       ("serial", 0,
+               Subsignal("source_stb", SimPins(1)),
+               Subsignal("source_ack", SimPins(1)),
+               Subsignal("source_data", SimPins(8)),
+
+               Subsignal("sink_stb", SimPins(1)),
+               Subsignal("sink_ack", SimPins(1)),
+               Subsignal("sink_data", SimPins(8)),
+       ),
+]
+
+class Platform(VerilatorPlatform):
+       is_sim = True
+       default_clk_name = "sys_clk"
+       default_clk_period = 1000 # on modern computers simulate at ~ 1MHz
+       def __init__(self):
+               VerilatorPlatform.__init__(self, "SIM", _io)
+
+       def do_finalize(self, fragment):
+               pass
diff --git a/mibuild/sim/__init__.py b/mibuild/sim/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/mibuild/sim/dut_tb.cpp b/mibuild/sim/dut_tb.cpp
new file mode 100644 (file)
index 0000000..1fb7e78
--- /dev/null
@@ -0,0 +1,158 @@
+// 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 */
+/* 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("averate speed: %3.3f MHz\n\r", speed/1000000);
+
+       tfp->close();
+
+       exit(0);
+}
diff --git a/mibuild/sim/verilator.py b/mibuild/sim/verilator.py
new file mode 100644 (file)
index 0000000..c3a23f5
--- /dev/null
@@ -0,0 +1,111 @@
+# This file is Copyright (c) 2015 Florent Kermarrec <florent@enjoy-digital.fr>
+# License: BSD
+
+import os, subprocess
+
+from migen.fhdl.std import *
+from migen.fhdl.structure import _Fragment
+from mibuild.generic_platform import *
+
+def _build_tb(platform, template):
+
+       def io_name(ressource, subsignal=None):
+               res = platform.lookup_request(ressource)
+               if subsignal is not None:
+                       res = getattr(res, subsignal)
+               return platform.vns.get_name(res)
+
+       ios = """
+#define SYS_CLK dut->{sys_clk}
+
+#define SERIAL_SOURCE_STB dut->{serial_source_stb}
+#define SERIAL_SOURCE_ACK dut->{serial_source_ack}
+#define SERIAL_SOURCE_DATA   dut->{serial_source_data}
+
+#define SERIAL_SINK_STB dut->{serial_sink_stb}
+#define SERIAL_SINK_ACK dut->{serial_sink_ack}
+#define SERIAL_SINK_DATA   dut->{serial_sink_data}
+""".format(
+       sys_clk=io_name("sys_clk"),
+
+       serial_source_stb=io_name("serial", "source_stb"),
+       serial_source_ack=io_name("serial", "source_ack"),
+       serial_source_data=io_name("serial", "source_data"),
+
+       serial_sink_stb=io_name("serial", "sink_stb"),
+       serial_sink_ack=io_name("serial", "sink_ack"),
+       serial_sink_data=io_name("serial", "sink_data"),
+       )
+
+       content = ""
+       f = open(template, "r")
+       done = False
+       for l in f:
+               content += l
+               if "/* ios */" in l and not done:
+                       content += ios
+                       done = True
+
+       f.close()
+       tools.write_to_file("dut_tb.cpp", content)
+
+def _build_sim(platform, build_name, include_paths, verilator_root_path, template_file, trace):
+       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}
+make -j -C obj_dir/ -f Vdut.mk Vdut VERILATOR_ROOT={verilator_root}
+
+""".format(verilator_root= os.path.join("../../", verilator_root_path), # XXX
+       disable_warnings="-Wno-lint -Wno-INITIALDLY",
+       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
+       r = subprocess.call(["bash", build_script_file])
+       if r != 0:
+               raise OSError("Subprocess failed")
+
+def _run_sim(build_name):
+       run_script_contents = """obj_dir/Vdut
+"""
+       run_script_file = "run_" + build_name + ".sh"
+       tools.write_to_file(run_script_file, run_script_contents, force_unix=True)
+       r = subprocess.call(["bash", run_script_file])
+       if r != 0:
+               raise OSError("Subprocess failed")
+
+class VerilatorPlatform(GenericPlatform):
+       # XXX fix template / verilator_path
+       def build(self, soc, build_dir="build", build_name="top", run=True, trace=True,
+                       template_file="../migen/mibuild/sim/dut_tb.cpp",
+                       verilator_root_path="../verilator"):
+               tools.mkdir_noerror(build_dir)
+               os.chdir(build_dir)
+
+               self.soc = soc
+               fragment = soc.get_fragment()
+               self.finalize(fragment)
+               v_src, vns = self.get_verilog(fragment)
+               named_sc, named_pc = self.resolve_signals(vns)
+               self.vns = vns
+               v_file = "dut.v"
+               tools.write_to_file(v_file, v_src)
+
+               include_paths = []
+               for source in self.sources:
+                       path = os.path.dirname(source[0]).replace("\\", "\/")
+                       if path not in include_paths:
+                               include_paths.append(path)
+               include_paths += self.verilog_include_paths
+               _build_sim(self, build_name, include_paths, verilator_root_path, template_file, trace)
+
+               if run:
+                       _run_sim(build_name)
+
+               os.chdir("..")