`ifndef SEVEN_SEG_H_SV
`define SEVEN_SEG_H_SV

module seven_seg(
input logic [3:0] num,          // Input binary
input logic [0:0] rbi_inv,      // Ripple blanking input
output logic [7:0] segments);   // segent[0] = Ripple blanking out, segment[1] to segment[7] 7 seg LEDs

	always_comb begin 
		case (num) // implement truth table as case statement
			4'b0000: segments = (rbi_inv) ? ~(8'b01111111) : ~(8'b00000000); // zero or blank
			4'b0001: segments = ~(8'b00001101);
			4'b0010: segments = ~(8'b10110111);
			4'b0011: segments = ~(8'b10011111);
			4'b0100: segments = ~(8'b11001101);
			4'b0101: segments = ~(8'b11011011);
			4'b0110: segments = ~(8'b11111011);
			4'b0111: segments = ~(8'b00001111);
			4'b1000: segments = ~(8'b11111111);
			4'b1001: segments = ~(8'b11001111);
			4'b1010: segments = ~(8'b11101111);
			4'b1011: segments = ~(8'b11111001);
			4'b1100: segments = ~(8'b01110011);
			4'b1101: segments = ~(8'b10111101);
			4'b1110: segments = ~(8'b11110011);
			4'b1111: segments = ~(8'b11100011);
			default: segments = ~(8'bxxxxxxxx);
		endcase     
	end
endmodule

module seven_seg_2digit(
input logic [3:0] b,			// left
input logic [3:0] a,			// right
output logic [7:0] seg_b,	// left
output logic [7:0] seg_a); // right
	
	// seg_b
	seven_seg left_digit(
		.num(b),
		.rbi_inv(1'b0), // blanking enabled
		.segments(seg_b)
	);
	
	// seg_a
	seven_seg right_digit(
		.num(a),
		.rbi_inv(1'b1), // blanking disabled
		.segments(seg_a)
	);

endmodule
`endif

// Testbench below

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'b01111111);
        true_segments[1 ] = ~(8'b00001101);
        true_segments[2 ] = ~(8'b10110111);
        true_segments[3 ] = ~(8'b10011111);
        true_segments[4 ] = ~(8'b11001101);
        true_segments[5 ] = ~(8'b11011011);
        true_segments[6 ] = ~(8'b11111011);
        true_segments[7 ] = ~(8'b00001111);
        true_segments[8 ] = ~(8'b11111111);
        true_segments[9 ] = ~(8'b11001111);
        true_segments[10] = ~(8'b11101111);
        true_segments[11] = ~(8'b11111001);
        true_segments[12] = ~(8'b01110011);
        true_segments[13] = ~(8'b10111101);
        true_segments[14] = ~(8'b11110011);
        true_segments[15] = ~(8'b11100011);
        true_segments[16] = ~(8'b00000000); // Special case for RBI
        
        $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


