Repository: cocotb/cocotb

Issue:
Mixed-languages multi-libraries not working as expected
# Issue description #
compilation of a design containing both Verilog and VHDL and also mixing libraries does not work. The RTL and the python script can be found here : https://github.com/oliverbm67/cocotb/tree/e63dda34cbfd64b0600cbbb3bfe4e1db7f26707c/examples/mixed_language_multi_libraries

I noticed the following :

- If hdl_toplevel is not provided in the runner.build a default top is used in the tool command
- If no hdl_library is provided, cocotb 

does not default to work as (at least in my experience) most tools do

# Logs #
The run.do script used to check compilation with questa is also attached
[cocotb.log](https://github.com/cocotb/cocotb/files/12500962/cocotb.log)
[modelsim.log](https://github.com/cocotb/cocotb/files/12500976/modelsim.log)
[run.do.txt](https://github.com/cocotb/cocotb/files/12500980/run.do.txt)

# Environment #
Python 3.11.4
cocotb 1.8.0
Questa 2021.2 ( from Intel FPGA suite)

# Notes #
I do think using Xcelium gives similar results. 


Discussion:
The Python runner is experimental and targeted to cocotb regression tests. You are free to copy and modify for your use, or [integrate cocotb](https://docs.cocotb.org/en/stable/custom_flows.html) to your build / simulate workflow.
I tried the python runner because I could not figure out how to do it with the standard Makefile flow. I updated my example with a cocotb Makefile ( https://github.com/oliverbm67/cocotb/blob/63ecde3f9527a8d2374e349a651ea82e1724d2a8/examples/mixed_language_multi_libraries/cocotb/Makefile ), but it still has compilation order and library management issue.

In the presented use case, should a user expect that cocotb will be able to handle it out-of-the-box or should the user resort to extending the build flow as you suggest ?
FWIW, #1707 is a relevant issue regarding the Makefiles. 
I don't see the problem? `work` is not a legal library name as of VHDL 2002, we aren't going to default to that. The build of the `reg_b` sources is not associated with a library and is called out in `top` as being in `cell_lib`, so of course it won't compile correctly. 

You've pasted a bunch of stuff and said "it doesn't work", but you haven't explained what you expect the behavior to be, why, and what needs to be changed to make it work.

Based on your post, I can reason that you think that not specifying a library name when building a library should put the entities into a default library. IMO, this argument should instead be required. Building libraries are different than building a top-level design. I wanted them to be different methods originally, but that isn't what ended up happening. Instead, we need to force the two distinct methods into one and reject invalid calls.
Nothing prevents us from separating the methods though, we always declared the Python runner as experimental.
Sorry for being unclear with the initial question. I was expecting cocotb compilation to successfully compile the initial code as it was possible to do so with what I thought was an equivalent modelsim compilation script. I think in this case Modelsim is too permissive and/or too forgiving.

I have updated my example according to your feedback to give explicit libraries in all cases except the top level. Using the python runner works in this latest example, but there is still an issue with the Makefile flow. 

There seem to be 2 issues at play :

1. All VHDL code is compiled first, including top level. Thus the needed Verilog are not compiled in the correct order
2. It does not seem possible to specify a compilation library for verilog files ( I would expect VERILOG_SOURCES_mylib to work ?)
Has your question been resolved? If so please close this issue. If it has not been resolved, you may need to provide more information. If no more activity on this issue occurs in 7 days, it will be closed.
From the original question I take the following :

- Complicated compilation setup can be handled using the python runner, but as this is considered beta feature it is not recommended
- Makefile flow do not support this type of use case as is. But this is already tracked by https://github.com/cocotb/cocotb/issues/1707 (at least partially)

I can make a pull request with the example code if it can be useful for testing or demo, otherwise the issue can be closed
> There seem to be 2 issues at play :
> 
> 1. All VHDL code is compiled first, including top level. Thus the needed Verilog are not compiled in the correct order
> 2. It does not seem possible to specify a compilation library for verilog files ( I would expect VERILOG_SOURCES_mylib to work ?)

These are both valid criticisms and limitations. Separating the VHDL and Verilog sources into separate variables was never a good idea. Mixed language simulators do allow creating libraries with only verilog code (and this is even supported by the SV standard), but the Makefiles weren't designed with them in mind and I don't think we will both updating them. However, I agree this should be solved by the Python runner.
It is very frustrating that we run in a loop, the same problem but described differently in tens of tickets and none of them yet solved.
@Nabih-Saleh You are free to fix it yourself or pay someone to. If not, you'll just have to wait for someone else.

Resolving patch:
diff --git a/src/cocotb_tools/runner.py b/src/cocotb_tools/runner.py
--- a/src/cocotb_tools/runner.py
+++ b/src/cocotb_tools/runner.py
@@ -60,6 +60,14 @@ def shlex_join(split_command):
     return " ".join(shlex.quote(arg) for arg in split_command)
 
 
+class VHDL(str):
+    """Tags source files and build arguments to :meth:`~cocotb_tools.runner.Simulator.build` as VHDL-specific."""
+
+
+class Verilog(str):
+    """Tags source files and build arguments to :meth:`~cocotb_tools.runner.Simulator.build` as Verilog-specific."""
+
+
 class Simulator(abc.ABC):
     supported_gpi_interfaces: Dict[str, List[str]] = {}
 
@@ -92,10 +100,17 @@ def _check_hdl_toplevel_lang(self, hdl_toplevel_lang: Optional[str]) -> str:
             ValueError: *hdl_toplevel_lang* is not supported by the simulator.
         """
         if hdl_toplevel_lang is None:
-            if self.vhdl_sources and not self.verilog_sources:
+            if self.vhdl_sources and not self.verilog_sources and not self.sources:
                 lang = "vhdl"
-            elif self.verilog_sources and not self.vhdl_sources:
+            elif self.verilog_sources and not self.vhdl_sources and not self.sources:
                 lang = "verilog"
+            elif self.sources and not self.vhdl_sources and not self.verilog_sources:
+                if is_vhdl_source(self.sources[-1]):
+                    lang = "vhdl"
+                elif is_verilog_source(self.sources[-1]):
+                    lang = "verilog"
+                else:
+                    raise UnknownFileExtension(self.sources[-1])
             else:
                 raise ValueError(
                     f"{type(self).__qualname__}: Must specify a hdl_toplevel_lang in a mixed-language design"
@@ -144,10 +159,11 @@ def build(
         hdl_library: str = "top",
         verilog_sources: Sequence[PathLike] = [],
         vhdl_sources: Sequence[PathLike] = [],
+        sources: Sequence[Union[PathLike, VHDL, Verilog]] = [],
         includes: Sequence[PathLike] = [],
         defines: Mapping[str, object] = {},
         parameters: Mapping[str, object] = {},
-        build_args: Sequence[str] = [],
+        build_args: Sequence[Union[str, VHDL, Verilog]] = [],
         hdl_toplevel: Optional[str] = None,
         always: bool = False,
         build_dir: PathLike = "sim_build",
@@ -159,10 +175,42 @@ def build(
     ) -> None:
         """Build the HDL sources.
 
+        With mixed language simulators, *sources* will be built,
+        followed by *vhdl_sources*, then *verilog_sources*.
+        With simulators that only support either VHDL or Verilog, *sources* will be built,
+        followed by *vhdl_sources* and *verilog_sources*, respectively.
+
+        If your source files use an atypical file extension,
+        use :class:`VHDL` and :class:`Verilog` to tag the path as a VHDL or Verilog source file, respectively.
+        If the filepaths aren't tagged, the extension is used to determine if they are VHDL or Verilog files.
+
+        +----------+------------------------------------+
+        | Language | File Extensions                    |
+        +==========+====================================+
+        | VHDL     | ``.vhd``, ``.vhdl``                |
+        +----------+------------------------------------+
+        | Verilog  | ``.v``, ``.sv``, ``.vh``, ``.svh`` |
+        +----------+------------------------------------+
+
+
+        .. code-block:: python3
+
+            runner.build(
+                sources=[
+                    VHDL("/my/file.is_actually_vhdl"),
+                    Verilog("/other/file.verilog"),
+                ],
+            )
+
+        The same tagging works for *build_args*.
+        Tagged *build_args* only supply that option to the compiler when building the source file for the tagged language.
+        Non-tagged *build_args* are supplied when compiling any language.
+
         Args:
             hdl_library: The library name to compile into.
             verilog_sources: Verilog source files to build.
             vhdl_sources: VHDL source files to build.
+            sources: Language-agnostic list of source files to build.
             includes: Verilog include directories.
             defines: Defines to set.
             parameters: Verilog parameters or VHDL generics.
@@ -190,6 +238,7 @@ def build(
         self.hdl_library: str = hdl_library
         self.verilog_sources: List[Path] = get_abs_paths(verilog_sources)
         self.vhdl_sources: List[Path] = get_abs_paths(vhdl_sources)
+        self.sources: List[Path] = get_abs_paths(sources)
         self.includes: List[Path] = get_abs_paths(includes)
         self.defines = dict(defines)
         self.parameters = dict(parameters)
@@ -491,6 +540,40 @@ def get_abs_paths(paths: Sequence[PathLike]) -> List[Path]:
     return [get_abs_path(path) for path in paths]
 
 
+_verilog_extensions = (".v", ".sv", ".vh", ".svh")
+_vhdl_extensions = (".vhd", ".vhdl")
+
+_vhdl_extensions_s = ", ".join(f"`{c}`" for c in _vhdl_extensions)
+_verilog_extensions_s = ", ".join(f"`{c}`" for c in _verilog_extensions)
+
+
+class UnknownFileExtension(ValueError):
+    def __init__(self, source: PathLike) -> None:
+        super().__init__(
+            f"Can't determine if {source} is a VHDL or Verilog file. "
+            f"Use a standard file extension ({_vhdl_extensions_s} for VHDL files and {_verilog_extensions_s} for Verilog files) "
+            "or tag the source with `VHDL(source)` or `Verilog(source)`."
+        )
+
+
+def is_vhdl_source(source: PathLike) -> bool:
+    if isinstance(source, VHDL):
+        return True
+    source_as_path = Path(source)
+    if source_as_path.suffix in _vhdl_extensions:
+        return True
+    return False
+
+
+def is_verilog_source(source: PathLike) -> bool:
+    if isinstance(source, Verilog):
+        return True
+    source_as_path = Path(source)
+    if source_as_path.suffix in _verilog_extensions:
+        return True
+    return False
+
+
 class Icarus(Simulator):
     supported_gpi_interfaces = {"verilog": ["vpi"]}
 
@@ -562,10 +645,16 @@ def _test_command(self) -> List[Command]:
         ]
 
     def _build_command(self) -> List[Command]:
-        if self.vhdl_sources:
-            raise ValueError(
-                f"{type(self).__qualname__}: Simulator does not support VHDL"
-            )
+        for source in self.sources:
+            if not is_verilog_source(source):
+                raise ValueError(
+                    f"{type(self).__qualname__} only supports Verilog. {str(source)!r} cannot be compiled."
+                )
+        for arg in self.build_args:
+            if type(arg) not in (str, Verilog):
+                print(
+                    f"WARNING: {type(self).__qualname__} only supports Verilog. build_args {arg!r} will not be applied."
+                )
 
         build_args = list(self.build_args)
         if self.waves:
@@ -577,7 +666,10 @@ def _build_command(self) -> List[Command]:
             build_args += ["-f", str(self.cmds_file)]
 
         cmds = []
-        if outdated(self.sim_file, self.verilog_sources) or self.always:
+        sources = [
+            source for source in self.sources if is_verilog_source(source)
+        ] + self.verilog_sources
+        if outdated(self.sim_file, sources) or self.always:
             cmds = [
                 [
                     "iverilog",
@@ -590,8 +682,8 @@ def _build_command(self) -> List[Command]:
                 + self._get_define_options(self.defines)
                 + self._get_include_options(self.includes)
                 + self._get_parameter_options(self.parameters)
-                + build_args
-                + [str(source_file) for source_file in self.verilog_sources]
+                + [arg for arg in build_args if type(arg) in (str, Verilog)]
+                + [str(source_file) for source_file in sources]
                 + [
                     str(source_file)
                     for source_file in [self.iverilog_dump_file]
@@ -631,30 +723,41 @@ def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
     def _build_command(self) -> List[Command]:
         cmds = []
 
-        if self.vhdl_sources:
-            cmds.append(["vlib", as_tcl_value(self.hdl_library)])
-            cmds.append(
-                ["vcom"]
-                + ["-work", as_tcl_value(self.hdl_library)]
-                + [as_tcl_value(v) for v in self.build_args]
-                + [as_tcl_value(str(v)) for v in self.vhdl_sources]
-            )
-
-        if self.verilog_sources:
-            cmds.append(["vlib", as_tcl_value(self.hdl_library)])
-            cmds.append(
-                ["vlog"]
-                + ([] if self.always else ["-incr"])
-                + ["-work", as_tcl_value(self.hdl_library)]
-                + ["-sv"]
-                + self._get_define_options(self.defines)
-                + self._get_include_options(self.includes)
-                + [as_tcl_value(v) for v in self.build_args]
-                + [as_tcl_value(str(v)) for v in self.verilog_sources]
-            )
+        cmds.append(["vlib", as_tcl_value(self.hdl_library)])
+        for source in self.sources:
+            if is_vhdl_source(source):
+                cmds.append(self._build_vhdl_command(source))
+            elif is_verilog_source(source):
+                cmds.append(self._build_verilog_command(source))
+            else:
+                raise UnknownFileExtension(source)
+        for source in self.vhdl_sources:
+            cmds.append(self._build_vhdl_command(source))
+        for source in self.verilog_sources:
+            cmds.append(self._build_verilog_command(source))
 
         return cmds
 
+    def _build_vhdl_command(self, source: PathLike) -> Command:
+        return (
+            ["vcom"]
+            + ["-work", as_tcl_value(self.hdl_library)]
+            + [as_tcl_value(v) for v in self.build_args if type(v) in (str, VHDL)]
+            + [as_tcl_value(str(source))]
+        )
+
+    def _build_verilog_command(self, source: PathLike) -> Command:
+        return (
+            ["vlog"]
+            + ([] if self.always else ["-incr"])
+            + ["-work", as_tcl_value(self.hdl_library)]
+            + ["-sv"]
+            + self._get_define_options(self.defines)
+            + self._get_include_options(self.includes)
+            + [as_tcl_value(v) for v in self.build_args if type(v) in (str, Verilog)]
+            + [as_tcl_value(str(source))]
+        )
+
     def _test_command(self) -> List[Command]:
         cmds = []
 
@@ -745,16 +848,23 @@ def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
         return [f"-g{name}={value}" for name, value in parameters.items()]
 
     def _build_command(self) -> List[Command]:
-        if self.verilog_sources:
-            raise ValueError(
-                f"{type(self).__qualname__}: Simulator does not support Verilog"
-            )
+        for source in self.sources:
+            if not is_vhdl_source(source):
+                raise ValueError(
+                    f"{type(self).__qualname__} only supports VHDL. {str(source)!r} cannot be compiled."
+                )
+        for arg in self.build_args:
+            if type(arg) not in (str, VHDL):
+                print(
+                    f"WARNING: {type(self).__qualname__} only supports VHDL. build_args {arg!r} will not be applied."
+                )
 
         cmds = [
             ["ghdl", "-i"]
             + [f"--work={self.hdl_library}"]
-            + self.build_args
-            + [str(source_file) for source_file in self.vhdl_sources]
+            + [arg for arg in self.build_args if type(arg) in (str, VHDL)]
+            + [str(source) for source in self.sources if is_vhdl_source(source)]
+            + [str(source) for source in self.vhdl_sources]
         ]
 
         if self.hdl_toplevel is not None:
@@ -826,16 +936,23 @@ def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
         return [f"-g{name}={value}" for name, value in parameters.items()]
 
     def _build_command(self) -> List[Command]:
-        if self.verilog_sources:
-            raise ValueError(
-                f"{type(self).__qualname__}: Simulator does not support Verilog"
-            )
+        for source in self.sources:
+            if not is_vhdl_source(source):
+                raise ValueError(
+                    f"{type(self).__qualname__} only supports VHDL. {str(source)!r} cannot be compiled."
+                )
+        for arg in self.build_args:
+            if type(arg) not in (str, VHDL):
+                print(
+                    f"WARNING: {type(self).__qualname__} only supports VHDL. build_args {arg!r} will not be applied."
+                )
 
         cmds = [
             ["nvc", f"--work={self.hdl_library}"]
-            + self.build_args
+            + [arg for arg in self.build_args if type(arg) in (str, VHDL)]
             + ["-a"]
-            + [str(source_file) for source_file in self.vhdl_sources]
+            + [str(source) for source in self.sources if is_vhdl_source(source)]
+            + [str(source) for source in self.vhdl_sources]
         ]
 
         return cmds
@@ -879,58 +996,66 @@ def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
         return [f"-g{name}={value}" for name, value in parameters.items()]
 
     def _build_command(self) -> List[Command]:
-        do_script = "\nonerror {\n quit -code 1 \n} \n"
+        do_script: List[str] = ["onerror {\n quit -code 1 \n}"]
 
         out_file = self.build_dir / self.hdl_library / f"{self.hdl_library}.lib"
 
         if outdated(out_file, self.verilog_sources + self.vhdl_sources) or self.always:
-            do_script += f"alib {as_tcl_value(self.hdl_library)} \n"
-
-            if self.vhdl_sources:
-                do_script += (
-                    "acom -work {RTL_LIBRARY} {EXTRA_ARGS} {VHDL_SOURCES}\n".format(
-                        RTL_LIBRARY=as_tcl_value(self.hdl_library),
-                        VHDL_SOURCES=" ".join(
-                            as_tcl_value(str(v)) for v in self.vhdl_sources
-                        ),
-                        EXTRA_ARGS=" ".join(as_tcl_value(v) for v in self.build_args),
-                    )
-                )
-
-            if self.verilog_sources:
-                do_script += "alog -work {RTL_LIBRARY} -pli {EXT_NAME} -sv {DEFINES} {INCDIR} {EXTRA_ARGS} {VERILOG_SOURCES} \n".format(
-                    RTL_LIBRARY=as_tcl_value(self.hdl_library),
-                    EXT_NAME=as_tcl_value(
-                        cocotb_tools.config.lib_name_path("vpi", "riviera").as_posix()
-                    ),
-                    VERILOG_SOURCES=" ".join(
-                        as_tcl_value(str(v)) for v in self.verilog_sources
-                    ),
-                    DEFINES=" ".join(self._get_define_options(self.defines)),
-                    INCDIR=" ".join(self._get_include_options(self.includes)),
-                    EXTRA_ARGS=" ".join(as_tcl_value(v) for v in self.build_args),
-                )
-        else:
-            print("WARNING: Skipping compilation of", out_file)
+            do_script.append(f"alib {as_tcl_value(self.hdl_library)}")
+
+            for source in self.sources:
+                if is_verilog_source(source):
+                    do_script.append(self._build_verilog_source(source))
+                elif is_vhdl_source(source):
+                    do_script.append(self._build_vhdl_source(source))
+                else:
+                    raise UnknownFileExtension(source)
+            for source in self.vhdl_sources:
+                do_script.append(self._build_vhdl_source(source))
+            for source in self.verilog_sources:
+                do_script.append(self._build_verilog_source(source))
 
         # Explicitly exit the script at the end. In batch mode, which is invoked
         # implicitly by redirecting STDOUT/STDERR of the alog/acom commands,
         # the tool exits by itself even without this 'exit' command -- but not
         # when running from an interactive terminal. Be explicit for predictable
         # behavior.
-        do_script += "\nexit"
+        do_script.append("exit")
 
         do_file = tempfile.NamedTemporaryFile(delete=False)
-        do_file.write(do_script.encode())
+        do_file.write("\n".join(do_script).encode())
         do_file.close()
 
         return [["vsimsa"] + ["-do"] + ["do"] + [do_file.name]]
 
+    def _build_vhdl_source(self, source: PathLike) -> str:
+        return "acom -work {RTL_LIBRARY} {EXTRA_ARGS} {VHDL_SOURCES}".format(
+            RTL_LIBRARY=as_tcl_value(self.hdl_library),
+            VHDL_SOURCES=as_tcl_value(str(source)),
+            EXTRA_ARGS=" ".join(
+                as_tcl_value(v) for v in self.build_args if type(v) in (str, VHDL)
+            ),
+        )
+
+    def _build_verilog_source(self, source: PathLike) -> str:
+        return "alog -work {RTL_LIBRARY} -pli {EXT_NAME} -sv {DEFINES} {INCDIR} {EXTRA_ARGS} {VERILOG_SOURCES}".format(
+            RTL_LIBRARY=as_tcl_value(self.hdl_library),
+            EXT_NAME=as_tcl_value(
+                cocotb_tools.config.lib_name_path("vpi", "riviera").as_posix()
+            ),
+            VERILOG_SOURCES=as_tcl_value(str(source)),
+            DEFINES=" ".join(self._get_define_options(self.defines)),
+            INCDIR=" ".join(self._get_include_options(self.includes)),
+            EXTRA_ARGS=" ".join(
+                as_tcl_value(v) for v in self.build_args if type(v) in (str, Verilog)
+            ),
+        )
+
     def _test_command(self) -> List[Command]:
         if self.pre_cmd:
             print("WARNING: pre_cmd is not implemented for Riviera.")
 
-        do_script = "\nonerror {\n quit -code 1 \n} \n"
+        do_script: str = "\nonerror {\n quit -code 1 \n} \n"
 
         if self.hdl_toplevel_lang == "vhdl":
             do_script += "asim +access +w_nets -interceptcoutput -loadvhpi {EXT_NAME} {EXTRA_ARGS} {TOPLEVEL} {PLUSARGS}\n".format(
@@ -1015,14 +1140,20 @@ def _get_parameter_options(parameters: Mapping[str, object]) -> Command:
     def _build_command(self) -> List[Command]:
         self._simulator_in_path_build_only()
 
-        if self.vhdl_sources:
-            raise ValueError(
-                f"{type(self).__qualname__}: Simulator does not support VHDL"
-            )
+        for source in self.sources:
+            if not is_verilog_source(source):
+                raise ValueError(
+                    f"{type(self).__qualname__} only supports Verilog. {str(source)!r} cannot be compiled."
+                )
+        for arg in self.build_args:
+            if type(arg) not in (str, Verilog):
+                print(
+                    f"WARNING: {type(self).__qualname__} only supports Verilog. build_args {arg!r} will not be applied."
+                )
 
         if self.hdl_toplevel is None:
             raise ValueError(
-                f"{type(self).__qualname__}: Simulator requires the hdl_toplevel parameter to be specified"
+                f"{type(self).__qualname__} requires the hdl_toplevel parameter to be specified"
             )
 
         # TODO: set "--debug" if self.verbose
@@ -1053,12 +1184,13 @@ def _build_command(self) -> List[Command]:
                 f"-Wl,-rpath,{cocotb_tools.config.libs_dir} -L{cocotb_tools.config.libs_dir} -lcocotbvpi_verilator",
             ]
             + (["--trace"] if self.waves else [])
-            + self.build_args
+            + [arg for arg in self.build_args if type(arg) in (str, Verilog)]
             + self._get_define_options(self.defines)
             + self._get_include_options(self.includes)
             + self._get_parameter_options(self.parameters)
             + [verilator_cpp]
-            + [str(source_file) for source_file in self.verilog_sources]
+            + [str(source) for source in self.sources if is_verilog_source(source)]
+            + [str(source) for source in self.verilog_sources]
         )
 
         cmds.append(
@@ -1159,7 +1291,9 @@ def _build_command(self) -> List[Command]:
             + [f"-top {self.hdl_toplevel}"]
             + [
                 str(source_file)
-                for source_file in self.vhdl_sources + self.verilog_sources
+                for source_file in (
+                    self.sources + self.vhdl_sources + self.verilog_sources
+                )
             ]
         ]
 


Test patch:
diff --git a/examples/mixed_language/tests/test_mixed_language.py b/examples/mixed_language/tests/test_mixed_language.py
--- a/examples/mixed_language/tests/test_mixed_language.py
+++ b/examples/mixed_language/tests/test_mixed_language.py
@@ -119,7 +119,7 @@ async def mixed_language_functional_test(dut):
 
 
 @pytest.mark.skipif(
-    sim in ["icarus", "ghdl", "verilator", "nvc"],
+    sim not in ["questa", "riviera", "xcelium"],
     reason=f"Skipping example mixed_language since {sim} doesn't support this",
 )
 def test_mixed_language_runner():
@@ -131,13 +131,15 @@ def test_mixed_language_runner():
 
     proj_path = Path(__file__).resolve().parent.parent
 
-    verilog_sources = [proj_path / "hdl" / "endian_swapper.sv"]
-    vhdl_sources = [proj_path / "hdl" / "endian_swapper.vhdl"]
+    sources = [
+        proj_path / "hdl" / "endian_swapper.sv",
+        proj_path / "hdl" / "endian_swapper.vhdl",
+    ]
 
     if hdl_toplevel_lang == "verilog":
-        verilog_sources += [proj_path / "hdl" / "toplevel.sv"]
+        sources += [proj_path / "hdl" / "toplevel.sv"]
     elif hdl_toplevel_lang == "vhdl":
-        vhdl_sources += [proj_path / "hdl" / "toplevel.vhdl"]
+        sources += [proj_path / "hdl" / "toplevel.vhdl"]
     else:
         raise ValueError(
             f"A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG={hdl_toplevel_lang}"
@@ -157,8 +159,7 @@ def test_mixed_language_runner():
 
     runner.build(
         hdl_toplevel="endian_swapper_mixed",
-        verilog_sources=verilog_sources,
-        vhdl_sources=vhdl_sources,
+        sources=sources,
         always=True,
         build_args=build_args,
     )