-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_grace_shifter.py
129 lines (102 loc) · 3.29 KB
/
test_grace_shifter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"""
An omnidirectional hardware shifter that supports rotation.
>>> defaults = {"enabled": True, "left": True, "rotate": False, "data": 0x800FB009, "shift_amount": 4}
>>> test_cases = [
... {},
... {"enabled": False},
... {"rotate": True},
... {"left": False},
... {"left": False, "rotate": True},
... {"shift_amount": 28},
... {"shift_amount": 30},
... {"data": 0xaaaaaaaa, "shift_amount": 1},
... ]
>>> for test_case in test_cases:
... result = shifter_software_spec(**{**defaults, **test_case})
... print(f"{test_case}: {result:08x}")
{}: 00fb0090
{'enabled': False}: 800fb009
{'rotate': True}: 00fb0098
{'left': False}: 0800fb00
{'left': False, 'rotate': True}: 9800fb00
{'shift_amount': 28}: 90000000
{'shift_amount': 30}: 40000000
{'data': 2863311530, 'shift_amount': 1}: 55555554
"""
from functools import partial
from cocotb import test
from cocotb.triggers import Timer
from hypothesis import strategies as st
from test_module import assert_hardware_matches_software, run_module_tests
def shifter_input_types(bit_width: int = 32):
return {
"enabled": st.booleans(),
"left": st.booleans(),
"rotate": st.booleans(),
"data": st.integers(0, 1 << bit_width - 1),
"shift_amount": st.integers(0, bit_width - 1),
}
def rotated(left: bool, shift_amount: int, data: int, bit_width: int = 32):
"Rotate a bitvector a given amount."
if left:
left_rot_amount = shift_amount
else:
left_rot_amount = bit_width - shift_amount
# Contains crap data above 32b boundary.
rotated_result = (data << left_rot_amount) | (data >> (bit_width - left_rot_amount))
# Mask away crap data in upper bytes.
return rotated_result & word_mask(bit_width)
def word_mask(bit_width: int = 32) -> int:
return (1 << bit_width) - 1
def shift_mask(rotate, left, shift_amount, bit_width: int = 32):
"A shifter's output mask. Generates masks from left/right, depending on args."
full_mask = word_mask(bit_width)
if rotate:
return full_mask
if left:
return (full_mask << shift_amount) & full_mask
else:
return full_mask >> shift_amount
def shifter_software_spec(
enabled: bool,
rotate: bool,
left: bool,
shift_amount: int,
data: int,
bit_width: int = 32,
):
"Perform a rotation, then mask the results according to flags."
if not enabled:
return data
rotated_result = rotated(left, shift_amount, data, bit_width)
return rotated_result & shift_mask(rotate, left, shift_amount, bit_width)
async def shifter_hardware_result(
dut,
enabled: bool,
rotate: bool,
left: bool,
data: int,
shift_amount: int,
bit_width: int = 32,
):
assert 0 <= shift_amount < bit_width
dut.En <= int(enabled)
dut.RotateEnable <= int(rotate)
dut.Left <= int(left)
dut.dIN <= data
dut.ShAmount <= shift_amount
await Timer(1, units="ns")
return dut.dOUT
@test()
async def grace_shifter_fuzzing_test(dut):
"""
Verify shifter works exactly as spec'd above.
"""
await assert_hardware_matches_software(
shifter_software_spec,
partial(shifter_hardware_result, dut),
input_types=shifter_input_types(),
)
# Pytest invocation.
def test_grace_shifter():
run_module_tests("grace_shifter")