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

sim_if/modelsim: use vmap to find modelsim.ini #975

Open
wants to merge 1 commit into
base: master
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
62 changes: 44 additions & 18 deletions tests/unit/test_modelsim_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,27 @@
from vunit.vhdl_standard import VHDL


def _get_test_path():
return Path(__file__).parent / "test_modelsim_out"


def _get_prefix_path():
return _get_test_path() / "prefix" / "bin"


def _get_installed_modelsim_ini():
return (_get_prefix_path() / ".." / "modelsim.ini").resolve()


class TestModelSimInterface(unittest.TestCase):
"""
Test the ModelSim interface
"""

@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
@mock.patch("vunit.sim_if.check_output", autospec=True, return_value="")
@mock.patch("vunit.sim_if.modelsim.Process", autospec=True)
def test_compile_project_vhdl_2008(self, process, check_output):
def test_compile_project_vhdl_2008(self, process, check_output, get_modelsim_ini):
simif = ModelSimInterface(prefix=self.prefix_path, output_path=self.output_path, persistent=False)
project = Project()
project.add_library("lib", "lib_path")
Expand All @@ -50,9 +63,10 @@ def test_compile_project_vhdl_2008(self, process, check_output):
]
check_output.assert_called_once_with(check_args, env=simif.get_env())

@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
@mock.patch("vunit.sim_if.check_output", autospec=True, return_value="")
@mock.patch("vunit.sim_if.modelsim.Process", autospec=True)
def test_compile_project_vhdl_2002(self, process, check_output):
def test_compile_project_vhdl_2002(self, process, check_output, get_modelsim_ini):
simif = ModelSimInterface(prefix=self.prefix_path, output_path=self.output_path, persistent=False)
project = Project()
project.add_library("lib", "lib_path")
Expand All @@ -73,9 +87,10 @@ def test_compile_project_vhdl_2002(self, process, check_output):
]
check_output.assert_called_once_with(check_args, env=simif.get_env())

@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
@mock.patch("vunit.sim_if.check_output", autospec=True, return_value="")
@mock.patch("vunit.sim_if.modelsim.Process", autospec=True)
def test_compile_project_vhdl_93(self, process, check_output):
def test_compile_project_vhdl_93(self, process, check_output, get_modelsim_ini):
simif = ModelSimInterface(prefix=self.prefix_path, output_path=self.output_path, persistent=False)
project = Project()
project.add_library("lib", "lib_path")
Expand All @@ -96,9 +111,10 @@ def test_compile_project_vhdl_93(self, process, check_output):
]
check_output.assert_called_once_with(check_args, env=simif.get_env())

@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
@mock.patch("vunit.sim_if.check_output", autospec=True, return_value="")
@mock.patch("vunit.sim_if.modelsim.Process", autospec=True)
def test_compile_project_vhdl_extra_flags(self, process, check_output):
def test_compile_project_vhdl_extra_flags(self, process, check_output, get_modelsim_ini):
simif = ModelSimInterface(prefix=self.prefix_path, output_path=self.output_path, persistent=False)
project = Project()
project.add_library("lib", "lib_path")
Expand All @@ -122,9 +138,10 @@ def test_compile_project_vhdl_extra_flags(self, process, check_output):
]
check_output.assert_called_once_with(check_args, env=simif.get_env())

@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
@mock.patch("vunit.sim_if.check_output", autospec=True, return_value="")
@mock.patch("vunit.sim_if.modelsim.Process", autospec=True)
def test_compile_project_verilog(self, process, check_output):
def test_compile_project_verilog(self, process, check_output, get_modelsim_ini):
simif = ModelSimInterface(prefix=self.prefix_path, output_path=self.output_path, persistent=False)
project = Project()
project.add_library("lib", "lib_path")
Expand All @@ -146,9 +163,10 @@ def test_compile_project_verilog(self, process, check_output):
]
check_output.assert_called_once_with(check_args, env=simif.get_env())

@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
@mock.patch("vunit.sim_if.check_output", autospec=True, return_value="")
@mock.patch("vunit.sim_if.modelsim.Process", autospec=True)
def test_compile_project_system_verilog(self, process, check_output):
def test_compile_project_system_verilog(self, process, check_output, get_modelsim_ini):
simif = ModelSimInterface(prefix=self.prefix_path, output_path=self.output_path, persistent=False)
project = Project()
project.add_library("lib", "lib_path")
Expand All @@ -171,9 +189,10 @@ def test_compile_project_system_verilog(self, process, check_output):
]
check_output.assert_called_once_with(check_args, env=simif.get_env())

