from soc.alu.pipe_data import ALUPipeSpec
import random
+class TestCase:
+ def __init__(self, program, regs, sprs, name):
+ self.program = program
+ self.regs = regs
+ self.sprs = sprs
+ self.name = name
+
def get_rec_width(rec):
recwidth = 0
# Setup random inputs for dut.op
yield alu.p.data_i.so.eq(so)
-class ALUTestCase(FHDLTestCase):
- def run_tst(self, program, initial_regs, initial_sprs):
- m = Module()
- comb = m.d.comb
- instruction = Signal(32)
-
- pdecode = create_pdecode()
+# This test bench is a bit different than is usual. Initially when I
+# was writing it, I had all of the tests call a function to create a
+# device under test and simulator, initialize the dut, run the
+# simulation for ~2 cycles, and assert that the dut output what it
+# should have. However, this was really slow, since it needed to
+# create and tear down the dut and simulator for every test case.
- m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
-
- rec = CompALUOpSubset()
-
- pspec = ALUPipeSpec(id_wid=2, op_wid=get_rec_width(rec))
- m.submodules.alu = alu = ALUBasePipe(pspec)
-
- comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
- comb += alu.p.valid_i.eq(1)
- comb += alu.n.ready_i.eq(1)
- simulator = ISA(pdecode2, initial_regs, initial_sprs)
- comb += pdecode2.dec.raw_opcode_in.eq(instruction)
- sim = Simulator(m)
- gen = program.generate_instructions()
+# Now, instead of doing that, every test case in ALUTestCase puts some
+# data into the test_data list below, describing the instructions to
+# be tested and the initial state. Once all the tests have been run,
+# test_data gets passed to TestRunner which then sets up the DUT and
+# simulator once, runs all the data through it, and asserts that the
+# results match the pseudocode sim at every cycle.
- sim.add_clock(1e-6)
- def process():
- instructions = list(zip(gen, program.assembly.splitlines()))
-
- index = simulator.pc.CIA.value//4
- while index < len(instructions):
- ins, code = instructions[index]
-
- print("0x{:X}".format(ins & 0xffffffff))
- print(code)
-
- # ask the decoder to decode this binary data (endian'd)
- yield pdecode2.dec.bigendian.eq(0) # little / big?
- yield instruction.eq(ins) # raw binary instr.
- yield Settle()
- yield from set_alu_inputs(alu, pdecode2, simulator)
- yield from set_extra_alu_inputs(alu, pdecode2, simulator)
- yield
- opname = code.split(' ')[0]
- yield from simulator.call(opname)
- index = simulator.pc.CIA.value//4
+# By doing this, I've reduced the time it takes to run the test suite
+# massively. Before, it took around 1 minute on my computer, now it
+# takes around 3 seconds
- vld = yield alu.n.valid_o
- while not vld:
- yield
- vld = yield alu.n.valid_o
- yield
- alu_out = yield alu.n.data_o.o
- out_reg_valid = yield pdecode2.e.write_reg.ok
- if out_reg_valid:
- write_reg_idx = yield pdecode2.e.write_reg.data
- expected = simulator.gpr(write_reg_idx).value
- print(f"expected {expected:x}, actual: {alu_out:x}")
- self.assertEqual(expected, alu_out)
- yield from self.check_extra_alu_outputs(alu, pdecode2,
- simulator)
+test_data = []
- sim.add_sync_process(process)
- with sim.write_vcd("simulator.vcd", "simulator.gtkw",
- traces=[]):
- sim.run()
- return simulator
- def check_extra_alu_outputs(self, alu, dec2, sim):
- rc = yield dec2.e.rc.data
- if rc:
- cr_expected = sim.crl[0].get_range().value
- cr_actual = yield alu.n.data_o.cr0
- self.assertEqual(cr_expected, cr_actual)
+class ALUTestCase(FHDLTestCase):
+ def __init__(self, name):
+ super().__init__(name)
+ self.test_name = name
def run_tst_program(self, prog, initial_regs=[0] * 32, initial_sprs={}):
- simulator = self.run_tst(prog, initial_regs, initial_sprs)
- simulator.gpr.dump()
- return simulator
+ tc = TestCase(prog, initial_regs, initial_sprs, self.test_name)
+ test_data.append(tc)
def test_rand(self):
insns = ["add", "add.", "and", "or", "xor", "subf"]
initial_regs = [0] * 32
initial_regs[1] = random.randint(0, (1<<64)-1)
initial_regs[2] = random.randint(0, (1<<64)-1)
- with Program(lst) as program:
- sim = self.run_tst_program(program, initial_regs)
+ self.run_tst_program(Program(lst), initial_regs)
def test_rand_imm(self):
insns = ["addi", "addis", "subfic"]
print(lst)
initial_regs = [0] * 32
initial_regs[1] = random.randint(0, (1<<64)-1)
- with Program(lst) as program:
- sim = self.run_tst_program(program, initial_regs)
+ self.run_tst_program(Program(lst), initial_regs)
def test_rand_imm_logical(self):
insns = ["andi.", "andis.", "ori", "oris", "xori", "xoris"]
print(lst)
initial_regs = [0] * 32
initial_regs[1] = random.randint(0, (1<<64)-1)
- with Program(lst) as program:
- sim = self.run_tst_program(program, initial_regs)
+ self.run_tst_program(Program(lst), initial_regs)
def test_shift(self):
insns = ["slw", "sld", "srw", "srd", "sraw", "srad"]
initial_regs[1] = random.randint(0, (1<<64)-1)
initial_regs[2] = random.randint(0, 63)
print(initial_regs[1], initial_regs[2])
- with Program(lst) as program:
- sim = self.run_tst_program(program, initial_regs)
+ self.run_tst_program(Program(lst), initial_regs)
def test_shift_arith(self):
initial_regs[1] = random.randint(0, (1<<64)-1)
initial_regs[2] = random.randint(0, 63)
print(initial_regs[1], initial_regs[2])
- with Program(lst) as program:
- sim = self.run_tst_program(program, initial_regs)
+ self.run_tst_program(Program(lst), initial_regs)
def test_rlwinm(self):
for i in range(10):
lst = [f"rlwinm 3, 1, {mb}, {me}, {sh}"]
initial_regs = [0] * 32
initial_regs[1] = random.randint(0, (1<<64)-1)
- with Program(lst) as program:
- sim = self.run_tst_program(program, initial_regs)
+ self.run_tst_program(Program(lst), initial_regs)
def test_rlwimi(self):
lst = ["rlwimi 3, 1, 5, 20, 6"]
initial_regs = [0] * 32
initial_regs[1] = 0xdeadbeef
initial_regs[3] = 0x12345678
- with Program(lst) as program:
- sim = self.run_tst_program(program, initial_regs)
+ self.run_tst_program(Program(lst), initial_regs)
def test_rlwnm(self):
lst = ["rlwnm 3, 1, 2, 20, 6"]
initial_regs = [0] * 32
initial_regs[1] = random.randint(0, (1<<64)-1)
initial_regs[2] = random.randint(0, 63)
- with Program(lst) as program:
- sim = self.run_tst_program(program, initial_regs)
+ self.run_tst_program(Program(lst), initial_regs)
def test_adde(self):
lst = ["adde. 5, 6, 7"]
xer = SelectableInt(0, 64)
xer[XER_bits['CA']] = 1
initial_sprs[special_sprs['XER']] = xer
- with Program(lst) as program:
- sim = self.run_tst_program(program, initial_regs, initial_sprs)
+ self.run_tst_program(Program(lst), initial_regs, initial_sprs)
def test_ilang(self):
rec = CompALUOpSubset()
with open("pipeline.il", "w") as f:
f.write(vl)
+
+class TestRunner(FHDLTestCase):
+ def __init__(self, test_data):
+ super().__init__("run_all")
+ self.test_data = test_data
+
+ def run_all(self):
+ m = Module()
+ comb = m.d.comb
+ instruction = Signal(32)
+
+ pdecode = create_pdecode()
+
+ m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
+
+ rec = CompALUOpSubset()
+
+ pspec = ALUPipeSpec(id_wid=2, op_wid=get_rec_width(rec))
+ m.submodules.alu = alu = ALUBasePipe(pspec)
+
+ comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
+ comb += alu.p.valid_i.eq(1)
+ comb += alu.n.ready_i.eq(1)
+ comb += pdecode2.dec.raw_opcode_in.eq(instruction)
+ sim = Simulator(m)
+
+ sim.add_clock(1e-6)
+ def process():
+ for test in self.test_data:
+ print(test.name)
+ program = test.program
+ self.subTest(test.name)
+ simulator = ISA(pdecode2, test.regs, test.sprs)
+ gen = program.generate_instructions()
+ instructions = list(zip(gen, program.assembly.splitlines()))
+
+ index = simulator.pc.CIA.value//4
+ while index < len(instructions):
+ ins, code = instructions[index]
+
+ print("0x{:X}".format(ins & 0xffffffff))
+ print(code)
+
+ # ask the decoder to decode this binary data (endian'd)
+ yield pdecode2.dec.bigendian.eq(0) # little / big?
+ yield instruction.eq(ins) # raw binary instr.
+ yield Settle()
+ yield from set_alu_inputs(alu, pdecode2, simulator)
+ yield from set_extra_alu_inputs(alu, pdecode2, simulator)
+ yield
+ opname = code.split(' ')[0]
+ yield from simulator.call(opname)
+ index = simulator.pc.CIA.value//4
+
+ vld = yield alu.n.valid_o
+ while not vld:
+ yield
+ vld = yield alu.n.valid_o
+ yield
+ alu_out = yield alu.n.data_o.o
+ out_reg_valid = yield pdecode2.e.write_reg.ok
+ if out_reg_valid:
+ write_reg_idx = yield pdecode2.e.write_reg.data
+ expected = simulator.gpr(write_reg_idx).value
+ print(f"expected {expected:x}, actual: {alu_out:x}")
+ self.assertEqual(expected, alu_out)
+ yield from self.check_extra_alu_outputs(alu, pdecode2,
+ simulator)
+
+ sim.add_sync_process(process)
+ with sim.write_vcd("simulator.vcd", "simulator.gtkw",
+ traces=[]):
+ sim.run()
+ def check_extra_alu_outputs(self, alu, dec2, sim):
+ rc = yield dec2.e.rc.data
+ if rc:
+ cr_expected = sim.crl[0].get_range().value
+ cr_actual = yield alu.n.data_o.cr0
+ self.assertEqual(cr_expected, cr_actual)
+
+
if __name__ == "__main__":
- unittest.main()
+ unittest.main(exit=False)
+ suite = unittest.TestSuite()
+ suite.addTest(TestRunner(test_data))
+
+ runner = unittest.TextTestRunner()
+ runner.run(suite)