Skip to content

Commit

Permalink
Reorganize how tb infrstructure selects toolchains
Browse files Browse the repository at this point in the history
  • Loading branch information
amykyta3 committed Oct 22, 2023
1 parent 683fc4d commit d689bb7
Show file tree
Hide file tree
Showing 24 changed files with 330 additions and 186 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@ jobs:
- name: Test
run: |
cd tests
export SKIP_SYNTH_TESTS=1
export STUB_SIMULATOR=1
pytest --cov=peakrdl_regblock
pytest --cov=peakrdl_regblock --synth-tool skip --sim-tool stub
- name: Coveralls
env:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
**/_build
**/*.out
**/transcript
**/htmlcov
**/*.log
**/*.pb
**/.Xil
Expand Down
12 changes: 7 additions & 5 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@ To run synthesis tests, Vivado needs to be installed and visible via the PATH en

Vivado can be downloaded for free from: https://www.xilinx.com/support/download.html

To skip synthesis tests, export the following environment variable:
```bash
export SKIP_SYNTH_TESTS=1
```



## Python Packages
Expand Down Expand Up @@ -62,6 +57,13 @@ You can also run a specific testcase. For example:
pytest tests/test_hw_access
```

Command-line arguments can be used to explicitly select which simulator/synthesis tools are used
If unspecified, the tool will be selected automatically based on what you have installed.
```bash
pytest --sim-tool questa --synth-tool vivado
```


Alternatively, launch tests using the helper script. This handles installing
dependencies into a virtual environment automatically.
```bash
Expand Down
25 changes: 25 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
def pytest_addoption(parser):
parser.addoption(
"--sim-tool",
choices=["questa", "xilinx", "stub", "skip", "auto"],
default="auto",
help="""
Select the simulator to use.
stub: run the testcase using a no-op simulator stub
skip: skip all the simulation tests
auto: choose the best simulator based on what is installed
"""
)

parser.addoption(
"--synth-tool",
choices=["vivado", "skip", "auto"],
default="auto",
help="""
Select the synthesis tool to use.
skip: skip all the simulation tests
auto: choose the best tool based on what is installed
"""
)
76 changes: 30 additions & 46 deletions tests/lib/base_testcase.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional, List
from typing import Optional
import unittest
import os
import glob
Expand Down Expand Up @@ -49,41 +49,37 @@ class BaseTestCase(unittest.TestCase):
def _load_request(self, request):
self.request = request

@classmethod
def get_testcase_dir(cls) -> str:
class_dir = os.path.dirname(inspect.getfile(cls))
def get_testcase_dir(self) -> str:
class_dir = os.path.dirname(inspect.getfile(self.__class__))
return class_dir

@classmethod
def get_run_dir(cls) -> str:
this_dir = cls.get_testcase_dir()
run_dir = os.path.join(this_dir, "run.out", cls.__name__)
def get_run_dir(self) -> str:
this_dir = self.get_testcase_dir()
run_dir = os.path.join(this_dir, "run.out", self.__class__.__name__)
return run_dir

@classmethod
def _write_params(cls) -> None:
def _write_params(self) -> None:
"""
Write out the class parameters to a file so that it is easier to debug
how a testcase was parameterized
"""
path = os.path.join(cls.get_run_dir(), "params.txt")
path = os.path.join(self.get_run_dir(), "params.txt")

with open(path, 'w') as f:
for k, v in cls.__dict__.items():
for k, v in self.__class__.__dict__.items():
if k.startswith("_") or callable(v):
continue
f.write(f"{k}: {repr(v)}\n")


@classmethod
def _export_regblock(cls):
def _export_regblock(self):
"""
Call the peakrdl_regblock exporter to generate the DUT
"""
this_dir = cls.get_testcase_dir()
this_dir = self.get_testcase_dir()

if cls.rdl_file:
rdl_file = cls.rdl_file
if self.rdl_file:
rdl_file = self.rdl_file
else:
# Find any *.rdl file in testcase dir
rdl_file = glob.glob(os.path.join(this_dir, "*.rdl"))[0]
Expand All @@ -98,45 +94,33 @@ def _export_regblock(cls):
rdlc.compile_file(udp_file)

rdlc.compile_file(rdl_file)
root = rdlc.elaborate(cls.rdl_elab_target, "regblock", cls.rdl_elab_params)
root = rdlc.elaborate(self.rdl_elab_target, "regblock", self.rdl_elab_params)

