Add testcases to cover design validation errors
This commit is contained in:
@@ -52,7 +52,7 @@ class BaseTestCase(unittest.TestCase):
|
|||||||
@property
|
@property
|
||||||
def rerun(self) -> bool:
|
def rerun(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Re-run wothout deleting and re-generating prior output directory.
|
Re-run without deleting and re-generating prior output directory.
|
||||||
"""
|
"""
|
||||||
return self.request.config.getoption("--rerun")
|
return self.request.config.getoption("--rerun")
|
||||||
|
|
||||||
@@ -79,14 +79,14 @@ class BaseTestCase(unittest.TestCase):
|
|||||||
f.write(f"{k}: {repr(v)}\n")
|
f.write(f"{k}: {repr(v)}\n")
|
||||||
|
|
||||||
|
|
||||||
def _export_regblock(self):
|
def export_regblock(self):
|
||||||
"""
|
"""
|
||||||
Call the peakrdl_regblock exporter to generate the DUT
|
Call the peakrdl_regblock exporter to generate the DUT
|
||||||
"""
|
"""
|
||||||
this_dir = self.get_testcase_dir()
|
this_dir = self.get_testcase_dir()
|
||||||
|
|
||||||
if self.rdl_file:
|
if self.rdl_file:
|
||||||
rdl_file = self.rdl_file
|
rdl_file = os.path.join(this_dir, self.rdl_file)
|
||||||
else:
|
else:
|
||||||
# Find any *.rdl file in testcase dir
|
# Find any *.rdl file in testcase dir
|
||||||
rdl_file = glob.glob(os.path.join(this_dir, "*.rdl"))[0]
|
rdl_file = glob.glob(os.path.join(this_dir, "*.rdl"))[0]
|
||||||
@@ -120,17 +120,21 @@ class BaseTestCase(unittest.TestCase):
|
|||||||
default_reset_async=self.default_reset_async,
|
default_reset_async=self.default_reset_async,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def delete_run_dir(self) -> None:
|
||||||
|
run_dir = self.get_run_dir()
|
||||||
|
if os.path.exists(run_dir):
|
||||||
|
shutil.rmtree(run_dir)
|
||||||
|
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
if self.rerun:
|
if self.rerun:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create fresh build dir
|
# Create fresh build dir
|
||||||
run_dir = self.get_run_dir()
|
run_dir = self.get_run_dir()
|
||||||
if os.path.exists(run_dir):
|
self.delete_run_dir()
|
||||||
shutil.rmtree(run_dir)
|
|
||||||
pathlib.Path(run_dir).mkdir(parents=True, exist_ok=True)
|
pathlib.Path(run_dir).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
self._write_params()
|
self._write_params()
|
||||||
|
|
||||||
# Convert testcase RDL file --> SV
|
# Convert testcase RDL file --> SV
|
||||||
self._export_regblock()
|
self.export_regblock()
|
||||||
|
|||||||
0
tests/test_validation_errors/__init__.py
Normal file
0
tests/test_validation_errors/__init__.py
Normal file
15
tests/test_validation_errors/external_ref.rdl
Normal file
15
tests/test_validation_errors/external_ref.rdl
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
addrmap sub {
|
||||||
|
reg {
|
||||||
|
field {} f;
|
||||||
|
} x;
|
||||||
|
};
|
||||||
|
|
||||||
|
addrmap top {
|
||||||
|
reg {
|
||||||
|
field {} f;
|
||||||
|
} x;
|
||||||
|
|
||||||
|
sub sub;
|
||||||
|
|
||||||
|
x.f->reset = sub.x.f;
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
addrmap top {
|
||||||
|
reg {
|
||||||
|
field {}f;
|
||||||
|
} x;
|
||||||
|
reg {
|
||||||
|
accesswidth = 16;
|
||||||
|
field {}f;
|
||||||
|
} y;
|
||||||
|
};
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
addrmap top {
|
||||||
|
reg {
|
||||||
|
field {
|
||||||
|
sw = rw;
|
||||||
|
hw = w;
|
||||||
|
singlepulse = true;
|
||||||
|
} a = 0;
|
||||||
|
field {
|
||||||
|
sw = rw;
|
||||||
|
hw = w;
|
||||||
|
singlepulse = true;
|
||||||
|
posedge intr;
|
||||||
|
stickybit = false;
|
||||||
|
} b = 0;
|
||||||
|
field {
|
||||||
|
sw = rw;
|
||||||
|
hw = w;
|
||||||
|
singlepulse = true;
|
||||||
|
negedge intr;
|
||||||
|
stickybit = false;
|
||||||
|
} c = 0;
|
||||||
|
field {
|
||||||
|
sw = rw;
|
||||||
|
hw = w;
|
||||||
|
singlepulse = true;
|
||||||
|
bothedge intr;
|
||||||
|
stickybit = false;
|
||||||
|
} d = 0;
|
||||||
|
} x;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
stickybit=false + intr posedge
|
||||||
|
stickybit=false + intr negedge
|
||||||
|
stickybit=false + intr bothedge
|
||||||
|
hw=w wel = false
|
||||||
|
singlepulse
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
7
tests/test_validation_errors/sharedextbus.rdl
Normal file
7
tests/test_validation_errors/sharedextbus.rdl
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
addrmap top {
|
||||||
|
sharedextbus;
|
||||||
|
|
||||||
|
reg {
|
||||||
|
field {}f;
|
||||||
|
} x;
|
||||||
|
};
|
||||||
78
tests/test_validation_errors/testcase.py
Normal file
78
tests/test_validation_errors/testcase.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import io
|
||||||
|
import contextlib
|
||||||
|
|
||||||
|
from systemrdl.messages import RDLCompileError
|
||||||
|
|
||||||
|
from ..lib.base_testcase import BaseTestCase
|
||||||
|
|
||||||
|
class TestValidationErrors(BaseTestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
# Stub usual pre-test setup
|
||||||
|
pass
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
# Delete any cruft that may get generated
|
||||||
|
self.delete_run_dir()
|
||||||
|
|
||||||
|
def assert_validate_error(self, rdl_file: str, err_regex: str) -> None:
|
||||||
|
self.rdl_file = rdl_file
|
||||||
|
f = io.StringIO()
|
||||||
|
with contextlib.redirect_stderr(f):
|
||||||
|
with self.assertRaises(RDLCompileError):
|
||||||
|
self.export_regblock()
|
||||||
|
stderr = f.getvalue()
|
||||||
|
self.assertRegex(stderr, err_regex)
|
||||||
|
|
||||||
|
|
||||||
|
def test_unaligned_reg(self) -> None:
|
||||||
|
self.assert_validate_error(
|
||||||
|
"unaligned_reg.rdl",
|
||||||
|
"Unaligned registers are not supported. Address offset of instance 'x' must be a multiple of 4",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_unaligned_stride(self) -> None:
|
||||||
|
self.assert_validate_error(
|
||||||
|
"unaligned_stride.rdl",
|
||||||
|
"Unaligned registers are not supported. Address stride of instance array 'x' must be a multiple of 4",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_bad_external_ref(self) -> None:
|
||||||
|
self.assert_validate_error(
|
||||||
|
"external_ref.rdl",
|
||||||
|
"Property is assigned a reference that points to a component not internal to the regblock being exported",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_sharedextbus_not_supported(self) -> None:
|
||||||
|
self.assert_validate_error(
|
||||||
|
"sharedextbus.rdl",
|
||||||
|
"This exporter does not support enabling the 'sharedextbus' property yet",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_inconsistent_accesswidth(self) -> None:
|
||||||
|
self.assert_validate_error(
|
||||||
|
"inconsistent_accesswidth.rdl",
|
||||||
|
r"Multi-word registers that have an accesswidth \(16\) that are inconsistent with this regblock's CPU bus width \(32\) are not supported",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_unbuffered_wide_w_fields(self) -> None:
|
||||||
|
self.assert_validate_error(
|
||||||
|
"unbuffered_wide_fields.rdl",
|
||||||
|
"Software-writable field 'xf' shall not span"
|
||||||
|
" multiple software-accessible subwords. Consider enabling"
|
||||||
|
" write double-buffering",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_unbuffered_wide_r_fields(self) -> None:
|
||||||
|
self.assert_validate_error(
|
||||||
|
"unbuffered_wide_fields.rdl",
|
||||||
|
"The field 'yf' spans multiple software-accessible"
|
||||||
|
" subwords and is modified on-read, making it impossible to"
|
||||||
|
" access its value correctly. Consider enabling read"
|
||||||
|
" double-buffering.",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_multiple_unconditional_assigns(self) -> None:
|
||||||
|
self.assert_validate_error(
|
||||||
|
"multiple_unconditional_assigns.rdl",
|
||||||
|
"Field has multiple conflicting properties that unconditionally set its state",
|
||||||
|
)
|
||||||
8
tests/test_validation_errors/unaligned_reg.rdl
Normal file
8
tests/test_validation_errors/unaligned_reg.rdl
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
addrmap top {
|
||||||
|
default regwidth = 32;
|
||||||
|
default accesswidth = 32;
|
||||||
|
|
||||||
|
reg {
|
||||||
|
field {}f;
|
||||||
|
} x @ 1;
|
||||||
|
};
|
||||||
8
tests/test_validation_errors/unaligned_stride.rdl
Normal file
8
tests/test_validation_errors/unaligned_stride.rdl
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
addrmap top {
|
||||||
|
default regwidth = 32;
|
||||||
|
default accesswidth = 32;
|
||||||
|
|
||||||
|
reg {
|
||||||
|
field {}f;
|
||||||
|
} x[4] @ 0 += 5;
|
||||||
|
};
|
||||||
21
tests/test_validation_errors/unbuffered_wide_fields.rdl
Normal file
21
tests/test_validation_errors/unbuffered_wide_fields.rdl
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
addrmap top {
|
||||||
|
reg {
|
||||||
|
regwidth = 64;
|
||||||
|
accesswidth = 32;
|
||||||
|
field {
|
||||||
|
sw=w;
|
||||||
|
hw=r;
|
||||||
|
} xf[64];
|
||||||
|
} x;
|
||||||
|
|
||||||
|
reg {
|
||||||
|
regwidth = 64;
|
||||||
|
accesswidth = 32;
|
||||||
|
field {
|
||||||
|
sw=r;
|
||||||
|
hw=w;
|
||||||
|
we;
|
||||||
|
onread=rclr;
|
||||||
|
} yf[64];
|
||||||
|
} y;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user