Merge branch 'main' into rv64im

This commit is contained in:
2025-11-13 10:28:22 +01:00
25 changed files with 347 additions and 34 deletions

View File

@@ -1,4 +1,4 @@
# expected: 0000000000000000, FFFFFFFFFFFFFFFF, 0000000000000010 # expected: 0000000000000000, FFFFFFFFFFFFFFFF, 0000000000000010, 0000000000000000
.text .text
_start: _start:
# This test CANNOT use li as it uses addiw under the hood # This test CANNOT use li as it uses addiw under the hood
@@ -12,3 +12,10 @@ _start:
# simple addition # simple addition
addi x1, x0, 8 addi x1, x0, 8
addiw x31, x1, 8 addiw x31, x1, 8
# 32bits trim
lui x1, 0xFFFFF
slli x1, x1, 20
addiw x31, x1, 0

19
bench/tests/op/addw.s Normal file
View File

@@ -0,0 +1,19 @@
# expected: 0000000000000000, FFFFFFFFFFFFFFFF, 0000000000000010, 0000000000000000
.text
_start:
# 0 addition
li x1, 0
addw x31, x0, x1
# all bits addition + sign extension to 32 bits then 64 bits
li x1, 0x00000000FFFFFFFF
addw x31, x0, x1
# simple addition
li x1, 8
li x2, 8
addw x31, x1, x2
# 32bits trim
li x1, 0xFFFFFFFF00000000
addw x31, x0, x1

View File

@@ -13,3 +13,4 @@ step2:
jalr x0, 4(x2) # return to step1, but 1 further jalr x0, 4(x2) # return to step1, but 1 further
finish: finish:
nop nop
addi x31, x0, 0x0AB # This instruction should never be executed

7
bench/tests/op/ld.s Normal file
View File

@@ -0,0 +1,7 @@
# expected: 1023456789ABCDEF
.text
_start:
li x1, 0x1023456789ABCDEF
sd x1, 100(x0)
ld x31, 100(x0)

View File

@@ -1,7 +1,11 @@
# expected: 123451BC # expected: 123451BC, FFFFFFFFF0000000
.text .text
_start: _start:
lui x1, 0x12345 lui x1, 0x12345
ori x1, x1, 0x1BC ori x1, x1, 0x1BC
sw x1, 100(x0) sw x1, 100(x0)
lw x31, 100(x0) lw x31, 100(x0)
li x1, 0x00000000F0000000
sw x1, 100(x0)
lw x31, 100(x0)

6
bench/tests/op/lwu.s Normal file
View File

@@ -0,0 +1,6 @@
# expected: 00000000FFFFFFFF
.text
_start:
li x1, 0xFFFFFFFFFFFFFFFF
sw x1, 100(x0)
lwu x31, 100(x0)

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

@@ -0,0 +1,9 @@
# expected: 000000009ABCDEF0, 0000000012345678
.text
_start:
li x1, 0x123456789ABCDEF0
sd x1, 128(x0)
lwu x31, 128(x0)
lwu x31, 132(x0)

10
bench/tests/op/slliw.s Normal file
View File

@@ -0,0 +1,10 @@
# expected: 0000000000000000, FFFFFFFF80000000
.text
_start:
# Shift out of 32bits
li x1, 0x0000000080000000
slliw x31, x1, 1
# Test sign extension
li x1, 0x0000000040000000
slliw x31, x1, 1

16
bench/tests/op/sllw.s Normal file
View File

@@ -0,0 +1,16 @@
# expected: 0000000000000000, FFFFFFFF80000000, 0000000000000000
.text
_start:
# Shift out of 32bits
li x1, 0x0000000080000000
li x2, 1
sllw x31, x1, x2
# Test sign extension
li x1, 0x0000000040000000
li x2, 1
sllw x31, x1, x2
li x1, 1
li x2, 33
sllw x31, x1, x2

22
bench/tests/op/sraiw.s Normal file
View File

