diff --git a/mibuild/platforms/sim.py b/mibuild/platforms/sim.py new file mode 100644 index 000000000..db48ec2c3 --- /dev/null +++ b/mibuild/platforms/sim.py @@ -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 index 000000000..e69de29bb diff --git a/mibuild/sim/dut_tb.cpp b/mibuild/sim/dut_tb.cpp new file mode 100644 index 000000000..1fb7e78cb --- /dev/null +++ b/mibuild/sim/dut_tb.cpp @@ -0,0 +1,158 @@ +// 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 + +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 index 000000000..c3a23f594 --- /dev/null +++ b/mibuild/sim/verilator.py @@ -0,0 +1,111 @@ +# This file is Copyright (c) 2015 Florent Kermarrec +# 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("..")