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:
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(