In older post, I went through cocotb Makefile and i wished if there python runner. Well, I should have wished for something better because I found there is already a runner. I just didn’t see it :)

The runner is called as follows to build and run test

      runner = get_runner(sim)()
      runner.build(
          verilog_sources=verilog_sources, vhdl_sources=vhdl_sources, toplevel="dff"
      )

      runner.test(toplevel="dff", py_module="test_dff")

Build Link to heading

In runner.py, get_runner returns the runner depending on simulator string. In case of icarus, Icarus is used

    sim_name = simulator_name.lower()
    supported_sims: Dict[str, Type[Simulator]] = {
        "icarus": Icarus,
        "questa": Questa,
        "ghdl": Ghdl,
        "riviera": Riviera,
        "verilator": Verilator,
        "xcelium": Xcelium,
        # TODO: "vcs": Vcs,
    }
    try:
        return supported_sims[sim_name]

and Icarus extends Simulator

class Icarus(Simulator):

To build the files, runner.build is called which is defined in Simulator. In build(), build_command is called.

        cmds = self.build_command()

build_command is defined in Icarus to construct


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

        if self.vhdl_sources:
            raise ValueError("This simulator does not support VHDL")

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

            cmd = [
                ["iverilog", "-o", self.sim_file, "-D", "COCOTB_SIM=1", "-g2012"]
                + self.get_define_options(self.defines)
                + self.get_include_options(self.includes)
                + self.get_parameter_options(self.parameters)
                + self.compile_args
                + self.verilog_sources
            ]

        else:
            print("WARNING: Skipping compilation:" + self.sim_file)

        return cmd

Run Link to heading

runner.test is used to run the test which follows the same pattern as build. In this case, test calls _test_command after setting some log files

        if pytest_current_test:
            self.current_test_name = pytest_current_test.split(":")[-1].split(" ")[0]
            results_xml_name = f"{self.current_test_name}.results.xml"
        else:
            self.current_test_name = "test"
            results_xml_name = "results.xml"

        results_xml_file = os.getenv(
            "COCOTB_RESULTS_FILE", os.path.join(self.build_dir, results_xml_name)
        )

        self.env["COCOTB_RESULTS_FILE"] = results_xml_file

        with suppress(OSError):
            os.remove(results_xml_file)

        cmds = self.test_command()

And test_command build iverilog command to run the build executable

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

        return [
            [
                "vvp",
                "-M",
                cocotb.config.libs_dir,
                "-m",
                cocotb.config.lib_name("vpi", "icarus"),
            ]
            + self.sim_args
            + [self.sim_file]
            + self.plus_args
        ]