Moves control unit ops to separate files

This commit is contained in:
2025-11-12 11:53:28 +01:00
parent f9cbcaf20f
commit 992cf37b92
10 changed files with 438 additions and 355 deletions

View File

@@ -55,6 +55,10 @@ CFLAGS :=$(ASFLAGS)
ELFFLAGS :=$(ASFLAGS) $(MEM_DIR)/crt.o -Os -fno-unroll-loops
ODFLAGS :=-j .text -j .rodata -j .data -s
## Paramètres de tests
TEST_MAX_RAM := 11000000 ## Limite de ram pour les tests en KB
TEST_MAX_PROCS := $$(($$(nproc) - 2)) ## Garde un processeur libre
.PRECIOUS: $(MEM_DIR)/%.elf
.PHONY: synthese fpga clean compile autotest simulation
@@ -103,7 +107,11 @@ mill:
autotest: mill test-bench/mem/tests/$(PROG).mem ##! Lance la simulation automatique pour tous les tests ou juste celui fourni dans PROG
testall: mill compile
@PROOT=$(PWD) NPROC=$(shell nproc) ./mill TPchisel.test.testOnly $(TOP_REP).ZzTopAllSpec
@# Limite le nombre de processeurs et la ram utilisée pour les tests parellèle
@( \
ulimit -v $(TEST_MAX_RAM); \
PROOT=$(PWD) taskset -c 0-$(TEST_MAX_PROCS) ./mill TPchisel.test.testOnly $(TOP_REP).ZzTopAllSpec \
)
simulation: mill compile ##! Lance gtkwave sur le test fourni dans PROG
@$(call check_vars,PROG)

View File

@@ -1,4 +1,5 @@
# expected: 000003FC, 80000000, 00000000
# expected: 000003FC, 80000000, 00000000, F000000000000000
.text
_start:
addi x1, x0, 0x0FF
@@ -9,3 +10,6 @@ _start:
addi x1, x0, 0x0F0
slli x31, x1, 63
li x1, 0xF
slli x31, x1, 60

View File

@@ -1,4 +1,4 @@
# expected: 000000FF, 00000001, 00000000
# expected: 000000FF, 00000001, 00000000, 000000000000000F
.text
_start:
addi x1, x0, 0x3FC
@@ -10,3 +10,6 @@ _start:
lui x1, 0x0A000
srli x31, x1, 31
li x1, 0xF000000000000000
srli x31, x1, 60

View File

@@ -3,6 +3,12 @@ package projet
import chisel3._
import chisel3.util.switch
import chisel3.util.is
import projet.op.OpImm
import projet.op.OpR
import projet.op.OpBranch
import projet.op.OpStore
import projet.op.OpLoad
import projet.op.OpImmW
class CUInterface extends Bundle {
val instruction = Input(UInt(32.W))
@@ -57,7 +63,13 @@ class CUInterface extends Bundle {
}
object OpType extends ChiselEnum {
val U, I, IR, J, B, S = Value
val U, I, J, B, S = Value
// Type IR is not defined in the RiscV specification
// but the immediate needs to be handled differently
// since the 31st bit indicates wether it is an
// arithmetic or logical shift
val IR = Value
}
object OpCode extends ChiselEnum {
@@ -148,13 +160,13 @@ class ControlUnit() extends Module {
// OpImm instructions
is(OpCode.OpImm) {
io.reg_file_we := true.B // Write to regfile
opImm(funct3, funct7, io);
OpImm.opImm(io)
}
// OpR instructions
is(OpCode.OpR) {
io.reg_file_we := true.B // Write to regfile
opR(funct3, funct7, io);
OpR.opR(io);
}
// J instructions
@@ -191,368 +203,23 @@ class ControlUnit() extends Module {
is(OpCode.OpBranch) {
io.optype := OpType.B;
opBranch(funct3, io);
OpBranch.opBranch(io);
}
is(OpCode.OpStore) {
io.optype := OpType.S
opStore(funct3, io);
OpStore.opStore(io);
}
is(OpCode.OpLoad) {
io.optype := OpType.I
opLoad(funct3, io);
OpLoad.opLoad(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; // 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.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
}
}
}
}
}
// 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
}
OpImmW.opImmW(io);
}
}
}

View File

@@ -0,0 +1,85 @@
package projet.op
import chisel3._
import chisel3.util.switch
import projet.CUInterface
import projet.AluOpCode
import chisel3.util.is
object OpBranch {
// Implements functions for all instruction of the branch kind
def opBranch(io: CUInterface) = {
val funct3 = io.instruction(14, 12);
// 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;
}
}
}

View File

@@ -0,0 +1,87 @@
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.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
}
}
}
}
}

View File

@@ -0,0 +1,36 @@
package projet.op
import chisel3._
import chisel3.util.switch
import projet.CUInterface
import projet.AluOpCode
import chisel3.util.is
import projet.OpType
object OpImmW {
// Implements functions for all instruction of the opImm, w kind
def opImmW(io: CUInterface) = {
val funct3 = io.instruction(14, 12);
// Meaning of Funct3 in the context of an OpImm instruction
object Funct3 extends ChiselEnum {
// Type I
val ADDIW = "b000".U
val SLLIW = "b001".U
// Unimplemented
val SRLIW_SRAIW = "b101".U // Right shifts
}
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
}
}
}
}

View File

@@ -0,0 +1,59 @@
package projet.op
import chisel3._
import chisel3.util.switch
import projet.CUInterface
import projet.AluOpCode
import chisel3.util.is
import projet.DMemSize
object OpLoad {
// Implements functions for all instruction of the OpLoad kind
def opLoad(io: CUInterface) = {
val funct3 = io.instruction(14, 12);
// 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
}
}
}
}

View File

@@ -0,0 +1,94 @@
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.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
}
}
}
}
}
}

View File

@@ -0,0 +1,40 @@
package projet.op
import chisel3._
import chisel3.util.switch
import projet.CUInterface
import projet.AluOpCode
import chisel3.util.is
import projet.DMemSize
object OpStore {
// Implements functions for all instruction of the OpStore kind
def opStore(io: CUInterface) = {
val funct3 = io.instruction(14, 12);
// 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
}
}
}
}