Merge branch 'rv64i' into rv64im

This commit is contained in:
2025-11-11 19:17:46 +01:00
9 changed files with 216 additions and 71 deletions

14
bench/tests/op/addiw.s Normal file
View File

@@ -0,0 +1,14 @@
# expected: 0000000000000000, FFFFFFFFFFFFFFFF, 0000000000000010
.text
_start:
# This test CANNOT use li as it uses addiw under the hood
# 0 addition
addiw x31, x0, 0
# all bits addition + sign extension to 32 bits then 64 bits
addiw x31, x0, (-1)
# simple addition
addi x1, x0, 8
addiw x31, x1, 8

9
bench/tests/op/li.s Normal file
View File

@@ -0,0 +1,9 @@
# exepected: 1234567890ABCDEF, 00000000FFFFFFFF
# li is a PSEUDO-INSTRUCTION.
# This test makes sure we can reliably load 64bit words
# into the processor as it is essential for all other tests
.text
_start:
li x31, 0x1234567890ABCDEF
li x31, 0x00000000FFFFFFFF

View File

@@ -13,6 +13,17 @@ import mill.bsp._
// to properly generate and handle test directories and output files. // to properly generate and handle test directories and output files.
// See: https://github.com/com-lihaoyi/mill/issues/3840 // See: https://github.com/com-lihaoyi/mill/issues/3840
object macros extends ScalaModule {
def scalaVersion = "2.13.16"
def scalacOptions = Seq[String]()
override def ivyDeps = Agg(
ivy"org.chipsalliance::chisel:7.2.0"
)
override def scalacPluginIvyDeps = Agg(
ivy"org.chipsalliance:::chisel-plugin:7.2.0"
)
}
object `TPchisel` extends SbtModule { m => object `TPchisel` extends SbtModule { m =>
override def millSourcePath = super.millSourcePath / os.up override def millSourcePath = super.millSourcePath / os.up
override def scalaVersion = "2.13.16" override def scalaVersion = "2.13.16"
@@ -27,6 +38,7 @@ object `TPchisel` extends SbtModule { m =>
"-Ywarn-dead-code", "-Ywarn-dead-code",
"-Ywarn-unused" "-Ywarn-unused"
) )
override def moduleDeps = Seq(macros)
override def ivyDeps = Agg( override def ivyDeps = Agg(
ivy"org.chipsalliance::chisel:7.2.0" ivy"org.chipsalliance::chisel:7.2.0"
) )

View File

@@ -17,7 +17,7 @@ TOP.svsimTestbench.dut.core.wbVal[31:0]
TOP.svsimTestbench.dut.core.clock TOP.svsimTestbench.dut.core.clock
TOP.svsimTestbench.dut.core.reset TOP.svsimTestbench.dut.core.reset
@22 @22
TOP.svsimTestbench.dut.io_x31[31:0] TOP.svsimTestbench.dut.io_x31[63:0]
@28 @28
TOP.svsimTestbench.dut.io_valid_x31 TOP.svsimTestbench.dut.io_valid_x31
TOP.svsimTestbench.dut.core.rdValid TOP.svsimTestbench.dut.core.rdValid

View File

@@ -0,0 +1,77 @@
package macros.utils
import scala.language.experimental.macros
import scala.reflect.macros.blackbox._
import chisel3._
import chisel3.experimental.SourceInfo
import chisel3.util.BitPat
object bis {
/** Executes `block` if the bswitch condition matchs `v`.
*/
def apply(v: BitPat)(block: => Any): Unit = {
locally((v, block))
require(
false,
"The 'bis' keyword may not be used outside of a bswitch."
)
}
}
class BSwitchContext(
cond: UInt,
whenContext: Option[WhenContext],
lits: Set[BitPat]
) {
def bis(
pat: BitPat
)(block: => Any)(implicit
sourceInfo: SourceInfo
): BSwitchContext = {
require(
!lits.exists(w => {
w.overlap(pat)
}),
"all bis conditions must be mutually exclusive!"
)
// def instead of val so that logic ends up in legal place
def p = (cond === pat)
whenContext match {
case Some(w) =>
new BSwitchContext(
cond,
Some(w.elsewhen(p)(block)),
lits + pat
)
case None =>
new BSwitchContext(
cond,
Some(when(p)(block)),
lits + pat
)
}
}
}
object bswitch {
def apply[T <: Element](cond: T)(x: => Any): Unit = macro impl
def impl(c: Context)(cond: c.Tree)(x: c.Tree): c.Tree = {
import c.universe._
val q"..$body" = x
val res = body.foldLeft(
q"""new macros.utils.BSwitchContext($cond, None, Set.empty)"""
) { case (acc, tree) =>
tree match {
case q"macros.utils.bis.apply( ..$params )( ..$body )" =>
q"$acc.bis( ..$params )( ..$body )"
case _ =>
throw new Exception(
s"Cannot include blocks that do not begin with bis() in bswitch."
)
}
}
q"""{ $res }"""
}
}

View File

@@ -7,7 +7,7 @@ import chisel3.util.Cat
import chisel3.util.Fill import chisel3.util.Fill
object AluOpCode extends ChiselEnum { object AluOpCode extends ChiselEnum {
val Add, AddW, Sub, Mul, Mulh, Mulhsu, Mulhu, Div, Divu, Rem, Remu, Mulw, val Add, Sub, Mul, Mulh, Mulhsu, Mulhu, Div, Divu, Rem, Remu, Mulw,
Divw, Divuw, Remw, Remuw, And, Or, Xor, ShiftLeft, ShiftRight, Divw, Divuw, Remw, Remuw, And, Or, Xor, ShiftLeft, ShiftRight,
ShiftArithmeticRight, LessThanSigned, LessThanUnsigned, ShiftArithmeticRight, LessThanSigned, LessThanUnsigned,
GreaterEqualSigned, GreaterEqualUnsigned, Equal = GreaterEqualSigned, GreaterEqualUnsigned, Equal =
@@ -23,122 +23,87 @@ class Alu extends Module {
val out = Output(UInt(64.W)) val out = Output(UInt(64.W))
val opcode = Input(AluOpCode()) val opcode = Input(AluOpCode())
val comp_result = Output(Bool()) val comp_result = Output(Bool())
// ~~ Rv64i ~~
// If word_mode is enabled, operations are performed
// on 32-bit-truncated inputs and then sign extended to 64bits
val word_mode = Input(Bool())
}) })
io.out := 0.U; io.out := 0.U;
io.comp_result := false.B; io.comp_result := false.B;
val out = WireInit(0.U(64.W))
val op_a = WireInit(0.U(64.W))
val op_b = WireInit(0.U(64.W))
// Defaults
op_a := io.a
op_b := io.b
io.out := out
// Truncate operands, sext result
when(io.word_mode) {
op_a := io.a(31, 0)
op_b := io.b(31, 0)
io.out := SignExtend.Sext(out(31, 0), 64)
}
switch(io.opcode) { switch(io.opcode) {
is(Add) { is(Add) {
io.out := io.a + io.b; out := io.a + io.b;
} }
is(Sub) { is(Sub) {
io.out := io.a - io.b; out := io.a - io.b;
}
is(Mul) {
io.out := (io.a.asSInt * io.b.asSInt).asUInt;
}
is(Mulh) {
io.out := ((io.a.asSInt * io.b.asSInt) >> 64.U).asUInt;
}
is(Mulhsu) {
io.out := ((io.a.asSInt * io.b).asSInt >> 64.U).asUInt;
}
is(Mulhu) {
io.out := (io.a * io.b) >> 64.U;
}
is(Div) {
io.out := (io.a.asSInt / io.b.asSInt).asUInt;
}
is(Divu) {
io.out := io.a / io.b;
}
is(Rem) {
io.out := (io.a.asSInt % io.b.asSInt).asUInt;
}
is(Remu) {
io.out := io.a % io.b;
}
is(Mulw) {
val res = (io.a.asSInt * io.b.asSInt).asUInt
io.out := Cat(Fill(32, res(31)), res(31, 0))
}
is(Divw) {
val res = (io.a(31, 0).asSInt / io.b(31, 0).asSInt).asUInt;
io.out := Cat(Fill(32, res(31)), res(31, 0));
}
is(Divuw) {
val res = io.a(31, 0) / io.b(31, 0);
io.out := Cat(Fill(32, res(31)), res(31, 0));
}
is(Remw) {
val res = (io.a(31, 0).asSInt % io.b(31, 0).asSInt).asUInt;
io.out := Cat(Fill(32, res(31)), res(31, 0));
}
is(Remuw) {
val res = io.a(31, 0) % io.b(31, 0);
io.out := Cat(Fill(32, res(31)), res(31, 0));
} }
is(And) { is(And) {
io.out := io.a & io.b; out := io.a & io.b;
} }
is(Or) { is(Or) {
io.out := io.a | io.b; out := io.a | io.b;
} }
is(Xor) { is(Xor) {
io.out := io.a ^ io.b; out := io.a ^ io.b;
} }
is(ShiftLeft) { is(ShiftLeft) {
io.out := io.a << io.b(5, 0); out := io.a << io.b(5, 0);
} }
is(ShiftRight) { is(ShiftRight) {
io.out := io.a >> io.b(5, 0); out := io.a >> io.b(5, 0);
} }
is(ShiftArithmeticRight) { is(ShiftArithmeticRight) {
// >> means arithmetic shift for SInts // >> means arithmetic shift for SInts
io.out := (io.a.asSInt >> io.b(5, 0)).asUInt; out := (io.a.asSInt >> io.b(5, 0)).asUInt;
} }
is(LessThanUnsigned) { is(LessThanUnsigned) {
val less_than = io.a < io.b; val less_than = io.a < io.b;
io.out := less_than; out := less_than;
io.comp_result := less_than; io.comp_result := less_than;
} }
is(LessThanSigned) { is(LessThanSigned) {
val less_than = io.a.asSInt < io.b.asSInt val less_than = io.a.asSInt < io.b.asSInt
io.out := less_than out := less_than
io.comp_result := less_than; io.comp_result := less_than;
} }
is(GreaterEqualUnsigned) { is(GreaterEqualUnsigned) {
val geq_than = io.a >= io.b; val geq_than = io.a >= io.b;
io.out := geq_than out := geq_than
io.comp_result := geq_than; io.comp_result := geq_than;
} }
is(GreaterEqualSigned) { is(GreaterEqualSigned) {
val geq_than = io.a.asSInt >= io.b.asSInt val geq_than = io.a.asSInt >= io.b.asSInt
io.out := geq_than out := geq_than
io.comp_result := geq_than; io.comp_result := geq_than;
} }

View File

@@ -8,8 +8,11 @@ class CUInterface extends Bundle {
val instruction = Input(UInt(32.W)) val instruction = Input(UInt(32.W))
val reg_file_we = Output(Bool()) val reg_file_we = Output(Bool())
val alu_opcode = Output(AluOpCode()) val alu_opcode = Output(AluOpCode())
// TODO: Maybe bundle alu <> cu
// Alu comparison result // Alu comparison result
val alu_comp_result = Input(Bool()) val alu_comp_result = Input(Bool())
val alu_word_mode = Output(Bool())
val optype = Output(OpType()) val optype = Output(OpType())
@@ -98,6 +101,9 @@ object OpCode extends ChiselEnum {
// Type I // Type I
val OpLoad = "b0000011".U val OpLoad = "b0000011".U
// ~~ Rv64i ~~
val OpImmW = "b0011011".U
} }
class ControlUnit() extends Module { class ControlUnit() extends Module {
@@ -105,6 +111,7 @@ class ControlUnit() extends Module {
io.reg_file_we := false.B io.reg_file_we := false.B
io.alu_opcode := AluOpCode.Add io.alu_opcode := AluOpCode.Add
io.alu_word_mode := false.B
io.optype := OpType.U io.optype := OpType.U
io.is_jump := false.B; io.is_jump := false.B;
io.memory_size := DMemSize.Word io.memory_size := DMemSize.Word
@@ -205,6 +212,12 @@ class ControlUnit() extends Module {
io.optype := OpType.I io.optype := OpType.I
opLoad(funct3, io); 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 // Implements functions for all instruction of the opImm kind
@@ -233,6 +246,7 @@ class ControlUnit() extends Module {
io.mux_regb_imm := false.B; // OpImm are operations with imm, so send imm to ALU io.mux_regb_imm := false.B; // OpImm are operations with imm, so send imm to ALU
io.mux_alu_imm := true.B; io.mux_alu_imm := true.B;
io.mux_rega_pc := true.B; io.mux_rega_pc := true.B;
io.alu_word_mode := false.B;
switch(funct3) { switch(funct3) {
// Type I // Type I
is(Funct3.ADDI) { is(Funct3.ADDI) {
@@ -520,6 +534,45 @@ class ControlUnit() extends Module {
} }
} }
} }
// ~~~ 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 // Implements functions for instructions of the M extension of type R
def opM(funct3: UInt, io: CUInterface) = { def opM(funct3: UInt, io: CUInterface) = {
// Meaning of Funct3 in the context of an OpM instruction // Meaning of Funct3 in the context of an OpM instruction
@@ -561,6 +614,9 @@ class ControlUnit() extends Module {
} }
} }
} }
// ~~ Rv64M ~~
// Implements functions for instructions of the M extension with word size // Implements functions for instructions of the M extension with word size
def opMw(funct3: UInt, io: CUInterface) = { def opMw(funct3: UInt, io: CUInterface) = {
// Meaning of Funct3 in the context of an OpMw instruction // Meaning of Funct3 in the context of an OpMw instruction

View File

@@ -30,6 +30,7 @@ class Rv64i(sim: Boolean = true) extends Module {
dmem.io.sign_extend := control_unit.io.memory_sign_extend dmem.io.sign_extend := control_unit.io.memory_sign_extend
control_unit.io.alu_comp_result := alu.io.comp_result control_unit.io.alu_comp_result := alu.io.comp_result
alu.io.word_mode := control_unit.io.alu_word_mode
// PC delayed to execute stage for auipc op // PC delayed to execute stage for auipc op
val execute_pc = Delay.Delay(reg_pc, 1, 0.U); val execute_pc = Delay.Delay(reg_pc, 1, 0.U);

View File

@@ -0,0 +1,11 @@
package projet
import chisel3.UInt
import chisel3.util.Fill
// Utility function to sign extend number to higher amount of bits
object SignExtend {
def Sext(x: UInt, n: Int): UInt = {
Fill(n - x.getWidth, x(x.getWidth - 1)) ## x
}
}