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

Separate build and test for python runner #2853

Merged
merged 1 commit into from
Feb 18, 2022
Merged
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
91 changes: 58 additions & 33 deletions cocotb/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ def __init__(self) -> None:

self.env: Dict[str, str] = {}

# for running test() independently of build()
self.build_dir = "sim_build"
self.parameters = {}

@abc.abstractmethod
def simulator_in_path(self) -> None:
"""Check that the simulator executable exists in `PATH`."""
Expand Down Expand Up @@ -79,9 +83,6 @@ def set_env(self) -> None:
self.env["TOPLEVEL"] = self.sim_toplevel
self.env["MODULE"] = self.module

if not os.path.exists(self.build_dir):
os.makedirs(self.build_dir)

@abc.abstractmethod
def build_command(self) -> Sequence[Command]:
"""Return command to build the HDL sources."""
Expand Down Expand Up @@ -145,12 +146,20 @@ def test(
extra_env: Mapping[str, str] = {},
waves: Optional[bool] = None,
gui: Optional[bool] = None,
parameters: Mapping[str, object] = None,
build_dir: Optional[PathLike] = None,
sim_dir: Optional[PathLike] = None,
) -> PathLike:
"""Run a test."""

__tracebackhide__ = True # Hide the traceback when using pytest

if build_dir is not None:
self.build_dir = build_dir

if parameters is not None:
self.parameters = dict(parameters)

if sim_dir is None:
self.sim_dir = self.build_dir
else:
Expand Down Expand Up @@ -344,6 +353,10 @@ def get_parameter_options(self, parameters: Mapping[str, object]) -> List[str]:
for name, value in parameters.items()
]

@property
def sim_file(self) -> PathLike:
return os.path.join(self.build_dir, "sim.vvp")

def test_command(self) -> List[Command]:

return [
Expand All @@ -364,8 +377,6 @@ def build_command(self) -> List[Command]:
if self.vhdl_sources:
raise ValueError("This simulator does not support VHDL")

self.sim_file = os.path.join(self.build_dir, self.library_name + ".vvp")

cmd = []
if outdated(self.sim_file, self.verilog_sources) or self.always:

Expand Down Expand Up @@ -457,7 +468,17 @@ def test_command(self) -> List[Command]:
if not self.gui:
do_script += "run -all; quit"

fli_lib_path = cocotb.config.lib_name_path("fli", "questa")

if self.toplevel_lang == "vhdl":

if not os.path.isfile(fli_lib_path):
raise SystemExit(
"ERROR: cocotb was not installed with an FLI library, as the mti.h header could not be located.\n\
If you installed an FLI-capable simulator after cocotb, you will need to reinstall cocotb.\n\
Please check the cocotb documentation on ModelSim support."
)

cmd.append(
["vsim"]
+ ["-gui" if self.gui else "-c"]
Expand All @@ -473,11 +494,9 @@ def test_command(self) -> List[Command]:
+ ["-do", do_script]
)

if self.verilog_sources:
self.env["GPI_EXTRA"] = (
cocotb.config.lib_name_path("vpi", "questa")
+ ":cocotbvpi_entry_point"
)
self.env["GPI_EXTRA"] = (
cocotb.config.lib_name_path("vpi", "questa") + ":cocotbvpi_entry_point"
)

else:
cmd.append(
Expand All @@ -492,11 +511,15 @@ def test_command(self) -> List[Command]:
+ ["-do", do_script]
)

if self.vhdl_sources:
if os.path.isfile(fli_lib_path):
self.env["GPI_EXTRA"] = (
cocotb.config.lib_name_path("fli", "questa")
+ ":cocotbfli_entry_point"
)
else:
print(
"WARNING: FLI library not found. Mixed-mode simulation will not be available."
)

return cmd

Expand Down Expand Up @@ -532,34 +555,37 @@ def build_command(self) -> List[Command]:
if self.verilog_sources:
raise ValueError("This simulator does not support Verilog")

return [
if self.hdl_toplevel is None:
raise ValueError(
"This simulator requires the hdl_toplevel parameter to be specified"
)

cmd = [
["ghdl", "-i"]
+ [f"--work={self.library_name}"]
+ self.compile_args
+ [source_file]
for source_file in self.vhdl_sources
]

def test_command(self) -> List[Command]:

cmd_elaborate = (
cmd += [
["ghdl", "-m"]
+ [f"--work={self.library_name}"]
+ self.compile_args
+ [self.sim_toplevel]
)
+ [self.hdl_toplevel]
]

return cmd

def test_command(self) -> List[Command]:

cmd = [cmd_elaborate]
cmd_run = (
cmd = [
["ghdl", "-r"]
+ self.compile_args
+ [self.sim_toplevel]
+ ["--vpi=" + cocotb.config.lib_name_path("vpi", "ghdl")]
+ self.sim_args
+ self.get_parameter_options(self.parameters)
)

cmd.append(cmd_run)
]

return cmd

Expand Down Expand Up @@ -658,11 +684,10 @@ def test_command(self) -> List[Command]:
)
),
)
if self.verilog_sources:
self.env["GPI_EXTRA"] = (
cocotb.config.lib_name_path("vpi", "riviera")
+ "cocotbvpi_entry_point"
)

