-
Notifications
You must be signed in to change notification settings - Fork 19
Description
Main Issue: illegal_instruction occurs when accessing vlenb CSR even after mstatus.VS is confirmed set
0. luafile for platform-vp
platform = {
moduletype = "Container";
quantum_ns = 1000000;
-- Router for memory map
-- log level
-- 0: None
-- 1: Error
-- 2: Warning
-- 3: Info
-- 4: Debug
-- 5: Verbose ?
-- 6: Trace
router = {
moduletype = "router";
log_level = 6; -- TRACE
};
-- QEMU Instance Manager
qemu_inst_mgr = {
moduletype = "QemuInstanceManager";
};
-- QEMU Instance (RISC-V64, single core)
qemu_inst = {
moduletype = "QemuInstance";
args = {"&platform.qemu_inst_mgr", "RISCV64"};
qemu_args = "-cpu rv64,v=on,vlen=512";
tcg_mode = "MULTI";
sync_policy = "multithread-unconstrained";
log_level = 6;
};
-- Boot ROM at 0x0 - Bootrom that jumps to main firmware
-- Reset signal
reset = {
moduletype = "sifive_test",
args = {"&platform.qemu_inst"},
target_socket = {address = 0x100000, size = 0x1000, bind = "&router.initiator_socket"};
log_level = 6;
};
bootrom_0 = {
moduletype = "gs_memory";
target_socket = {address = 0x0, size = 0x2000, bind = "&router.initiator_socket"};
shared_memory = false;
reset = {bind = "&reset.reset"};
};
-- Main RAM at 0x80000000 - Main firmware
ram_0 = {
moduletype = "gs_memory";
target_socket = {address = 0x80000000, size = 0x8000000, bind = "&router.initiator_socket"};
shared_memory = true;
reset = {bind = "&reset.reset"};
};
-- Loader module to load binaries
loader = {
moduletype = "loader";
initiator_socket = {bind = "&router.target_socket"};
-- {bin_file = bootrom_path, address = 0x0};
{bin_file = bootrom_path, address = 0x1000};
{bin_file = fw_path, address = 0x80000000};
reset = {bind = "&reset.reset"};
};
gpex_0 = {
moduletype = "qemu_gpex";
args = {"&platform.qemu_inst"};
bus_master = {bind = "&router.target_socket"};
pio_iface = { address = 0x3000000, size = 0x10000, bind= "&router.initiator_socket"};
mmio_iface = { address = 0x40000000, size = 0x40000000, bind= "&router.initiator_socket" };
ecam_iface = { address = 0x30000000, size = 0x10000000, bind= "&router.initiator_socket" };
mmio_iface_high = { address = 0x400000000, size = 0x400000000, bind= "&router.initiator_socket" },
irq_out_0 = {bind = "&plic_0.irq_in_32"};
irq_out_1 = {bind = "&plic_0.irq_in_33"};
irq_out_2 = {bind = "&plic_0.irq_in_34"};
irq_out_3 = {bind = "&plic_0.irq_in_35"};
};
global_peripheral_initiator_riscv_0 = {
moduletype = "global_peripheral_initiator",
args = {"&platform.qemu_inst", "&platform.cpu_0"},
global_initiator = {bind = "&router.target_socket"},
};
-- PLIC (Platform Level Interrupt Controller)
plic_0 = {
moduletype = "plic_sifive",
args = {"&platform.qemu_inst"},
mem = {address=0x0c000000, size=0x600000, bind = "&router.initiator_socket"},
num_sources = 96,
num_priorities = 7,
priority_base = 0x0,
pending_base = 0x1000,
enable_base = 0x2000,
enable_stride = 0x80,
context_base = 0x200000,
context_stride = 0x1000,
aperture_size = 0x600000,
hart_config = "MS";
};
-- CLINT (Core Local Interruptor) - Timer
-- clint_0 = {
-- moduletype = "riscv_aclint_mtimer";
-- args = {"&platform.qemu_inst"};
-- mem = {address = 0x2000000, size = 0x10000, bind = "&router.initiator_socket"};
-- timecmp_base = 0x0;
-- time_base = 0xbff8;
-- provide_rdtime = true;
-- aperture_size = 0x10000;
-- num_harts = 1;
-- };
-- UART0 (16550 compatible)
uart_0 = {
moduletype = "uart_16550",
args = {"&platform.qemu_inst"},
mem = {address= 0x10000000, size=0x100, bind = "&router.initiator_socket"},
irq_out={bind = "&plic_0.irq_in_10"},
regshift=2,
baudbase=3686400;
};
-- RTC (Real Time Clock) - removed for simplicity
-- rtc_0 = {
-- moduletype = "goldfish_rtc";
-- args = {"&platform.qemu_inst"};
-- mem = {address = 0x101000, size = 0x1000, bind = "&router.initiator_socket"};
-- irq = {bind = "&cpu_0.irq_in_1"};
-- };
-- Flash memory (4MB)
flash_0 = {
moduletype = "gs_memory";
target_socket = {address = 0x20000000, size = 0x400000, bind = "&router.initiator_socket"};
shared_memory = false;
};
-- RISC-V64 CPU
cpu_0 = {
moduletype = "cpu_riscv64";
args = {"&platform.qemu_inst", 0};
mem = {bind = "&router.target_socket"};
reset = {bind = "&reset.reset"};
};
}
1. Summary
We are encountering an illegal_instruction (cause 2) exception when trying to initialize the RISC-V 'V' (Vector) extension in our firmware running on the QEMU/QBox platform.
The core of the problem is a contradiction:
- Our firmware successfully sets the
mstatus.VSbits to 'Initial' (0b01). - Our firmware successfully reads back
mstatusand confirms theVSbits are set. - Despite this, the very next attempt to read a vector CSR (
vlenb) immediately fails with anillegal_instructionexception.
2. Firmware Initialization Logic
Our firmware follows the standard procedure to enable the vector unit:
- It first checks
misa(viarvv_is_available()) to confirm the 'V' extension is present. This check passes. - It then reads
mstatus, sets theFSbits (13-14) to 'Initial' (0b01), and writes it back. This succeeds. - It then reads
mstatusagain, sets theVSbits (9-10) to 'Initial' (0b01), and writes it back. This also succeeds. - We added a verification step in our C code to double-check the
VSbits immediately after setting them:This check passes, confirming that the CPU reports// Verify VS field is set mstatus = read_csr(CSR_MSTATUS); if ((mstatus & MSTATUS_VS_MASK) == MSTATUS_VS_INITIAL) { // OK } else { return RVV_INIT_FAILED; // Failsafe }
mstatus.VSas 'Initial'.
3. The Crash
Immediately after this verification passes, the firmware attempts to read vlenb:
// This C code is executed:
uint32_t vlenb = read_csr(CSR_VLENB);This compiles to the following assembly instruction:
0x80000774: c22027f3 csrrs a5,vlenb,zeroWhen this instruction executes, we get an illegal_instruction trap.
4. Confusing Log Entry
Oddly, the trap log we captured shows an epc that does not match the vlenb instruction itself, which is confusing. This may be a separate issue, but it's the log we see when the vlenb read is attempted.
riscv_cpu_do_interrupt: hart:0, async:0, cause:0000000000000002, epc:0x0000000080001168, tval:0x00000000cc07f057, desc=illegal_instruction
5. Questions and Hypothesis
Main Question: Why would an access to vlenb (or any vector CSR) cause an illegal_instruction after the mstatus.VS bits have been successfully set and verified as 'Initial' (0b01)?
Hypothesis: We suspect this might be a libQEMU configuration issue rather than a firmware logic error. Is it possible that the QEMU instance is being launched without the v=true flag (e.g., -cpu rv64,v=true)?
- I found that the same firmware binary works on the standard QEMU (10.1), virt with --kernel loader. So I think it's weird..
- Do I have to build libqemu with additional options for this purpose?