diff --git a/flake.nix b/flake.nix index 68f0318..b14af4b 100644 --- a/flake.nix +++ b/flake.nix @@ -11,7 +11,8 @@ verilator = import ./verilator.nix pkgs ; - vflags = "-Wpedantic -Wwarn-lint -Wwarn-style -CFLAGS -Wpedantic"; + vflags = ''-Wpedantic -Wwarn-lint -Wwarn-style -CFLAGS "-Wpedantic -std=c++20"''; + verilate-src = cmd: '' cp -r ${./verilog-src} ./verilog-src cp -r ${./simulation} ./simulation @@ -25,7 +26,7 @@ ''; alu-sim = pkgs.runCommandCC "alu-sim" {} '' - ${verilate-src "--cc --build --exe ./simulation/test_alu.cpp"} + ${verilate-src "--cc --build --exe ./simulation/tester.cpp ./simulation/test_alu.cpp"} mv obj_dir "$out" mkdir "$out/bin" && cp "$out/Valu" "$out/bin/alu-sim" ''; diff --git a/simulation/test_alu.cpp b/simulation/test_alu.cpp index 91b6f0f..c0601e4 100644 --- a/simulation/test_alu.cpp +++ b/simulation/test_alu.cpp @@ -1,112 +1,95 @@ #include "Valu.h" #include "verilated.h" -#include - -const std::string ANSI_RESET = "\033[0m"; -const std::string ANSI_GREEN = "\033[38;5;2m"; -const std::string ANSI_RED = "\033[38;5;1m"; -const std::string ANSI_YELLOW = "\033[38;5;11m"; - -class Tester { - private: - int succeeded, failed; - std::string name; - public: - Tester(std::string test_name) : name(test_name), succeeded(0), failed(0) { - printf("[ %s ] - %sstarting%s\n", name.c_str(), ANSI_YELLOW.c_str(), ANSI_RESET.c_str()); - } - - template - void assert_eq(std::string test_name, T got, U expected) { - printf("[ %s ] - %30s - ", name.c_str(), test_name.c_str()); - if (got == expected) { - printf("%ssuccess%s\n", ANSI_GREEN.c_str(), ANSI_RESET.c_str()); - succeeded++; - } else { - printf("%sfailed%s - got %08x, expected %08x\n", ANSI_RED.c_str(), ANSI_RESET.c_str(), got, expected); - failed++; - } - } - - ~Tester() { finish(); } - - void finish() { - if (failed == 0) { - printf("[ %s ] - %sall succeeded%s, out of %d total\n", name.c_str(), ANSI_GREEN.c_str(), ANSI_RESET.c_str(), succeeded + failed); - } else { - printf("[ %s ] - %s%d failed, %d succeeded%s, out of %d total\n", name.c_str(), ANSI_RED.c_str(), failed, succeeded, ANSI_RESET.c_str(), succeeded + failed); - } - } -}; +#include "tester.hpp" int main(int argc, char **argv) { VerilatedContext *vctx = new VerilatedContext; - - Valu *valu = new Valu(vctx); + Tester alu_t("alu"); { - Tester add_t("add"); + Tester add_t(&alu_t, "add"); - valu->op = 0b000; - valu->A = 0x2137; - valu->B = 0x1234; - valu->eval(); - add_t.assert_eq("0x2137 + 0x1234 == 0x336b", valu->O, 0x336b); - add_t.assert_eq("0x2137 + 0x1234 no overflow", valu->Fflow, 0x0); - add_t.assert_eq("0x2137 + 0x1234 no Fzero", valu->Fzero, 0x0); + { + Tester add_case(&add_t, "0x2137+0x1234"); + valu->op = 0b000; + valu->A = 0x2137; + valu->B = 0x1234; + valu->eval(); + add_case.assert_eq("O == 0x336b", valu->O, 0x336b); + add_case.assert_eq("no overflow", valu->Fflow, 0x0); + add_case.assert_eq("no Fzero", valu->Fzero, 0x0); + } - valu->op = 0b000; - valu->A = 0x9; - valu->B = 0x10; - valu->eval(); - add_t.assert_eq("0x9 + 0x10 == 0x19", valu->O, 0x19); - add_t.assert_eq("0x9 + 0x10 no overflow", valu->Fflow, 0x0); - add_t.assert_eq("0x9 + 0x10 no Fzero", valu->Fzero, 0x0); + { + Tester add_case(&add_t, "0x9+0x10"); + valu->op = 0b000; + valu->A = 0x9; + valu->B = 0x10; + valu->eval(); + add_case.assert_eq("O == 0x19", valu->O, 0x19); + add_case.assert_eq("no overflow", valu->Fflow, 0x0); + add_case.assert_eq("no Fzero", valu->Fzero, 0x0); + } - valu->op = 0b000; - valu->A = 0xffffffff; - valu->B = 0x1; - valu->eval(); - add_t.assert_eq("0xffffffff + 0x1 == 0x0", valu->O, 0x0); - add_t.assert_eq("0xffffffff + 0x1 has overflow", valu->Fflow, 0x1); - add_t.assert_eq("0xffffffff + 0x1 has Fzero", valu->Fzero, 0x1); + { + Tester add_case(&add_t, "0xffffffff+0x1"); + valu->op = 0b000; + valu->A = 0xffffffff; + valu->B = 0x1; + valu->eval(); + add_case.assert_eq("O == 0x0", valu->O, 0x0); + add_case.assert_eq("has overflow", valu->Fflow, 0x1); + add_case.assert_eq("has Fzero", valu->Fzero, 0x1); + } } { - Tester add_t("sub"); + Tester sub_t(&alu_t, "sub"); - valu->op = 0b001; - valu->A = 0x2137; - valu->B = 0x0420; - valu->eval(); - add_t.assert_eq("0x2137 - 0x0420 == 0x1d17", valu->O, 0x1d17); - add_t.assert_eq("0x2137 - 0x1234 no underflow", valu->Fflow, 0x0); - add_t.assert_eq("0x2137 - 0x1234 no Fzero", valu->Fzero, 0x0); + { + Tester sub_case(&sub_t, "0x2137-0x0420"); + valu->op = 0b001; + valu->A = 0x2137; + valu->B = 0x0420; + valu->eval(); + sub_case.assert_eq("O == 0x1d17", valu->O, 0x1d17); + sub_case.assert_eq("no underflow", valu->Fflow, 0x0); + sub_case.assert_eq("no Fzero", valu->Fzero, 0x0); + } - valu->op = 0b001; - valu->A = 0x100; - valu->B = 0x200; - valu->eval(); - add_t.assert_eq("0x100 - 0x200 == 0xffffff00", valu->O, 0xffffff00); - add_t.assert_eq("0x100 - 0x200 has underflow", valu->Fflow, 0x1); - add_t.assert_eq("0x100 - 0x200 no Fzero", valu->Fzero, 0x0); + { + Tester sub_case(&sub_t, "0x100-0x200"); + valu->op = 0b001; + valu->A = 0x100; + valu->B = 0x200; + valu->eval(); + sub_case.assert_eq("O == 0xffffff00", valu->O, 0xffffff00); + sub_case.assert_eq("has underflow", valu->Fflow, 0x1); + sub_case.assert_eq("no Fzero", valu->Fzero, 0x0); + } - valu->op = 0b001; - valu->A = 0x0; - valu->B = 0x1; - valu->eval(); - add_t.assert_eq("0x0 - 0x1 == 0xffffffff", valu->O, 0xffffffff); - add_t.assert_eq("0x0 - 0x1 has underflow", valu->Fflow, 0x1); - add_t.assert_eq("0x0 - 0x1 no Fzero", valu->Fzero, 0x0); + { + Tester sub_case(&sub_t, "0x0-0x1"); + valu->op = 0b001; + valu->A = 0x0; + valu->B = 0x1; + valu->eval(); + sub_case.assert_eq("O == 0xffffffff", valu->O, 0xffffffff); + sub_case.assert_eq("has underflow", valu->Fflow, 0x1); + sub_case.assert_eq("no Fzero", valu->Fzero, 0x0); + } - valu->op = 0b001; - valu->A = 0x20; - valu->B = 0x20; - valu->eval(); - add_t.assert_eq("0x20 - 0x20 == 0x0", valu->O, 0x0); - add_t.assert_eq("0x20 - 0x20 no underflow", valu->Fflow, 0x0); - add_t.assert_eq("0x20 - 0x20 has Fzero", valu->Fzero, 0x1); + { + Tester sub_case(&sub_t, "0x20-0x20"); + valu->op = 0b001; + valu->A = 0x20; + valu->B = 0x20; + valu->eval(); + sub_case.assert_eq("O == 0x0", valu->O, 0x0); + sub_case.assert_eq("no underflow", valu->Fflow, 0x0); + sub_case.assert_eq("has Fzero", valu->Fzero, 0x1); + } } diff --git a/simulation/tester.cpp b/simulation/tester.cpp new file mode 100644 index 0000000..d0c8d61 --- /dev/null +++ b/simulation/tester.cpp @@ -0,0 +1,120 @@ +#include +#include +#include "tester.hpp" + +const std::string ANSI_RESET = "\033[0m"; +const std::string ANSI_DARK = "\033[38;5;8m"; +const std::string ANSI_BLUE = "\033[38;5;6m"; +const std::string ANSI_GREEN = "\033[38;5;2m"; +const std::string ANSI_RED = "\033[38;5;1m"; +const std::string ANSI_YELLOW = "\033[38;5;11m"; + +int Tester::depth() { + int depth = 0; + for (std::optional at = this; at.has_value(); at = (*at)->parent) { + depth++; + } + return depth; +} + +std::string Tester::full_name() { + std::string full_name(name); + std::optional at = parent; + while (at.has_value()) { + full_name.insert(0, " "); + full_name.insert(0, (*at)->name); + at = (*at)->parent; + } + return full_name; +} + +std::string Tester::prefix() { + std::string line = full_name(); + line.insert(0, "[ "); + + line.append(" ]"); + + int wanted_len = 10 * depth(); + if (line.length() == wanted_len) { + line.insert(0, ANSI_BLUE); + line.append(ANSI_RESET); + line.append(" "); + return line; + } else if (line.length() > wanted_len) { + line.append("\n"); + line.append(ANSI_DARK); + for (int i = 0; i < wanted_len; i++) + line.append("—"); + + line.insert(0, ANSI_BLUE); + line.append(ANSI_RESET); + line.append(" "); + return line; + } else { + int extra = wanted_len - line.length(); + line.append(" "); + line.append(ANSI_DARK); + for (int i = 1; i < extra; i++) + line.append("—"); + + line.insert(0, ANSI_BLUE); + line.append(ANSI_RESET); + line.append(" "); + return line; + } +} + +void Tester::intro() { + std::cout << prefix() << "=== " << ANSI_YELLOW << "start" << ANSI_RESET << std::endl; +} + +Tester::Tester(std::string test_name) : + parent(), + name(test_name), + succeeded(0), failed(0) +{ + intro(); +} + +Tester::Tester(Tester* tester_parent, std::string test_name) : + parent(tester_parent), + name(test_name), + succeeded(0), failed(0) +{ + intro(); +} + +void Tester::assert_eq_str(std::string test_name, bool correct, std::string got_s, std::string expected_s) { + std::cout << prefix() << "- " << test_name; + if (correct) { + std::cout << " " << ANSI_GREEN << "succeded" << ANSI_RESET << std::endl; + for (std::optional at = this; at.has_value(); at = (*at)->parent) { + (*at)->succeeded++; + } + } else { + + std::cout << " " << ANSI_RED << "failed - got " << got_s << ", expected " << expected_s << ANSI_RESET << std::endl; + for (std::optional at = this; at.has_value(); at = (*at)->parent) { + (*at)->failed++; + } + } +} + +Tester::~Tester() { + finish(); +} + +void Tester::finish() { + char n_cases_s[10], n_succeeded_s[10], n_failed_s[10]; + snprintf(n_cases_s, sizeof n_cases_s, "%d", succeeded + failed); + snprintf(n_succeeded_s, sizeof n_succeeded_s, "%d", succeeded); + snprintf(n_failed_s, sizeof n_failed_s, "%d", failed); + if (failed == 0) { + // printf("[ %s ] - %sall succeeded%s, out of %d total\n", name.c_str(), ANSI_GREEN.c_str(), ANSI_RESET.c_str(), succeeded + failed); + std::cout << prefix() << "=== " << ANSI_GREEN << "all succeeded" << ANSI_RESET << ", out of " << n_cases_s << " total" << std::endl; + } else { + std::cout << prefix() << "=== " << ANSI_RED << n_failed_s << " failed" << ANSI_RESET << ", " << ANSI_GREEN << n_succeeded_s << " succeeded" << ANSI_RESET << " out of " << n_cases_s << " total" << std::endl; + // printf("[ %s ] - %s%d failed, %d succeeded%s, out of %d total\n", name.c_str(), ANSI_RED.c_str(), failed, succeeded, ANSI_RESET.c_str(), succeeded + failed); + } +} + diff --git a/simulation/tester.hpp b/simulation/tester.hpp new file mode 100644 index 0000000..4e5d0f3 --- /dev/null +++ b/simulation/tester.hpp @@ -0,0 +1,37 @@ +#ifndef tester_hpp_INCLUDED +#define tester_hpp_INCLUDED + +class Tester { + + public: + Tester(std::string test_name); + + Tester(Tester* tester_parent, std::string test_name); + + template + void assert_eq(std::string test_name, T got, U expected) { + char got_s[10], expected_s[10]; + snprintf(got_s, sizeof got_s, "%08x", got); + snprintf(expected_s, sizeof expected_s, "%08x", expected); + + assert_eq_str(test_name, got == expected, got_s, expected_s); + } + void assert_eq_str(std::string test_name, bool correct, std::string got_s, std::string expected_s); + + + ~Tester(); + + void intro(); + void finish(); + + private: + std::optional parent; + std::string name; + int succeeded, failed; + + int depth(); + std::string full_name(); + std::string prefix(); +}; + +#endif // tester_hpp_INCLUDED