perilus

git clone https://git.bradleygannon.com/perilus.git
Log | Files | Refs | Submodules | README

commit e73974f94f044a98ccf733852fd863c606e31e10
parent 6531dd4072ced92cdbacd56d4e681d5b5157aa96
Author: Bradley Gannon <bradley@bradleygannon.com>
Date:   Tue, 24 Feb 2026 19:32:29 -0500

Add support for xor, sll, srl, sra, and sltu

Also add and update some tests, remove explicit values from ChiselEnums
that don't need them, and other tweaks

Diffstat:
Msrc/main/scala/alu.scala | 45++++++++++++++++++++++++---------------------
Msrc/main/scala/controlUnit.scala | 21+++++++++++++++------
Msrc/main/scala/extendUnit.scala | 5+----
Msrc/test/scala/perilus/alu.scala | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/test/scala/perilus/controlUnit.scala | 172++++++++++++++++++++++++++-----------------------------------------------------
Msrc/test/scala/perilus/main.scala | 57+++++++++++++++++++++++++++++++++++++++++++++++++++------
6 files changed, 281 insertions(+), 175 deletions(-)

diff --git a/src/main/scala/alu.scala b/src/main/scala/alu.scala @@ -20,14 +20,29 @@ class Alu extends Module { is(AluControl.sub) { io.aluResult := io.srcA - io.srcB } - is(AluControl.and) { - io.aluResult := io.srcA & io.srcB + is(AluControl.sll) { + io.aluResult := io.srcA << io.srcB(5, 0) + } + is(AluControl.slt) { + io.aluResult := (io.srcA.asSInt < io.srcB.asSInt).asUInt + } + is(AluControl.sltu) { + io.aluResult := io.srcA < io.srcB + } + is(AluControl.xor) { + io.aluResult := io.srcA ^ io.srcB + } + is(AluControl.srl) { + io.aluResult := io.srcA >> io.srcB(5, 0) + } + is(AluControl.sra) { + io.aluResult := (io.srcA.asSInt >> io.srcB(5, 0)).asUInt } is(AluControl.or) { io.aluResult := io.srcA | io.srcB } - is(AluControl.slt) { - io.aluResult := io.srcA < io.srcB + is(AluControl.and) { + io.aluResult := io.srcA & io.srcB } } @@ -35,33 +50,21 @@ class Alu extends Module { } object AluControl extends ChiselEnum { - val add = Value("b000".U) - val sub = Value("b001".U) - val and = Value("b010".U) - val or = Value("b011".U) - val slt = Value("b101".U) + val add, sub, sll, slt, sltu, xor, srl, sra, or, and = Value } object AluSrcA extends ChiselEnum { - val pc = Value("b00".U) - val oldPc = Value("b01".U) - val rd1 = Value("b10".U) + val pc, oldPc, rd1 = Value } object AluSrcB extends ChiselEnum { - val rd2 = Value("b00".U) - val immExt = Value("b01".U) - val four = Value("b10".U) + val rd2, immExt, four = Value } object AluOp extends ChiselEnum { - val memory = Value("b00".U) - val branch = Value("b01".U) - val arithmetic = Value("b10".U) + val memory, branch, arithmetic = Value } object ResultSrc extends ChiselEnum { - val aluOutBuf = Value("b00".U) - val readDataBuf = Value("b01".U) - val aluResult = Value("b10".U) + val aluOutBuf, readDataBuf, aluResult = Value } diff --git a/src/main/scala/controlUnit.scala b/src/main/scala/controlUnit.scala @@ -70,7 +70,7 @@ class ControlUnit(withDebug: Boolean = false) extends Module { }.elsewhen(io.op === Opcode.jal) { state := ControlUnitState.jal }.elsewhen(io.op === Opcode.branch) { - state := ControlUnitState.beq + state := ControlUnitState.branch } } is(ControlUnitState.memAdr) { @@ -127,7 +127,7 @@ class ControlUnit(withDebug: Boolean = false) extends Module { pcUpdate := true.B state := ControlUnitState.aluWb } - is(ControlUnitState.beq) { + is(ControlUnitState.branch) { io.aluSrcA := AluSrcA.rd1 io.aluSrcB := AluSrcB.rd2 io.resultSrc := ResultSrc.aluOutBuf @@ -170,15 +170,23 @@ class AluDecoder extends Module { } is(AluOp.arithmetic) { when( - io.funct3 === "b000".U && ((!io.op.asUInt(5) && !io.funct7_5) || (!io.op.asUInt( - 5 - ) && io.funct7_5) || (io.op.asUInt(5) && !io.funct7_5)) + io.funct3 === "b000".U && (!io.op.asUInt(5) || (io.op.asUInt(5) && !io.funct7_5)) ) { io.aluControl := AluControl.add }.elsewhen(io.funct3 === "b000".U && io.op.asUInt(5) && io.funct7_5) { io.aluControl := AluControl.sub + }.elsewhen(io.funct3 === "b001".U) { + io.aluControl := AluControl.sll }.elsewhen(io.funct3 === "b010".U) { io.aluControl := AluControl.slt + }.elsewhen(io.funct3 === "b011".U) { + io.aluControl := AluControl.sltu + }.elsewhen(io.funct3 === "b100".U) { + io.aluControl := AluControl.xor + }.elsewhen(io.funct3 === "b101".U && !io.funct7_5) { + io.aluControl := AluControl.srl + }.elsewhen(io.funct3 === "b101".U && io.funct7_5) { + io.aluControl := AluControl.sra }.elsewhen(io.funct3 === "b110".U) { io.aluControl := AluControl.or }.elsewhen(io.funct3 === "b111".U) { @@ -217,7 +225,8 @@ class InstructionDecoder extends Module { } object ControlUnitState extends ChiselEnum { - val fetch, decode, memAdr, memRead, memWb, memWrite, executeR, aluWb, executeI, jal, beq = Value + val fetch, decode, memAdr, memRead, memWb, memWrite, executeR, aluWb, executeI, jal, branch = + Value } object Opcode extends ChiselEnum { diff --git a/src/main/scala/extendUnit.scala b/src/main/scala/extendUnit.scala @@ -42,8 +42,5 @@ class ExtendUnit extends Module { } object ImmSrc extends ChiselEnum { - val iType = Value("b00".U) - val sType = Value("b01".U) - val bType = Value("b10".U) - val jType = Value("b11".U) + val iType, sType, bType, jType = Value } diff --git a/src/test/scala/perilus/alu.scala b/src/test/scala/perilus/alu.scala @@ -46,26 +46,121 @@ class AluTests extends AnyFunSpec with ChiselSim { } } } - it("computes bitwise AND") { + it("shifts left") { simulate(new Alu) { alu => { - alu.io.aluControl.poke(AluControl.and) + alu.io.aluControl.poke(AluControl.sll) - alu.io.srcA.poke("h9ae8ddd".U) - alu.io.srcB.poke("ha8387b0c".U) - alu.io.aluResult.expect("h828090c".U) + alu.io.srcA.poke("h3baf038e".U) + alu.io.srcB.poke(19.U) + alu.io.aluResult.expect("h1c700000".U) - alu.io.srcA.poke("h96f36a94".U) - alu.io.srcB.poke("h52e96078".U) - alu.io.aluResult.expect("h12e16010".U) + alu.io.srcA.poke("h1d6f9fae".U) + alu.io.srcB.poke(7.U) + alu.io.aluResult.expect("hb7cfd700".U) - alu.io.srcA.poke("hf9209575".U) - alu.io.srcB.poke("h931544ba".U) - alu.io.aluResult.expect("h91000430".U) + alu.io.srcA.poke("h21c69d4f".U) + alu.io.srcB.poke(29.U) + alu.io.aluResult.expect("he0000000".U) } } } - it("computes bitwise OR") { + it("computes set if less than (SLT)") { + simulate(new Alu) { alu => + { + alu.io.aluControl.poke(AluControl.slt) + + alu.io.srcA.poke("h8a831306".U) + alu.io.srcB.poke("h6e7ec46f".U) + alu.io.aluResult.expect(1.U) + + alu.io.srcA.poke("hcd30f9c7".U) + alu.io.srcB.poke("hb15d24bd".U) + alu.io.aluResult.expect(0.U) + + alu.io.srcA.poke("h33b6db74".U) + alu.io.srcB.poke("hd6faaa4d".U) + alu.io.aluResult.expect(0.U) + } + } + } + it("computes set if less than unsigned (SLTU)") { + simulate(new Alu) { alu => + { + alu.io.aluControl.poke(AluControl.sltu) + + alu.io.srcA.poke("h3c22f6f3".U) + alu.io.srcB.poke("ha2fec497".U) + alu.io.aluResult.expect(1.U) + + alu.io.srcA.poke("hbdb58b94".U) + alu.io.srcB.poke("h2d3d7d3e".U) + alu.io.aluResult.expect(0.U) + + alu.io.srcA.poke("hed304348".U) + alu.io.srcB.poke("hc5770573".U) + alu.io.aluResult.expect(0.U) + } + } + } + it("computes XOR") { + simulate(new Alu) { alu => + { + alu.io.aluControl.poke(AluControl.xor) + + alu.io.srcA.poke("h55e732a9".U) + alu.io.srcB.poke("h5ba7c1f3".U) + alu.io.aluResult.expect("h0e40f35a".U) + + alu.io.srcA.poke("he3bf6c67".U) + alu.io.srcB.poke("he040b522".U) + alu.io.aluResult.expect("h03ffd945".U) + + alu.io.srcA.poke("h335724c7".U) + alu.io.srcB.poke("h60b5dfe0".U) + alu.io.aluResult.expect("h53e2fb27".U) + } + } + } + it("shifts right (logical)") { + simulate(new Alu) { alu => + { + alu.io.aluControl.poke(AluControl.srl) + + alu.io.srcA.poke("ha428d136".U) + alu.io.srcB.poke(21.U) + alu.io.aluResult.expect("h00000521".U) + + alu.io.srcA.poke("hada24983".U) + alu.io.srcB.poke(3.U) + alu.io.aluResult.expect("h15b44930".U) + + alu.io.srcA.poke("h9c393ad6".U) + alu.io.srcB.poke(8.U) + alu.io.aluResult.expect("h009c393a".U) + } + } + } + it("shifts right (arithmetic)") { + simulate(new Alu) { alu => + { + alu.io.aluControl.poke(AluControl.sra) + + alu.io.srcA.poke("h7d803c74".U) + alu.io.srcB.poke(26.U) + alu.io.aluResult.expect("h0000001f".U) + + alu.io.srcA.poke("haaecbd03".U) + alu.io.srcB.poke(9.U) + alu.io.aluResult.expect("hffd5765e".U) + + alu.io.srcA.poke("ha5113a05".U) + alu.io.srcB.poke(26.U) + alu.io.aluResult.expect("hffffffe9".U) + } + } + } + it("computes OR") { simulate(new Alu) { alu => { alu.io.aluControl.poke(AluControl.or) @@ -84,22 +179,37 @@ class AluTests extends AnyFunSpec with ChiselSim { } } } - it("computes set if less than (SLT)") { + it("computes AND") { simulate(new Alu) { alu => { - alu.io.aluControl.poke(AluControl.slt) + alu.io.aluControl.poke(AluControl.and) - alu.io.srcA.poke("h8a831306".U) - alu.io.srcB.poke("h6e7ec46f".U) - alu.io.aluResult.expect(0.U) + alu.io.srcA.poke("h9ae8ddd".U) + alu.io.srcB.poke("ha8387b0c".U) + alu.io.aluResult.expect("h828090c".U) - alu.io.srcA.poke("hcd30f9c7".U) - alu.io.srcB.poke("hb15d24bd".U) - alu.io.aluResult.expect(0.U) + alu.io.srcA.poke("h96f36a94".U) + alu.io.srcB.poke("h52e96078".U) + alu.io.aluResult.expect("h12e16010".U) - alu.io.srcA.poke("h33b6db74".U) - alu.io.srcB.poke("hd6faaa4d".U) - alu.io.aluResult.expect(1.U) + alu.io.srcA.poke("hf9209575".U) + alu.io.srcB.poke("h931544ba".U) + alu.io.aluResult.expect("h91000430".U) + } + } + } + it("sets the zero flag") { + simulate(new Alu) { alu => + { + alu.io.aluControl.poke(AluControl.sub) + + alu.io.srcA.poke("h3dd9e4a3".U) + alu.io.srcB.poke("hb4a64831".U) + alu.io.zero.expect(false.B) + + alu.io.srcA.poke("h157aa03a".U) + alu.io.srcB.poke("h157aa03a".U) + alu.io.zero.expect(true.B) } } } diff --git a/src/test/scala/perilus/controlUnit.scala b/src/test/scala/perilus/controlUnit.scala @@ -9,21 +9,11 @@ import com.rinthyAi.perilus.controlUnit._ import com.rinthyAi.perilus.extendUnit._ class ControlUnitTests extends AnyFunSpec with ChiselSim { - def checkFsmStates(dut: ControlUnit, states: List[ControlUnitState.Type]) = { - val debug = dut.io.debug.get - debug.state.expect(states.head) - states.tail.foreach(state => { - dut.clock.step(1) - debug.state.expect(state) - }) - dut.clock.step(1) - debug.state.expect(states.head) - } describe("AluDecoder") { it("decodes correctly") { simulate(new AluDecoder) { aluDecoder => { - aluDecoder.io.funct3.poke(0.U) + aluDecoder.io.funct3.poke("b000".U) aluDecoder.io.funct7_5.poke(false.B) aluDecoder.io.op.poke(Opcode.rType) @@ -37,10 +27,21 @@ class ControlUnitTests extends AnyFunSpec with ChiselSim { aluDecoder.io.op.poke(Opcode.immediate) aluDecoder.io.aluControl.expect(AluControl.add) aluDecoder.io.op.poke(Opcode.rType) + aluDecoder.io.aluControl.expect(AluControl.add) aluDecoder.io.funct7_5.poke(true.B) aluDecoder.io.aluControl.expect(AluControl.sub) + aluDecoder.io.funct3.poke("b001".U) + aluDecoder.io.aluControl.expect(AluControl.sll) aluDecoder.io.funct3.poke("b010".U) aluDecoder.io.aluControl.expect(AluControl.slt) + aluDecoder.io.funct3.poke("b011".U) + aluDecoder.io.aluControl.expect(AluControl.sltu) + aluDecoder.io.funct3.poke("b100".U) + aluDecoder.io.aluControl.expect(AluControl.xor) + aluDecoder.io.funct3.poke("b101".U) + aluDecoder.io.aluControl.expect(AluControl.sra) + aluDecoder.io.funct7_5.poke(false.B) + aluDecoder.io.aluControl.expect(AluControl.srl) aluDecoder.io.funct3.poke("b110".U) aluDecoder.io.aluControl.expect(AluControl.or) aluDecoder.io.funct3.poke("b111".U) @@ -50,119 +51,60 @@ class ControlUnitTests extends AnyFunSpec with ChiselSim { } } describe("ControlUnit") { - it("controls the beq instruction") { + it("advances from Fetch to Decode") { simulate(new ControlUnit(withDebug = true)) { controlUnit => { - controlUnit.io.op.poke(Opcode.branch) - controlUnit.io.funct3.poke(0.U) - controlUnit.io.funct7_5.poke(false.B) - controlUnit.io.zero.poke(false.B) - - val states = List( - ControlUnitState.fetch, - ControlUnitState.decode, - ControlUnitState.beq - ) - - checkFsmStates(controlUnit, states) + val controlUnitDebug = controlUnit.io.debug.get + controlUnitDebug.state.expect(ControlUnitState.fetch) + controlUnit.clock.step() + controlUnitDebug.state.expect(ControlUnitState.decode) } } } - it("controls the jal instruction") { - simulate(new ControlUnit(withDebug = true)) { controlUnit => - { - controlUnit.io.op.poke(Opcode.jal) - controlUnit.io.funct3.poke(0.U) - controlUnit.io.funct7_5.poke(false.B) - controlUnit.io.zero.poke(false.B) - - val states = List( - ControlUnitState.fetch, - ControlUnitState.decode, - ControlUnitState.jal, - ControlUnitState.aluWb - ) - - checkFsmStates(controlUnit, states) - } - } + it("advances from Decode to MemAdr for loads and stores") { + cancel("Not yet implemented") } - it("controls the lw instruction") { - simulate(new ControlUnit(withDebug = true)) { controlUnit => - { - controlUnit.io.op.poke(Opcode.load) - controlUnit.io.funct3.poke(2.U) - controlUnit.io.funct7_5.poke(false.B) - controlUnit.io.zero.poke(false.B) - - val states = List( - ControlUnitState.fetch, - ControlUnitState.decode, - ControlUnitState.memAdr, - ControlUnitState.memRead, - ControlUnitState.memWb - ) - - checkFsmStates(controlUnit, states) - } - } + it("advances from Decode to ExecuteR for R-types") { + cancel("Not yet implemented") } - it("controls the or instruction") { - simulate(new ControlUnit(withDebug = true)) { controlUnit => - { - controlUnit.io.op.poke(Opcode.rType) - controlUnit.io.funct3.poke(6.U) - controlUnit.io.funct7_5.poke(false.B) - controlUnit.io.zero.poke(false.B) - - val states = List( - ControlUnitState.fetch, - ControlUnitState.decode, - ControlUnitState.executeR, - ControlUnitState.aluWb - ) - - checkFsmStates(controlUnit, states) - } - } + it("advances from Decode to ExecuteI for I-types") { + cancel("Not yet implemented") } - it("controls the slti instruction") { - simulate(new ControlUnit(withDebug = true)) { controlUnit => - { - controlUnit.io.op.poke(Opcode.immediate) - controlUnit.io.funct3.poke(2.U) - controlUnit.io.funct7_5.poke(false.B) - controlUnit.io.zero.poke(false.B) - - val states = List( - ControlUnitState.fetch, - ControlUnitState.decode, - ControlUnitState.executeI, - ControlUnitState.aluWb - ) - - checkFsmStates(controlUnit, states) - } - } + it("advances from Decode to Jal for jal instruction") { + cancel("Not yet implemented") } - it("controls the sw instruction") { - simulate(new ControlUnit(withDebug = true)) { controlUnit => - { - controlUnit.io.op.poke(Opcode.store) - controlUnit.io.funct3.poke(2.U) - controlUnit.io.funct7_5.poke(false.B) - controlUnit.io.zero.poke(false.B) - - val states = List( - ControlUnitState.fetch, - ControlUnitState.decode, - ControlUnitState.memAdr, - ControlUnitState.memWrite - ) - - checkFsmStates(controlUnit, states) - } - } + it("advances from Decode to Branch for B-types") { + cancel("Not yet implemented") + } + it("advances from MemAdr to MemRead for loads") { + cancel("Not yet implemented") + } + it("advances from MemAdr to MemWrite for stores") { + cancel("Not yet implemented") + } + it("advances from MemRead to MemWb") { + cancel("Not yet implemented") + } + it("advances from MemWb to Fetch") { + cancel("Not yet implemented") + } + it("advances from MemWrite to Fetch") { + cancel("Not yet implemented") + } + it("advances from ExecuteR to AluWb") { + cancel("Not yet implemented") + } + it("advances from AluWb to Fetch") { + cancel("Not yet implemented") + } + it("advances from ExecuteI to AluWb") { + cancel("Not yet implemented") + } + it("advances from Jal to AluWb") { + cancel("Not yet implemented") + } + it("advances from Branch to Fetch") { + cancel("Not yet implemented") } } describe("InstructionDecoder") { diff --git a/src/test/scala/perilus/main.scala b/src/test/scala/perilus/main.scala @@ -219,7 +219,16 @@ class PerilusTests extends AnyFunSpec with ChiselSim { ) } it("sll") { - cancel("Not yet implemented") + testRType( + funct3 = 1, + funct7_5 = false, + rd = 21, + rs1 = 24, + rs2 = 25, + rs1Value = 0xd021eb67, + rs2Value = 17, + operation = (rs1, rs2) => rs1 << rs2 + ) } it("slt") { testRType( @@ -230,20 +239,56 @@ class PerilusTests extends AnyFunSpec with ChiselSim { rs2 = 11, rs1Value = 0xe680ac63, rs2Value = 0x33afcd8f, - operation = (rs1, rs2) => if (toULong(rs1) < toULong(rs2)) 1 else 0 + operation = (rs1, rs2) => if (rs1 < rs2) 1 else 0 ) } it("sltu") { - cancel("Not yet implemented") + testRType( + funct3 = 3, + funct7_5 = false, + rd = 13, + rs1 = 25, + rs2 = 31, + rs1Value = 0x2ea46594, + rs2Value = 0xb4258409, + operation = (rs1, rs2) => if (toULong(rs1) < toULong(rs2)) 1 else 0 + ) } it("xor") { - cancel("Not yet implemented") + testRType( + funct3 = 4, + funct7_5 = false, + rd = 12, + rs1 = 10, + rs2 = 18, + rs1Value = 0xc52f9cac, + rs2Value = 0xd2a4c0dc, + operation = (rs1, rs2) => rs1 ^ rs2 + ) } it("srl") { - cancel("Not yet implemented") + testRType( + funct3 = 5, + funct7_5 = false, + rd = 26, + rs1 = 2, + rs2 = 25, + rs1Value = 0x3fa60574, + rs2Value = 29, + operation = (rs1, rs2) => rs1 >>> rs2 + ) } it("sra") { - cancel("Not yet implemented") + testRType( + funct3 = 5, + funct7_5 = true, + rd = 30, + rs1 = 19, + rs2 = 5, + rs1Value = 0xee91cf2d, + rs2Value = 10, + operation = (rs1, rs2) => rs1 >> rs2 + ) } it("or") { testRType(