Skip to content

Commit

Permalink
Merge pull request #69 from efabless/temp
Browse files Browse the repository at this point in the history
read
  • Loading branch information
M0stafaRady authored Dec 31, 2023
2 parents bbef2b3 + 11f31bd commit 577aede
Show file tree
Hide file tree
Showing 14 changed files with 187 additions and 50 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/caravel_cocotb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ jobs:
- name: Run Flake8
run: flake8 $GITHUB_WORKSPACE/cocotb --config $GITHUB_WORKSPACE/cocotb/.flake8

- name: download docker
run: |
docker pull efabless/dv:cocotb
- name: Install caravel_cocotb
run: |
cd $GITHUB_WORKSPACE/cocotb
Expand All @@ -43,4 +47,4 @@ jobs:
- name: Run main.py
run: |
cd $GITHUB_WORKSPACE/cocotb/caravel_cocotb/CI
python3 main.py
python3 main.py
42 changes: 41 additions & 1 deletion cocotb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,10 @@ New directory named ``sim`` would be created under ``<repo root>/cocotb/`` or to
# Update design_info.yaml
<!-- start Update design_info include -->

> **Note**: This step is required only if make setup isn't used after cloning <caravel_user_project>
> After any change `make setup-cocotb` can be used.
``design_info.yaml`` are used to reference all the needed repos and paths needed to run the tests:

