# 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("..")