self.env["GPI_EXTRA"] = (
cocotb.config.lib_name_path("vpi", "riviera") + ":cocotbvpi_entry_point"
)
else:
do_script += "asim +access +w -interceptcoutput -O2 -pli {EXT_NAME} {EXTRA_ARGS} {TOPLEVEL} {PLUS_ARGS} \n".format(
TOPLEVEL=as_tcl_value(self.sim_toplevel),
Expand All @@ -675,11 +700,11 @@ def test_command(self) -> List[Command]:
),
PLUS_ARGS=" ".join(as_tcl_value(v) for v in self.plus_args),
)
if self.vhdl_sources:
self.env["GPI_EXTRA"] = (
cocotb.config.lib_name_path("vhpi", "riviera")
+ ":cocotbvhpi_entry_point"
)

self.env["GPI_EXTRA"] = (
cocotb.config.lib_name_path("vhpi", "riviera")
+ ":cocotbvhpi_entry_point"
)

if self.waves:
do_script += "log -recursive /*;"
Expand Down
47 changes: 24 additions & 23 deletions tests/pytest/test_cocotb.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,43 +28,44 @@
"test_timing_triggers",
]

verilog_sources = []
vhdl_sources = []
toplevel_lang = os.getenv("TOPLEVEL_LANG", "verilog")

def test_cocotb():
verilog_sources = []
vhdl_sources = []
toplevel_lang = os.getenv("TOPLEVEL_LANG", "verilog")
if toplevel_lang == "verilog":
verilog_sources = [
os.path.join(tests_dir, "designs", "sample_module", "sample_module.sv")
]
else:
vhdl_sources = [
os.path.join(tests_dir, "designs", "sample_module", "sample_module_pack.vhdl"),
os.path.join(tests_dir, "designs", "sample_module", "sample_module_1.vhdl"),
os.path.join(tests_dir, "designs", "sample_module", "sample_module.vhdl"),
]

if toplevel_lang == "verilog":
verilog_sources = [
os.path.join(tests_dir, "designs", "sample_module", "sample_module.sv")
]
else:
vhdl_sources = [
os.path.join(
tests_dir, "designs", "sample_module", "sample_module_pack.vhdl"
),
os.path.join(tests_dir, "designs", "sample_module", "sample_module_1.vhdl"),
os.path.join(tests_dir, "designs", "sample_module", "sample_module.vhdl"),
]
sim = os.getenv("SIM", "icarus")
sim_args = ["-t", "ps"] if sim == "questa" else []
compile_args = ["+acc"] if sim == "questa" else []
toplevel = "sample_module"
python_search = [os.path.join(tests_dir, "test_cases", "test_cocotb")]

sim = os.getenv("SIM", "icarus")
runner = get_runner(sim)()

compile_args = ["+acc"] if sim == "questa" else []
def test_cocotb():

runner = get_runner(sim)()

runner.build(
verilog_sources=verilog_sources,
vhdl_sources=vhdl_sources,
toplevel="sample_module",
toplevel=toplevel,
build_dir=sim_build,
extra_args=compile_args,
)
sim_args = ["-t", "ps"] if sim == "questa" else []

runner.test(
toplevel_lang=toplevel_lang,
python_search=[os.path.join(tests_dir, "test_cases", "test_cocotb")],
toplevel="sample_module",
python_search=python_search,
toplevel=toplevel,
py_module=module_name,
extra_args=sim_args,
)
Expand Down
50 changes: 50 additions & 0 deletions tests/pytest/test_parallel_cocotb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright cocotb contributors
# Licensed under the Revised BSD License, see LICENSE for details.
# SPDX-License-Identifier: BSD-3-Clause

import pytest
from test_cocotb import (
compile_args,
module_name,
python_search,
sim,
sim_args,
sim_build,
toplevel,
toplevel_lang,
verilog_sources,
vhdl_sources,
)

from cocotb.runner import get_runner


@pytest.mark.compile
def test_cocotb_parallel_compile():

runner = get_runner(sim)()

runner.build(
always=True,
verilog_sources=verilog_sources,
vhdl_sources=vhdl_sources,
toplevel=toplevel,
build_dir=sim_build,
extra_args=compile_args,
)


@pytest.mark.parametrize("seed", list(range(4)))
def test_cocotb_parallel(seed):

runner = get_runner(sim)()

runner.test(
seed=seed,
toplevel_lang=toplevel_lang,
python_search=python_search,
toplevel=toplevel,
py_module=module_name,
extra_args=sim_args,
build_dir=sim_build,
)