Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ModelSim to tools supported in flow #422

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 220 additions & 0 deletions edalize/tools/modelsim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
# 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.tools.edatool import Edatool
from edalize.utils import EdaCommands

logger = logging.getLogger(__name__)


class Modelsim(Edatool):
description = "ModelSim simulator from Mentor Graphics"

TOOL_OPTIONS = {
"compilation_mode": {
"type": "str",
"desc": "Common or separate compilation, sep - for separate compilation, common - for common compilation",
},
"vcom_options": {
"type": "str",
"desc": "Additional options for compilation with vcom",
},
"vlog_options": {
"type": "str",
"desc": "Additional options for compilation with vlog",
},
"vsim_options": {
"type": "str",
"desc": "Additional run options for vsim",
},
# run_options?
}

def _write_build_rtl_tcl_file(self, tcl_main):
incdirs = []
libs = []
vlog_files = []

tcl_build_rtl = open(os.path.join(self.work_root, "edalize_build_rtl.tcl"), "w")

# Fill up incdirs with all include directories, before looping through all files.
for f in self.files:
self._add_include_dir(f, incdirs)

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

common_compilation = self.tool_options.get("compilation_mode") == "common"

for f in self.files:
if not f.get("logical_name"):
f["logical_name"] = "work"
if not f["logical_name"] in libs:
tcl_build_rtl.write("vlib {}\n".format(f["logical_name"]))
libs.append(f["logical_name"])
if f["file_type"].startswith("verilogSource") or f["file_type"].startswith(
"systemVerilogSource"
):
cmd = None

if not f.get("is_include_file"):
# Add vlog command for non-include file types
vlog_files.append(f["name"])
cmd = "vlog"

args = []

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

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 = "vcom"
if f["file_type"].endswith("-87"):
args = ["-87"]
if f["file_type"].endswith("-93"):
args = ["-93"]
if f["file_type"].endswith("-2008"):
args = ["-2008"]
else:
args = []

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

elif f["file_type"] == "tclSource":
cmd = None
tcl_main.write("do {}\n".format(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 and ((cmd != "vlog") or not common_compilation):
args += ["-quiet"]
args += ["-work", f["logical_name"]]
args += [f["name"].replace("\\", "/")]
tcl_build_rtl.write("{} {}\n".format(cmd, " ".join(args)))
if common_compilation:
args = self.tool_options.get("vlog_options", [])
for k, v in self.vlogdefine.items():
args += ["+define+{}={}".format(k, self._param_value_str(v))]

_vlog_files = []
has_sv = False
for f in vlog_files:
_vlog_files.append(f["name"].replace("\\", "/"))
if f["file_type"].startswith("systemVerilogSource"):
has_sv = True

if has_sv:
args += ["-sv"]
args += vlog_include_dirs
args += ["-quiet"]
args += ["-work", "work"]
args += ["-mfcu"]
tcl_build_rtl.write(f"vlog {' '.join(args)} {' '.join(_vlog_files)}")

def write_config_files(self):
"""
Generate ModelSim specific makefile & TCL build files from template.
"""

tcl_main = open(os.path.join(self.work_root, "edalize_main.tcl"), "w")
tcl_main.write("onerror { quit -code 1; }\n")
tcl_main.write("do edalize_build_rtl.tcl\n")
self._write_build_rtl_tcl_file(tcl_main)
tcl_main.close()

self.render_template(
"modelsim-makefile.j2",
"modelsim-makefile",
self.template_vars,
)

def setup(self, edam):
super().setup(edam)

depfiles = []

_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))]

_vsim_options = self.tool_options.get("vsim_options", [])
_modules = [m["name"] for m in self.vpi_modules]
_clean_targets = " ".join(["clean_" + m for m in _modules])

self.template_vars = {
"toplevel": self.toplevel,
"name": self.name,
"vsim_options": " ".join(_vsim_options),
"parameters": " ".join(_parameters),
"plusargs": " ".join(_plusargs),
"modules": " ".join(_modules),
}

commands = EdaCommands()

commands.add(
["make"]
+ [
"-f",
"modelsim-makefile",
"work",
],
["modelsim-build"],
depfiles,
)

commands.add(
["make"]
+ [
"-f",
"modelsim-makefile",
"run",
],
["modelsim-run"],
["modelsim-build"],
)

commands.add(
["make"]
+ [
"-f",
"modelsim-makefile",
"run-gui",
],
["modelsim-run-gui"],
["modelsim-build"],
)

commands.set_default_target("modelsim-build")
self.commands = commands

