Skip to content

Commit

Permalink
rewrite functional backend test code in python
Browse files Browse the repository at this point in the history
  • Loading branch information
aiju committed Jul 12, 2024
1 parent d7f58b9 commit 4e2bcd3
Show file tree
Hide file tree
Showing 57 changed files with 554 additions and 1,238 deletions.
19 changes: 19 additions & 0 deletions tests/functional/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Tests for the functional backend use pytest as a testrunner.

Run with `pytest -v`

Pytest options you might want:

- `-v`: More progress indication.

- `--basetemp tmp`: Store test files (including vcd results) in tmp.
CAREFUL: contents of tmp will be deleted

- `-k <pattern>`: Run only tests that contain the pattern, e.g.
`-k cxx` or `-k smt` or `-k demux` or `-k 'cxx[demux`

- `-s`: Don't hide stdout/stderr from the test code.

Custom options for functional backend tests:

- `--per-cell N`: Run only N tests for each cell.
14 changes: 14 additions & 0 deletions tests/functional/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import pytest
from rtlil_cells import generate_test_cases

def pytest_addoption(parser):
parser.addoption(
"--per-cell", type=int, default=None, help="run only N tests per cell"
)

def pytest_generate_tests(metafunc):
if "cell" in metafunc.fixturenames:
print(dir(metafunc.config))
per_cell = metafunc.config.getoption("per_cell", default=None)
names, cases = generate_test_cases(per_cell)
metafunc.parametrize("cell,parameters", cases, ids=names)
275 changes: 275 additions & 0 deletions tests/functional/rtlil_cells.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
from itertools import chain
import random

widths = [
(16, 32, 48, True),
(16, 32, 48, False),
(32, 16, 48, True),
(32, 16, 48, False),
(32, 32, 16, True),
(32, 32, 16, False)
]

shift_widths = [
(32, 6, 32, True, False),
(32, 6, 32, False, False),
(32, 6, 64, True, False),
(32, 6, 64, False, False),
(32, 32, 16, True, False),
(32, 32, 16, False, False),
(32, 6, 32, True, True),
(32, 6, 32, False, True),
(32, 6, 64, True, True),
(32, 6, 64, False, True),
(32, 32, 16, True, True),
(32, 32, 16, False, True),
]

def write_rtlil_cell(f, cell_type, inputs, outputs, parameters):
f.write('autoidx 1\n')
f.write('module \\gold\n')
idx = 1
for name, width in inputs.items():
f.write(f'\twire width {width} input {idx} \\{name}\n')
idx += 1
for name, width in outputs.items():
f.write(f'\twire width {width} output {idx} \\{name}\n')
idx += 1
f.write(f'\tcell ${cell_type} \\UUT\n')
for (name, value) in parameters.items():
f.write(f'\t\tparameter \\{name} {value}\n')
for name in chain(inputs.keys(), outputs.keys()):
f.write(f'\t\tconnect \\{name} \\{name}\n')
f.write(f'\tend\nend\n')

class BaseCell:
def __init__(self, name):
self.name = name

class UnaryCell(BaseCell):
def __init__(self, name):
super().__init__(name)
def generate_tests(self):
for (a_width, _, y_width, signed) in widths:
yield (f'{a_width}-{y_width}-{'S' if signed else 'U'}',
{'A_WIDTH' : a_width,
'A_SIGNED' : int(signed),
'Y_WIDTH' : y_width})
def write_rtlil_file(self, f, parameters):
write_rtlil_cell(f, self.name, {'A': parameters['A_WIDTH']}, {'Y': parameters['Y_WIDTH']}, parameters)

class BinaryCell(BaseCell):
def __init__(self, name):
super().__init__(name)
def generate_tests(self):
for (a_width, b_width, y_width, signed) in widths:
yield (f'{a_width}-{b_width}-{y_width}-{'S' if signed else 'U'}',
{'A_WIDTH' : a_width,
'A_SIGNED' : int(signed),
'B_WIDTH' : b_width,
'B_SIGNED' : int(signed),
'Y_WIDTH' : y_width})
def write_rtlil_file(self, f, parameters):
write_rtlil_cell(f, self.name, {'A': parameters['A_WIDTH'], 'B': parameters['B_WIDTH']}, {'Y': parameters['Y_WIDTH']}, parameters)

