`timescale 1ns / 1ps

module ControlUnit(
    input wire [3:0] instIn,
    input wire functBit,
    output reg [3:0] aluOut,
    output reg [2:0] FU,
    output reg [1:0] bank,
    output reg addi, mem, dataMemEn, RegEn, halt, link, js, compare0, compare1
        );
    
    always @(instIn, functBit)
    begin
        case(instIn)
        4'b0000: // Halt/NOP
            begin 
            halt <= functBit;
            RegEn <= 1'b1; 
            FU <= 3'b001; // Disable Branching
            addi <= 1'b0;
            dataMemEn <= 1'b0; // Disabled
            aluOut <= 4'b0000;
            mem <= 1'b0;
            link <= 1'b0;
            bank <= 2'b10;
            js <= 1'b0;
            compare0 <= 1'b0;
            compare1 <= 1'b0;
            end
        4'b0001: // Load Byte
            begin
            aluOut <= 4'b0000;
            mem <= 1'b1;
            dataMemEn <= 1'b0; // Disabled
            RegEn <= 1'b0;
            FU <= 3'b001; // Disable Branching
            addi <= 1'b0;
            halt <= 1'b0;
            link <= 1'b0;
            bank <= 2'b10;
            js <= 1'b0;
            compare0 <= 1'b1;
            compare1 <= 1'b1;
            end
        4'b0010: // Store Byte
            begin
            aluOut <= 4'b0000;
            mem <= 1'b0;
            dataMemEn <= 1'b1; // Enabled
            RegEn <= 1'b1;
            FU <= 3'b001; // Disable Branching
            halt <= 1'b0;
            addi <= 1'b0;
            link <= 1'b0;
            bank <= 2'b10;
            js <= 1'b0;
            compare0 <= 1'b1;
            compare1 <= 1'b1;
            end
        4'b0011: // Link
            begin
            halt <= 1'b0;
            RegEn <= 1'b0; 
            FU <= 3'b001; 
            addi <= 1'b0;
            aluOut <= 4'b0000;
            mem <= 1'b0;
            dataMemEn <= 1'b0; // Disabled
            link <= 1'b1;
            bank <= 2'b10;
            js <= 1'b0;
            compare0 <= 1'b0;
            compare1 <= 1'b0;
            end
        4'b0100: // Zero
            begin
            aluOut <= 4'b1011;
            RegEn <= 1'b0;
            FU <= 3'b001; // Disable Branching
            halt <= 1'b0;
            addi <= 1'b0;
            mem <= 1'b0;
            dataMemEn <= 1'b0; // Disabled
            link <= 1'b0;
            bank <= 2'b10;
            js <= 1'b0;
            compare0 <= 1'b0;
            compare1 <= 1'b0;
            end
        4'b0101: // Add/Subtract
            if(functBit == 1) // Subtract
                begin
                aluOut <= 4'b0001;
                RegEn <= 1'b0;
                FU <= 3'b001;
                halt <= 1'b0;
                addi <= 1'b0;
                mem <= 1'b0;
                dataMemEn <= 1'b0; // Disabled
                link <= 1'b0;
                bank <= 2'b10;
                js <= 1'b0;
                compare0 <= 1'b1;
                compare1 <= 1'b1;
                end
            else // Add
                begin
                aluOut <= 4'b0000;
                RegEn <= 1'b0;
                FU <= 3'b001; // Disable Branching
                halt <= 1'b0;
                addi <= 1'b0;
                mem <= 1'b0;
                dataMemEn <= 1'b0; // Disabled
                link <= 1'b0;
                bank <= 2'b10;
                js <= 1'b0;
                compare0 <= 1'b1;
                compare1 <= 1'b1;
                end
        4'b0110: // Add Immediate
            begin
            aluOut <= 4'b1010;
            addi <= 1'b1;
            RegEn <= 1'b0;
            FU <= 3'b001; // Disable Branching
            halt <= 1'b0;
            mem <= 1'b0;
            dataMemEn <= 1'b0; // Disabled
            link <= 1'b0;
            bank <= 2'b10;
            js <= 1'b0;
            compare0 <= 1'b1;
            compare1 <= 1'b0;
            end
        4'b0111: // Set if Less Than
            begin 
            aluOut <= 4'b1001;
            RegEn <= 1'b0;
            FU <= 3'b001; // Disable Branching
            halt <= 1'b0;
            addi <= 1'b0;
            mem <= 1'b0;
            dataMemEn <= 1'b0; // Disabled
            link <= 1'b0;
            bank <= 2'b10;
            js <= 1'b0;
            compare0 <= 1'b1;
            compare1 <= 1'b1;
            end
        4'b1000: // Jump to Register
            begin 
            aluOut <= 4'b0000;
            FU <= 3'b000;
            RegEn <= 1'b1;
            halt <= 1'b0;
            addi <= 1'b0;
            mem <= 1'b0;
            dataMemEn <= 1'b0; // Disabled
            link <= 1'b0;
            bank <= 2'b10;
            js <= 1'b0;
            compare0 <= 1'b1;
            compare1 <= 1'b0;
            end
        4'b1001: // Jump Forward
            begin 
            aluOut <= 4'b0000;
            FU <= 3'b010;
            RegEn <= 1'b1;
            halt <= 1'b0;
            addi <= 1'b0;
            mem <= 1'b0;
            dataMemEn <= 1'b0; // Disabled
            link <= 1'b0;
            bank <= 2'b10;
            js <= 1'b0;
            compare0 <= 1'b0;
            compare1 <= 1'b0;
            end
        4'b1010: // Bank Load/Bank Store
            begin
            halt <= 1'b0;
            RegEn <= !functBit; 
            FU <= 3'b001; // Disable Branching
            addi <= 1'b0;
            aluOut <= 4'b0000;
            dataMemEn <= 1'b0; // Disabled
            mem <= 1'b0;
            link <= 1'b0;
            bank <= {functBit,functBit};
            js <= 1'b0;
            compare0 <= 1'b1;
            compare1 <= 1'b0;
            end
        4'b1011: // Jump Backward
            begin 
            aluOut <= 4'b0000;
            FU <= 3'b010;
            RegEn <= 1'b1;
            halt <= 1'b0;
            addi <= 1'b0;
            mem <= 1'b0;
            dataMemEn <= 1'b0; // Disabled
            link <= 1'b0;
            bank <= 2'b10;
            js <= 1'b1;
            compare0 <= 1'b0;
            compare1 <= 1'b0;
            end
        4'b1100: // Branch if Zero
            begin
            aluOut <= 4'b1010;
            FU <= 3'b110;
            RegEn <= 1'b1;
            halt <= 1'b0;
            addi <= 1'b0;
            mem <= 1'b0;
            dataMemEn <= 1'b0; // Disabled
            link <= 1'b0;
            bank <= 2'b10;
            js <= 1'b0;
            compare0 <= 1'b1;
            compare1 <= 1'b0;
            end
        4'b1101: // NOR
            begin
            aluOut <= 4'b0011;
            RegEn <= 1'b0;
            FU <= 3'b001; // Disable Branching
            halt <= 1'b0;
            addi <= 1'b0;
            mem <= 1'b0;
            dataMemEn <= 1'b0; // Disabled
            link <= 1'b0;
            bank <= 2'b10;
            js <= 1'b0;
            compare0 <= 1'b1;
            compare1 <= 1'b1;
            end
        4'b1110: // OR/AND
            if(functBit == 1) // AND
                begin
                aluOut <= 4'b0100;
                RegEn <= 1'b0;
                FU <= 3'b001; // Disable Branching
                halt <= 1'b0;
                addi <= 1'b0;
                mem <= 1'b0;
                dataMemEn <= 1'b0; // Disabled
                link <= 1'b0;
                bank <= 2'b10;
                js <= 1'b0;
                compare0 <= 1'b1;
                compare1 <= 1'b1;
                end
            else // OR
                begin
                aluOut <= 4'b0010;
                RegEn <= 1'b0;
                FU <= 3'b001; // Disable Branching
                halt <= 1'b0;
                addi <= 1'b0;
                mem <= 1'b0;
                dataMemEn <= 1'b0; // Disabled
                link <= 1'b0;
                bank <= 2'b10;
                js <= 1'b0;
                compare0 <= 1'b1;
                compare1 <= 1'b1;
                end           
        4'b1111: // Shift Right Logical/Shift Left Logical
            if(functBit == 1) // Shift Right Logical
                begin
                aluOut <= 4'b0110;
                RegEn <= 1'b0;
                FU <= 3'b001; // Disable Branching
                halt <= 1'b0;
                addi <= 1'b0;
                mem <= 1'b0;
                dataMemEn <= 1'b0; // Disabled
                link <= 1'b0;
                bank <= 2'b10;
                js <= 1'b0;
                compare0 <= 1'b1;
                compare1 <= 1'b1;
                end
            else // Shift Left Logical
                begin
                aluOut <= 4'b0101;
                RegEn <= 1'b0;
                FU <= 3'b001; // Disable Branching
                halt <= 1'b0;
                addi <= 1'b0;
                mem <= 1'b0;
                dataMemEn <= 1'b0; // Disabled
                link <= 1'b0;
                bank <= 2'b10;
                js <= 1'b0;
                compare0 <= 1'b1;
                compare1 <= 1'b1;
                end
        default:
            begin
            halt <= 1'b1;
            RegEn <= 1'b1; 
            FU <= 3'b001;
            dataMemEn <= 1'b0; // Disabled
            addi <= 1'b0;
            aluOut <= 4'b0000;
            mem <= 1'b0;
            link <= 1'b0;
            bank <= 2'b10;
            js <= 1'b0;
            compare0 <= 1'b0;
            compare1 <= 1'b0;
            end
        endcase
    end
endmodule


module ControlUnit_tb();
    reg [3:0] instruction;
    reg functionB;

    wire [3:0] aluOutput;
    wire [2:0] FetchUnit;
    wire addImmediate, memory, RegEnable;


    ControlUnit ControlUnit0(
        .instIn(instruction),
        .functBit(functionB),
        .aluOut(aluOutput),
        .FU(FetchUnit),
        .addi(addImmediate),
        .mem(memory),
        .RegEn(RegEnable)
        );
    
    initial begin
        functionB = 1'b0;
        instruction = 4'b0101;
        #5
        functionB = 1'b1;
        #5
        functionB = 1'b0;
        instruction = 4'b1110;
        #5
        functionB = 1'b1;
        #5
        functionB = 1'b0;
        instruction = 4'b1111;
        #5
        functionB = 1'b1;
        #5
        instruction = 4'b0111;
        #5
        instruction = 4'b0110;
        #5
        instruction = 4'b1001;
        #5
        instruction = 4'b1100;
        #5
        instruction = 4'b1000;
        #5
        instruction = 4'b0001;
        #5
        instruction = 4'b0010;
        #5
        $finish;

    end
endmodule