@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
@mock.patch("vunit.sim_if.check_output", autospec=True, return_value="")
@mock.patch("vunit.sim_if.modelsim.Process", autospec=True)
def test_compile_project_verilog_extra_flags(self, process, check_output):
def test_compile_project_verilog_extra_flags(self, process, check_output, get_modelsim_ini):
simif = ModelSimInterface(prefix=self.prefix_path, output_path=self.output_path, persistent=False)
project = Project()
project.add_library("lib", "lib_path")
Expand All @@ -198,9 +217,10 @@ def test_compile_project_verilog_extra_flags(self, process, check_output):
]
check_output.assert_called_once_with(check_args, env=simif.get_env())

@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
@mock.patch("vunit.sim_if.check_output", autospec=True, return_value="")
@mock.patch("vunit.sim_if.modelsim.Process", autospec=True)
def test_compile_project_verilog_include(self, process, check_output):
def test_compile_project_verilog_include(self, process, check_output, get_modelsim_ini):
simif = ModelSimInterface(prefix=self.prefix_path, output_path=self.output_path, persistent=False)
project = Project()
project.add_library("lib", "lib_path")
Expand All @@ -223,9 +243,10 @@ def test_compile_project_verilog_include(self, process, check_output):
]
check_output.assert_called_once_with(check_args, env=simif.get_env())

@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
@mock.patch("vunit.sim_if.check_output", autospec=True, return_value="")
@mock.patch("vunit.sim_if.modelsim.Process", autospec=True)
def test_compile_project_verilog_define(self, process, check_output):
def test_compile_project_verilog_define(self, process, check_output, get_modelsim_ini):
simif = ModelSimInterface(prefix=self.prefix_path, output_path=self.output_path, persistent=False)
project = Project()
project.add_library("lib", "lib_path")
Expand All @@ -251,11 +272,12 @@ def test_compile_project_verilog_define(self, process, check_output):
def _get_inis(self):
return (
str(Path(self.output_path) / "modelsim.ini"),
str(Path(self.prefix_path) / ".." / "modelsim.ini"),
str(_get_installed_modelsim_ini()),
str(Path(self.test_path) / "my_modelsim.ini"),
)

def test_copies_modelsim_ini_file_from_install(self):
@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
def test_copies_modelsim_ini_file_from_install(self, get_modelsim_ini):
(modelsim_ini, installed_modelsim_ini, user_modelsim_ini) = self._get_inis()

with open(installed_modelsim_ini, "w") as fptr:
Expand All @@ -268,7 +290,8 @@ def test_copies_modelsim_ini_file_from_install(self):
with open(modelsim_ini, "r") as fptr:
self.assertEqual(fptr.read(), "installed")

def test_copies_modelsim_ini_file_from_user(self):
@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
def test_copies_modelsim_ini_file_from_user(self, get_modelsim_ini):
(modelsim_ini, installed_modelsim_ini, user_modelsim_ini) = self._get_inis()

with open(installed_modelsim_ini, "w") as fptr:
Expand All @@ -283,7 +306,8 @@ def test_copies_modelsim_ini_file_from_user(self):
with open(modelsim_ini, "r") as fptr:
self.assertEqual(fptr.read(), "user")

def test_overwrites_modelsim_ini_file_from_install(self):
@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
def test_overwrites_modelsim_ini_file_from_install(self, get_modelsim_ini):
(modelsim_ini, installed_modelsim_ini, user_modelsim_ini) = self._get_inis()

with open(modelsim_ini, "w") as fptr:
Expand All @@ -299,7 +323,8 @@ def test_overwrites_modelsim_ini_file_from_install(self):
with open(modelsim_ini, "r") as fptr:
self.assertEqual(fptr.read(), "installed")

def test_overwrites_modelsim_ini_file_from_user(self):
@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
def test_overwrites_modelsim_ini_file_from_user(self, get_modelsim_ini):
(modelsim_ini, installed_modelsim_ini, user_modelsim_ini) = self._get_inis()

with open(modelsim_ini, "w") as fptr:
Expand All @@ -318,10 +343,11 @@ def test_overwrites_modelsim_ini_file_from_user(self):
self.assertEqual(fptr.read(), "user")