class ShiftCell(BaseCell):
def __init__(self, name):
super().__init__(name)
def generate_tests(self):
for (a_width, b_width, y_width, a_signed, b_signed) in shift_widths:
if not self.name in ('shift', 'shiftx') and b_signed: continue
if self.name == 'shiftx' and a_signed: continue
yield (f'{a_width}-{b_width}-{y_width}-{'S' if a_signed else 'U'}{'S' if b_signed else 'U'}',
{'A_WIDTH' : a_width,
'A_SIGNED' : int(a_signed),
'B_WIDTH' : b_width,
'B_SIGNED' : int(b_signed),
'Y_WIDTH' : y_width})
def write_rtlil_file(self, f, parameters):
write_rtlil_cell(f, self.name, {'A': parameters['A_WIDTH'], 'B': parameters['B_WIDTH']}, {'Y': parameters['Y_WIDTH']}, parameters)

class MuxCell(BaseCell):
def __init__(self, name):
super().__init__(name)
def generate_tests(self):
for width in [10, 20, 40]:
yield (f'{width}', {'WIDTH' : width})
def write_rtlil_file(self, f, parameters):
write_rtlil_cell(f, self.name, {'A': parameters['WIDTH'], 'B': parameters['WIDTH'], 'S': 1}, {'Y': parameters['WIDTH']}, parameters)

class BWCell(BaseCell):
def __init__(self, name):
super().__init__(name)
def generate_tests(self):
for width in [10, 20, 40]:
yield (f'{width}', {'WIDTH' : width})
def write_rtlil_file(self, f, parameters):
inputs = {'A': parameters['WIDTH'], 'B': parameters['WIDTH']}
if self.name == "bwmux": inputs['S'] = parameters['WIDTH']
write_rtlil_cell(f, self.name, inputs, {'Y': parameters['WIDTH']}, parameters)

class PMuxCell(BaseCell):
def __init__(self, name):
super().__init__(name)
def generate_tests(self):
for (width, s_width) in [(10, 1), (10, 4), (20, 4)]:
yield (f'{width}-{s_width}',
{'WIDTH' : width,
'S_WIDTH' : s_width})
def write_rtlil_file(self, f, parameters):
s_width = parameters['S_WIDTH']
b_width = parameters['WIDTH'] * s_width
write_rtlil_cell(f, self.name, {'A': parameters['WIDTH'], 'B': b_width, 'S': s_width}, {'Y': parameters['WIDTH']}, parameters)

class BMuxCell(BaseCell):
def __init__(self, name):
super().__init__(name)
def generate_tests(self):
for (width, s_width) in [(10, 1), (10, 2), (10, 4)]:
yield (f'{width}-{s_width}', {'WIDTH' : width, 'S_WIDTH' : s_width})
def write_rtlil_file(self, f, parameters):
write_rtlil_cell(f, self.name, {'A': parameters['WIDTH'] << parameters['S_WIDTH'], 'S': parameters['S_WIDTH']}, {'Y': parameters['WIDTH']}, parameters)

class DemuxCell(BaseCell):
def __init__(self, name):
super().__init__(name)
def generate_tests(self):
for (width, s_width) in [(10, 1), (32, 2), (16, 4)]:
yield (f'{width}-{s_width}', {'WIDTH' : width, 'S_WIDTH' : s_width})
def write_rtlil_file(self, f, parameters):
write_rtlil_cell(f, self.name, {'A': parameters['WIDTH'], 'S': parameters['S_WIDTH']}, {'Y': parameters['WIDTH'] << parameters['S_WIDTH']}, parameters)

def seeded_randint(seed, a, b):
r = random.getstate()
random.seed(seed)
n = random.randint(a, b)
random.setstate(r)
return n

class LUTCell(BaseCell):
def __init__(self, name):
super().__init__(name)
def generate_tests(self):
for width in [4, 6, 8]:
lut = seeded_randint(width, 0, 2**width - 1)
yield (f'{width}', {'WIDTH' : width, 'LUT' : lut})
def write_rtlil_file(self, f, parameters):
write_rtlil_cell(f, self.name, {'A': parameters['WIDTH']}, {'Y': 1}, parameters)

class ConcatCell(BaseCell):
def __init__(self, name):
super().__init__(name)
def generate_tests(self):
for (a_width, b_width) in [(16, 16), (8, 14), (20, 10)]:
yield (f'{a_width}-{b_width}', {'A_WIDTH' : a_width, 'B_WIDTH' : b_width})
def write_rtlil_file(self, f, parameters):
write_rtlil_cell(f, self.name, {'A': parameters['A_WIDTH'], 'B' : parameters['B_WIDTH']}, {'Y': parameters['A_WIDTH'] + parameters['B_WIDTH']}, parameters)

