perilus

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

commit f8f8f5ab9fe2d59a0cf4a37eb15a37309e512041
parent 1d79656fbee5a3f89cdef2bf54200038de6280e1
Author: Bradley Gannon <bradley@bradleygannon.com>
Date:   Tue, 31 Mar 2026 17:06:27 -0400

Add support for all remaining B-type instructions in RV32I

Diffstat:
Masm.sh | 1-
Msrc/main/scala/alu.scala | 9++++++---
Msrc/main/scala/controlUnit.scala | 18+++++++++++++++---
Msrc/main/scala/main.scala | 1+
Msrc/test/scala/perilus/main.scala | 175++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
5 files changed, 160 insertions(+), 44 deletions(-)

diff --git a/asm.sh b/asm.sh @@ -10,4 +10,3 @@ llvm-objcopy -O binary -j .text "$TMPOBJ" "$TMPBIN" od -An -tx4 -w4 -v "$TMPBIN" | sed "s/^ *//" rm "$TMPOBJ" "$TMPBIN" - diff --git a/src/main/scala/alu.scala b/src/main/scala/alu.scala @@ -8,10 +8,12 @@ class Alu extends Module { val aluControl = Input(AluControl()) val srcA, srcB = Input(UInt(32.W)) val aluResult = Output(UInt(32.W)) - val zero = Output(Bool()) + val zero, lessThan = Output(Bool()) }) io.aluResult := 0.U + io.zero := false.B + io.lessThan := false.B val srcBShift = WireInit(0.U) when(io.srcB(5)) { @@ -26,15 +28,18 @@ class Alu extends Module { } is(AluControl.sub) { io.aluResult := io.srcA - io.srcB + io.zero := io.aluResult === 0.U } is(AluControl.sll) { io.aluResult := io.srcA << srcBShift } is(AluControl.slt) { io.aluResult := (io.srcA.asSInt < io.srcB.asSInt).asUInt + io.lessThan := io.aluResult === 1.U } is(AluControl.sltu) { io.aluResult := io.srcA < io.srcB + io.lessThan := io.aluResult === 1.U } is(AluControl.xor) { io.aluResult := io.srcA ^ io.srcB @@ -52,8 +57,6 @@ class Alu extends Module { io.aluResult := io.srcA & io.srcB } } - - io.zero := io.aluResult === 0.U } object AluControl extends ChiselEnum { diff --git a/src/main/scala/controlUnit.scala b/src/main/scala/controlUnit.scala @@ -13,7 +13,7 @@ class ControlUnit(withDebug: Boolean = false) extends Module { val op = Input(Opcode()) val funct3 = Input(UInt(3.W)) val funct7_5 = Input(Bool()) - val zero = Input(Bool()) + val zero, lessThan = Input(Bool()) val adrSrc, irWrite, memWrite, pcWrite, regWrite = Output(Bool()) val aluSrcA = Output(AluSrcA()) val aluSrcB = Output(AluSrcB()) @@ -171,7 +171,11 @@ class ControlUnit(withDebug: Boolean = false) extends Module { } } - io.pcWrite := (io.zero && branch) || pcUpdate + io.pcWrite := ((branch && ((io.funct3 === 0.U && io.zero) + || (io.funct3 === 1.U && !io.zero) + || ((io.funct3 === 4.U || io.funct3 === 6.U) && io.lessThan) + || ((io.funct3 === 5.U || io.funct3 === 7.U) && !io.lessThan))) + || pcUpdate) aluDecoder.io.aluOp := aluOp aluDecoder.io.funct3 := io.funct3 @@ -201,7 +205,15 @@ class AluDecoder extends Module { io.aluControl := AluControl.add } is(AluOp.branch) { - io.aluControl := AluControl.sub + when(io.funct3 === 0.U || io.funct3 === 1.U) { + io.aluControl := AluControl.sub + }.elsewhen(io.funct3 === 4.U || io.funct3 === 5.U) { + io.aluControl := AluControl.slt + }.elsewhen(io.funct3 === 6.U || io.funct3 === 7.U) { + io.aluControl := AluControl.sltu + }.otherwise { + io.aluControl := AluControl.sub + } } is(AluOp.arithmetic) { when( diff --git a/src/main/scala/main.scala b/src/main/scala/main.scala @@ -137,6 +137,7 @@ class Perilus( } } controlUnit.io.zero := alu.io.zero + controlUnit.io.lessThan := alu.io.lessThan val (opcode, opcodeValid) = Opcode.safe(instr(6, 0)) controlUnit.io.op := opcode diff --git a/src/test/scala/perilus/main.scala b/src/test/scala/perilus/main.scala @@ -563,19 +563,28 @@ class PerilusTests extends AnyFunSpec with ChiselSim { } } describe("executes B-type RV32I instructions") { - it("beq") { - val rs1 = 3 - val rs2 = 6 - val rs3 = 9 - val offset = 16.U + def testBType( + funct3: Int, + rs1: Int, + rs1Value: Int, + rs2: Int, + rs2Value: Int, + imm: Int, + takeBranch: Boolean + ) = { + val immUpper = (((imm & 0x1000) >> 6) | (imm & 0x7e0) >> 5) << 25 + val rs2Shifted = (rs2 & 0x1f) << 20 + val rs1Shifted = (rs1 & 0x1f) << 15 + val funct3Shifted = (funct3 & 0x7) << 12 + val immLower = ((imm & 0x1e) | ((imm & 0x800) >> 11)) << 7 + val op = 0x63 + val instr = immUpper | rs2Shifted | rs1Shifted | funct3Shifted | immLower | op var registerFile = ArrayBuffer.fill(32)(0x00000000) - registerFile(rs1) = 0xabcdef01 - registerFile(rs2) = 0xabcde012 - registerFile(rs3) = 0xabcde012 + registerFile(rs1) = rs1Value + registerFile(rs2) = rs2Value var memory = ArrayBuffer.fill(64)(0x00000000) - memory(0) = 0x02618563 // beq x3, x6, 0x4 - memory(1) = 0x00930863 // beq x6, x9, 0x4 + memory(0) = instr simulate( new Perilus( @@ -587,50 +596,142 @@ class PerilusTests extends AnyFunSpec with ChiselSim { { val perilusDebug = perilus.io.debug.get - // Verify that registers are/aren't equal as expected + perilusDebug.memAddr.poke(0) + perilusDebug.memData.expect(toULong(instr)) perilusDebug.reg.poke(rs1) - val rs1_t = perilusDebug.regData.peek() + perilusDebug.regData.expect(toULong(rs1Value)) perilusDebug.reg.poke(rs2) - val rs2_t = perilusDebug.regData.peek() - perilusDebug.reg.poke(rs3) - val rs3_t = perilusDebug.regData.peek() - assert(rs1_t.litValue != rs2_t.litValue) - assert(rs2_t.litValue == rs3_t.litValue) - - var pc_t = perilus.io.pc.peek() - // execute the first beq instruction:fe64ae23 - // beq x3, x6, 0x4 - perilus.clock.step(3) - - // pc should be at address 1 because x3 != x6 - val expected_1 = pc_t.litValue + 4 - perilus.io.pc.expect(expected_1) - pc_t = perilus.io.pc.peek() + perilusDebug.regData.expect(toULong(rs2Value)) - // execute the second beq instruction: - // beq x6, x9, 0x4 perilus.clock.step(3) - // pc should be at address 5 because x6 == x9 - val expected_2 = pc_t.litValue + offset.litValue - perilus.io.pc.expect(expected_2) + if (takeBranch) { + perilus.io.pc.expect(toULong(imm)) + } else { + perilus.io.pc.expect(4.U) + } } } } + it("beq") { + testBType( + funct3 = 0, + rs1 = 3, + rs1Value = 0xe62cca72, + rs2 = 6, + rs2Value = 0xe62cca72, + imm = 0x548, + takeBranch = true + ) + testBType( + funct3 = 0, + rs1 = 13, + rs1Value = 0x9eec72e3, + rs2 = 4, + rs2Value = 0xb7899861, + imm = 0x794, + takeBranch = false + ) + } it("bne") { - cancel("Not yet implemented") + testBType( + funct3 = 1, + rs1 = 26, + rs1Value = 0x980022bb, + rs2 = 18, + rs2Value = 0xc9b29fd7, + imm = 0x272, + takeBranch = true + ) + testBType( + funct3 = 1, + rs1 = 17, + rs1Value = 0x9eec72e3, + rs2 = 18, + rs2Value = 0x9eec72e3, + imm = -0x2b4, + takeBranch = false + ) } it("blt") { - cancel("Not yet implemented") + testBType( + funct3 = 4, + rs1 = 14, + rs1Value = -0x787a0531, + rs2 = 25, + rs2Value = -0x3f9c200f, + imm = -0x418, + takeBranch = true + ) + testBType( + funct3 = 4, + rs1 = 4, + rs1Value = 0x68c0f4c7, + rs2 = 5, + rs2Value = 0x4fadf777, + imm = 0x162, + takeBranch = false + ) } it("bge") { - cancel("Not yet implemented") + testBType( + funct3 = 5, + rs1 = 1, + rs1Value = 0x3f18402f, + rs2 = 4, + rs2Value = -0x5f292dce, + imm = 0x6a8, + takeBranch = true + ) + testBType( + funct3 = 5, + rs1 = 4, + rs1Value = -0x6c409e00, + rs2 = 30, + rs2Value = 0x7572f2ef, + imm = 0x364, + takeBranch = false + ) } it("bltu") { - cancel("Not yet implemented") + testBType( + funct3 = 6, + rs1 = 28, + rs1Value = 0xc881d8e2, + rs2 = 15, + rs2Value = 0xfbad6f5e, + imm = -0x3b8, + takeBranch = true + ) + testBType( + funct3 = 6, + rs1 = 28, + rs1Value = 0xf85eb707, + rs2 = 1, + rs2Value = 0x97f574ad, + imm = -0x1b0, + takeBranch = false + ) } it("bgeu") { - cancel("Not yet implemented") + testBType( + funct3 = 7, + rs1 = 7, + rs1Value = 0xcac58493, + rs2 = 19, + rs2Value = 0xbbaaac85, + imm = -0x4dc, + takeBranch = true + ) + testBType( + funct3 = 7, + rs1 = 7, + rs1Value = 0x8530cd57, + rs2 = 19, + rs2Value = 0x90f720e9, + imm = 0x756, + takeBranch = false + ) } } describe("skips environment instructions") {