themperek / cocotb-test

Unit testing for cocotb
BSD 2-Clause "Simplified" License
144 stars 71 forks source link

[Enhancement] Allow running of tcl scripts before compilation/simulation steps #201

Closed alexgomp closed 1 year ago

alexgomp commented 2 years ago

Problem:

Proposed Solution Add a parameter to the Simulator block, tcl_sources:

I've written a solution for Questa/Modelsim:

--------------------------- cocotb_test/simulator.py ---------------------------
index f304ae8..c3cc463 100644
@@ -39,6 +39,7 @@ class Simulator:
         work_dir=None,
         python_search=None,
         toplevel_lang="verilog",
+        tcl_sources=None,
         verilog_sources=None,
         vhdl_sources=None,
         includes=None,
@@ -94,6 +95,10 @@ class Simulator:
         self.toplevel = toplevel
         self.toplevel_lang = toplevel_lang

+        if tcl_sources is None:
+            tcl_sources = []
+        self.tcl_sources = self.get_abs_paths(tcl_sources)
+
         if verilog_sources is None:
             verilog_sources = []
         self.verilog_sources = self.get_abs_paths(verilog_sources)
@@ -495,6 +500,10 @@ class Questa(Simulator):

         cmd = []

+        if self.tcl_sources:
+            for src in self.tcl_sources:
+                cmd.append(["vsim", "-do", src])
+
         if self.vhdl_sources:
             compile_args = self.compile_args + self.vhdl_compile_args

------------------------------ tests/test_dff.py ------------------------------
index 40a3243..e8a4f8a 100644
@@ -1,3 +1,5 @@
+import pathlib
+
 from cocotb_test.simulator import run
 import pytest
 import os
@@ -17,6 +19,13 @@ def test_dff_vhdl():
     run(vhdl_sources=[os.path.join(tests_dir, "dff.vhdl")], toplevel="dff_test_vhdl", module="dff_cocotb", toplevel_lang="vhdl")

+@pytest.mark.skipif(os.getenv("SIM") not in ["questa", "modelsim"], reason="TCL is not supported")
+def test_dff_tcl(tmp_path: pathlib.Path):
+    tcl_file_path = pathlib.Path.joinpath(tmp_path, "dff.tcl")
+    with tcl_file_path.open('w') as tcl_file:
+        tcl_file.write(f'vcom {os.path.join(tests_dir, "dff.vhdl")}')
+    run(tcl_sources=[tcl_file_path], toplevel="work.dff_test_vhdl", module="dff_cocotb", toplevel_lang="vhdl")
+
 if __name__ == "__main__":
     test_dff_verilog()
     # test_dff_vhdl()
themperek commented 2 years ago

I would do this instead by extension

Something like (not tested):

class QCustom(Questa):
    def __init__(self, tcl_sources, *argv, **kwargs):
        self.tcl_sources= tcl_sources
        super().__init__(*argv, **kwargs)

    def  do_script(self):
         return = f"source {self.tcl_sources};" + super().do_script()

def test_abc():
    QCustom(
        ...
        tcl_sources="abc.tcl"
    ).run()

Would this work for you ?

alexgomp commented 1 year ago

Sorry for the slow reply, I've just returned from a long holiday. I'll be quicker now :)

Unfortunately, I don't think that this would work for my problem

1. Ideally, in the case of IP which is compiled by script, these compile scripts can be called before the vcom and vlog commands generated for the vhdl_sources and verilog_sources arguments. This is because in almost all cases, IP is instantiated by user HDL sources and the IP must therefore be compiled first. Unfortunately, the do_script generated command is appended to the end of the cmd list. One could work around this issue by including the full compilation process in a prewritten script instead of using the vhdl_sources and verilog_sources arguments. But, it would be a shame to lose the advantages and flexibility provided by using these arguments.

  1. For Questa, the do_script generated command is appended only if not self.compile_only: (simulator.py:562).

themperek commented 1 year ago

And something like this (not tested):

class QCustom(Questa):
    def __init__(self, tcl_sources, *argv, **kwargs):
        self.tcl_sources= tcl_sources
        super().__init__(*argv, **kwargs)

    def  build_command(self):
        cms = super().build_command()
        cmd.insert(0, ['vsim', '-do', self.tcl_sources]
        return cmd

def test_abc():
    QCustom(
        ...
        tcl_sources="abc.tcl"
    ).run()
alexgomp commented 1 year ago

Yep, that should do the trick. Thank you!