Skip to content

Commit

Permalink
Add cobertura coverage to codecov for cocotb
Browse files Browse the repository at this point in the history
  • Loading branch information
jsouter committed Jan 16, 2025
1 parent 2d2d564 commit 64d23f4
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 7 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,13 @@ jobs:
ghcr.io/pandablocks/pandablocks-dev-container:4.0a7 \
/bin/bash -c \
"cd PandABlocks-fpga && ln -s CONFIG.example CONFIG && make cocotb_tests"
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
name: nvc-coverage
files: cocotb_coverage.xml
# env:
# CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

test:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
Expand Down
16 changes: 9 additions & 7 deletions common/python/cocotb_simulate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
MODULES_PATH = TOP_PATH / "modules"

Dut = cocotb.handle.HierarchyObject
SignalsInfo = dict[str, dict[str, str]]
SignalsInfo = dict[str, dict[str, str | int]]


def read_ini(path: list[str] | str) -> ConfigParser:
Expand Down Expand Up @@ -83,7 +83,7 @@ async def initialise_dut(dut: Dut, signals_info: SignalsInfo):
dut_signal_name = signals_info[signal_name]["name"]
if is_input_signal(signals_info, signal_name):
getattr(dut, "{}".format(dut_signal_name)).value = 0
wstb_name = signals_info[signal_name].get("wstb_name", "")
wstb_name: str = signals_info[signal_name].get("wstb_name", "") # type: ignore
if wstb_name:
getattr(dut, wstb_name).value = 0

Expand Down Expand Up @@ -130,7 +130,7 @@ def assign(dut: Dut, name: str, val: int):
getattr(dut, name).set(val)


def do_assignments(dut: Dut, assignments, signals_info: SignalsInfo):
def do_assignments(dut: Dut, assignments: dict[str, int], signals_info: SignalsInfo):
"""Assign values to input signals.
Args:
Expand All @@ -142,9 +142,11 @@ def do_assignments(dut: Dut, assignments, signals_info: SignalsInfo):
if "[" in signal_name: # partial assignent to bus
index = int(signal_name.split("[")[1][:-1])
signal_name = signal_name.split("[")[0]
n_bits = signals_info[get_ini_signal_name(signal_name, signals_info)][
"bits"
]
ini_signal_name = get_ini_signal_name(signal_name, signals_info)
if ini_signal_name is None:
raise ValueError("Could not get ini signal name", {signal_name})
n_bits = signals_info[ini_signal_name]["bits"]
assert isinstance(n_bits, int), f"{n_bits} is not an integer"
val = get_bus_value(
int(getattr(dut, signal_name).value), n_bits, val, index
)
Expand Down Expand Up @@ -304,7 +306,7 @@ def get_signals_info(block_ini: ConfigParser) -> SignalsInfo:
return signals_info


def get_ini_signal_name(name: str, signals_info: SignalsInfo):
def get_ini_signal_name(name: str, signals_info: SignalsInfo) -> str | None:
"""Get signal name as seen in the INI files from the VHDL signal name.
Args:
Expand Down
24 changes: 24 additions & 0 deletions common/python/cocotb_timing_test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,27 @@ def merge_coverage_data(
return merged_path


def export_coverage_data(
output_path: Path,
file_paths: list[Path],
format: str = "cobertura",
):
"""Merges merge coverage files from each module to create an overall coverage
report in an xml format suitable for codecov.
Args:
output_path: Path of the output coverage report to write
file_paths: List of Paths to coverage files from each module.
format: coverage format to pass to nvc simulator
"""
command = (
["nvc", "--cover-export", f"--format={format}", "-o"]
+ [str(output_path)]
+ [str(file_path) for file_path in file_paths]
)
subprocess.run(command)


def cleanup_dir(test_name: str, build_dir: str | Path):
"""Creates a subdirectory for a test and moves all files generated from
that test into it.
Expand Down Expand Up @@ -607,6 +628,9 @@ def run_tests():
print("___________________________________________________\n")
print(f"Simulator: {simulator}\n")

report_paths = [path for path in coverage_reports.values() if path is not None]
export_coverage_data(Path("cocotb_coverage.xml"), report_paths)


def get_ip(module: str | None = None, verbose: bool = False) -> dict[str, str | None]:
if not module:
Expand Down

0 comments on commit 64d23f4

Please sign in to comment.