@@ -0,0 +1,22 @@
# expected: 0000000000000000, 000000003FFFFFFF, FFFFFFFFC0000000, FFFFFFFFFFFFFFFF, FFFFFFFFFFFFFFFF
.text
_start:
# Test 1: 1 >> 1 => 0
li x1, 1
sraiw x31, x1, 1 # x31 = 0
# Test 2: 0x7fffffff >> 1 => 0x3fffffff
li x1, 0x7fffffff
sraiw x31, x1, 1 # x31 = 0x000000003fffffff
# Test 3: 0x80000000 >> 1 => 0xc0000000 (sign-extended)
li x1, 0x80000000
sraiw x31, x1, 1 # x31 = 0xffffffffc0000000
# Test 4: 0x80000000 >> 31 => 0xffffffff (-1)
li x1, 0x80000000
sraiw x31, x1, 31 # x31 = 0xffffffffffffffff
# Test 5: 0xffffffff >> 31 => 0xffffffff (-1)
li x1, -1
sraiw x31, x1, 31 # x31 = 0xffffffffffffffff

27
bench/tests/op/sraw.s Normal file
View File

@@ -0,0 +1,27 @@
# expected: 0000000000000000, 000000003FFFFFFF, FFFFFFFFC0000000, FFFFFFFFFFFFFFFF, FFFFFFFFFFFFFFFF
.text
_start:
# Test 1: 1 >> 1 => 0
li x1, 1
li x2, 1
sraw x31, x1, x2 # x31 = 0
# Test 2: 0x7fffffff >> 1 => 0x3fffffff
li x1, 0x7fffffff
li x2, 1
sraw x31, x1, x2 # x31 = 0x000000003fffffff
# Test 3: 0x80000000 >> 1 => 0xc0000000 (sign-extended)
li x1, 0x80000000
li x2, 1
sraw x31, x1, x2 # x31 = 0xffffffffc0000000
# Test 4: 0x80000000 >> 31 => 0xffffffff (-1)
li x1, 0x80000000
li x2, 31
sraw x31, x1, x2 # x31 = 0xffffffffffffffff
# Test 5: 0xffffffff >> 31 => 0xffffffff (-1)
li x1, -1
li x2, 31
sraw x31, x1, x2 # x31 = 0xffffffffffffffff

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

@@ -0,0 +1,14 @@
# expected: 0000000000000000, 000000003fffffff, 0000000040000000
.text
_start:
# Test 1: 1 >> 1 => 0
li x1, 1
srliw x31, x1, 1 # x31 = 0
# Test 2: 0x7fffffff >> 1 => 0x3fffffff
li x1, 0x7fffffff
srliw x31, x1, 1 # x31 = 0x000000003fffffff
# Test 3: 0x80000000 >> 1 => 0x40000000 (not sign-extended)
li x1, 0x80000000
srliw x31, x1, 1 # x31 = 0xffffffffc0000000

22
bench/tests/op/srlw.s Normal file
View File

@@ -0,0 +1,22 @@
# expected: 0000000000000000, 000000003fffffff, 0000000040000000, 0000000000000000
.text
_start:
# Test 1: 1 >> 1 => 0
li x1, 1
li x2, 1
srlw x31, x1, x2 # x31 = 0
# Test 2: 0x7fffffff >> 1 => 0x3fffffff
li x1, 0x7fffffff
li x2, 1
srlw x31, x1, x2 # x31 = 0x000000003fffffff
# Test 3: 0x80000000 >> 1 => 0x40000000 (not sign-extended)
li x1, 0x80000000
li x2, 1
srlw x31, x1, x2 # x31 = 0xffffffffc0000000
li x1, 0x40000000
li x2, 33
srlw x31, x1, x2

19
bench/tests/op/subw.s Normal file
View File

