# Copyright edalize contributors
# Licensed under the 2-Clause BSD License, see LICENSE for details.
# SPDX-License-Identifier: BSD-2-Clause

import os
import logging

from edalize.edatool import Edatool

logger = logging.getLogger(__name__)

MAKE_HEADER = """#Generated by Edalize
ifeq (, $(shell which xmroot))
$(error "No Xcelium installation in $(PATH)")
endif

XCELIUM_HOME = $(shell xmroot)

CC ?= gcc
CFLAGS := -c -std=c99 -fPIC -fno-stack-protector -g

LD ?= ld
LDFLAGS := -shared -E

#Only 32 bits is currently supported
CFLAGS  += -m32
LDFLAGS += -melf_i386

RM ?= rm
INCS := -I$(XCELIUM_HOME)/tools/include

XRUN ?= $(XCELIUM_HOME)/tools/bin/xrun

TOPLEVEL      := {toplevel}
DPI_LIBRARIES :={dpi_libraries}
PARAMETERS    ?= {parameters}
PLUSARGS      ?= {plusargs}
XMSIM_OPTIONS ?= {xmsim_options}
XRUN_OPTIONS  ?= {xrun_options}
EXTRA_OPTIONS ?= $(XRUN_OPTIONS) $(DPI_LIBRARIES) $(if $(XMSIM_OPTIONS),-xmsimargs '$(XMSIM_OPTIONS)',) $(addprefix -defparam ,$(PARAMETERS)) $(addprefix +,$(PLUSARGS))

XRUN_CALL = $(XRUN) -q -f edalize_main.f $(EXTRA_OPTIONS) -top $(TOPLEVEL)

all:

run:
	$(XRUN_CALL)

run-gui:
	$(XRUN_CALL) -gui -access rwc
clean:
"""


class Xcelium(Edatool):

    argtypes = ["plusarg", "vlogdefine", "vlogparam", "generic"]

    @classmethod
    def get_doc(cls, api_ver):
        if api_ver == 0:
            return {
                "description": "Xcelium simulator from Cadence Design Systems",
                "lists": [
                    {
                        "name": "xmvhdl_options",
                        "type": "String",
                        "desc": "Additional options for compilation with xmvhdl",
                    },
                    {
                        "name": "xmvlog_options",
                        "type": "String",
                        "desc": "Additional options for compilation with xmvlog",
                    },
                    {
                        "name": "xmsim_options",
                        "type": "String",
                        "desc": "Additional run options for xmsim",
                    },
                    {
                        "name": "xrun_options",
                        "type": "String",
                        "desc": "Additional run options for xrun",
                    },
                ],
            }

    def _write_build_rtl_f_file(self, tcl_main):
        tcl_build_rtl = open(os.path.join(self.work_root, "edalize_build_rtl.f"), "w")

        (src_files, incdirs) = self._get_fileset_files()
        vlog_include_dirs = ["+incdir+" + d.replace("\\", "/") for d in incdirs]

        libs = []
        self.dpi_libraries = ""
        for f in src_files:
            if not f.logical_name:
                f.logical_name = "worklib"
            if f.file_type.startswith("verilogSource") or f.file_type.startswith(
                "systemVerilogSource"
            ):
                cmd = "xmvlog"
                args = []

                args += self.tool_options.get("xmvlog_options", [])

                # Sort dictionary items, to ensure stable output, which makes testing easier
                for k, v in self.vlogdefine.items():
                    args += ["+define+{}={}".format(k, self._param_value_str(v))]

                if f.file_type.startswith("systemVerilogSource"):
                    args += ["-sv"]
                args += vlog_include_dirs
            elif f.file_type.startswith("vhdlSource"):
                cmd = "xmvhdl"
                if f.file_type.endswith("-93"):
                    args = ["-v93"]
                if f.file_type.endswith("-2008"):
                    args = ["-v200x"]
                else:
                    args = []

                args += self.tool_options.get("xmvhdl_options", [])

            elif f.file_type == "tclSource":
                cmd = None
                tcl_main.write("-input {}\n".format(f.name))
            elif f.file_type == "dpiLibrary":
                cmd = None
                self.dpi_libraries += f" -sv_lib {f.name}"
            elif f.file_type == "user":
                cmd = None
            else:
                _s = "{} has unknown file type '{}'"
                logger.warning(_s.format(f.name, f.file_type))
                cmd = None
            if cmd:
                args += [f.name.replace("\\", "/")]
                line = "-makelib {} {} -endlib".format(f.logical_name, " ".join(args))
                tcl_build_rtl.write(line + "\n")

    def _write_makefile(self):
        vpi_make = open(os.path.join(self.work_root, "Makefile"), "w")
        _parameters = []
        for key, value in self.vlogparam.items():
            _parameters += ["{}={}".format(key, self._param_value_str(value))]
        for key, value in self.generic.items():
            _parameters += [
                "{}={}".format(key, self._param_value_str(value, bool_is_str=True))
            ]
        _plusargs = []
        for key, value in self.plusarg.items():
            _plusargs += ["{}={}".format(key, self._param_value_str(value))]

        _xmsim_options = self.tool_options.get("xmsim_options", [])
        _xrun_options = self.tool_options.get("xrun_options", [])

        _s = MAKE_HEADER.format(
            toplevel=self.toplevel,
            parameters=" ".join(_parameters),
            plusargs=" ".join(_plusargs),
            xmsim_options=" ".join(_xmsim_options),
            xrun_options=" ".join(_xrun_options),
            dpi_libraries=self.dpi_libraries,
        )
        vpi_make.write(_s)

        vpi_make.close()

    def configure_main(self):
        tcl_main = open(os.path.join(self.work_root, "edalize_main.f"), "w")
        tcl_main.write("-f edalize_build_rtl.f\n")

        self._write_build_rtl_f_file(tcl_main)
        self._write_makefile()
        tcl_main.close()

    def run_main(self):
        args = ["run"]

        # Set plusargs
        if self.plusarg:
            plusargs = []
            for key, value in self.plusarg.items():
                plusargs += ["{}={}".format(key, self._param_value_str(value))]
            args.append("PLUSARGS=" + " ".join(plusargs))

        self._run_tool("make", args)