def run(self):
args = ["modelsim-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))

return ("make", args, self.work_root)
44 changes: 44 additions & 0 deletions edalize/tools/templates/modelsim/modelsim-makefile.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#Generated by Edalize
ifndef MODEL_TECH
$(error Environment variable MODEL_TECH was not found. It should be set to <modelsim install path>/bin)
endif

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

LD ?= ld
LDFLAGS := -shared -E

#Try to determine if ModelSim is 32- or 64-bit.
#To manually override, set the environment MTI_VCO_MODE to 32 or 64
ifeq ($(findstring 64, $(shell $(MODEL_TECH)/../vco)),)
CFLAGS += -m32
CXXFLAGS += -m32
LDFLAGS += -melf_i386
endif

RM ?= rm
INCS := -I$(MODEL_TECH)/../include

VSIM ?= $(MODEL_TECH)/vsim

TOPLEVEL := {{ toplevel }}
VPI_MODULES := {{ modules }}
PARAMETERS ?= {{ parameters }}
PLUSARGS ?= {{ plusargs }}
VSIM_OPTIONS ?= {{ vsim_options }}
EXTRA_OPTIONS ?= $(VSIM_OPTIONS) $(addprefix -g,$(PARAMETERS)) $(addprefix +,$(PLUSARGS))

all: work $(VPI_MODULES)

run: work $(VPI_MODULES)
$(VSIM) -c $(addprefix -pli ,$(VPI_MODULES)) $(EXTRA_OPTIONS) -do "run -all; quit -code [expr [coverage attribute -name TESTSTATUS -concise] >= 2 ? [coverage attribute -name TESTSTATUS -concise] : 0]; exit" $(TOPLEVEL)

run-gui: work $(VPI_MODULES)
$(VSIM) -gui $(addprefix -pli ,$(VPI_MODULES)) $(EXTRA_OPTIONS) $(TOPLEVEL)

work:
$(VSIM) -c -do "do edalize_main.tcl; exit"

clean: {{ clean_targets }}
22 changes: 22 additions & 0 deletions tests/flows/sim/modelsim/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#Auto generated by Edalize

all: post_build

pre_build:

modelsim-build: | pre_build
$(EDALIZE_LAUNCHER) make -f modelsim-makefile work

modelsim-run: modelsim-build
$(EDALIZE_LAUNCHER) make -f modelsim-makefile run

modelsim-run-gui: modelsim-build
$(EDALIZE_LAUNCHER) make -f modelsim-makefile run-gui

post_build: modelsim-build

pre_run:

run: pre_run

post_run: run
9 changes: 9 additions & 0 deletions tests/flows/sim/modelsim/edalize_build_rtl.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
vlib work
vlog +define+vlogdefine_bool=1 +define+vlogdefine_int=42 +define+vlogdefine_str=hello -sv +incdir+. -quiet -work work sv_file.sv
vlog +define+vlogdefine_bool=1 +define+vlogdefine_int=42 +define+vlogdefine_str=hello +incdir+. -quiet -work work vlog_file.v
vlog +define+vlogdefine_bool=1 +define+vlogdefine_int=42 +define+vlogdefine_str=hello +incdir+. -quiet -work work vlog05_file.v
vcom -quiet -work work vhdl_file.vhd
vlib libx
vcom -quiet -work libx vhdl_lfile
vcom -2008 -quiet -work work vhdl2008_file
vlog +define+vlogdefine_bool=1 +define+vlogdefine_int=42 +define+vlogdefine_str=hello -sv +incdir+. -quiet -work work another_sv_file.sv
3 changes: 3 additions & 0 deletions tests/flows/sim/modelsim/edalize_main.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
onerror { quit -code 1; }
do edalize_build_rtl.tcl
do tcl_file.tcl
44 changes: 44 additions & 0 deletions tests/flows/sim/modelsim/modelsim-makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#Generated by Edalize
ifndef MODEL_TECH
$(error Environment variable MODEL_TECH was not found. It should be set to <modelsim install path>/bin)
endif

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

LD ?= ld
LDFLAGS := -shared -E

#Try to determine if ModelSim is 32- or 64-bit.
#To manually override, set the environment MTI_VCO_MODE to 32 or 64
ifeq ($(findstring 64, $(shell $(MODEL_TECH)/../vco)),)
CFLAGS += -m32
CXXFLAGS += -m32
LDFLAGS += -melf_i386
endif

RM ?= rm
INCS := -I$(MODEL_TECH)/../include

VSIM ?= $(MODEL_TECH)/vsim

TOPLEVEL := top_module
VPI_MODULES :=
PARAMETERS ?= vlogparam_bool=1 vlogparam_int=42 vlogparam_str=hello
PLUSARGS ?= plusarg_bool=1 plusarg_int=42 plusarg_str=hello
VSIM_OPTIONS ?=
EXTRA_OPTIONS ?= $(VSIM_OPTIONS) $(addprefix -g,$(PARAMETERS)) $(addprefix +,$(PLUSARGS))

all: work $(VPI_MODULES)

run: work $(VPI_MODULES)
$(VSIM) -c $(addprefix -pli ,$(VPI_MODULES)) $(EXTRA_OPTIONS) -do "run -all; quit -code [expr [coverage attribute -name TESTSTATUS -concise] >= 2 ? [coverage attribute -name TESTSTATUS -concise] : 0]; exit" $(TOPLEVEL)

run-gui: work $(VPI_MODULES)
$(VSIM) -gui $(addprefix -pli ,$(VPI_MODULES)) $(EXTRA_OPTIONS) $(TOPLEVEL)

work:
$(VSIM) -c -do "do edalize_main.tcl; exit"

clean:
17 changes: 17 additions & 0 deletions tests/test_flow_sim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import filecmp
import os
from .edalize_flow_common import flow_fixture
from .edalize_common import tests_dir


def test_flow_sim_modelsim(flow_fixture):
flow_options = {
"tool": "modelsim",
}

ff = flow_fixture("sim", flow_options=flow_options, ref_subdir="modelsim")

ff.compare_makefile()
ff.compare_config_files(
["edalize_build_rtl.tcl", "edalize_main.tcl", "modelsim-makefile"]
)