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.
// 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 =>
override def millSourcePath = super.millSourcePath / os.up
override def scalaVersion = "2.13.16"
@@ -27,6 +38,7 @@ object `TPchisel` extends SbtModule { m =>
"-Ywarn-dead-code",
"-Ywarn-unused"
)
override def moduleDeps = Seq(macros)
override def ivyDeps = Agg(
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.reset
@22
TOP.svsimTestbench.dut.io_x31[31:0]
TOP.svsimTestbench.dut.io_x31[63:0]
@28
TOP.svsimTestbench.dut.io_valid_x31
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
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,
ShiftArithmeticRight, LessThanSigned, LessThanUnsigned,
GreaterEqualSigned, GreaterEqualUnsigned, Equal =
@@ -23,122 +23,87 @@ class Alu extends Module {
val out = Output(UInt(64.W))
val opcode = Input(AluOpCode())
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.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) {
is(Add) {
io.out := io.a + io.b;
out := io.a + io.b;
}
is(Sub) {
io.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));
out := io.a - io.b;
}
is(And) {
io.out := io.a & io.b;
out := io.a & io.b;
}
is(Or) {
io.out := io.a | io.b;
out := io.a | io.b;
}
is(Xor) {
io.out := io.a ^ io.b;
out := io.a ^ io.b;
}
is(ShiftLeft) {
io.out := io.a << io.b(5, 0);
out := io.a << io.b(5, 0);
}
is(ShiftRight) {
io.out := io.a >> io.b(5, 0);
out := io.a >> io.b(5, 0);
}
is(ShiftArithmeticRight) {
// >> 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) {
val less_than = io.a < io.b;
io.out := less_than;
out := less_than;
io.comp_result := less_than;
}
is(LessThanSigned) {
val less_than = io.a.asSInt < io.b.asSInt
io.out := less_than
out := less_than
io.comp_result := less_than;
}
is(GreaterEqualUnsigned) {
val geq_than = io.a >= io.b;
io.out := geq_than
out := geq_than
io.comp_result := geq_than;
}
is(GreaterEqualSigned) {
val geq_than = io.a.asSInt >= io.b.asSInt
io.out := geq_than
out := geq_than
io.comp_result := geq_than;
}

View File

@@ -8,8 +8,11 @@ class CUInterface extends Bundle {
val instruction = Input(UInt(32.W))
val reg_file_we = Output(Bool())
val alu_opcode = Output(AluOpCode())
// TODO: Maybe bundle alu <> cu
// Alu comparison result
val alu_comp_result = Input(Bool())
val alu_word_mode = Output(Bool())
val optype = Output(OpType())
@@ -98,6 +101,9 @@ object OpCode extends ChiselEnum {
// Type I
val OpLoad = "b0000011".U
// ~~ Rv64i ~~
val OpImmW = "b0011011".U
}
class ControlUnit() extends Module {
@@ -105,6 +111,7 @@ class ControlUnit() extends Module {
io.reg_file_we := false.B
io.alu_opcode := AluOpCode.Add
io.alu_word_mode := false.B
io.optype := OpType.U
io.is_jump := false.B;
io.memory_size := DMemSize.Word
@@ -205,6 +212,12 @@ class ControlUnit() extends Module {
io.optype := OpType.I
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
@@ -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_alu_imm := true.B;
io.mux_rega_pc := true.B;
io.alu_word_mode := false.B;
switch(funct3) {
// Type I
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
def opM(funct3: UInt, io: CUInterface) = {
// 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
def opMw(funct3: UInt, io: CUInterface) = {
// 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
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
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
}
}