fill it like the following
Expand Down Expand Up @@ -311,6 +315,42 @@ fill it like the following
# optional email address to send the results to
emailto: [None]
```
> **Note**: This step is required only in the first run.
<!-- end Update design_info include -->
# HDL include files format
<!-- start Update include files format include -->
Include files from ``<caravel_user_project>/verilog/include`` like ``includes.rtl.caravel_user_project``, ``includes.gl.caravel_user_project`` and ``includes.sdf.caravel_user_project`` are used to reference all the needed repos and paths needed to run the tests:
The legacy format to reference files is:
- support verilog file include
```bash
-v $(USER_PROJECT_VERILOG)/rtl/user_project_wrapper.v
```

Coctb flow supports this format in addition to other formats:
- support systemVerilg file include
```bash
-sv $(USER_PROJECT_VERILOG)/rtl/counter.sv
```
- support wild card use
```bash
-sv $(USER_PROJECT_VERILOG)/rtl/peripherals/*.sv
```
- support add search path for `.vh` and `.svh` files
```bash
-I $(USER_PROJECT_VERILOG)/rtl/peripherals
```

<!-- end Update include files format include -->

# Unexisted python modules
<!-- start unexisted python modules include -->
If the testbench use python modules that are not installed in the docker image, there are 2 options:

1. use `-no_docker` option to run without docker if you have all the required tools installed.
2. Add requirments file to `<caravel_user_project>/verilog/dv/cocotb/requirements.txt`
```txt
rich==12.0.1
```
<!-- end unexisted python modules include -->
4 changes: 2 additions & 2 deletions cocotb/caravel_cocotb/CI/gen_run_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def all_switches_chooser(self):
self.test_list_chooser = RandomChooser([f"{self.cocotb_path}/test_io10/test_io10.yaml", f"{self.cocotb_path}/test_mgmt_gpio/test_mgmt_gpio.yaml"])
self.design_infos_chooser = RandomChooser([None, f"{self.cocotb_path}/design_info.yaml"])
self.sims_chooser = RandomChooser([None, 'RTL', "GL", "GL_SDF", "RTL GL", "GL RTL"])
self.tags_chooser = RandomChooser([''.join(random.choice(string.ascii_lowercase) for _ in range(i)) for i in range(5, 10)]) # change string length to make sure tag is unique
self.tags_chooser = RandomChooser([''.join(random.choice(string.ascii_lowercase) for _ in range(i)) for i in range(5, 40)]) # change string length to make sure tag is unique
self.max_errors_chooser = RandomChooser([None, "7", "5", "100"])
self.corners_chooser = RandomChooser([None, "nom-t", "nom-f", "nom-s", "max-t", "max-f", "max-s", "min-t", "min-f", "min-s"])
self.seed_chooser = RandomChooser([None] + [random.randint(0, 100000) for _ in range(5)])
Expand All @@ -87,7 +87,7 @@ def all_switches_chooser(self):
self.macros_chooser = RandomChooser([None, "USE_MACRO_1", "USE_MACRO_2"])
self.sim_paths_chooser = RandomChooser([None, os.path.abspath(os.path.join(self.cocotb_path, ".."))])
# self.verbosities_chooser = RandomChooser([None, "quiet", "normal", "debug"])
self.verbosities_chooser = RandomChooser(["quiet"]) # to speed sims
self.verbosities_chooser = RandomChooser(["debug"]) # to speed sims
self.compiles_chooser = RandomChooser([None, True])
self.check_commits_chooser = RandomChooser([None, True])
self.run_location = RandomChooser([self.cocotb_path, os.path.abspath(os.path.join(self.cocotb_path, "..", ".."))])
Expand Down
1 change: 0 additions & 1 deletion cocotb/caravel_cocotb/CI/setup_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ def __init__(self, paths):
super().__init__()
self.paths = paths
self.clone_needed_repos()
self.pull_cocotb_docker()

def clone_needed_repos(self):
self.clone_repo(repo_url="https://github.com/efabless/caravel.git", target_dir=self.paths.caravel_root, depth=1, branch="main")
Expand Down
1 change: 0 additions & 1 deletion cocotb/caravel_cocotb/docker/DockerFile
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,3 @@ WORKDIR /app

# Default command when the container starts
CMD ["bash"]

16 changes: 16 additions & 0 deletions cocotb/caravel_cocotb/docs/source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,19 @@ Update design_info.yaml
:parser: markdown
:start-after: <!-- start Update design_info include -->
:end-before: <!-- end Update design_info include -->

HDL include files format
*************************

.. include:: ../../../README.md
:parser: markdown
:start-after: <!-- start Update include files format include -->
:end-before: <!-- end Update include files format include -->

Unexisted python modules
*************************

.. include:: ../../../README.md
:parser: markdown
:start-after: <!-- start unexisted python modules include -->
:end-before: <!-- end unexisted python modules include -->
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ def read_config_file():
async def test_configure(
dut: cocotb.handle.SimHandle,
timeout_cycles=1000000,
clk=25,
clk=read_config_file()['clock'],
timeout_precision=0.2,
num_error=3,
num_error=int(read_config_file()['max_err']),
start_up=True
) -> caravel.Caravel_env:
"""
Expand Down
4 changes: 2 additions & 2 deletions cocotb/caravel_cocotb/scripts/merge_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ def merge_fun_cov(path, reports_path=None):
tree = {}
for key in yaml_file_object:
level = key.count(".")
nodes = key.split(".")
key_parent = key.replace(f".{nodes[level]}", "")
last_node_index = key.rfind(".")
key_parent = key[:last_node_index] if last_node_index != -1 else key
if level == 0:
root = key
tree[key] = Node(key=key)
Expand Down
57 changes: 57 additions & 0 deletions cocotb/caravel_cocotb/scripts/verify_cocotb/DockerProcess.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import subprocess
import os

class DockerProcess:
def __init__(self, image_name, user_project_path=None, sim_path=None) -> None:
self.image_name = image_name
self.user_project_path = user_project_path
self.sim_path = sim_path

def run(self):
# pull/update docker image
self.pull_docker_image()
# update docker image with pip commands if requirements.txt exists
if os.path.exists(f"{self.user_project_path}/verilog/dv/cocotb/requirements.txt"):
self.write_docker_file()
self.build_docker_image()

def pull_docker_image(self):
# Check if the image exists locally
try:
subprocess.run(["docker", "inspect", self.image_name], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(f"check update for docker image {self.image_name}.")
command = ["docker", "pull", "-q", f"{self.image_name}"]
except subprocess.CalledProcessError:
print(f"pulling docker image {self.image_name}.")
command = ["docker", "pull", f"{self.image_name}"]
try:
# Run the docker pull command
subprocess.run(command, check=True)
except subprocess.CalledProcessError as e:
print(f"Error: Failed to pull {self.image_name}")
print(e)

def build_docker_image(self):
try:
# Build the Docker image using subprocess
subprocess.run(["docker", "build", "-t", self.image_name, "-f", f"{self.sim_path}/Dockerfile", "."], check=True)
print(f"Docker image '{self.image_name}' built successfully.")
except subprocess.CalledProcessError as e:
print(f"Error building Docker image: {e}")

def write_docker_file(self):
with open(f"{self.sim_path}/Dockerfile", "w") as f:
with open("requirements.txt", "r") as file:
requirements = file.readlines()
requirements = [requirement.strip() for requirement in requirements]

f.write("# Use the efabless/dv:cocotb base image\n")
f.write("FROM efabless/dv:cocotb\n")
f.write("\n")
# f.write("# Copy requirements.txt into the container\n")
# f.write("WORKDIR /app\n")
# f.write(f"COPY {self.user_project_path}/verilog/dv/cocotb/requirements.txt .\n")
f.write("\n")
f.write("# Install additional packages\n")
f.write(f"RUN pip install --upgrade {' '.join(requirements)}")
f.write("\n")
20 changes: 2 additions & 18 deletions cocotb/caravel_cocotb/scripts/verify_cocotb/RunFlow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
import re
import logging
import random
import subprocess

from caravel_cocotb.scripts.verify_cocotb.DockerProcess import DockerProcess

def check_valid_mail_addr(address):
pat = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b"
Expand All @@ -32,7 +31,7 @@ def __init__(self, args) -> None:
self.set_tag()
self.set_args(design_info)
self.set_config_script(design_info)
self.pull_docker_image("efabless/dv:cocotb")
DockerProcess("efabless/dv:cocotb", self.paths.USER_PROJECT_ROOT, f"{self.paths.SIM_PATH}/{self.args.tag}").run()
RunRegression(self.args, self.paths, self.logger)

def configure_logger(self):
Expand Down Expand Up @@ -214,21 +213,6 @@ def get_design_info(self):
design_info = yaml.safe_load(yaml_file)
return design_info

def pull_docker_image(self, image_full_name):
# Check if the image exists locally
try:
subprocess.run(["docker", "inspect", image_full_name], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(f"check update for docker image {image_full_name}.")
except subprocess.CalledProcessError:
print(f"pulling docker image {image_full_name}.")
command = ["docker", "pull", "-q", f"{image_full_name}"]
try:
# Run the docker pull command
subprocess.run(command, check=True)
except subprocess.CalledProcessError as e:
print(f"Error: Failed to pull {image_full_name}")
print(e)


class CocotbArgs:
def __init__(
Expand Down
63 changes: 50 additions & 13 deletions cocotb/caravel_cocotb/scripts/verify_cocotb/RunRegression.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@
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
from rich.table import Table
from rich.console import Console


class RunRegression:
def __init__(self, args, paths, logger) -> None:
self.args = args
self.paths = paths
self.logger = logger
self.is_first_test_run = False
self.total_start_time = datetime.now()
self.write_command_log()
self.write_git_log()
Expand Down Expand Up @@ -176,20 +180,14 @@ def run_defaults_script(self):

def run_regression(self):
# threads = list()
for test in self.tests:
if self.args.iverilog: # threading
# x = threading.Thread(target=self.test_run_function,args=(test,sim_type,corner))
# threads.append(x)
# x.start()
# time.sleep(10)
self.test_run_function(test)
else:
self.test_run_function(test)
if self.args.verbosity == "quiet" and not self.args.CI: # run live screen
with Live(self.live_table(), auto_refresh=False) as self.live_screen:
self.run_all_tests()
else:
self.run_all_tests()
# Print the table in the top-left corner
Console().print(self.live_table(), justify="left")

# run defaults
if self.args.run_defaults:
self.args.compile = True
TestDefaults(self.args, self.paths, self.test_run_function, self.tests)
# for index, thread in enumerate(threads):
# thread.join()
# # Coverage
Expand All @@ -207,11 +205,28 @@ def run_regression(self):
except Exception as e:
self.logger.error(e)

def run_all_tests(self):
for test in self.tests:
if self.args.iverilog: # threading
# x = threading.Thread(target=self.test_run_function,args=(test,sim_type,corner))
# threads.append(x)
# x.start()
# time.sleep(10)
self.test_run_function(test)
else:
self.test_run_function(test)
# run defaults
if self.args.run_defaults:
self.args.compile = True
TestDefaults(self.args, self.paths, self.test_run_function, self.tests)

def test_run_function(self, test):
test.start_of_test()
self.update_run_log()
self.update_live_table()
RunTest(self.args, self.paths, test, self.logger).run_test()
self.update_run_log()
self.update_live_table()

def update_run_log(self):
file_name = f"{self.paths.SIM_PATH}/{self.args.tag}/runs.log"
Expand All @@ -229,6 +244,28 @@ def update_run_log(self):
)
f.close()

def live_table(self):
table = Table()
table.add_column("Total")
table.add_column("Passed")
table.add_column("Failed")
table.add_column("Unknown")
table.add_column("duration")
table.add_column(" ")
table.add_column(" ")
table.add_row(str(len(self.tests)), str(self.tests[0].passed_count), str(self.tests[0].failed_count), str(self.tests[0].unknown_count), f"{('%.10s' % (datetime.now() - self.total_start_time))}", "", "", style="bold")
table.add_row("", "", "", "", "", "", "", style="on white bold")
table.add_row("Test", "status", "start", "end", "duration", "p/f", "seed", style="white bold")
for row in self.tests:
style = "green" if row.passed == "passed" else "red bold" if row.passed == "failed" else "cyan italic"
table.add_row(row.full_name, row.status, row.start_time, row.endtime, row.duration, row.passed, row.seed, style=style)
return table

def update_live_table(self):
if self.args.verbosity != "quiet" or self.args.CI:
return
self.live_screen.update(self.live_table(), refresh=True)

def write_command_log(self):
file_name = f"{self.paths.SIM_PATH}/{self.args.tag}/command.log"
f = open(file_name, "w")
Expand Down
6 changes: 3 additions & 3 deletions cocotb/caravel_cocotb/scripts/verify_cocotb/RunTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,11 @@ def write_vcs_includes_file(self):

def vcs_compile(self):
macros = " +define+" + " +define+".join(self.test.macros)
if self.args.seed is not None:
os.environ["RANDOM_SEED"] = self.args.seed
vlogan_cmd = f"cd {self.test.compilation_dir}; vlogan -full64 -sverilog +error+30 {self.paths.CARAVEL_VERILOG_PATH}/rtl/toplevel_cocotb.v {self.vcs_dirs} {macros} -l {self.test.compilation_dir}/analysis.log -o {self.test.compilation_dir} "
self.run_command_write_to_file(vlogan_cmd, self.test.compilation_log, self.logger, quiet=False if self.args.verbosity == "debug" else True)
lint = "+lint=all" if self.args.lint else ""
ignored_errors = " -error=noZMMCM "
vcs_cmd = f"cd {self.test.compilation_dir}; vcs {lint} {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 -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)"
self.run_command_write_to_file(
vcs_cmd,
self.test.compilation_log,
Expand All @@ -206,6 +204,8 @@ def vcs_compile(self):

def vcs_run(self):
defines = GetDefines(self.test.includes_file)
if self.args.seed is not None:
os.environ["RANDOM_SEED"] = self.args.seed
run_sim = f"cd {self.test.test_dir}; {self.test.compilation_dir}/simv +vcs+dumpvars+all {self.vcs_coverage_command} -cm_name {self.test.name} +{ ' +'.join(self.test.macros)} {' '.join([f'+{k}={v}' if v != ''else f'+{k}' for k, v in defines.defines.items()])}"
self.run_command_write_to_file(
run_sim,
Expand Down
13 changes: 7 additions & 6 deletions cocotb/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
click==8.0.3
click
cocotb==1.8.0
oyaml==1.0
oyaml
cocotb_coverage==1.1.0
PrettyTable==3.8.0
anytree==2.9.0
PyYAML==6.0.1
tabulate==0.9.0
PrettyTable
anytree
PyYAML
tabulate
rich
breathe

0 comments on commit 577aede

Please sign in to comment.