From b313eba89d6911ae327289be7b9e40e4509a912e Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Sun, 8 Dec 2024 15:00:43 +0200 Subject: [PATCH 1/5] hash netlist to recompile if it changes --- .../scripts/verify_cocotb/RunTest.py | 65 +++++++++++++++++-- .../scripts/verify_cocotb/Test.py | 4 ++ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/cocotb/caravel_cocotb/scripts/verify_cocotb/RunTest.py b/cocotb/caravel_cocotb/scripts/verify_cocotb/RunTest.py index eb3afa2..87110fb 100644 --- a/cocotb/caravel_cocotb/scripts/verify_cocotb/RunTest.py +++ b/cocotb/caravel_cocotb/scripts/verify_cocotb/RunTest.py @@ -5,9 +5,11 @@ import re import logging import caravel_cocotb +import hashlib class RunTest: + COMPILE_LOCK = set() def __init__(self, args, paths, test, logger) -> None: self.args = args self.paths = paths @@ -139,15 +141,23 @@ def runTest(self): def runTest_iverilog(self): if self.test.sim == "GL_SDF": raise RuntimeError( - f"iverilog can't run SDF for test {self.test.name} Please use anothor simulator like cvc" + f"{bcolors.FAIL}iverilog can't run SDF for test {self.test.name} Please use anothor simulator like cvc{bcolors.ENDC}" ) return self.write_iverilog_includes_file() - if ( - not os.path.isfile(f"{self.test.compilation_dir}/sim.vvp") - or self.args.compile - ): + if not os.path.isfile(f"{self.test.compilation_dir}/sim.vvp"): + print(f"{bcolors.OKCYAN}Compiling as sim.vvp not found{bcolors.ENDC}") self.iverilog_compile() + elif self.args.compile: + print(f"{bcolors.OKCYAN}Compiling as compile flag is set{bcolors.ENDC}") + self.iverilog_compile() + elif not self.is_same_hash(self.test.netlist) and f"{self.test.compilation_dir}/sim.vvp" not in RunTest.COMPILE_LOCK: + print(f"{bcolors.OKCYAN}Compiling since netlist has has changed{bcolors.ENDC}") + self.iverilog_compile() + else: + if f"{self.test.compilation_dir}/sim.vvp" not in RunTest.COMPILE_LOCK: + print(f"{bcolors.OKCYAN}Skipping compilation as netlist has not changed{bcolors.ENDC}") + RunTest.COMPILE_LOCK.add(f"{self.test.compilation_dir}/sim.vvp") # locked means if it is copiled for the first time then it will not be compiled again even if netlist changes if not self.args.compile_only: self.iverilog_run() @@ -216,8 +226,19 @@ def runTest_vcs(self): self.vcs_coverage_command = "-cm line+tgl+cond+fsm+branch+assert " os.environ["TESTCASE"] = f"{self.test.name}" os.environ["MODULE"] = "module_trail" - if not os.path.isfile(f"{self.test.compilation_dir}/simv") or self.args.compile: + if not os.path.isfile(f"{self.test.compilation_dir}/simv"): + print(f"{bcolors.OKCYAN}Compiling as simv not found{bcolors.ENDC}") + self.vcs_compile() + elif self.args.compile: + print(f"{bcolors.OKCYAN}Compiling as compile flag is set{bcolors.ENDC}") + self.vcs_compile() + elif not self.is_same_hash(self.test.netlist) and f"{self.test.compilation_dir}/simv" not in RunTest.COMPILE_LOCK: + print(f"{bcolors.OKCYAN}Compiling since netlist has has changed{bcolors.ENDC}") self.vcs_compile() + else: + if f"{self.test.compilation_dir}/simv" not in RunTest.COMPILE_LOCK: + print(f"{bcolors.OKCYAN}Skipping compilation as netlist has not changed{bcolors.ENDC}") + RunTest.COMPILE_LOCK.add(f"{self.test.compilation_dir}/simv") # locked means if it is copiled for the first time then it will not be compiled again even if netlist changes if not self.args.compile_only: self.vcs_run() @@ -241,7 +262,7 @@ def vcs_compile(self): ) lint = "+lint=all" if self.args.lint else "" ignored_errors = " -error=noZMMCM " - vcs_cmd = f"cd {self.test.compilation_dir}; vcs {lint} -negdelay {self.vcs_coverage_command} {ignored_errors}-debug_access+all +error+50 +vcs+loopreport+1000000 -diag=sdf:verbose +sdfverbose +neg_tchk -debug_access -full64 -l {self.test.compilation_dir}/test_compilation.log caravel_top -Mdir={self.test.compilation_dir}/csrc -o {self.test.compilation_dir}/simv +vpi -P pli.tab -load $(cocotb-config --lib-name-path vpi vcs)" + vcs_cmd = f"cd {self.test.compilation_dir}; vcs {lint} -negdelay {self.vcs_coverage_command} {ignored_errors} -debug_access+all +error+50 +vcs+loopreport+1000000 -diag=sdf:verbose +sdfverbose +neg_tchk -full64 -l {self.test.compilation_dir}/test_compilation.log caravel_top -Mdir={self.test.compilation_dir}/csrc -o {self.test.compilation_dir}/simv +vpi -P pli.tab -load $(cocotb-config --lib-name-path vpi vcs)" self.run_command_write_to_file( vcs_cmd, self.test.compilation_log, @@ -309,6 +330,36 @@ def run_command_write_to_file(self, cmd, file, logger, quiet=True): return process.returncode + @staticmethod + def calculate_netlist_hash(netlist, hash_algorithm="sha256"): + """Calculate a combined hash of multiple files ignoring the order.""" + hash_func = getattr(hashlib, hash_algorithm)() + try: + for file_path in sorted(netlist): # Ensure consistent order + with open(file_path, "rb") as f: + while chunk := f.read(8192): + hash_func.update(chunk) + return hash_func.hexdigest() + except FileNotFoundError as e: + return f"File not found: {e.filename}" + except PermissionError as e: + return f"Permission denied: {e.filename}" + + def is_same_hash(self, netlist): + # read old hash if exists + try: + with open(self.test.hash_log, "r") as f: + old_hash = f.read().strip() + except FileNotFoundError: + old_hash = 0 + # calculate new hash + new_hash = self.calculate_netlist_hash(netlist) + # write new hash + with open(self.test.hash_log, "w") as f: + f.write(new_hash) + return new_hash == old_hash + + class bcolors: HEADER = "\033[95m" diff --git a/cocotb/caravel_cocotb/scripts/verify_cocotb/Test.py b/cocotb/caravel_cocotb/scripts/verify_cocotb/Test.py index 26ea54d..bb4f269 100644 --- a/cocotb/caravel_cocotb/scripts/verify_cocotb/Test.py +++ b/cocotb/caravel_cocotb/scripts/verify_cocotb/Test.py @@ -31,6 +31,7 @@ def __init__(self, name, sim, corner, args, paths, logger, local_macros=None): ) self.include_dirs = set() self.init_test() + self.netlist = set() def init_test(self): self.start_time = "-" @@ -148,6 +149,7 @@ def create_logs(self): # self.test_log=open(test_log, "w") self.compilation_log = f"{self.compilation_dir}/compilation.log" self.hex_log = f"{self.test_dir}/firmware.log" + self.hash_log = f"{self.compilation_dir}/hash.txt" # self.full_terminal = open(self.compilation_log, "w") def create_lint_log(self): @@ -337,8 +339,10 @@ def convert_list_to_include(self, file): if "*" in file_path: for wild_match in glob.glob(file_path): paths += f'`include "{wild_match}"\n' + self.netlist.add(wild_match) else: paths += f'`include "{file_path}"\n' + self.netlist.add(file_path) # Add Includes to Set include_indices = [ i for i, flag in enumerate(split_line) if flag == "-I" From c339baad02dd8c6b484fdc825a5677b8ec9b0416 Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Sun, 8 Dec 2024 15:09:02 +0200 Subject: [PATCH 2/5] reformat to pass flake8 errors --- cocotb/caravel_cocotb/caravel_interfaces.py | 8 ++------ .../caravel_cocotb/scripts/verify_cocotb/RunRegression.py | 8 -------- cocotb/caravel_cocotb/scripts/verify_cocotb/RunTest.py | 6 +++--- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/cocotb/caravel_cocotb/caravel_interfaces.py b/cocotb/caravel_cocotb/caravel_interfaces.py index 3a476a3..f6c1731 100644 --- a/cocotb/caravel_cocotb/caravel_interfaces.py +++ b/cocotb/caravel_cocotb/caravel_interfaces.py @@ -1,10 +1,6 @@ from caravel_cocotb.interfaces.caravel import Caravel_env # noqa: F401 from caravel_cocotb.interfaces.SPI import SPI # noqa: F401 from caravel_cocotb.interfaces.UART import UART # noqa: F401 -from caravel_cocotb.interfaces.common_functions.test_functions import ( - test_configure, -) # noqa: F401 -from caravel_cocotb.interfaces.common_functions.test_functions import ( - report_test, -) # noqa: F401 +from caravel_cocotb.interfaces.common_functions.test_functions import (test_configure) # noqa: F401 +from caravel_cocotb.interfaces.common_functions.test_functions import (report_test) # noqa: F401 from caravel_cocotb.interfaces.common import GPIO_MODE # noqa: F401 diff --git a/cocotb/caravel_cocotb/scripts/verify_cocotb/RunRegression.py b/cocotb/caravel_cocotb/scripts/verify_cocotb/RunRegression.py index 821852e..150ddc0 100644 --- a/cocotb/caravel_cocotb/scripts/verify_cocotb/RunRegression.py +++ b/cocotb/caravel_cocotb/scripts/verify_cocotb/RunRegression.py @@ -16,7 +16,6 @@ import smtplib import socket import yaml -import time from caravel_cocotb.scripts.merge_coverage import merge_fun_cov from caravel_cocotb.scripts.test_defaults.test_defaults import TestDefaults from rich.live import Live @@ -490,12 +489,6 @@ def unzip_sdf_files(self): return elif self.args.sim != "GL_SDF": return - # make corners list in case in is n't - if not isinstance(self.args.corner, list): - corners = [self.args.corner] - else: - corners = self.args.corner - # keep caravel sdf dir sdf_dir = f"{self.paths.CARAVEL_ROOT}/signoff/{'caravan' if self.args.caravan else 'caravel'}/primetime/sdf" if self.args.sdfs_dir is None: @@ -512,4 +505,3 @@ def unzip_sdf_files(self): for gz_file in gz_files: subprocess.run(f"gzip {gz_file} -d".split()) self.args.macros.append(f'SDF_PATH=\\"{sdf_dir}\\"') - diff --git a/cocotb/caravel_cocotb/scripts/verify_cocotb/RunTest.py b/cocotb/caravel_cocotb/scripts/verify_cocotb/RunTest.py index 87110fb..91a8fe5 100644 --- a/cocotb/caravel_cocotb/scripts/verify_cocotb/RunTest.py +++ b/cocotb/caravel_cocotb/scripts/verify_cocotb/RunTest.py @@ -10,6 +10,7 @@ class RunTest: COMPILE_LOCK = set() + def __init__(self, args, paths, test, logger) -> None: self.args = args self.paths = paths @@ -157,7 +158,7 @@ def runTest_iverilog(self): else: if f"{self.test.compilation_dir}/sim.vvp" not in RunTest.COMPILE_LOCK: print(f"{bcolors.OKCYAN}Skipping compilation as netlist has not changed{bcolors.ENDC}") - RunTest.COMPILE_LOCK.add(f"{self.test.compilation_dir}/sim.vvp") # locked means if it is copiled for the first time then it will not be compiled again even if netlist changes + RunTest.COMPILE_LOCK.add(f"{self.test.compilation_dir}/sim.vvp") # locked means if it is copiled for the first time then it will not be compiled again even if netlist changes if not self.args.compile_only: self.iverilog_run() @@ -238,7 +239,7 @@ def runTest_vcs(self): else: if f"{self.test.compilation_dir}/simv" not in RunTest.COMPILE_LOCK: print(f"{bcolors.OKCYAN}Skipping compilation as netlist has not changed{bcolors.ENDC}") - RunTest.COMPILE_LOCK.add(f"{self.test.compilation_dir}/simv") # locked means if it is copiled for the first time then it will not be compiled again even if netlist changes + RunTest.COMPILE_LOCK.add(f"{self.test.compilation_dir}/simv") # locked means if it is copiled for the first time then it will not be compiled again even if netlist changes if not self.args.compile_only: self.vcs_run() @@ -359,7 +360,6 @@ def is_same_hash(self, netlist): f.write(new_hash) return new_hash == old_hash - class bcolors: HEADER = "\033[95m" From 279a2b13572e463d3a92e023d2ba3c84581a82b2 Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Sun, 8 Dec 2024 15:17:59 +0200 Subject: [PATCH 3/5] update pdk version for CI --- cocotb/caravel_cocotb/CI/setup_env.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/caravel_cocotb/CI/setup_env.py b/cocotb/caravel_cocotb/CI/setup_env.py index 11b45ba..1262194 100644 --- a/cocotb/caravel_cocotb/CI/setup_env.py +++ b/cocotb/caravel_cocotb/CI/setup_env.py @@ -29,7 +29,7 @@ def clone_needed_repos(self): depth=1, branch="main", ) - self.download_sky130_pdk("e3b630d9b7c0e23615367d52c4f78b2d2ede58ac") + self.download_sky130_pdk("a918dc7c8e474a99b68c85eb3546b4ed91fe9e7b") def pull_cocotb_docker(self): image_name = "efabless/dv" From 49af362e55127b58d216100933f384f59515740e Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Sun, 8 Dec 2024 15:18:33 +0200 Subject: [PATCH 4/5] update requirments.txt --- cocotb/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/requirements.txt b/cocotb/requirements.txt index 1f3accf..b8bb232 100644 --- a/cocotb/requirements.txt +++ b/cocotb/requirements.txt @@ -10,3 +10,4 @@ ruamel.yaml>=0.18.6 volare>=0.18.0 tabulate setuptools +hashlib \ No newline at end of file From 66cad7517148b617b1a45fc04bcb0cf8021dc978 Mon Sep 17 00:00:00 2001 From: M0stafaRady Date: Sun, 8 Dec 2024 15:24:08 +0200 Subject: [PATCH 5/5] Revert "update requirments.txt" This reverts commit 49af362e55127b58d216100933f384f59515740e. --- cocotb/requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/cocotb/requirements.txt b/cocotb/requirements.txt index b8bb232..1f3accf 100644 --- a/cocotb/requirements.txt +++ b/cocotb/requirements.txt @@ -10,4 +10,3 @@ ruamel.yaml>=0.18.6 volare>=0.18.0 tabulate setuptools -hashlib \ No newline at end of file