Compare commits
3 Commits
0adb95447e
...
1f53f7e90f
Author | SHA1 | Date | |
---|---|---|---|
1f53f7e90f | |||
82c6312f9d | |||
49bad232bc |
|
@ -11,7 +11,8 @@
|
||||||
|
|
||||||
verilator = import ./verilator.nix pkgs ;
|
verilator = import ./verilator.nix pkgs ;
|
||||||
|
|
||||||
vflags = "-Wpedantic -Wwarn-lint -Wwarn-style";
|
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"
|
||||||
'';
|
'';
|
||||||
|
|
|
@ -1,18 +1,96 @@
|
||||||
#include "Valu.h"
|
#include "Valu.h"
|
||||||
#include "verilated.h"
|
#include "verilated.h"
|
||||||
#include <iostream>
|
#include "tester.hpp"
|
||||||
|
|
||||||
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);
|
||||||
valu->A = 0x2137;
|
|
||||||
valu->B = 0x1234;
|
|
||||||
|
|
||||||
valu->op = 0b000;
|
Tester alu_t("alu");
|
||||||
valu->eval();
|
{
|
||||||
printf("A = %04x + B = %04x = O = %04x\n", valu->A, valu->B, valu->O);
|
Tester add_t(&alu_t, "add");
|
||||||
valu->op = 0b001;
|
|
||||||
valu->eval();
|
{
|
||||||
printf("A = %04x - B = %04x = O = %04x\n", valu->A, valu->B, valu->O);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
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 sub_t(&alu_t, "sub");
|
||||||
|
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
120
simulation/tester.cpp
Normal file
120
simulation/tester.cpp
Normal 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
37
simulation/tester.hpp
Normal 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
|
|
@ -15,14 +15,17 @@ module alu(
|
||||||
input [31:0] B,
|
input [31:0] B,
|
||||||
input [2:0] op,
|
input [2:0] op,
|
||||||
output [31:0] O,
|
output [31:0] O,
|
||||||
output Cout,
|
output Fflow,
|
||||||
output Fzero
|
output Fzero
|
||||||
);
|
);
|
||||||
|
|
||||||
wire subtraction = op == 3'b001;
|
wire subtraction = op == 3'b001;
|
||||||
wire [31:0] adder_B = subtraction ? ~B : B;
|
wire [31:0] adder_B = subtraction ? ~B : B;
|
||||||
|
wire adder_cout;
|
||||||
|
|
||||||
carry_select_adder a(A, adder_B, subtraction, O, Cout);
|
carry_select_adder a(A, adder_B, subtraction, O, adder_cout);
|
||||||
assign Fzero = & O;
|
|
||||||
|
assign Fflow = subtraction ? ~adder_cout : adder_cout;
|
||||||
|
assign Fzero = ~ (| O);
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
Loading…
Reference in New Issue
Block a user