#include "Valu.h" #include "verilated.h" #include "tester.hpp" #include #include struct alu_testcase { std::string name; // Inputs uint32_t A, B; uint8_t op; // Outputs uint32_t O; std::optional overflow, zero; std::optional max_cycles; }; std::string fmt_hex(uint32_t n) { char hex[100]; if (n < 0x100) snprintf(hex, sizeof hex, "0x%02x", n); else if (n < 0x10000) snprintf(hex, sizeof hex, "0x%04x", n); else snprintf(hex, sizeof hex, "0x%08x", n); return hex; } void test_op(Valu *valu, Tester *tester, alu_testcase test) { Tester subtester(tester, test.name); // assign inputs valu->op = test.op; valu->A = test.A; valu->B = test.B; valu->eval(); std::string o_name("O == "); o_name.append(fmt_hex(test.O)); subtester.assert_eq(o_name, valu->O, test.O); if (test.overflow.has_value()) { if (*test.overflow) subtester.assert_eq("overflow flag set", valu->Fflow, 1); else subtester.assert_eq("no overflow flag", valu->Fflow, 0); } if (test.zero.has_value()) { if (*test.zero) subtester.assert_eq("zero flag set", valu->Fzero, 1); else subtester.assert_eq("no zero flag", valu->Fzero, 0); } } int main(int argc, char **argv) { VerilatedContext *vctx = new VerilatedContext; Valu *valu = new Valu(vctx); Tester alu_t("alu"); { Tester add_t(&alu_t, "add", true); test_op(valu, &add_t, { .name = "0x2137+0x1234", .A = 0x2137, .B = 0x1234, .op = 0b000, .O = 0x336b, .overflow = false, }); test_op(valu, &add_t, { .name = "0x09+0x10", .A = 0x09, .B = 0x10, .op = 0b000, .O = 0x19, .overflow = false, }); test_op(valu, &add_t, { .name = "0x5555+0x5555", .A = 0x5555, .B = 0x5555, .op = 0b000, .O = 0xaaaa, .overflow = false, }); test_op(valu, &add_t, { .name = "0xfffffffe+0x1", .A = 0xfffffffe, .B = 0x1, .op = 0b000, .O = 0xffffffff, .overflow = false, .zero = false, }); test_op(valu, &add_t, { .name = "0xffffffff+0x1", .A = 0xffffffff, .B = 0x1, .op = 0b000, .O = 0x0, .overflow = true, .zero = true, }); test_op(valu, &add_t, { .name = "0xffffffff+0x2", .A = 0xffffffff, .B = 0x2, .op = 0b000, .O = 0x1, .overflow = true, .zero = false, }); test_op(valu, &add_t, { .name = "0x0+0x0", .A = 0x0, .B = 0x0, .op = 0b000, .O = 0x0, .overflow = false, .zero = true, }); } { Tester sub_t(&alu_t, "sub", true); test_op(valu, &sub_t, { .name = "0x2137-0x0420", .A = 0x2137, .B = 0x0420, .op = 0b001, .O = 0x1d17, .overflow = false, }); test_op(valu, &sub_t, { .name = "0x0-0x1", .A = 0x0, .B = 0x1, .op = 0b001, .O = 0xffffffff, .overflow = true, }); test_op(valu, &sub_t, { .name = "0x100-0x0200", .A = 0x100, .B = 0x200, .op = 0b001, .O = 0xffffff00, .overflow = true, }); test_op(valu, &sub_t, { .name = "0x21-0x9", .A = 0x21, .B = 0x9, .op = 0b001, .O = 0x18, .overflow = false, .zero = false, }); test_op(valu, &sub_t, { .name = "0x20-0x20", .A = 0x20, .B = 0x20, .op = 0b001, .O = 0x0, .overflow = false, .zero = true, }); } { Tester bitwise_t(&alu_t, "bitwise", true); // 0x3 = 0b0011, 0x5 = 0b0101 test_op(valu, &bitwise_t, { .name = "0x3&0x5", .A = 0x3, .B = 0x5, .op = 0b100, .O = 0x1, .zero = false, }); test_op(valu, &bitwise_t, { .name = "0x3|0x5", .A = 0x3, .B = 0x5, .op = 0b101, .O = 0x7, .zero = false, }); test_op(valu, &bitwise_t, { .name = "0x3^0x5", .A = 0x3, .B = 0x5, .op = 0b110, .O = 0x6, .zero = false, }); test_op(valu, &bitwise_t, { .name = "~0xa5a5a5a5", .A = 0xa5a5a5a5, .B = 0x0, .op = 0b111, .O = 0x5a5a5a5a, .zero = false, }); test_op(valu, &bitwise_t, { .name = "~0xffffffff", .A = 0xffffffff, .B = 0x0, .op = 0b111, .O = 0x0, .zero = true, }); } { Tester auto_t(&alu_t, "auto", true); std::default_random_engine eng; std::uniform_int_distribution op_gen(0, 5); std::uniform_int_distribution gen(0, 0xffffffff); for (int i = 0; i < 100; i++) { uint32_t A = gen(eng); uint32_t B = gen(eng); std::string name; switch (op_gen(eng)) { case 0: // Add name.append(fmt_hex(A)); name.append("+"); name.append(fmt_hex(B)); test_op(valu, &auto_t, { .name = name, .A = A, .B = B, .op = 0b000, .O = A + B, .overflow = (A + B < A), .zero = (A + B == 0), }); break; case 1: // Subtract name.append(fmt_hex(A)); name.append("-"); name.append(fmt_hex(B)); test_op(valu, &auto_t, { .name = name, .A = A, .B = B, .op = 0b001, .O = A - B, .overflow = (B > A), .zero = (A == B), }); break; case 2: // Bitwise AND name.append(fmt_hex(A)); name.append("&"); name.append(fmt_hex(B)); test_op(valu, &auto_t, { .name = name, .A = A, .B = B, .op = 0b100, .O = A & B, .overflow = 0, .zero = ((A & B) == 0), }); break; case 3: // Bitwise OR name.append(fmt_hex(A)); name.append("|"); name.append(fmt_hex(B)); test_op(valu, &auto_t, { .name = name, .A = A, .B = B, .op = 0b101, .O = A | B, .overflow = 0, .zero = ((A | B) == 0), }); break; case 4: // Bitwise XOR name.append(fmt_hex(A)); name.append("^"); name.append(fmt_hex(B)); test_op(valu, &auto_t, { .name = name, .A = A, .B = B, .op = 0b110, .O = A ^ B, .overflow = 0, .zero = ((A ^ B) == 0), }); break; case 5: // Bitwise NOT name.append("~"); name.append(fmt_hex(A)); test_op(valu, &auto_t, { .name = name, .A = A, .B = B, .op = 0b111, .O = ~A, .overflow = 0, .zero = (A == 0xffffffff), }); break; } } } }