Add vill bit to config state and trap on bad state.
Change-Id: I09c100b46bcfd64dbf09922efe6ced794ad4fbe9
diff --git a/hdl/chisel/src/kelvin/rvv/RvvCore.scala b/hdl/chisel/src/kelvin/rvv/RvvCore.scala
index 2917755..c80defe 100644
--- a/hdl/chisel/src/kelvin/rvv/RvvCore.scala
+++ b/hdl/chisel/src/kelvin/rvv/RvvCore.scala
@@ -136,6 +136,13 @@
| output [15:0] rd_rob2rt_o_GENI_vxsaturate,""".stripMargin.replaceAll("GENI", i.toString)
}
+ // Add trap interface outputs
+ moduleInterface += """
+ | output trap_valid,
+ | output [31:0] trap_bits_pc,
+ | output [1:0] trap_bits_opcode,
+ | output [24:0] trap_bits_bits,""".stripMargin
+
// Remove last comma/linebreak
moduleInterface = moduleInterface.dropRight(1)
moduleInterface += "\n);\n"
@@ -151,9 +158,8 @@
coreInstantiation += " RVVInstruction [GENN-1:0] inst_data;\n".replaceAll(
"GENN", instructionLanes.toString)
for (i <- 0 until instructionLanes) {
- // TODO(derekjchow): Plumb in pc later
- // coreInstantiation += " assign inst_data[GENI].pc = inst_GENI_bits_pc;\n".replaceAll(
- // "GENI", i.toString)
+ coreInstantiation += " assign inst_data[GENI].pc = inst_GENI_bits_pc;\n".replaceAll(
+ "GENI", i.toString)
coreInstantiation += " assign inst_data[GENI].opcode = RVVOpCode'(inst_GENI_bits_opcode);\n".replaceAll(
"GENI", i.toString)
coreInstantiation += " assign inst_data[GENI].bits = inst_GENI_bits_bits;\n".replaceAll(
@@ -240,6 +246,7 @@
|""".stripMargin
coreInstantiation += " ROB2RT_t [3:0] rd_rob2rt_o;\n"
+ coreInstantiation += " RVVInstruction trap_data;\n"
coreInstantiation += """ RvvCore#(.N (GENN)) core(
| .clk(clk),
@@ -281,7 +288,9 @@
| .config_state(config_state),
| .rvv_idle(rvv_idle),
| .queue_capacity(queue_capacity),
- | .rd_rob2rt_o(rd_rob2rt_o)
+ | .rd_rob2rt_o(rd_rob2rt_o),
+ | .trap_valid_o(trap_valid),
+ | .trap_data_o(trap_data)
|""".stripMargin.replaceAll("GENN", instructionLanes.toString)
coreInstantiation += " );\n"
@@ -302,6 +311,10 @@
| assign rd_rob2rt_o_GENI_vxsaturate = rd_rob2rt_o[GENI].vxsaturate;
|""".stripMargin.replaceAll("GENI", i.toString)
}
+ coreInstantiation += """ assign trap_bits_pc = trap_data.pc;
+ | assign trap_bits_opcode = trap_data.opcode;
+ | assign trap_bits_bits = trap_data.bits;
+ |""".stripMargin
for (i <- 0 until instructionLanes) {
coreInstantiation += " assign inst_GENI_ready = inst_ready[GENI];\n".replaceAll("GENI", i.toString)
}
@@ -344,6 +357,7 @@
val async_rd = Decoupled(new RegfileWriteDataIO)
val rd_rob2rt_o = Vec(4, new Rob2Rt(p))
+ val trap = Output(Valid(new RvvCompressedInstruction))
val vcsr_valid = Output(Bool())
val vcsr_vstart = Output(UInt(7.W))
@@ -447,6 +461,7 @@
rvvCoreWrapper.io.rd <> io.rd
rvvCoreWrapper.io.async_rd <> io.async_rd
rvvCoreWrapper.io.rd_rob2rt_o <> io.rd_rob2rt_o
+ io.trap := rvvCoreWrapper.io.trap
rvvCoreWrapper.io.vstart := Mux(
io.csr.vstart_write.valid, io.csr.vstart_write.bits, vstart)
diff --git a/hdl/chisel/src/kelvin/rvv/RvvDecode.scala b/hdl/chisel/src/kelvin/rvv/RvvDecode.scala
index 7967a5e..e508492 100644
--- a/hdl/chisel/src/kelvin/rvv/RvvDecode.scala
+++ b/hdl/chisel/src/kelvin/rvv/RvvDecode.scala
@@ -37,6 +37,15 @@
val opcode = RvvCompressedOpcode()
val bits = UInt(25.W)
+ def originalEncoding(): UInt = {
+ val lower7bits = MuxLookup(opcode, 0.U)(Seq(
+ RvvCompressedOpcode.RVVLOAD -> "b0000111".U,
+ RvvCompressedOpcode.RVVSTORE -> "b0100111".U,
+ RvvCompressedOpcode.RVVALU -> "b1010111".U,
+ ))
+ Cat(bits, lower7bits)
+ }
+
def funct6(): UInt = {
bits(24, 19)
}
diff --git a/hdl/chisel/src/kelvin/rvv/RvvInterface.scala b/hdl/chisel/src/kelvin/rvv/RvvInterface.scala
index befb80f..86ebc2c 100644
--- a/hdl/chisel/src/kelvin/rvv/RvvInterface.scala
+++ b/hdl/chisel/src/kelvin/rvv/RvvInterface.scala
@@ -72,6 +72,9 @@
// Async scalar regfile writes.
val async_rd = Decoupled(new RegfileWriteDataIO)
+ // Async trap.
+ val trap = Output(Valid(new RvvCompressedInstruction))
+
// Csr Interface.
val csr = new RvvCsrIO(p)
diff --git a/hdl/chisel/src/kelvin/scalar/FaultManager.scala b/hdl/chisel/src/kelvin/scalar/FaultManager.scala
index 5470413..33a2785 100644
--- a/hdl/chisel/src/kelvin/scalar/FaultManager.scala
+++ b/hdl/chisel/src/kelvin/scalar/FaultManager.scala
@@ -39,6 +39,8 @@
}))
val memory_fault = Input(Valid(new FaultInfo(p)))
val ibus_fault = Input(Bool())
+ val rvv_fault = Option.when(p.enableRvv)(Input(
+ Valid(new FaultManagerOutput)))
val undef = Input(Vec(p.instructionLanes, new Bundle {
val inst = UInt(32.W)
}))
@@ -71,39 +73,43 @@
val jalr_fault_idx = PriorityEncoder(io.in.fault.map(_.jalr))
val bxx_fault = io.in.fault.map(_.bxx).reduce(_|_)
val bxx_fault_idx = PriorityEncoder(io.in.fault.map(_.bxx))
- val rvv_fault = io.in.fault.map(_.rvv.getOrElse(false.B)).reduce(_|_)
- val rvv_fault_idx = PriorityEncoder(io.in.fault.map(_.rvv.getOrElse(false.B)))
+ val rvv_dispatch_fault = io.in.fault.map(_.rvv.getOrElse(false.B)).reduce(_|_)
+ val rvv_dispatch_fault_idx = PriorityEncoder(io.in.fault.map(_.rvv.getOrElse(false.B)))
val instr_access_fault = io.in.memory_fault.valid && io.in.ibus_fault
val load_fault = io.in.memory_fault.valid && !io.in.memory_fault.bits.write && !io.in.ibus_fault
val store_fault = io.in.memory_fault.valid && io.in.memory_fault.bits.write && !io.in.ibus_fault
+ val rvv_fault = io.in.rvv_fault.map(_.valid).getOrElse(false.B)
- io.out.valid := fault || io.in.memory_fault.valid
+ io.out.valid := fault || io.in.memory_fault.valid || rvv_fault
io.out.bits.mepc := MuxCase(0.U(32.W), Seq(
load_fault -> io.in.memory_fault.bits.epc,
store_fault -> io.in.memory_fault.bits.epc,
instr_access_fault -> io.in.memory_fault.bits.epc,
+ rvv_fault -> io.in.rvv_fault.map(_.bits.mepc).getOrElse(0.U),
fault -> io.in.pc(first_fault).pc,
))
io.out.bits.mcause := MuxCase(0.U(32.W), Seq(
load_fault -> 5.U(32.W),
store_fault -> 7.U(32.W),
instr_access_fault -> 1.U(32.W),
+ rvv_fault -> io.in.rvv_fault.map(_.bits.mcause).getOrElse(2.U(32.W)),
(csr_fault && (csr_fault_idx === first_fault)) -> 2.U(32.W),
(jal_fault && (jal_fault_idx === first_fault)) -> 0.U(32.W),
(jalr_fault && (jalr_fault_idx === first_fault)) -> 0.U(32.W),
(bxx_fault && (bxx_fault_idx === first_fault)) -> 0.U(32.W),
(undef_fault && (undef_fault_idx === first_fault)) -> 2.U(32.W),
- (rvv_fault && (rvv_fault_idx === first_fault)) -> 2.U(32.W),
+ (rvv_dispatch_fault && (rvv_dispatch_fault_idx === first_fault)) -> 2.U(32.W),
))
io.out.bits.mtval := MuxCase(0.U(32.W), Seq(
load_fault -> io.in.memory_fault.bits.addr,
store_fault -> io.in.memory_fault.bits.addr,
instr_access_fault -> 0.U(32.W),
+ rvv_fault -> io.in.rvv_fault.map(_.bits.mtval).getOrElse(0.U),
(csr_fault && (csr_fault_idx === first_fault)) -> 0.U,
(jal_fault && (jal_fault_idx === first_fault)) -> io.in.jal(jal_fault_idx).target,
(jalr_fault && (jalr_fault_idx === first_fault)) -> (io.in.jalr(jalr_fault_idx).target & "xFFFFFFFE".U),
(bxx_fault && (bxx_fault_idx === first_fault)) -> 0.U(32.W),
(undef_fault && (undef_fault_idx === first_fault)) -> io.in.undef(undef_fault_idx).inst,
- (rvv_fault && (rvv_fault_idx === first_fault)) -> io.in.undef(rvv_fault_idx).inst,
+ (rvv_dispatch_fault && (rvv_dispatch_fault_idx === first_fault)) -> io.in.undef(rvv_dispatch_fault_idx).inst,
))
}
\ No newline at end of file
diff --git a/hdl/chisel/src/kelvin/scalar/SCore.scala b/hdl/chisel/src/kelvin/scalar/SCore.scala
index 999992a..1fe4c1d 100644
--- a/hdl/chisel/src/kelvin/scalar/SCore.scala
+++ b/hdl/chisel/src/kelvin/scalar/SCore.scala
@@ -155,6 +155,13 @@
lsu.io.fault.valid -> lsu.io.fault,
))
fault_manager.io.in.ibus_fault := io.ibus.fault.valid
+ if (p.enableRvv) {
+ fault_manager.io.in.rvv_fault.get.valid := io.rvvcore.get.trap.valid
+ fault_manager.io.in.rvv_fault.get.bits.mepc := io.rvvcore.get.trap.bits.pc
+ fault_manager.io.in.rvv_fault.get.bits.mcause := 2.U(32.W)
+ fault_manager.io.in.rvv_fault.get.bits.mtval :=
+ io.rvvcore.get.trap.bits.originalEncoding()
+ }
bru(0).io.fault_manager.get := fault_manager.io.out
// ---------------------------------------------------------------------------
diff --git a/hdl/verilog/rvv/design/RvvCore.sv b/hdl/verilog/rvv/design/RvvCore.sv
index fdd885a..23c6c1b 100644
--- a/hdl/verilog/rvv/design/RvvCore.sv
+++ b/hdl/verilog/rvv/design/RvvCore.sv
@@ -80,7 +80,11 @@
output logic [$clog2(2*N + 1)-1:0] queue_capacity,
// Writeback from reorder buffer
- output ROB2RT_t [`NUM_RT_UOP-1:0] rd_rob2rt_o
+ output ROB2RT_t [`NUM_RT_UOP-1:0] rd_rob2rt_o,
+
+ // Trap output
+ output logic trap_valid_o,
+ output RVVInstruction trap_data_o
);
logic [N-1:0] frontend_cmd_valid;
RVVCmd [N-1:0] frontend_cmd_data;
@@ -103,6 +107,8 @@
.cmd_data_o(frontend_cmd_data),
.queue_capacity_i(queue_capacity_internal),
.queue_capacity_o(queue_capacity),
+ .trap_valid_o(trap_valid_o),
+ .trap_data_o(trap_data_o),
.config_state_valid(config_state_valid),
.config_state(config_state)
);
diff --git a/hdl/verilog/rvv/design/RvvFrontEnd.sv b/hdl/verilog/rvv/design/RvvFrontEnd.sv
index d69c93c..3946b56 100644
--- a/hdl/verilog/rvv/design/RvvFrontEnd.sv
+++ b/hdl/verilog/rvv/design/RvvFrontEnd.sv
@@ -51,6 +51,10 @@
input logic [CAPACITYBITS-1:0] queue_capacity_i, // Number of elements that can be enqueued
output logic [CAPACITYBITS-1:0] queue_capacity_o,
+ // Trap output.
+ output logic trap_valid_o,
+ output RVVInstruction trap_data_o,
+
// Config state
output config_state_valid,
output RVVConfigState config_state
@@ -135,6 +139,7 @@
(inst_q[i].opcode == RVV) &&
(inst_q[i].bits[7:5] == 3'b111)) begin
if (inst_q[i].bits[24] == 0) begin // vsetvli
+ inst_config_state[i+1].vill = 0;
inst_config_state[i+1].vl = reg_read_data_i[2*i];
inst_config_state[i+1].lmul = RVVLMUL'(inst_q[i].bits[15:13]);
inst_config_state[i+1].sew = RVVSEW'(inst_q[i].bits[18:16]);
@@ -142,6 +147,7 @@
inst_config_state[i+1].ma = inst_q[i].bits[20];
is_setvl[i] = 1;
end else if (inst_q[i].bits[24:23] == 2'b11) begin // vsetivli
+ inst_config_state[i+1].vill = 0;
inst_config_state[i+1].vl =
{{(`VL_WIDTH - 5){1'b0}}, inst_q[i].bits[12:8]};
inst_config_state[i+1].lmul = RVVLMUL'(inst_q[i].bits[15:13]);
@@ -150,6 +156,7 @@
inst_config_state[i+1].ma = inst_q[i].bits[20];
is_setvl[i] = 1;
end else if (inst_q[i].bits[24:23] == 2'b10) begin // vsetvl
+ inst_config_state[i+1].vill = 0;
inst_config_state[i+1].vl = reg_read_data_i[2*i];
inst_config_state[i+1].lmul =
RVVLMUL'(reg_read_data_i[(2*i) + 1][2:0]);
@@ -165,7 +172,7 @@
always_ff @(posedge clk or negedge rstn) begin
if (!rstn) begin
- // TODO(derekjchow): check if RVV spec specifies arch state on reset.
+ config_state_q.vill <= 1; // Config is illegal on reset.
config_state_q.ma <= 0;
config_state_q.ta <= 0;
config_state_q.sew <= SEW8;
@@ -180,13 +187,19 @@
// Propagate outputs
logic [N-1:0] unaligned_cmd_valid;
RVVCmd [N-1:0] unaligned_cmd_data;
+ logic [N-1:0] unaligned_trap_valid; // Should this instruction trap
+ RVVInstruction [N-1:0] unaligned_trap_data;
always_comb begin
for (int i = 0; i < N; i++) begin
- unaligned_cmd_valid[i] = valid_inst_q[i] && !is_setvl[i];
+ unaligned_trap_valid[i] = valid_inst_q[i] && !is_setvl[i] &&
+ inst_config_state[i].vill;
+ unaligned_trap_data[i] = inst_q[i];
+ unaligned_cmd_valid[i] = valid_inst_q[i] && !is_setvl[i] &&
+ !inst_config_state[i].vill;
// Combine instruction + arch state into command
`ifdef TB_SUPPORT
- unaligned_cmd_data[i].insts_pc = inst_q[i].pc;
+ unaligned_cmd_data[i].inst_pc = inst_q[i].pc;
`endif
unaligned_cmd_data[i].opcode = inst_q[i].opcode;
unaligned_cmd_data[i].bits = inst_q[i].bits;
@@ -211,6 +224,27 @@
.data_out(cmd_data_o)
);
+ // Trap
+ logic trap_occurred;
+ RVVInstruction trap_data;
+ assign trap_valid_o = trap_occurred;
+ assign trap_data_o = trap_data;
+ always_comb begin
+ trap_occurred = (unaligned_trap_valid != 0);
+ // Initialize all trap_data fields to some zero value
+ trap_data.pc = '0;
+ trap_data.bits = '0;
+ trap_data.opcode = RVV;
+
+ for (int i = 0; i < N; i++) begin
+ if (unaligned_trap_valid[i]) begin
+ trap_occurred = 1'b1;
+ trap_data = unaligned_trap_data[i];
+ break;
+ end
+ end
+ end
+
// Assertions
`ifndef SYNTHESIS
logic [N-1:0] lsu_requires_rs1_read;
diff --git a/hdl/verilog/rvv/inc/rvv_backend.svh b/hdl/verilog/rvv/inc/rvv_backend.svh
index 170478b..5388cd0 100755
--- a/hdl/verilog/rvv/inc/rvv_backend.svh
+++ b/hdl/verilog/rvv/inc/rvv_backend.svh
@@ -41,6 +41,7 @@
// The architectural configuration state of the RVV core.
typedef struct packed {
+ logic vill; // This configuration is illegal
logic [`VL_WIDTH-1:0] vl; // Max 128, need one extra bit
logic [`VSTART_WIDTH-1:0] vstart;
logic [`VTYPE_VMA_WIDTH-1:0] ma; // 0:inactive element undisturbed, 1:inactive element agnostic
@@ -61,6 +62,7 @@
// A decoded instruction forwarded to the RVVCore from the scalar core.
typedef struct packed {
+ logic [`PC_WIDTH-1:0] pc;
RVVOpCode opcode; // effectively bits [6:0] from instruction
logic [24:0] bits; // bits [31:7] from instruction
} RVVInstruction;
diff --git a/tests/cocotb/BUILD b/tests/cocotb/BUILD
index 464c935..5c2fad2 100644
--- a/tests/cocotb/BUILD
+++ b/tests/cocotb/BUILD
@@ -165,6 +165,7 @@
"core_mini_vmsbf_test",
"core_mini_vmsof_test",
"core_mini_vmsif_test",
+ "core_mini_vill_test",
]
# END_TESTCASES_FOR_rvv_assembly_cocotb_test
diff --git a/tests/cocotb/rvv/BUILD b/tests/cocotb/rvv/BUILD
index 9320989..bb767b6 100644
--- a/tests/cocotb/rvv/BUILD
+++ b/tests/cocotb/rvv/BUILD
@@ -32,6 +32,9 @@
"vcsr_test": {
"srcs": ["vcsr_test.cc"],
},
+ "vill_test": {
+ "srcs": ["vill_test.cc"],
+ },
"viota_test": {
"srcs": ["viota_test.cc"],
},
@@ -63,6 +66,7 @@
"rvv_load.elf",
"vstart_store.elf",
"vcsr_test.elf",
+ "vill_test.elf",
"viota_test.elf",
"vfirst_test.elf",
"vcpop_test.elf",
diff --git a/tests/cocotb/rvv/vill_test.cc b/tests/cocotb/rvv/vill_test.cc
new file mode 100644
index 0000000..2fbcccd
--- /dev/null
+++ b/tests/cocotb/rvv/vill_test.cc
@@ -0,0 +1,43 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Tests instruction raises fault when vill bit is set.
+
+#include <riscv_vector.h>
+#include <stdint.h>
+
+uint32_t faulted __attribute__((section(".data"))) = 0;
+uint32_t mcause __attribute__((section(".data"))) = 0;
+uint32_t mtval __attribute__((section(".data"))) = 0;
+
+// Fault handler to log fault
+extern "C" {
+void kelvin_exception_handler() {
+ faulted = 1;
+ uint32_t local_mcause;
+ asm volatile("csrr %0, mcause" : "=r"(local_mcause));
+ mcause = local_mcause;
+ uint32_t local_mtval;
+ asm volatile("csrr %0, mtval" : "=r"(local_mtval));
+ mtval = local_mtval;
+
+ asm volatile("ebreak");
+ while (1) {}
+}
+}
+
+int main(int argc, char **argv) {
+ asm volatile("vadd.vv v0, v1, v2");
+ return 0;
+}
\ No newline at end of file
diff --git a/tests/cocotb/rvv_assembly_cocotb_test.py b/tests/cocotb/rvv_assembly_cocotb_test.py
index 0c366a0..d66d356 100644
--- a/tests/cocotb/rvv_assembly_cocotb_test.py
+++ b/tests/cocotb/rvv_assembly_cocotb_test.py
@@ -302,4 +302,31 @@
async def core_mini_vmsif_test(dut):
"""Testbench to test vstart!=0 vmsbf."""
await test_vstart_not_zero_failure(
- dut, "kelvin_hw/tests/cocotb/rvv/vmsif_test.elf")
\ No newline at end of file
+ dut, "kelvin_hw/tests/cocotb/rvv/vmsif_test.elf")
+
+
+@cocotb.test()
+async def core_mini_vill_test(dut):
+ core_mini_axi = CoreMiniAxiInterface(dut)
+ await core_mini_axi.init()
+ await core_mini_axi.reset()
+ cocotb.start_soon(core_mini_axi.clock.start())
+ r = runfiles.Create()
+
+ elf_path = r.Rlocation("kelvin_hw/tests/cocotb/rvv/vill_test.elf")
+ if not elf_path:
+ raise ValueError("elf_path must consist a valid path")
+ with open(elf_path, "rb") as f:
+ entry_point = await core_mini_axi.load_elf(f)
+ faulted_addr = core_mini_axi.lookup_symbol(f, "faulted")
+ mcause_addr = core_mini_axi.lookup_symbol(f, "mcause")
+
+ await core_mini_axi.execute_from(entry_point)
+ await core_mini_axi.wait_for_halted()
+
+ faulted_result = (
+ await core_mini_axi.read_word(faulted_addr)).view(np.uint32)[0]
+ assert (faulted_result == 1)
+ mcause_result = (
+ await core_mini_axi.read_word(mcause_addr)).view(np.uint32)[0]
+ assert (mcause_result == 0x2)