@mock.patch("vunit.sim_if.modelsim.LOGGER", autospec=True)
@mock.patch("vunit.sim_if.modelsim.ModelSimInterface._get_modelsim_ini_from_vmap", return_value=_get_installed_modelsim_ini())
@mock.patch("vunit.sim_if.check_output", autospec=True, return_value="")
@mock.patch("vunit.sim_if.modelsim.Process", autospec=True)
@mock.patch("vunit.sim_if.vsim_simulator_mixin.Process", autospec=True)
def test_optimize(self, vsim_simulator_mixin_process, modelsim_process, check_output, LOGGER):
def test_optimize(self, vsim_simulator_mixin_process, modelsim_process, check_output, get_modelsim_ini, LOGGER):
simif = ModelSimInterface(prefix=self.prefix_path, output_path=self.output_path, persistent=False)
project = Project()
project.add_library("lib", str(Path(self.libraries_path) / "lib"))
Expand Down Expand Up @@ -361,18 +387,18 @@ def test_optimize(self, vsim_simulator_mixin_process, modelsim_process, check_ou
LOGGER.error.assert_has_calls(expected_error_calls)

def setUp(self):
self.test_path = str(Path(__file__).parent / "test_modelsim_out")
self.test_path = str(_get_test_path())

self.output_path = str(Path(self.test_path) / "modelsim")
self.prefix_path = str(Path(self.test_path) / "prefix" / "bin")
self.prefix_path = str(_get_prefix_path)
self.libraries_path = str(Path(self.output_path) / "libraries")
self.simulation_output_path = str(Path(self.test_path) / "test_output" / "lib.tb")
renew_path(self.test_path)
renew_path(self.output_path)
renew_path(self.prefix_path)
renew_path(self.libraries_path)
renew_path(self.simulation_output_path)
installed_modelsim_ini = str(Path(self.prefix_path) / ".." / "modelsim.ini")
installed_modelsim_ini = str(_get_installed_modelsim_ini())
write_file(installed_modelsim_ini, "[Library]")
self.project = Project()
self.cwd = os.getcwd()
Expand Down
43 changes: 37 additions & 6 deletions vunit/sim_if/modelsim.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from pathlib import Path
import os
import re
import logging
from threading import Lock, Event
from time import sleep
Expand Down Expand Up @@ -84,11 +85,11 @@ def find_prefix_from_path(cls):
"""
Find first valid modelsim toolchain prefix
"""

def has_modelsim_ini(path):
return os.path.isfile(str(Path(path).parent / "modelsim.ini"))

return cls.find_toolchain(["vsim"], constraints=[has_modelsim_ini])
def not_aldec_simulator(path): # pylint: disable=unused-argument
is_active_hdl = cls.find_toolchain(["vsim", "avhdl"]) is not None
is_riviera = cls.find_toolchain(["vsim", "vsimsa"]) is not None
return not (is_active_hdl or is_riviera)
return cls.find_toolchain(["vsim"], constraints=[not_aldec_simulator])

@classmethod
def supports_vhdl_package_generics(cls):
Expand Down Expand Up @@ -126,6 +127,32 @@ def __init__(self, prefix, output_path, *, persistent=False, gui=False, debugger
# Lock to access the two shared variables above
self._shared_state_lock = Lock()

def _get_modelsim_ini_from_vmap(self):
"""
Get the path to modelsim.ini, as used by vmap.

This means it listens to the MODELSIM environment variable, allowing
both vunit and other code/scripts to use the same modelsim.ini.
"""
vmap_output = []
proc = Process([str(Path(self._prefix) / "vmap")])
try:
proc.consume_output(callback=vmap_output.append)
except Process.NonZeroExitCode:
# The responsibility of this code is only detecting where
# modelsim.ini is, not to check that all libraries defined in it
# exist. So suppress non-zero exit codes.
pass
for line in vmap_output:
match = re.match(r"Reading (.*modelsim\.ini)", line)
if match is None:
continue
modelsim_ini = Path(match.group(1)).resolve()
if not modelsim_ini.exists():
raise FileNotFoundError(modelsim_ini)
return modelsim_ini
raise Exception("Failed to get the path to modelsim.ini from vmap")

def _create_modelsim_ini(self):
"""
Create the modelsim.ini file
Expand All @@ -134,7 +161,11 @@ def _create_modelsim_ini(self):
if not file_exists(parent):
os.makedirs(parent)

original_modelsim_ini = os.environ.get("VUNIT_MODELSIM_INI", str(Path(self._prefix).parent / "modelsim.ini"))
# Try vunit specific environment variable first, then query vmap, which
# reads the MODELSIM environment variable if it exists, then checks the
# current working directory and eventually falls back to modelsim.ini
# bundled with ModelSim.
original_modelsim_ini = os.environ.get("VUNIT_MODELSIM_INI", str(self._get_modelsim_ini_from_vmap()))
with Path(original_modelsim_ini).open("rb") as fread:
with Path(self._sim_cfg_file_name).open("wb") as fwrite:
fwrite.write(fread.read())
Expand Down