Pretty up the testing framework, split into files

This commit is contained in:
xenia 2024-01-12 22:32:45 +01:00
parent 82c6312f9d
commit 1f53f7e90f
4 changed files with 234 additions and 93 deletions

View File

@ -11,7 +11,8 @@
verilator = import ./verilator.nix pkgs ; 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: '' verilate-src = cmd: ''
cp -r ${./verilog-src} ./verilog-src cp -r ${./verilog-src} ./verilog-src
cp -r ${./simulation} ./simulation cp -r ${./simulation} ./simulation
@ -25,7 +26,7 @@
''; '';
alu-sim = pkgs.runCommandCC "alu-sim" {} '' 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" mv obj_dir "$out"
mkdir "$out/bin" && cp "$out/Valu" "$out/bin/alu-sim" mkdir "$out/bin" && cp "$out/Valu" "$out/bin/alu-sim"
''; '';

View File

@ -1,112 +1,95 @@
#include "Valu.h" #include "Valu.h"
#include "verilated.h" #include "verilated.h"
#include <iostream> #include "tester.hpp"
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<typename T, typename U>
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);
}
}
};
int main(int argc, char **argv) { int main(int argc, char **argv) {
VerilatedContext *vctx = new VerilatedContext; VerilatedContext *vctx = new VerilatedContext;
Valu *valu = new Valu(vctx); 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; Tester add_case(&add_t, "0x2137+0x1234");
valu->B = 0x1234; valu->op = 0b000;
valu->eval(); valu->A = 0x2137;
add_t.assert_eq("0x2137 + 0x1234 == 0x336b", valu->O, 0x336b); valu->B = 0x1234;
add_t.assert_eq("0x2137 + 0x1234 no overflow", valu->Fflow, 0x0); valu->eval();
add_t.assert_eq("0x2137 + 0x1234 no Fzero", valu->Fzero, 0x0); 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; Tester add_case(&add_t, "0x9+0x10");
valu->B = 0x10; valu->op = 0b000;
valu->eval(); valu->A = 0x9;
add_t.assert_eq("0x9 + 0x10 == 0x19", valu->O, 0x19); valu->B = 0x10;
add_t.assert_eq("0x9 + 0x10 no overflow", valu->Fflow, 0x0); valu->eval();
add_t.assert_eq("0x9 + 0x10 no Fzero", valu->Fzero, 0x0); 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; Tester add_case(&add_t, "0xffffffff+0x1");
valu->B = 0x1; valu->op = 0b000;
valu->eval(); valu->A = 0xffffffff;
add_t.assert_eq("0xffffffff + 0x1 == 0x0", valu->O, 0x0); valu->B = 0x1;
add_t.assert_eq("0xffffffff + 0x1 has overflow", valu->Fflow, 0x1); valu->eval();
add_t.assert_eq("0xffffffff + 0x1 has Fzero", valu->Fzero, 0x1); 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; Tester sub_case(&sub_t, "0x2137-0x0420");
valu->B = 0x0420; valu->op = 0b001;
valu->eval(); valu->A = 0x2137;
add_t.assert_eq("0x2137 - 0x0420 == 0x1d17", valu->O, 0x1d17); valu->B = 0x0420;
add_t.assert_eq("0x2137 - 0x1234 no underflow", valu->Fflow, 0x0); valu->eval();
add_t.assert_eq("0x2137 - 0x1234 no Fzero", valu->Fzero, 0x0); 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; Tester sub_case(&sub_t, "0x100-0x200");
valu->B = 0x200; valu->op = 0b001;
valu->eval(); valu->A = 0x100;
add_t.assert_eq("0x100 - 0x200 == 0xffffff00", valu->O, 0xffffff00); valu->B = 0x200;
add_t.assert_eq("0x100 - 0x200 has underflow", valu->Fflow, 0x1); valu->eval();
add_t.assert_eq("0x100 - 0x200 no Fzero", valu->Fzero, 0x0); 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; Tester sub_case(&sub_t, "0x0-0x1");
valu->B = 0x1; valu->op = 0b001;
valu->eval(); valu->A = 0x0;
add_t.assert_eq("0x0 - 0x1 == 0xffffffff", valu->O, 0xffffffff); valu->B = 0x1;
add_t.assert_eq("0x0 - 0x1 has underflow", valu->Fflow, 0x1); valu->eval();
add_t.assert_eq("0x0 - 0x1 no Fzero", valu->Fzero, 0x0); 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; Tester sub_case(&sub_t, "0x20-0x20");
valu->B = 0x20; valu->op = 0b001;
valu->eval(); valu->A = 0x20;
add_t.assert_eq("0x20 - 0x20 == 0x0", valu->O, 0x0); valu->B = 0x20;
add_t.assert_eq("0x20 - 0x20 no underflow", valu->Fflow, 0x0); valu->eval();
add_t.assert_eq("0x20 - 0x20 has Fzero", valu->Fzero, 0x1); 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);
}
} }

120
simulation/tester.cpp Normal file
View File

@ -0,0 +1,120 @@
#include <iostream>
#include <optional>
#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<Tester*> at = this; at.has_value(); at = (*at)->parent) {
depth++;
}
return depth;
}
std::string Tester::full_name() {
std::string full_name(name);
std::optional<Tester*> 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<Tester*> 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<Tester*> 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);
}
}

37
simulation/tester.hpp Normal file
View File

@ -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<typename T, typename U>
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<Tester*> parent;
std::string name;
int succeeded, failed;
int depth();
std::string full_name();
std::string prefix();
};
#endif // tester_hpp_INCLUDED