@@ -0,0 +1,19 @@
# expected: 0000000000000000, FFFFFFFFFFFFFFFF, 0000000000000004, 0000000000000000
.text
_start:
# 0 sub
li x1, 0
subw x31, x0, x1
# all bits sub + sign extension to 32 bits then 64 bits
li x1, 1
subw x31, x0, x1
# simple subtraction
li x1, 8
li x2, 4
subw x31, x1, x2
# 32bits trim
li x1, 0xFFFFFFFF00000000
subw x31, x1, x1

View File

@@ -51,6 +51,13 @@ object `TPchisel` extends SbtModule { m =>
) )
val nproc = sys.env.get("NPROC").getOrElse("1"); val nproc = sys.env.get("NPROC").getOrElse("1");
override def forkEnv = T {
super.forkEnv() ++ Map(
"VERILATOR_FLAGS" -> "--O3 --noassert --no-coverage --x-assign fast --x-initial fast --trace-fst -CFLAGS -O3 -CFLAGS -march=native -CFLAGS -flto -LDFLAGS -flto --threads 4",
"VERILATOR_RECOMPILE" -> "false"
)
}
override def forkArgs = super.forkArgs() ++ Seq( override def forkArgs = super.forkArgs() ++ Seq(
"-Dscalatest.parallel.enabled=true", "-Dscalatest.parallel.enabled=true",
"-Dscalatest.parallel.mode=concurrent", "-Dscalatest.parallel.mode=concurrent",

View File

@@ -49,11 +49,11 @@ class Alu extends Module {
switch(io.opcode) { switch(io.opcode) {
is(Add) { is(Add) {
out := io.a + io.b; out := op_a + op_b;
} }
is(Sub) { is(Sub) {
out := io.a - io.b; out := op_a - op_b;
} }
is(Mul) { is(Mul) {
@@ -109,56 +109,61 @@ class Alu extends Module {
} }
is(And) { is(And) {
out := io.a & io.b; out := op_a & op_b;
} }
is(Or) { is(Or) {
out := io.a | io.b; out := op_a | op_b;
} }
is(Xor) { is(Xor) {
out := io.a ^ io.b; out := op_a ^ op_b;
} }
is(ShiftLeft) { is(ShiftLeft) {
out := io.a << io.b(5, 0); out := op_a << op_b(5, 0);
} }
is(ShiftRight) { is(ShiftRight) {
out := io.a >> io.b(5, 0); out := op_a >> op_b(5, 0);
} }
is(ShiftArithmeticRight) { is(ShiftArithmeticRight) {
// >> means arithmetic shift for SInts // >> means arithmetic shift for SInts
out := (io.a.asSInt >> io.b(5, 0)).asUInt; out := (op_a.asSInt >> op_b(5, 0)).asUInt;
// Signed shift on 32bit requires careful handling
when(io.word_mode) {
out := (op_a(31, 0).asSInt >> op_b(5, 0)).asUInt
}
} }
is(LessThanUnsigned) { is(LessThanUnsigned) {
val less_than = io.a < io.b; val less_than = op_a < op_b;
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 = op_a.asSInt < op_b.asSInt
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 = op_a >= op_b;
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 = op_a.asSInt >= op_b.asSInt
out := geq_than out := geq_than
io.comp_result := geq_than; io.comp_result := geq_than;
} }
is(Equal) { is(Equal) {
io.comp_result := io.a === io.b; io.comp_result := op_a === op_b;
} }
} }

View File

@@ -10,6 +10,7 @@ import projet.op.OpStore
import projet.op.OpLoad import projet.op.OpLoad
import projet.op.OpImmW import projet.op.OpImmW
import projet.op.OpM import projet.op.OpM
import projet.op.OpRW
class CUInterface extends Bundle { class CUInterface extends Bundle {
val instruction = Input(UInt(32.W)) val instruction = Input(UInt(32.W))
@@ -54,7 +55,7 @@ class CUInterface extends Bundle {
val mux_regpc_executepc = Output(Bool()) val mux_regpc_executepc = Output(Bool())
// Selects pc adder or write back for pc write // Selects pc adder or write back for pc write
val mux_pcadder_writeback = Output(Bool()) val mux_pcadder_jalrwriteback = Output(Bool())
// Selects pc or write back for register write // Selects pc or write back for register write
val mux_writeback_pc = Output(Bool()) val mux_writeback_pc = Output(Bool())
@@ -117,6 +118,9 @@ object OpCode extends ChiselEnum {
// ~~ Rv64i ~~ // ~~ Rv64i ~~
val OpImmW = "b0011011".U val OpImmW = "b0011011".U
// TypeR
val OpRW = "b0111011".U
} }
class ControlUnit() extends Module { class ControlUnit() extends Module {
@@ -137,7 +141,7 @@ class ControlUnit() extends Module {
io.mux_incrpc4_imm := true.B; io.mux_incrpc4_imm := true.B;
io.mux_regpc_executepc := true.B; io.mux_regpc_executepc := true.B;
io.mux_writeback_pc := true.B; io.mux_writeback_pc := true.B;
io.mux_pcadder_writeback := true.B; io.mux_pcadder_jalrwriteback := true.B;
io.mux_executeout_dout := true.B; io.mux_executeout_dout := true.B;
// Decode instruction // Decode instruction
@@ -175,7 +179,7 @@ class ControlUnit() extends Module {
io.is_jump := true.B io.is_jump := true.B
io.mux_incrpc4_imm := false.B; // Send the immediate in PC io.mux_incrpc4_imm := false.B; // Send the immediate in PC
io.mux_regpc_executepc := false.B; // Add the immediate to the previous PC io.mux_regpc_executepc := false.B; // Add the immediate to the previous PC
io.mux_pcadder_writeback := true.B; io.mux_pcadder_jalrwriteback := true.B;
io.optype := OpType.J io.optype := OpType.J
} }
@@ -190,7 +194,7 @@ class ControlUnit() extends Module {
io.mux_regb_imm := false.B; // Send imm to ALU B io.mux_regb_imm := false.B; // Send imm to ALU B
io.mux_alu_imm := true.B; // Send alu out to write back line io.mux_alu_imm := true.B; // Send alu out to write back line
io.mux_pcadder_writeback := false.B; // Set pc to writeback dest io.mux_pcadder_jalrwriteback := false.B; // Set pc to writeback dest
io.optype := OpType.I io.optype := OpType.I
} }
@@ -222,5 +226,9 @@ class ControlUnit() extends Module {
is(OpCode.OpImmW) { is(OpCode.OpImmW) {
OpImmW.opImmW(io); OpImmW.opImmW(io);
} }
is(OpCode.OpRW) {
OpRW.opRW(io);
}
} }
} }

View File

@@ -80,7 +80,7 @@ class DMem extends Module {
} }
// Data out is available one cycle after the read request // Data out is available one cycle after the read request
val read_addr = Delay.Delay(io.addr, 2, 0.U); val read_addr = Delay.Delay(io.addr, 1, 0.U);
val read_sign_extend = Delay.Delay(io.sign_extend, 1, false.B); val read_sign_extend = Delay.Delay(io.sign_extend, 1, false.B);
when(Delay.Delay(io.en && !io.we, 1, false.B)) { when(Delay.Delay(io.en && !io.we, 1, false.B)) {
switch(Delay.Delay(io.size, 1, DMemSize.Byte)) { switch(Delay.Delay(io.size, 1, DMemSize.Byte)) {

View File

@@ -119,6 +119,8 @@ class Rv64i(sim: Boolean = true) extends Module {
// Select what to send on writeback line // Select what to send on writeback line
val execute_out = Mux(control_unit.io.mux_alu_imm, alu.io.out, imm); val execute_out = Mux(control_unit.io.mux_alu_imm, alu.io.out, imm);
reg_execute_out := execute_out; reg_execute_out := execute_out;
// Jalr line is directly the execute output
val jalr_line = execute_out
reg_mux_executeout_dout := control_unit.io.mux_executeout_dout; reg_mux_executeout_dout := control_unit.io.mux_executeout_dout;
@@ -131,9 +133,9 @@ class Rv64i(sim: Boolean = true) extends Module {
// Writeback pipelining registers // Writeback pipelining registers
reg_pc := Mux( reg_pc := Mux(
Delay.Delay(control_unit.io.mux_pcadder_writeback, 1, true.B), control_unit.io.mux_pcadder_jalrwriteback,
pc_adder_out, pc_adder_out,
writeback_line jalr_line
); );
dmem.io.data_in := rs2_data dmem.io.data_in := rs2_data

View File

@@ -78,7 +78,7 @@ object OpBranch {
// Branch if comparison is successful // Branch if comparison is successful
when(branch) { when(branch) {
io.mux_pcadder_writeback := true.B; io.mux_pcadder_jalrwriteback := true.B;
io.mux_incrpc4_imm := false.B; io.mux_incrpc4_imm := false.B;
io.mux_regpc_executepc := false.B; io.mux_regpc_executepc := false.B;
io.is_jump := true.B; io.is_jump := true.B;

View File

@@ -17,8 +17,7 @@ object OpImmW {
val ADDIW = "b000".U val ADDIW = "b000".U
val SLLIW = "b001".U val SLLIW = "b001".U
// Unimplemented val ShiftWordRight = "b101".U // Right shifts
val SRLIW_SRAIW = "b101".U // Right shifts
} }
io.optype := OpType.I; io.optype := OpType.I;
@@ -26,13 +25,29 @@ object OpImmW {
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 := true.B
switch(funct3) { switch(funct3) {
// Type I // Type I
is(Funct3.ADDIW) { is(Funct3.ADDIW) {
io.alu_word_mode := true.B
io.alu_opcode := AluOpCode.Add io.alu_opcode := AluOpCode.Add
io.optype := OpType.I io.optype := OpType.I
} }
is(Funct3.SLLIW) {
io.alu_opcode := AluOpCode.ShiftLeft
io.optype := OpType.I
}
is(Funct3.ShiftWordRight) {
io.optype := OpType.IR
// Second-to-last bit indicates arithmetic or logical shift
when(io.instruction(30)) {
io.alu_opcode := AluOpCode.ShiftArithmeticRight
}.otherwise {
io.alu_opcode := AluOpCode.ShiftRight
}
}
} }
} }
} }

View File

@@ -15,11 +15,18 @@ object OpLoad {
// Meaning of Funct3 in the context of an OpLoad instruction // Meaning of Funct3 in the context of an OpLoad instruction
object Funct3 extends ChiselEnum { object Funct3 extends ChiselEnum {
// Zero extend
val LB = "b000".U val LB = "b000".U
val LH = "b001".U val LH = "b001".U
val LW = "b010".U val LW = "b010".U
// Sign extend
val LBU = "b100".U val LBU = "b100".U
val LHU = "b101".U val LHU = "b101".U
val LWU = "b110".U
// Arch width
val LD = "b011".U
} }
io.optype := OpType.I io.optype := OpType.I
@@ -32,6 +39,7 @@ object OpLoad {
io.reg_file_we := true.B io.reg_file_we := true.B
io.memory_en := true.B io.memory_en := true.B
io.memory_we := false.B io.memory_we := false.B
io.memory_sign_extend := false.B;
switch(funct3) { switch(funct3) {
// Byte load // Byte load
is(Funct3.LB) { is(Funct3.LB) {
@@ -54,6 +62,15 @@ object OpLoad {
// Word load // Word load
is(Funct3.LW) { is(Funct3.LW) {
io.memory_size := DMemSize.Word io.memory_size := DMemSize.Word
io.memory_sign_extend := true.B;
}
is(Funct3.LWU) {
io.memory_size := DMemSize.Word
}
// Long load
is(Funct3.LD) {
io.memory_size := DMemSize.Long
} }
} }
} }

View File

@@ -0,0 +1,70 @@
package projet.op
import chisel3._
import chisel3.util.switch
import projet.CUInterface
import projet.AluOpCode
import chisel3.util.is
object OpRW {
// Implements functions for all instruction of the opImm, w kind
def opRW(io: CUInterface) = {
val funct3 = io.instruction(14, 12);
val funct7 = io.instruction(31, 25);
// Meaning of Funct3 in the context of an OpImm instruction
object Funct3 extends ChiselEnum {
// Type I
val AdditionSubstraction = "b000".U
val SLLW = "b001".U
val ShiftRight = "b101".U
// Unimplemented
val ShiftWordRight = "b101".U // Right shifts
}
object AdditionSubstractionFunct7 extends ChiselEnum {
val ADDW = "b0000000".U
val SUBW = "b0100000".U
}
object ShiftWordRightFunct7 extends ChiselEnum {
val SRLW = "b0000000".U
val SRAW = "b0100000".U
}
io.reg_file_we := true.B // Write to regfile
io.alu_word_mode := true.B
io.mux_regb_imm := true.B; // OpImm are operations with imm, so send imm to ALU
io.mux_rega_pc := true.B;
io.mux_alu_imm := true.B;
switch(funct3) {
// Type I
is(Funct3.AdditionSubstraction) {
switch(funct7) {
is(AdditionSubstractionFunct7.ADDW) {
io.alu_opcode := AluOpCode.Add
}
is(AdditionSubstractionFunct7.SUBW) {
io.alu_opcode := AluOpCode.Sub
}
}
}
is(Funct3.SLLW) {
io.alu_opcode := AluOpCode.ShiftLeft
}
is(Funct3.ShiftWordRight) {
switch(funct7) {
is(ShiftWordRightFunct7.SRAW) {
io.alu_opcode := AluOpCode.ShiftArithmeticRight
}
is(ShiftWordRightFunct7.SRLW) {
io.alu_opcode := AluOpCode.ShiftRight
}
}
}
}
}
}

View File

@@ -18,6 +18,7 @@ object OpStore {
val SB = "b000".U val SB = "b000".U
val SH = "b001".U val SH = "b001".U
val SW = "b010".U val SW = "b010".U
val SD = "b011".U
} }
io.optype := OpType.S io.optype := OpType.S
@@ -37,6 +38,9 @@ object OpStore {
is(Funct3.SW) { is(Funct3.SW) {
io.memory_size := DMemSize.Word io.memory_size := DMemSize.Word
} }
is(Funct3.SD) {
io.memory_size := DMemSize.Long
}
} }
} }
} }

View File

@@ -7,11 +7,12 @@ import chisel3.simulator.scalatest.ChiselSim
object ZzTopCommon extends AnyFreeSpec with ChiselSim { object ZzTopCommon extends AnyFreeSpec with ChiselSim {
def expectedValuesFromAsm(path: String): Seq[BigInt] = { def expectedValuesFromAsm(path: String): Seq[BigInt] = {
val line = Source
Source.fromFile(path).getLines().find(_.startsWith("# expected:")) .fromFile(path)
line match { .getLines()
case Some(l) => .filter(_.startsWith("# expected:"))
l.stripPrefix("# expected:") .map(
_.stripPrefix("# expected:")
.split(",") .split(",")
.map(_.trim) .map(_.trim)
.filter(_.nonEmpty) .filter(_.nonEmpty)
@@ -24,9 +25,10 @@ object ZzTopCommon extends AnyFreeSpec with ChiselSim {
} }
}) })
.toSeq .toSeq
case None => )
Seq.empty .fold(Seq.empty)((acc, line) => {
} acc.concat(line)
})
} }
def failTrace(msg: String = "Erreur", trace: ListBuffer[String]): Unit = { def failTrace(msg: String = "Erreur", trace: ListBuffer[String]): Unit = {