Files
fmn_processor/src/main/scala/projet/ControlUnit.scala
2025-11-12 11:50:09 +01:00

619 lines
18 KiB
Scala
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package projet
import chisel3._
import chisel3.util.switch
import chisel3.util.is
class CUInterface extends Bundle {
val instruction = Input(UInt(32.W))
val reg_file_we = Output(Bool())
val alu_opcode = Output(AluOpCode())
// TODO: Maybe bundle alu <> cu
// Alu comparison result
val alu_comp_result = Input(Bool())
val alu_word_mode = Output(Bool())
val optype = Output(OpType())
// True if the decoded instruction is a jump
val is_jump = Output(Bool())
// DMem connections
val memory_size = Output(DMemSize())
val memory_en = Output(Bool())
val memory_we = Output(Bool())
val memory_sign_extend = Output(
Bool()
) // Wether to signe extend the output of the memory for bytes, half loads
// Muxers
// The naming convention is as follows : mux_xxx_yyy.
// If the signal is hot, xxx is selected otherwise yyy is selected
// Sends alu or immediate to write back
val mux_alu_imm = Output(Bool())
// Selects regA or pc for alu a
val mux_rega_pc = Output(Bool())
// Selects regB or immediate for alu b
val mux_regb_imm = Output(Bool())
// Selects 4 or immediate for pc incrementation
val mux_incrpc4_imm = Output(Bool())
// Selects pc or previous pc for pc incrementation
val mux_regpc_executepc = Output(Bool())
// Selects pc adder or write back for pc write
val mux_pcadder_writeback = Output(Bool())
// Selects pc or write back for register write
val mux_writeback_pc = Output(Bool())
// Selects aluout or memory data out for writeback_line
val mux_executeout_dout = Output(Bool())
}
object OpType extends ChiselEnum {
val U, I, IR, J, B, S = Value
}
object OpCode extends ChiselEnum {
// Type U
val LUI = "b0110111".U
val AUIPC = "b0010111".U
// Type I + Type IR
// Instruction format
// xxx rd, rs1, imm
// Perfomrs some operation with rs1 and immediate
// and writes back to a register
// addi, andi, ori, xori, slli, srli, srai,
// slti, sltiu
val OpImm = "b0010011".U
// Instruction format
// xxx rd, rs1, rs2
// Perfomrs some operation with rs1 and rd2
// and writes back to a register
// add, and, or, xor, sll, srl, sra, slt, stlu, mul, mulh, mulhsu, mulhu, div, divu, rem, remu
val OpR = "b0110011".U;
// mulw, divw, remw
val OpMw = "b0111011".U;
// Type J
val JAL = "b1101111".U
// Type I
val JALR = "b1100111".U
// Type B
val OpBranch = "b1100011".U
// Type S
val OpStore = "b0100011".U
// Type I
val OpLoad = "b0000011".U
// ~~ Rv64i ~~
val OpImmW = "b0011011".U
}
class ControlUnit() extends Module {
val io = IO(new CUInterface())
io.reg_file_we := false.B
io.alu_opcode := AluOpCode.Add
io.alu_word_mode := false.B
io.optype := OpType.U
io.is_jump := false.B;
io.memory_size := DMemSize.Word
io.memory_en := false.B
io.memory_we := false.B
io.memory_sign_extend := false.B;
io.mux_alu_imm := true.B;
io.mux_rega_pc := true.B;
io.mux_regb_imm := true.B;
io.mux_incrpc4_imm := true.B;
io.mux_regpc_executepc := true.B;
io.mux_writeback_pc := true.B;
io.mux_pcadder_writeback := true.B;
io.mux_executeout_dout := true.B;
// Decode instruction
val opcode = io.instruction(6, 0);
val funct3 = io.instruction(14, 12);
val funct7 = io.instruction(31, 25);
switch(opcode) {
// U instructions
is(OpCode.LUI) {
io.reg_file_we := true.B // Write to regfile
io.mux_alu_imm := false.B // Send immy directly to refile
io.optype := OpType.U
}
is(OpCode.AUIPC) {
io.alu_opcode := AluOpCode.Add
io.reg_file_we := true.B // Write to regfile
io.mux_rega_pc := false.B // Send PC to ALU A
io.mux_regb_imm := false.B; // Send immu to ALU B
io.optype := OpType.U
}
// OpImm instructions
is(OpCode.OpImm) {
io.reg_file_we := true.B // Write to regfile
opImm(funct3, funct7, io);
}
// OpR instructions
is(OpCode.OpR) {
io.reg_file_we := true.B // Write to regfile
opR(funct3, funct7, io);
}
// OpMw instructions
is(OpCode.OpMw) {
io.reg_file_we := true.B // Write to regfile
io.alu_word_mode := true.B
opM(funct3, io);
}
// J instructions
is(OpCode.JAL) {
io.alu_opcode := AluOpCode.Add // ALU is unused
io.reg_file_we := true.B // Write to regfile
io.mux_writeback_pc := false.B;
// io.mux_rega_pc := false.B // Send PC to ALU A
// io.mux_regb_imm := false.B; // Send imm to ALU B
io.is_jump := true.B
io.mux_incrpc4_imm := false.B; // Send the immediate in PC
io.mux_regpc_executepc := false.B; // Add the immediate to the previous PC
io.mux_pcadder_writeback := true.B;
io.optype := OpType.J
}
is(OpCode.JALR) {
io.alu_opcode := AluOpCode.Add
io.reg_file_we := true.B // Write to regfile
io.mux_writeback_pc := false.B // Write next instruction PC to rd
io.is_jump := true.B // Insert no-op to discard fetched next instruction
io.mux_rega_pc := true.B // Send rs1 to ALU A
io.mux_regb_imm := false.B; // Send imm to ALU B
io.mux_alu_imm := true.B; // Send alu out to write back line
io.mux_pcadder_writeback := false.B; // Set pc to writeback dest
io.optype := OpType.I
}
is(OpCode.OpBranch) {
io.optype := OpType.B;
opBranch(funct3, io);
}
is(OpCode.OpStore) {
io.optype := OpType.S
opStore(funct3, io);
}
is(OpCode.OpLoad) {
io.optype := OpType.I
opLoad(funct3, io);
}
is(OpCode.OpImmW) {
io.reg_file_we := true.B // Write to regfile
io.optype := OpType.I
opImmW(funct3, funct7, io);
}
}
// Implements functions for all instruction of the opImm kind
def opImm(funct3: UInt, funct7: UInt, io: CUInterface) = {
// Meaning of Funct3 in the context of an OpImm instruction
object Funct3 extends ChiselEnum {
// Type I
val ADDI = "b000".U
val SLTI = "b010".U
val SLTIU = "b011".U
val XORI = "b100".U
val ORI = "b110".U
val ANDI = "b111".U
// Type IR
val SLLI = "b001".U
val SRLI_SRAI = "b101".U // Right shifts
}
// Meaning of Funct7 in the context of SHIFTR IR instructions
object IRFunct7 extends ChiselEnum {
val SRLI = "b0000000".U
val SRAI = "b0100000".U
}
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.SLLI) {
io.alu_opcode := AluOpCode.ShiftLeft
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.SRLI_SRAI) {
io.optype := OpType.IR
switch(funct7) {
is(IRFunct7.SRLI) {
io.alu_opcode := AluOpCode.ShiftRight
}
is(IRFunct7.SRAI) {
io.alu_opcode := AluOpCode.ShiftArithmeticRight
}
}
}
}
}
// Implements functions for all instruction of the opR kind
def opR(funct3: UInt, funct7: UInt, io: CUInterface) = {
// Meaning of Funct3 in the context of an OpR instruction
object Funct3 extends ChiselEnum {
// Type I
val ADD_SUB = "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
// Arithmetic op (discriminate by Funct7)
val SRL_SRA = "b101".U
}
// Meaning of Funct7 in the context of SHIFTR IR instructions
object IRFunct7 extends ChiselEnum {
val SRL = "b0000000".U
val SRA = "b0100000".U
}
object ADDFunct7 extends ChiselEnum {
val ADD = "b0000000".U
val SUB = "b0100000".U
}
io.mux_regb_imm := true.B;
io.mux_rega_pc := true.B;
io.mux_alu_imm := true.B;
switch(funct3) {
// Type I
is(Funct3.ADD_SUB) {
switch(funct7) {
is(ADDFunct7.ADD) {
io.alu_opcode := AluOpCode.Add
}
is(ADDFunct7.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.SRL_SRA) {
switch(funct7) {
is(IRFunct7.SRL) {
io.alu_opcode := AluOpCode.ShiftRight
}
is(IRFunct7.SRA) {
io.alu_opcode := AluOpCode.ShiftArithmeticRight
}
}
}
}
// Switch to mul extension
val RV32M_FUNCT7 = "b0000001".U
when(funct7 === RV32M_FUNCT7) {
opM(funct3, io);
}
}
// Implements functions for all instruction of the branch kind
def opBranch(funct3: UInt, io: CUInterface) = {
// Meaning of Funct3 in the context of an OpBranch instruction
object Funct3 extends ChiselEnum {
val BEQ = "b000".U
val BNE = "b001".U
val BLT = "b100".U
val BGE = "b101".U
val BLTU = "b110".U
val BGEU = "b111".U
}
val branch = WireInit(false.B);
switch(funct3) {
is(Funct3.BEQ) {
// Send operands from registers to alu
io.alu_opcode := AluOpCode.Equal
io.mux_rega_pc := true.B;
io.mux_regb_imm := true.B;
branch := io.alu_comp_result
}
is(Funct3.BNE) {
// Send operands from registers to alu
io.alu_opcode := AluOpCode.Equal
io.mux_rega_pc := true.B;
io.mux_regb_imm := true.B;
branch := ~io.alu_comp_result
}
// Signed comparison
is(Funct3.BLT) {
// Send operands from registers to alu
io.alu_opcode := AluOpCode.LessThanSigned
io.mux_rega_pc := true.B;
io.mux_regb_imm := true.B;
branch := io.alu_comp_result
}
is(Funct3.BGE) {
// Send operands from registers to alu
io.alu_opcode := AluOpCode.LessThanSigned
io.mux_rega_pc := true.B;
io.mux_regb_imm := true.B;
branch := ~io.alu_comp_result
}
// Signed comparison
is(Funct3.BLTU) {
// Send operands from registers to alu
io.alu_opcode := AluOpCode.LessThanUnsigned
io.mux_rega_pc := true.B;
io.mux_regb_imm := true.B;
branch := io.alu_comp_result
}
is(Funct3.BGEU) {
// Send operands from registers to alu
io.alu_opcode := AluOpCode.LessThanUnsigned
io.mux_rega_pc := true.B;
io.mux_regb_imm := true.B;
branch := ~io.alu_comp_result
}
}
// Branch if comparison is successful
when(branch) {
io.mux_pcadder_writeback := true.B;
io.mux_incrpc4_imm := false.B;
io.mux_regpc_executepc := false.B;
io.is_jump := true.B;
}
}
// Implements functions for all instruction of the OpStore kind
def opStore(funct3: UInt, io: CUInterface) = {
// Meaning of Funct3 in the context of an OpStore instruction
object Funct3 extends ChiselEnum {
val SB = "b000".U
val SH = "b001".U
val SW = "b010".U
}
io.alu_opcode := AluOpCode.Add
io.mux_alu_imm := true.B
io.mux_rega_pc := true.B
io.mux_regb_imm := false.B
io.memory_we := true.B
io.memory_en := true.B
switch(funct3) {
is(Funct3.SB) {
io.memory_size := DMemSize.Byte
}
is(Funct3.SH) {
io.memory_size := DMemSize.Half
}
is(Funct3.SW) {
io.memory_size := DMemSize.Word
}
}
}
// Implements functions for all instruction of the OpLoad kind
def opLoad(funct3: UInt, io: CUInterface) = {
// Meaning of Funct3 in the context of an OpLoad instruction
object Funct3 extends ChiselEnum {
val LB = "b000".U
val LH = "b001".U
val LW = "b010".U
val LBU = "b100".U
val LHU = "b101".U
}
io.alu_opcode := AluOpCode.Add
io.mux_alu_imm := true.B
io.mux_rega_pc := true.B
io.mux_regb_imm := false.B
io.mux_writeback_pc := true.B;
io.mux_executeout_dout := false.B;
io.reg_file_we := true.B
io.memory_en := true.B
io.memory_we := false.B
switch(funct3) {
// Byte load
is(Funct3.LB) {
io.memory_size := DMemSize.Byte
io.memory_sign_extend := true.B;
}
is(Funct3.LBU) {
io.memory_size := DMemSize.Byte
}
// Half load
is(Funct3.LH) {
io.memory_size := DMemSize.Half
io.memory_sign_extend := true.B;
}
is(Funct3.LHU) {
io.memory_size := DMemSize.Half
}
// Word load
is(Funct3.LW) {
io.memory_size := DMemSize.Word
}
}
}
// ~~~ EXTENSIONS ~~~
// ~~ Rv64i ~~
// Implements functions for all instruction of the opImm, w kind
def opImmW(funct3: UInt, funct7: UInt, io: CUInterface) = {
// Meaning of Funct3 in the context of an OpImm instruction
val _ = funct7;
object Funct3 extends ChiselEnum {
// Type I
val ADDIW = "b000".U
val SLLIW = "b001".U
// Unimplemented
val SRLIW_SRAIW = "b101".U // Right shifts
}
// Meaning of Funct7 in the context of SHIFTR IR instructions
// object IRFunct7 extends ChiselEnum {
// val SRLI = "b0000000".U
// val SRAI = "b0100000".U
// }
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;
switch(funct3) {
// Type I
is(Funct3.ADDIW) {
io.alu_word_mode := true.B
io.alu_opcode := AluOpCode.Add
io.optype := OpType.I
}
}
}
// ~~ Rv32M ~~
// Implements functions for instructions of the M extension of type R
def opM(funct3: UInt, io: CUInterface) = {
// Meaning of Funct3 in the context of an OpM instruction
object Funct3 extends ChiselEnum {
val MUL = "b000".U
val MULH = "b001".U
val MULHSU = "b010".U
val MULHU = "b011".U
val DIV = "b100".U
val DIVU = "b101".U
val REM = "b110".U
val REMU = "b111".U
}
switch(funct3) {
is(Funct3.MUL) {
io.alu_opcode := AluOpCode.Mul
}
is(Funct3.MULH) {
io.alu_opcode := AluOpCode.Mulh
}
is(Funct3.MULHSU) {
io.alu_opcode := AluOpCode.Mulhsu
}
is(Funct3.MULHU) {
io.alu_opcode := AluOpCode.Mulhu
}
is(Funct3.DIV) {
io.alu_opcode := AluOpCode.Div
}
is(Funct3.DIVU) {
io.alu_opcode := AluOpCode.Divu
}
is(Funct3.REM) {
io.alu_opcode := AluOpCode.Rem
}
is(Funct3.REMU) {
io.alu_opcode := AluOpCode.Remu
}
}
}
}