Reorganize how tb infrstructure selects toolchains

This commit is contained in:
Alex Mykyta
2023-10-22 11:04:43 -07:00
parent 683fc4d0ac
commit d689bb7077
24 changed files with 323 additions and 179 deletions

View File

@@ -1,34 +1,37 @@
from typing import Type, TYPE_CHECKING, List
from typing import Type, Optional, List
import functools
if TYPE_CHECKING:
from ..sim_testcase import SimTestCase
from .base import Simulator
from .questa import Questa
from .xilinx import Xilinx
from .stub import StubSimulator
class Simulator:
ALL_SIMULATORS: List[Simulator]
ALL_SIMULATORS = [
Questa,
Xilinx,
StubSimulator,
]
def __init__(self, testcase_cls: 'Type[SimTestCase]' = None, testcase_cls_inst: 'SimTestCase' = None) -> None:
self.testcase_cls = testcase_cls
self.testcase_cls_inst = testcase_cls_inst
@functools.lru_cache()
def get_simulator_cls(name: str) -> Optional[Type[Simulator]]:
if name == "skip":
return None
@property
def tb_files(self) -> List[str]:
files = []
files.extend(self.testcase_cls.cpuif.get_sim_files())
files.extend(self.testcase_cls.get_extra_tb_files())
files.append("regblock_pkg.sv")
files.append("regblock.sv")
files.append("tb.sv")
if name == "auto":
# Find the first simulator that is installed
for sim_cls in ALL_SIMULATORS:
if sim_cls is StubSimulator:
# Never offer the stub as an automatic option
continue
if sim_cls.is_installed():
return sim_cls
raise ValueError("Could not find any installed simulators")
return files
def compile(self) -> None:
raise NotImplementedError
def run(self, plusargs:List[str] = None) -> None:
raise NotImplementedError
class StubSimulator(Simulator):
def compile(self) -> None:
pass
def run(self, plusargs:List[str] = None) -> None:
pass
# Look up which explicit simulator name was specified
for sim_cls in ALL_SIMULATORS:
if sim_cls.name == name:
if not sim_cls.is_installed():
raise ValueError("Simulator '%s' is not installed" % sim_cls.name)
return sim_cls
raise RuntimeError

View File

@@ -0,0 +1,31 @@
from typing import TYPE_CHECKING, List
if TYPE_CHECKING:
from ..sim_testcase import SimTestCase
class Simulator:
name = ""
@classmethod
def is_installed(cls) -> bool:
raise NotImplementedError
def __init__(self, testcase: 'SimTestCase' = None) -> None:
self.testcase = testcase
@property
def tb_files(self) -> List[str]:
files = []
files.extend(self.testcase.cpuif.get_sim_files())
files.extend(self.testcase.get_extra_tb_files())
files.append("regblock_pkg.sv")
files.append("regblock.sv")
files.append("tb.sv")
return files
def compile(self) -> None:
raise NotImplementedError
def run(self, plusargs:List[str] = None) -> None:
raise NotImplementedError

View File

@@ -1,10 +1,20 @@
from typing import List
import subprocess
import os
import shutil
from . import Simulator
from .base import Simulator
class Questa(Simulator):
name = "questa"
@classmethod
def is_installed(cls) -> bool:
return (
shutil.which("vlog") is not None
and shutil.which("vsim") is not None
)
def compile(self) -> None:
cmd = [
"vlog", "-sv", "-quiet", "-l", "build.log",
@@ -31,7 +41,7 @@ class Questa(Simulator):
def run(self, plusargs:List[str] = None) -> None:
plusargs = plusargs or []
test_name = self.testcase_cls_inst.request.node.name
test_name = self.testcase.request.node.name
# call vsim
cmd = [
@@ -54,11 +64,11 @@ class Questa(Simulator):
def assertSimLogPass(self, path: str):
self.testcase_cls_inst.assertTrue(os.path.isfile(path))
self.testcase.assertTrue(os.path.isfile(path))
with open(path, encoding="utf-8") as f:
for line in f:
if line.startswith("# ** Error"):
self.testcase_cls_inst.fail(line)
self.testcase.fail(line)
elif line.startswith("# ** Fatal"):
self.testcase_cls_inst.fail(line)
self.testcase.fail(line)

View File

@@ -0,0 +1,17 @@
from typing import List
from .base import Simulator
class StubSimulator(Simulator):
name = "stub"
@classmethod
def is_installed(cls) -> bool:
# Always available!
return True
def compile(self) -> None:
pass
def run(self, plusargs: List[str] = None) -> None:
pass

View File

@@ -1,22 +1,34 @@
from typing import List
import subprocess
import os
import shutil
from . import Simulator
from .base import Simulator
class Xilinx(Simulator):
"""
Don't bother using the Xilinx simulator... Its buggy and extraordinarily slow.
As observed in v2021.1:
As observed in v2023.2:
- clocking block assignments do not seem to actually simulate correctly.
assignment statements get ignored or the values get mangled.
- Streaming operators have all sorts of limitations.
Keeping this here in case someday it works better...
"""
name = "xilinx"
@classmethod
def is_installed(cls) -> bool:
return (
shutil.which("xvlog") is not None
and shutil.which("xelab") is not None
and shutil.which("xsim") is not None
)
def compile(self) -> None:
cmd = [
"xvlog", "--sv",
"--log", "compile.log",
"--include", os.path.join(os.path.dirname(__file__), ".."),
"--define", "XSIM",
]
@@ -25,6 +37,7 @@ class Xilinx(Simulator):
cmd = [
"xelab",
"--log", "elaborate.log",
"--timescale", "1ns/1ps",
"--debug", "all",
"tb",
@@ -35,7 +48,7 @@ class Xilinx(Simulator):
def run(self, plusargs:List[str] = None) -> None:
plusargs = plusargs or []
test_name = self.testcase_cls_inst.request.node.name
test_name = self.testcase.request.node.name
# call vsim
cmd = [
@@ -54,13 +67,13 @@ class Xilinx(Simulator):
def assertSimLogPass(self, path: str):
self.testcase_cls_inst.assertTrue(os.path.isfile(path))
self.testcase.assertTrue(os.path.isfile(path))
with open(path, encoding="utf-8") as f:
for line in f:
if line.startswith("Error:"):
self.testcase_cls_inst.fail(line)
self.testcase.fail(line)
elif line.startswith("Fatal:"):
self.testcase_cls_inst.fail(line)
self.testcase.fail(line)
elif line.startswith("FATAL_ERROR:"):
self.testcase_cls_inst.fail(line)
self.testcase.fail(line)