cls.exporter.export(
self.exporter.export(
root,
cls.get_run_dir(),
self.get_run_dir(),
module_name="regblock",
package_name="regblock_pkg",
cpuif_cls=cls.cpuif.cpuif_cls,
retime_read_fanin=cls.retime_read_fanin,
retime_read_response=cls.retime_read_response,
reuse_hwif_typedefs=cls.reuse_hwif_typedefs,
retime_external_reg=cls.retime_external,
retime_external_regfile=cls.retime_external,
retime_external_mem=cls.retime_external,
retime_external_addrmap=cls.retime_external,
default_reset_activelow=cls.default_reset_activelow,
default_reset_async=cls.default_reset_async,
cpuif_cls=self.cpuif.cpuif_cls,
retime_read_fanin=self.retime_read_fanin,
retime_read_response=self.retime_read_response,
reuse_hwif_typedefs=self.reuse_hwif_typedefs,
retime_external_reg=self.retime_external,
retime_external_regfile=self.retime_external,
retime_external_mem=self.retime_external,
retime_external_addrmap=self.retime_external,
default_reset_activelow=self.default_reset_activelow,
default_reset_async=self.default_reset_async,
)

@classmethod
def setUpClass(cls):
def setUp(self) -> None:
# Create fresh build dir
run_dir = cls.get_run_dir()
run_dir = self.get_run_dir()
if os.path.exists(run_dir):
shutil.rmtree(run_dir)
pathlib.Path(run_dir).mkdir(parents=True, exist_ok=True)

cls._write_params()
self._write_params()

# Convert testcase RDL file --> SV
cls._export_regblock()


def setUp(self) -> None:
# cd into the run directory
self.original_cwd = os.getcwd()
os.chdir(self.get_run_dir())


def run_test(self, plusargs:List[str] = None) -> None:
simulator = self.simulator_cls(testcase_cls_inst=self)
simulator.run(plusargs)
self._export_regblock()
4 changes: 2 additions & 2 deletions tests/lib/cpuifs/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def get_synth_files(self) -> List[str]:
return self._get_file_paths("rtl_files")


def get_tb_inst(self, tb_cls: 'SimTestCase', exporter: 'RegblockExporter') -> str:
def get_tb_inst(self, testcase: 'SimTestCase', exporter: 'RegblockExporter') -> str:
class_dir = self._get_class_dir_of_variable("tb_template")
loader = jj.FileSystemLoader(class_dir)
jj_env = jj.Environment(
Expand All @@ -77,7 +77,7 @@ def get_tb_inst(self, tb_cls: 'SimTestCase', exporter: 'RegblockExporter') -> st

context = {
"cpuif": self,
"cls": tb_cls,
"testcase": testcase,
"exporter": exporter,
"type": type,
}
Expand Down
63 changes: 37 additions & 26 deletions tests/lib/sim_testcase.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
from typing import List
import os

import jinja2 as jj
import pytest

from .sv_line_anchor import SVLineAnchor

from .simulators.questa import Questa
from .simulators import StubSimulator
from .simulators import get_simulator_cls

from .base_testcase import BaseTestCase

SIM_CLS = Questa
if os.environ.get("STUB_SIMULATOR", False):
SIM_CLS = StubSimulator


class SimTestCase(BaseTestCase):
#: Abort test if it exceeds this number of clock cycles
timeout_clk_cycles = 5000

simulator_cls = SIM_CLS
incompatible_sim_tools = set()

tb_template_file = "tb_template.sv"

Expand All @@ -32,17 +29,14 @@ class SimTestCase(BaseTestCase):
clocking_hwif_in = True
clocking_hwif_out = True


@classmethod
def get_extra_tb_files(cls) -> List[str]:
def get_extra_tb_files(self) -> List[str]:
paths = []
for path in cls.extra_tb_files:
path = os.path.join(cls.get_testcase_dir(), path)
for path in self.extra_tb_files:
path = os.path.join(self.get_testcase_dir(), path)
paths.append(path)
return paths

@classmethod
def _generate_tb(cls):
def _generate_tb(self):
"""
Render the testbench template into actual tb.sv
"""
Expand All @@ -57,32 +51,38 @@ def _generate_tb(cls):
)

context = {
"cls": cls,
"exporter": cls.exporter,
"testcase": self,
"exporter": self.exporter,
}

# template path needs to be relative to the Jinja loader root
template_path = os.path.join(cls.get_testcase_dir(), cls.tb_template_file)
template_path = os.path.join(self.get_testcase_dir(), self.tb_template_file)
template_path = os.path.relpath(template_path, template_root_path)
template = jj_env.get_template(template_path)

output_path = os.path.join(cls.get_run_dir(), "tb.sv")
output_path = os.path.join(self.get_run_dir(), "tb.sv")
stream = template.stream(context)
stream.dump(output_path)


@classmethod
def setUpClass(cls):
super().setUpClass()
def setUp(self):
name = self.request.config.getoption("--sim-tool")
if name in self.incompatible_sim_tools:
pytest.skip()
simulator_cls = get_simulator_cls(name)
if simulator_cls is None:
pytest.skip()

super().setUp()

# Create testbench from template
cls._generate_tb()
self._generate_tb()

simulator = cls.simulator_cls(testcase_cls=cls)
simulator = simulator_cls(self)

# cd into the build directory
cwd = os.getcwd()
os.chdir(cls.get_run_dir())
os.chdir(self.get_run_dir())
try:
simulator.compile()
finally:
Expand All @@ -91,5 +91,16 @@ def setUpClass(cls):


def run_test(self, plusargs:List[str] = None) -> None:
simulator = self.simulator_cls(testcase_cls_inst=self)
simulator.run(plusargs)
name = self.request.config.getoption("--sim-tool")
simulator_cls = get_simulator_cls(name)
simulator = simulator_cls(self)

# cd into the build directory
cwd = os.getcwd()
os.chdir(self.get_run_dir())

try:
simulator.run(plusargs)
finally:
# cd back
os.chdir(cwd)
Loading

0 comments on commit d689bb7

Please sign in to comment.