class SliceCell(BaseCell):
def __init__(self, name):
super().__init__(name)
def generate_tests(self):
for (a_width, offset, y_width) in [(32, 10, 15), (8, 0, 4), (10, 0, 10)]:
yield (f'{a_width}-{offset}-{y_width}', {'A_WIDTH' : a_width, 'OFFSET' : offset, 'Y_WIDTH': y_width})
def write_rtlil_file(self, f, parameters):
write_rtlil_cell(f, self.name, {'A': parameters['A_WIDTH']}, {'Y': parameters['Y_WIDTH']}, parameters)

class FailCell(BaseCell):
def __init__(self, name):
super().__init__(name)
def generate_tests(self):
yield ('', {})
def write_rtlil_file(self, f, parameters):
raise Exception(f'\'{self.name}\' cell unimplemented in test generator')

rtlil_cells = [
UnaryCell("not"),
UnaryCell("pos"),
UnaryCell("neg"),
BinaryCell("and"),
BinaryCell("or"),
BinaryCell("xor"),
BinaryCell("xnor"),
UnaryCell("reduce_and"),
UnaryCell("reduce_or"),
UnaryCell("reduce_xor"),
UnaryCell("reduce_xnor"),
UnaryCell("reduce_bool"),
ShiftCell("shl"),
ShiftCell("shr"),
ShiftCell("sshl"),
ShiftCell("sshr"),
ShiftCell("shift"),
ShiftCell("shiftx"),
# ("fa", ["A", "B", "C", "X", "Y"]),
# ("lcu", ["P", "G", "CI", "CO"]),
# ("alu", ["A", "B", "CI", "BI", "X", "Y", "CO"]),
BinaryCell("lt"),
BinaryCell("le"),
BinaryCell("eq"),
BinaryCell("ne"),
BinaryCell("eqx"),
BinaryCell("nex"),
BinaryCell("ge"),
BinaryCell("gt"),
BinaryCell("add"),
BinaryCell("sub"),
BinaryCell("mul"),
# BinaryCell("macc"),
BinaryCell("div"),
BinaryCell("mod"),
BinaryCell("divfloor"),
BinaryCell("modfloor"),
BinaryCell("pow"),
UnaryCell("logic_not"),
BinaryCell("logic_and"),
BinaryCell("logic_or"),
SliceCell("slice"),
ConcatCell("concat"),
MuxCell("mux"),
BMuxCell("bmux"),
PMuxCell("pmux"),
DemuxCell("demux"),
LUTCell("lut"),
# ("sop", ["A", "Y"]),
# ("tribuf", ["A", "EN", "Y"]),
# ("specify2", ["EN", "SRC", "DST"]),
# ("specify3", ["EN", "SRC", "DST", "DAT"]),
# ("specrule", ["EN_SRC", "EN_DST", "SRC", "DST"]),
BWCell("bweqx"),
BWCell("bwmux"),
# ("assert", ["A", "EN"]),
# ("assume", ["A", "EN"]),
# ("live", ["A", "EN"]),
# ("fair", ["A", "EN"]),
# ("cover", ["A", "EN"]),
# ("initstate", ["Y"]),
# ("anyconst", ["Y"]),
# ("anyseq", ["Y"]),
# ("anyinit", ["D", "Q"]),
# ("allconst", ["Y"]),
# ("allseq", ["Y"]),
# ("equiv", ["A", "B", "Y"]),
# ("print", ["EN", "TRG", "ARGS"]),
# ("check", ["A", "EN", "TRG", "ARGS"]),
# ("set_tag", ["A", "SET", "CLR", "Y"]),
# ("get_tag", ["A", "Y"]),
# ("overwrite_tag", ["A", "SET", "CLR"]),
# ("original_tag", ["A", "Y"]),
# ("future_ff", ["A", "Y"]),
# ("scopeinfo", []),
]

def generate_test_cases(per_cell):
tests = []
names = []
for cell in rtlil_cells:
seen_names = set()
for (name, parameters) in cell.generate_tests():
if not name in seen_names:
seen_names.add(name)
tests.append((cell, parameters))
names.append(f'{cell.name}-{name}' if name != '' else cell.name)
if per_cell is not None and len(seen_names) >= per_cell:
break
return (names, tests)
2 changes: 2 additions & 0 deletions tests/functional/run-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
pytest -v "$@"
2 changes: 0 additions & 2 deletions tests/functional/single_cells/rtlil/README.md

This file was deleted.

Loading

0 comments on commit 4e2bcd3

Please sign in to comment.