package projet.op

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

object OpImm {
// Implements functions for all instruction of the opImm kind
    def opImm(io: CUInterface) = {
        val funct3 = io.instruction(14, 12);

        // Meaning of Funct3 in the context of an OpImm instruction
        object Funct3 extends ChiselEnum {
            val ADDI = "b000".U
            val SLTI = "b010".U
            val SLTIU = "b011".U
            val XORI = "b100".U
            val ORI = "b110".U
            val ANDI = "b111".U

            // Shifts (Type IR)
            val SLLI = "b001".U // Shift left

            // We do not use a "Funct7" here as technically, per the specification :
            // (https://docs.riscv.org/reference/isa/unpriv/rv32.html#integer-register-immediate-instructions)
            // it's not a funct7, but rather a bit at index 30 that indicates right shift kind
            val ShiftRight = "b101".U
        }

        io.reg_file_we := true.B // Write to regfile
        io.mux_regb_imm := false.B; // OpImm are operations with imm, so send imm to ALU
        io.mux_alu_imm := true.B;
        io.mux_rega_pc := true.B;
        io.alu_word_mode := false.B;
        switch(funct3) {
            // Type I
            is(Funct3.ADDI) {
                io.alu_opcode := AluOpCode.Add
                io.optype := OpType.I
            }

            is(Funct3.ANDI) {
                io.alu_opcode := AluOpCode.And
                io.optype := OpType.I
            }

            is(Funct3.ORI) {
                io.alu_opcode := AluOpCode.Or
                io.optype := OpType.I
            }

            is(Funct3.XORI) {
                io.alu_opcode := AluOpCode.Xor
                io.optype := OpType.I
            }

            is(Funct3.SLTI) {
                io.alu_opcode := AluOpCode.LessThanSigned
                io.optype := OpType.I
            }

            is(Funct3.SLTIU) {
                io.alu_opcode := AluOpCode.LessThanUnsigned
                io.optype := OpType.I
            }

            // Type IR
            is(Funct3.SLLI) {
                io.alu_opcode := AluOpCode.ShiftLeft
                io.optype := OpType.IR
            }

            is(Funct3.ShiftRight) {
                io.optype := OpType.IR

                // Second-to-last bit indicates arithmetic or logical shift
                when(io.instruction(30)) {
                    io.alu_opcode := AluOpCode.ShiftArithmeticRight
                }.otherwise {
                    io.alu_opcode := AluOpCode.ShiftRight
                }
            }
        }
    }
}
