"""
The objective of this file is to provide the simulation connection and interconnection to consider modelling digital and mixed signal logic.
The main simulation driver is cocotb, and this generates a set of files that correspond to time-domain digital
simulations. The cocotb verification software can also be used to perform mixed signal simulation, and digital files
can be inputted as a bitstream into a photonic solver, although the ideal situation would be to have integrated
photonic time-domain measurement alongside the electronic simulation solver, and maybe this is where it will go. It can be
assumed that, as is currently, cocotb can interface python with multiple solvers until someone (and I'd love to do
this) writes an equivalent python-based or C++ based python time-domain simulation solver.
The nice thing about cocotb is that as long as the photonic simulations can be written asynchronously, time-domain
simulations can be closely integrated or simulated through this verification software."""
import functools
import pathlib
import subprocess
from piel.file_system import return_path, write_file, delete_path_list_in_directory
from piel.types.digital import HDLSimulator, HDLTopLevelLanguage
__all__ = [
"check_cocotb_testbench_exists",
"configure_cocotb_simulation",
"delete_simulation_output_files",
"run_cocotb_simulation",
]
[docs]
def check_cocotb_testbench_exists(
design_directory: str | pathlib.Path,
) -> bool:
"""
Checks if a Cocotb testbench exists in the specified design directory.
Args:
design_directory (str | pathlib.Path): The directory where the design files are located.
Returns:
bool: True if a Cocotb testbench exists, False otherwise.
Examples:
>>> check_cocotb_testbench_exists("/path/to/design")
True
"""
design_directory = return_path(design_directory)
testbench_directory = design_directory / "tb"
testbench_directory_exists = testbench_directory.exists()
if testbench_directory_exists:
# Check if there are Python files in the testbench directory excluding __init__.py
cocotb_python_files = list(testbench_directory.glob("*.py"))
if len(cocotb_python_files) > 1:
return True
return False
# Partial function to delete specific simulation output files from a directory
delete_simulation_output_files = functools.partial(
delete_path_list_in_directory,
path_list=["sim_build", "__pycache__", "ivl_vhdl_work"],
)
[docs]
def run_cocotb_simulation(
design_directory: str,
raise_error: bool = False,
) -> subprocess.CompletedProcess:
"""
Runs the Cocotb simulation by executing the Makefile in the specified design directory.
Args:
design_directory (str): The directory where the design files are located.
Returns:
subprocess.CompletedProcess: The completed process object containing the result of the simulation run.
Examples:
>>> run_cocotb_simulation("/path/to/design")
"""
test_directory = return_path(design_directory) / "tb"
commands_list = ["cd " + str(test_directory.resolve()), "make"]
script = "; \n".join(commands_list)
# Save the script to a file for potential direct execution
write_file(
directory_path=test_directory,
file_text=script,
file_name="run_cocotb_simulation.sh",
)
try:
# Execute the script and capture the output
run = subprocess.run(script, capture_output=True, shell=True, check=True)
# Print the standard output and standard error
print("Standard Output (stdout):")
print(run.stdout.decode()) # Decode bytes to string
print("Standard Error (stderr):")
print(run.stderr.decode()) # Decode bytes to string
return run
except subprocess.CalledProcessError as e:
# Print detailed error information
print(f"Command '{e.cmd}' returned non-zero exit status {e.returncode}.")
print("Standard Output (stdout):")
print(e.stdout.decode()) # Decode bytes to string
print("Standard Error (stderr):")
print(e.stderr.decode()) # Decode bytes to string
if raise_error:
raise