From 0c217344475fb10727a32c31d3da535476f054f3 Mon Sep 17 00:00:00 2001 From: Riley King-Saunders Date: Thu, 3 Sep 2026 15:25:07 +1000 Subject: [PATCH] Created general project structure. Only seven segment, logic unit and adder/subtractor modules and tests implemented --- .gitignore | 4 + modules/alu.v | 20 +++++ modules/asu.v | 16 ++++ modules/instruction_classifier.v | 25 ++++++ modules/lu.v | 12 +++ modules/seven_seg.v | 23 ++++++ modules/shifter.v | 13 ++++ run.bat | 4 + test.txt | 0 tests/alu_tb.v | 6 ++ tests/asu_tb.v | 124 ++++++++++++++++++++++++++++++ tests/instruction_classifier_tb.v | 1 + tests/lu_tb.v | 59 ++++++++++++++ tests/seven_seg_tb.v | 68 ++++++++++++++++ tests/shifter_tb.v | 5 ++ tests/tests.v | 3 + 16 files changed, 383 insertions(+) create mode 100644 .gitignore create mode 100644 modules/alu.v create mode 100644 modules/asu.v create mode 100644 modules/instruction_classifier.v create mode 100644 modules/lu.v create mode 100644 modules/seven_seg.v create mode 100644 modules/shifter.v create mode 100644 run.bat delete mode 100644 test.txt create mode 100644 tests/alu_tb.v create mode 100644 tests/asu_tb.v create mode 100644 tests/instruction_classifier_tb.v create mode 100644 tests/lu_tb.v create mode 100644 tests/seven_seg_tb.v create mode 100644 tests/shifter_tb.v create mode 100644 tests/tests.v diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd44550 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Ignore compiled vvp files +*.vvp +# Ignore generated waveform files for iverilog/vvp +*.vcd \ No newline at end of file diff --git a/modules/alu.v b/modules/alu.v new file mode 100644 index 0000000..9f447b0 --- /dev/null +++ b/modules/alu.v @@ -0,0 +1,20 @@ +`include "asu.v" +`include "lu.v" + +// flags[0:0] = Cout +// flags[1:1] = Overflow +module alu #(width=8)(input logic [3:0] op, + input logic [width-1:0] A, input logic [width-1:0] B. + output logic [width-1:0] OUT_1, output logic flags[1:0]); + + logic [width-1:0] asu_out; + logic [1:0] asu_flags; + logic [0:0] asu_op; + asu #(width) adder_unit(asu_op, A, B, asu_out, asu_flags[0], asu_flags[1]); + + logic [width-1:0] lu_out; + lu #(width) logic_unit(op[1:0], A, B, lu_out); + + // Cout = asu_flags[0] + // Overflow = asu_flags[0] ^ asu_flags[1] +endmodule \ No newline at end of file diff --git a/modules/asu.v b/modules/asu.v new file mode 100644 index 0000000..eb4fd6f --- /dev/null +++ b/modules/asu.v @@ -0,0 +1,16 @@ +module asu #(width=8)(input logic [0:0] op, + input logic[width-1:0] A, input logic[width-1:0] B, + output logic[width-1:0] OUT_1, output logic[0:0] COUT, output logic[0:0] OUT_CIN); + + logic [width-1:0] operand; + always_comb begin + // A + (sub) ? B : -B + // Adds an extra bit to all operands to calculate the carry bit. Not optimal for hardware synthesis + operand = (B ^ {(width){op}}); + {COUT, OUT_1} = A + operand + {{(width-1){1'b0}}, op}; + end + + // Need to do this outside of the always_comb block because parameter-based bitslicing/indexing isn't supported? + // Cin for width-2'th bit to later be xor'ed with COUT to determine if a signed overflow occured for the MIPS architecture + assign OUT_CIN = A[width-1] ^ operand[width-1] ^ OUT_1[width-1]; +endmodule diff --git a/modules/instruction_classifier.v b/modules/instruction_classifier.v new file mode 100644 index 0000000..2b4861b --- /dev/null +++ b/modules/instruction_classifier.v @@ -0,0 +1,25 @@ +module instruction_classifier(input logic op[5:0], + output logic[0:0] R, output logic[0:0] B1, output logic[0:0] J, + output logic[0:0] B2, output logic[0:0] I, output logic[0:0] F, + output logic[0:0] M); + + logic [6:0] OUT_1; + casez (op) + 6'b1?????: OUT_1 = 7'b1000000; + 6'b01????: OUT_1 = 7'b0100000; + 6'b001???: OUT_1 = 7'b0010000; + 6'b0001??: OUT_1 = 7'b0001000; + 6'b00001?: OUT_1 = 7'b0000100; + 6'b000001: OUT_1 = 7'b0000010; + 6'b000000: OUT_1 = 7'b0000001; + endcase + + assign R = OUT_1[6]; + assign B1 = OUT_1[5]; + assign J = OUT_1[4]; + assign B2 = OUT_1[3]; + assign I = OUT_1[2]; + assign F = OUT_1[1]; + assign M = OUT_1[0]; + +endmodule \ No newline at end of file diff --git a/modules/lu.v b/modules/lu.v new file mode 100644 index 0000000..145eda2 --- /dev/null +++ b/modules/lu.v @@ -0,0 +1,12 @@ +module lu #(width=8) ( + input logic [1:0] op, + input logic [width-1:0] A, input logic [width-1:0] B, + output logic [width-1:0] OUT_1); + + always_comb begin + if (op == 2'b00) OUT_1 = A & B; + else if (op == 2'b01) OUT_1 = A | B; + else if (op == 2'b10) OUT_1 = A ^ B; + else if (op == 2'b10) OUT_1 = ~(A | B); + end +endmodule \ No newline at end of file diff --git a/modules/seven_seg.v b/modules/seven_seg.v new file mode 100644 index 0000000..e0b27a8 --- /dev/null +++ b/modules/seven_seg.v @@ -0,0 +1,23 @@ +module seven_seg(input logic [3:0] num, input logic [0:0] rbi_inv, output logic [7:0] segments); + always_comb begin + case (num) + 4'b0000: segments = (rbi_inv) ? 8'b11111101 : 8'b00000000; + 4'b0001: segments = 8'b01100001; + 4'b0010: segments = 8'b11011011; + 4'b0011: segments = 8'b11110011; + 4'b0100: segments = 8'b01100111; + 4'b0101: segments = 8'b10110111; + 4'b0110: segments = 8'b10111111; + 4'b0111: segments = 8'b11100001; + 4'b1000: segments = 8'b11111111; + 4'b1001: segments = 8'b11100111; + 4'b1010: segments = 8'b11101111; + 4'b1011: segments = 8'b00111111; + 4'b1100: segments = 8'b10011101; + 4'b1101: segments = 8'b01111011; + 4'b1110: segments = 8'b10011111; + 4'b1111: segments = 8'b10001111; + default: segments = 8'bxxxxxxxx; + endcase + end +endmodule diff --git a/modules/shifter.v b/modules/shifter.v new file mode 100644 index 0000000..e55e621 --- /dev/null +++ b/modules/shifter.v @@ -0,0 +1,13 @@ +module shifter #(width=8)(input logic op[1:0], + input logic[width-1:0] A, input logic[width-1:0] B, + output logic[width-1:0] OUT_1); + + always_comb begin + case (op) + 00: OUT_1 = A << B; + 01: OUT_1 = A >> B; + 10: OUT_1 = $signed(A) >>> B; + default:; + endcase + end +endmodule \ No newline at end of file diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..c7f9c83 --- /dev/null +++ b/run.bat @@ -0,0 +1,4 @@ +@echo off +cls +iverilog -g2012 -o testbench.vvp -I"./modules" -I"./tests" tests/tests.v +vvp testbench.vvp diff --git a/test.txt b/test.txt deleted file mode 100644 index e69de29..0000000 diff --git a/tests/alu_tb.v b/tests/alu_tb.v new file mode 100644 index 0000000..1631e29 --- /dev/null +++ b/tests/alu_tb.v @@ -0,0 +1,6 @@ +// TODO: Both the ALU and this test bench is incomplete +`#include "alu.v" + +module alu_tb #(width=8)(); + +endmodule \ No newline at end of file diff --git a/tests/asu_tb.v b/tests/asu_tb.v new file mode 100644 index 0000000..61e5a2f --- /dev/null +++ b/tests/asu_tb.v @@ -0,0 +1,124 @@ +`include "asu.v" + +module asu_tb #(width=8)(); + logic [0:0] op; + logic [width-1:0] A; + logic [width-1:0] B; + logic [width-1:0] C; + logic [0:0] Cout; + logic [0:0] Cin; + logic [width-1:0] C_exp; + logic [0:0] Cout_exp; + logic [0:0] Cin_exp; // TODO + + logic [0:0] Signed_overflow; + + asu #(width) dut(op, A, B, C, Cout, Cin); + + integer error_count; + initial begin + error_count = 0; + $display("Beginning ASU tests"); + + // Test basic addition of 0+1 = 1 + op = 1'b0; + A = {width{1'b0}}; + B = { {(width-1){1'b0}}, 1'b1}; + C_exp = { {(width-1){1'b0}}, 1'b1}; + Cout_exp = 1'b0; + Cin_exp = 1'b0; + #1; + if (C != C_exp || Cout != Cout_exp || Cin != Cin_exp || (Cin ^ Cout) != (Cin_exp ^ Cout_exp)) begin + $display("\t\x1b[38;5;9mOp 0b%b(0b%b, 0b%b) produced 0b%b%b_%b but 0b%b%b_%b was expected\x1b[m", op, A, B, Cin, Cout, C, Cin_exp, Cout_exp, C_exp); + error_count = error_count + 1; + end + + // Test underflow from 0-1 = UINTMAX + op = 1'b1; + C_exp = {(width){1'b1}}; + Cout_exp = 1'b0; + Cin_exp = 1'b0; + #1; + if (C != C_exp || Cout != Cout_exp || Cin != Cin_exp || (Cin ^ Cout) != (Cin_exp ^ Cout_exp)) begin + $display("\t\x1b[38;5;9mOp 0b%b(0b%b, 0b%b) produced 0b%b%b_%b but 0b%b%b_%b was expected\x1b[m", op, A, B, Cin, Cout, C, Cin_exp, Cout_exp, C_exp); + error_count = error_count + 1; + end + + // Test carry addition of 1+1 = 2 + op = 1'b0; + A = { {(width-1){1'b0}}, 1'b1}; + B = { {(width-1){1'b0}}, 1'b1}; + C_exp = { {(width-2){1'b0}}, 2'b10}; + Cout_exp = 1'b0; + Cin_exp = 1'b0; + #1; + if (C != C_exp || Cout != Cout_exp || Cin != Cin_exp || (Cin ^ Cout) != (Cin_exp ^ Cout_exp)) begin + $display("\t\x1b[38;5;9mOp 0b%b(0b%b, 0b%b) produced 0b%b%b_%b but 0b%b%b_%b was expected\x1b[m", op, A, B, Cin, Cout, C, Cin_exp, Cout_exp, C_exp); + error_count = error_count + 1; + end + + // Test overflow from UINTMAX+1 = 0 + A = {(width){1'b1}}; // UINTMAX + B = { {(width-1){1'b0}}, 1'b1}; // 1 + C_exp = {(width){1'b0}}; + Cout_exp = 1'b1; + Cin_exp = 1'b1; + #1; + if (C != C_exp || Cout != Cout_exp || Cin != Cin_exp || (Cin ^ Cout) != (Cin_exp ^ Cout_exp)) begin + $display("\t\x1b[38;5;9mOp 0b%b(0b%b, 0b%b) produced 0b%b%b_%b but 0b%b%b_%b was expected\x1b[m", op, A, B, Cin, Cout, C, Cin_exp, Cout_exp, C_exp); + error_count = error_count + 1; + end + + // Tests INTMAX+1 + op = 1'b0; + A = {1'b0, {(width-1){1'b1}}}; // INTMAX + B = {{(width-1){1'b0}}, 1'b1}; // 1 + C_exp = {1'b1, {(width-1){1'b0}}}; // -INTMAX + Cin_exp = 1'b1; + Cout_exp = 1'b0; + #1; + if (C != C_exp || Cout != Cout_exp || Cin != Cin_exp || (Cin ^ Cout) != (Cin_exp ^ Cout_exp)) begin + $display("\t\x1b[38;5;9mOp 0b%b(0b%b, 0b%b) produced 0b%b%b_%b but 0b%b%b_%b was expected\x1b[m", op, A, B, Cin, Cout, C, Cin_exp, Cout_exp, C_exp); + error_count = error_count + 1; + end + + op = 1'b0; + A = {1'b1, {(width-1){1'b0}}}; // -INTMAX + B = {1'b1, {(width-1){1'b0}}}; // -INTMAX + C_exp = {width{1'b0}}; // 0 + Cin_exp = 1'b0; + Cout_exp = 1'b1; + #1; + if (C != C_exp || Cout != Cout_exp || Cin != Cin_exp || (Cin ^ Cout) != (Cin_exp ^ Cout_exp)) begin + $display("\t\x1b[38;5;9mOp 0b%b(0b%b, 0b%b) produced 0b%b%b_%b but 0b%b%b_%b was expected\x1b[m", op, A, B, Cin, Cout, C, Cin_exp, Cout_exp, C_exp); + error_count = error_count + 1; + end + + op = 1'b1; + A = {1'b0, {(width-1){1'b1}}}; // INTMAX + B = {1'b1, {(width-1){1'b0}}}; // -INTMAX + C_exp = {width{1'b1}}; // -1 + Cin_exp = 1'b1; + Cout_exp = 1'b0; + #1; + if (C != C_exp || Cout != Cout_exp || Cin != Cin_exp || (Cin ^ Cout) != (Cin_exp ^ Cout_exp)) begin + $display("\t\x1b[38;5;9mOp 0b%b(0b%b, 0b%b) produced 0b%b%b_%b but 0b%b%b_%b was expected\x1b[m", op, A, B, Cin, Cout, C, Cin_exp, Cout_exp, C_exp); + error_count = error_count + 1; + end + + op = 1'b1; + A = {1'b1, {(width-1){1'b0}}}; // -INTMAX + B = {{(width-1){1'b0}}, 1'b1}; // 1 + C_exp = {1'b0, {(width-1){1'b1}}}; // INTMAX + Cin_exp = 1'b0; + Cout_exp = 1'b1; + #1; + if (C != C_exp || Cout != Cout_exp || Cin != Cin_exp || (Cin ^ Cout) != (Cin_exp ^ Cout_exp)) begin + $display("\t\x1b[38;5;9mOp 0b%b(0b%b, 0b%b) produced 0b%b%b_%b but 0b%b%b_%b was expected\x1b[m", op, A, B, Cin, Cout, C, Cin_exp, Cout_exp, C_exp); + error_count = error_count + 1; + end + + if (error_count == 0) $display("\x1b[38;5;10mAll ASU tests passing\x1b[m"); + else $display("\x1b[38;5;9m%0d ASU test(s) failing\x1b[m", error_count); + end +endmodule \ No newline at end of file diff --git a/tests/instruction_classifier_tb.v b/tests/instruction_classifier_tb.v new file mode 100644 index 0000000..1575d2a --- /dev/null +++ b/tests/instruction_classifier_tb.v @@ -0,0 +1 @@ +`#include "instruction_classifier.v" diff --git a/tests/lu_tb.v b/tests/lu_tb.v new file mode 100644 index 0000000..d0f478d --- /dev/null +++ b/tests/lu_tb.v @@ -0,0 +1,59 @@ +`include "lu.v" + +module lu_tb #(W=8)(); + logic [1:0] op; + logic [W-1:0] A; + logic [W-1:0] B; + logic [W-1:0] C; + logic [W-1:0] C_exp[16]; + + lu #(W) dut(); + + integer error_count; + initial begin + // AND + C_exp[0] = {W{1'b0}}; + C_exp[1] = {W{1'b0}}; + C_exp[2] = {W{1'b0}}; + C_exp[3] = {W{1'b0}}; + // OR + C_exp[4] = {W{1'b0}}; + C_exp[5] = {W{1'b1}}; + C_exp[6] = {W{1'b1}}; + C_exp[7] = {W{1'b1}}; + // XOR + C_exp[8] = {W{1'b0}}; + C_exp[9] = {W{1'b1}}; + C_exp[10] = {W{1'b1}}; + C_exp[11] = {W{1'b0}}; + // NOR + C_exp[12] = {W{1'b1}}; + C_exp[13] = {W{1'b0}}; + C_exp[14] = {W{1'b0}}; + C_exp[15] = {W{1'b0}}; + + error_count = 0; + op = 0; + A = 0; + B = 0; + $display("Beginning LU tests"); + for (integer N = 0; N < 4; ++N) begin + for (integer M = 0; M < 2; ++M) begin + for (integer K = 0; K < 2; ++K) begin + #1; + if (C != C_exp[N*2*2 + M*2 + K]) begin + $display("\t\x1b[38;5;9mOp 0b%b(0b%b, 0b%b) produced 0b%b but 0b%b was expected\x1b[m", op, A, B, C, C_exp[N*2*2 + M*2 + K]); + error_count = error_count + 1; + end + B = 1; + end + A = 1; + end + A = 0; + B = 0; + op = op + 1; + end + if (error_count == 0) $display("\x1b[38;5;10mAll LU tests passing\x1b[m"); + else $display("\x1b[38;5;9m%0d LU test(s) failing\x1b[m", error_count); + end +endmodule \ No newline at end of file diff --git a/tests/seven_seg_tb.v b/tests/seven_seg_tb.v new file mode 100644 index 0000000..f165509 --- /dev/null +++ b/tests/seven_seg_tb.v @@ -0,0 +1,68 @@ +`include "seven_seg.v" + +module seven_seg_tb(); + logic [3:0] in_num; + logic [0:0] in_rbi_inv; + logic [7:0] out_segments; + logic [7:0] true_segments[17]; + + seven_seg dut(in_num, in_rbi_inv, out_segments); + + integer N; + integer i; + integer error_count; + initial begin + + // Initialise truth table + true_segments[0 ] = 8'b11111101; + true_segments[1 ] = 8'b01100001; + true_segments[2 ] = 8'b11011011; + true_segments[3 ] = 8'b11110011; + true_segments[4 ] = 8'b01100111; + true_segments[5 ] = 8'b10110111; + true_segments[6 ] = 8'b10111111; + true_segments[7 ] = 8'b11100001; + true_segments[8 ] = 8'b11111111; + true_segments[9 ] = 8'b11100111; + true_segments[10] = 8'b11101111; + true_segments[11] = 8'b00111111; + true_segments[12] = 8'b10011101; + true_segments[13] = 8'b01111011; + true_segments[14] = 8'b10011111; + true_segments[15] = 8'b10001111; + true_segments[16] = 8'b00000000; + + $display("Beginning seven-segment tests"); + + in_num = 4'd0; + in_rbi_inv = 1'b0; + error_count = 0; + #1; + if (out_segments !== true_segments[16]) begin + $display("\t\x1b[38;5;9mNumber 0b%b failed to decode to 0b%b, producing 0b%b\x1b[m", in_num, true_segments[16], out_segments); + error_count = error_count + 1; + end + for (N=1; N < 15; ++N) begin + in_num = in_num + 1; + #1; + if (out_segments !== true_segments[N]) begin + $display("\t\x1b[38;5;9mNumber 0b%b failed to decode to 0b%b, producing 0b%b\x1b[m", in_num, true_segments[N], out_segments); + error_count = error_count + 1; + end + end + + in_num = 4'd0; + in_rbi_inv = 1'b1; + for (N=0; N < 15; ++N) begin + #1; + if (out_segments !== true_segments[N]) begin + $display("\t\x1b[38;5;9mNumber 0b%b failed to decode to 0b%b, producing 0b%b\x1b[m", in_num, true_segments[N], out_segments); + error_count = error_count + 1; + end + in_num = in_num + 1; + end + + if (error_count == 0) $display("\x1b[38;5;10mAll seven-segment tests passing\x1b[m"); + else $display("\x1b[38;5;9m%0d seven-segment test(s) failing\x1b[m", error_count); + end +endmodule \ No newline at end of file diff --git a/tests/shifter_tb.v b/tests/shifter_tb.v new file mode 100644 index 0000000..ae75c7b --- /dev/null +++ b/tests/shifter_tb.v @@ -0,0 +1,5 @@ +`#include "shifter.v" + +module shifter_tb #(width=8)(); + +endmodule \ No newline at end of file diff --git a/tests/tests.v b/tests/tests.v new file mode 100644 index 0000000..033b5fd --- /dev/null +++ b/tests/tests.v @@ -0,0 +1,3 @@ +`include "tests/seven_seg_tb.v" +`include "tests/lu_tb.v" +`include "tests/asu_tb.v" \ No newline at end of file