package projet.op

import chisel3._
import chisel3.util.switch
import projet.CUInterface
import projet.AluOpCode
import chisel3.util.is

object OpR {
    // implements functions for all instruction of the opr kind
    def opR(io: CUInterface) = {
        val funct3 = io.instruction(14, 12);
        val funct7 = io.instruction(31, 25);

        // Meaning of Funct3 in the context of an OpR instruction
        object Funct3 extends ChiselEnum {
            // Type I
            val AdditionSubstraction = "b000".U
            val AND = "b111".U
            val OR = "b110".U
            val XOR = "b100".U
            val SLL = "b001".U
            val SLT = "b010".U
            val SLTU = "b011".U

            val ShiftRight = "b101".U
        }

        // Meaning of Funct7 in the context of SHIFTR IR instructions
        object ShiftRightFunct7 extends ChiselEnum {
            val SRL = "b0000000".U
            val SRA = "b0100000".U
        }

        object AdditionSubstractionFunct7 extends ChiselEnum {
            val ADD = "b0000000".U
            val SUB = "b0100000".U
        }

        io.reg_file_we := true.B // Write to regfile
        io.mux_regb_imm := true.B; // OpImm are operations with imm, so send imm to ALU
        io.mux_rega_pc := true.B;
        io.mux_alu_imm := true.B;
        switch(funct3) {
            // Type I
            is(Funct3.AdditionSubstraction) {
                switch(funct7) {
                    is(AdditionSubstractionFunct7.ADD) {
                        io.alu_opcode := AluOpCode.Add
                    }
                    is(AdditionSubstractionFunct7.SUB) {
                        io.alu_opcode := AluOpCode.Sub
                    }
                }
            }

            is(Funct3.AND) {
                io.alu_opcode := AluOpCode.And
            }

            is(Funct3.OR) {
                io.alu_opcode := AluOpCode.Or
            }

            is(Funct3.XOR) {
                io.alu_opcode := AluOpCode.Xor
            }

            is(Funct3.SLL) {
                io.alu_opcode := AluOpCode.ShiftLeft
            }

            is(Funct3.SLT) {
                io.alu_opcode := AluOpCode.LessThanSigned
            }

            is(Funct3.SLTU) {
                io.alu_opcode := AluOpCode.LessThanUnsigned
            }

            // Type IR
            is(Funct3.ShiftRight) {
                switch(funct7) {
                    is(ShiftRightFunct7.SRL) {
                        io.alu_opcode := AluOpCode.ShiftRight
                    }

                    is(ShiftRightFunct7.SRA) {
                        io.alu_opcode := AluOpCode.ShiftArithmeticRight
                    }
                }
            }
        }
        
        // Switch to mul extension
        val RV32M_FUNCT7 = "b0000001".U
        when(funct7 === RV32M_FUNCT7) {
            OpM.opM(io);
        }
    }
}
