From 589f210d4281fd59c632c2febfccf604864080f5 Mon Sep 17 00:00:00 2001 From: Pavel Pisa Date: Sat, 25 Nov 2023 13:30:03 +0100 Subject: [PATCH] Machine: CSR: basic exceptions support functional for external interrupts Signed-off-by: Pavel Pisa --- src/machine/core.cpp | 28 +++++++++--- src/machine/csr/controlstate.cpp | 76 ++++++++++++++++++++------------ src/machine/csr/controlstate.h | 3 +- src/machine/pipeline.h | 3 ++ 4 files changed, 75 insertions(+), 35 deletions(-) diff --git a/src/machine/core.cpp b/src/machine/core.cpp index 0e5afb07..3a66cd59 100644 --- a/src/machine/core.cpp +++ b/src/machine/core.cpp @@ -142,7 +142,7 @@ bool Core::handle_exception( control_state->update_exception_cause(excause); if (control_state->read_internal(CSR::Id::MTVEC) != 0 && !get_step_over_exception(excause)) { - control_state->set_status_exl(true); + control_state->exception_initiate(CSR::PrivilegeLevel::MACHINE, CSR::PrivilegeLevel::MACHINE); regs->write_pc(control_state->exception_pc_address()); } } @@ -297,6 +297,7 @@ DecodeState Core::decode(const FetchInterstage &dt) { .csr = bool(flags & IMF_CSR), .csr_to_alu = bool(flags & IMF_CSR_TO_ALU), .csr_write = csr_write, + .xret = bool(flags & IMF_XRET), .insert_stall_before = bool(flags & IMF_CSR) } }; } @@ -365,6 +366,7 @@ ExecuteState Core::execute(const DecodeInterstage &dt) { .alu_zero = alu_val == 0, .csr = dt.csr, .csr_write = dt.csr_write, + .xret = dt.xret, } }; } @@ -374,6 +376,7 @@ MemoryState Core::memory(const ExecuteInterstage &dt) { bool memread = dt.memread; bool memwrite = dt.memwrite; bool regwrite = dt.regwrite; + Address computed_next_inst_addr; enum ExceptionCause excause = dt.excause; if (excause == EXCAUSE_NONE) { @@ -395,6 +398,13 @@ MemoryState Core::memory(const ExecuteInterstage &dt) { regwrite = false; } + // Conditional branch (BXX = BEQ | BNE...) is executed and should be taken. + const bool branch_bxx_taken = dt.branch_bxx && (!dt.branch_val ^ !dt.alu_zero); + // Unconditional jump should be taken (JALX = JAL | JALR). + const bool branch_jalx = dt.branch_jalr || dt.branch_jal; + + computed_next_inst_addr = compute_next_inst_addr(dt, branch_bxx_taken); + bool csr_written = false; if (control_state != nullptr && dt.is_valid && dt.excause == EXCAUSE_NONE) { control_state->increment_internal(CSR::Id::MINSTRET, 1); @@ -402,13 +412,16 @@ MemoryState Core::memory(const ExecuteInterstage &dt) { control_state->write(dt.csr_address, dt.alu_val); csr_written = true; } + if (dt.xret) { + control_state->exception_return(CSR::PrivilegeLevel::MACHINE); + if (this->xlen == Xlen::_32) + computed_next_inst_addr = Address(control_state->read_internal(CSR::Id::MEPC).as_u32()); + else + computed_next_inst_addr = Address(control_state->read_internal(CSR::Id::MEPC).as_u64()); + csr_written = true; + } } - // Conditional branch (BXX = BEQ | BNE...) is executed and should be taken. - const bool branch_bxx_taken = dt.branch_bxx && (!dt.branch_val ^ !dt.alu_zero); - // Unconditional jump should be taken (JALX = JAL | JALR). - const bool branch_jalx = dt.branch_jalr || dt.branch_jal; - return { MemoryInternalState { .mem_read_val = towrite_val, .mem_write_val = dt.val_rt, @@ -422,13 +435,14 @@ MemoryState Core::memory(const ExecuteInterstage &dt) { .branch_outcome = branch_bxx_taken || branch_jalx, .branch_jalx = branch_jalx, .branch_jalr = dt.branch_jalr, + .xret = dt.xret, }, MemoryInterstage { .inst = dt.inst, .inst_addr = dt.inst_addr, .next_inst_addr = dt.next_inst_addr, .predicted_next_inst_addr = dt.predicted_next_inst_addr, - .computed_next_inst_addr = compute_next_inst_addr(dt, branch_bxx_taken), + .computed_next_inst_addr = computed_next_inst_addr, .mem_addr = mem_addr, .towrite_val = [=]() -> RegisterValue { if (dt.csr) return dt.csr_read_val; diff --git a/src/machine/csr/controlstate.cpp b/src/machine/csr/controlstate.cpp index 89d86a7a..3ee3f0e5 100644 --- a/src/machine/csr/controlstate.cpp +++ b/src/machine/csr/controlstate.cpp @@ -9,14 +9,6 @@ LOG_CATEGORY("machine.csr.control_state"); namespace machine { namespace CSR { - // TODO this is mips - enum StatusReg { - Status_IE = 0x00000001, - Status_EXL = 0x00000002, - Status_ERL = 0x00000004, - Status_IntMask = 0x0000ff00, - }; - ControlState::ControlState() { reset(); } @@ -100,20 +92,34 @@ namespace machine { namespace CSR { void ControlState::update_exception_cause(enum ExceptionCause excause) { RegisterValue &value = register_data[Id::MCAUSE]; - value = value.as_u32() & ~0x80000000 & ~0x0000007f; if (excause != EXCAUSE_INT) { - value = value.as_u32() | static_cast(excause) << 2; + value = static_cast(excause); + } else { + RegisterValue mie = register_data[Id::MIE]; + RegisterValue mip = register_data[Id::MIP]; + int irq_to_signal = 0; + + uint64_t irqs = mie.as_u64() & mip.as_u64() & 0xffffffff; + + // use ffs or __builtin_ffsl where available + for (int i = 0; i < 32; i++) { + if (irqs & (1UL << i)) { + irq_to_signal = i; + break; + } + } + + value = (uint64_t)(irq_to_signal | + ((uint64_t)1 << ((xlen == Xlen::_32)? 31: 63))); } - // TODO: this is known ahead of time emit write_signal(Id::MCAUSE, value); } - // TODO this is mips void ControlState::set_interrupt_signal(uint irq_num, bool active) { - if (irq_num >= 8) { return; } + if (irq_num >= 32) { return; } uint64_t mask = 1 << irq_num; size_t reg_id = Id::MIP; - RegisterValue value = register_data[reg_id]; + RegisterValue &value = register_data[reg_id]; if (active) { value = value.as_xlen(xlen) | mask; } else { @@ -122,29 +128,45 @@ namespace machine { namespace CSR { emit write_signal(reg_id, value); } - // TODO this is mips bool ControlState::core_interrupt_request() { - RegisterValue mstatus = register_data[Id::MSTATUS]; - RegisterValue mcause = register_data[Id::MCAUSE]; + RegisterValue mie = register_data[Id::MIE]; + RegisterValue mip = register_data[Id::MIP]; - uint64_t irqs = mstatus.as_u64() & mcause.as_u64() & Status_IntMask; + uint64_t irqs = mie.as_u64() & mip.as_u64() & 0xffffffff; - return irqs && mstatus.as_u64() & Status_IntMask && !(mstatus.as_u64() & Status_EXL) - && !(mstatus.as_u64() & Status_ERL); + return irqs && read_field(Field::mstatus::MIE).as_u64(); } - // TODO this is mips - void ControlState::set_status_exl(bool value) { + void ControlState::exception_initiate(PrivilegeLevel act_privlev, PrivilegeLevel to_privlev) { size_t reg_id = Id::MSTATUS; RegisterValue ® = register_data[reg_id]; - if (value) { - reg = reg.as_xlen(xlen) & Status_EXL; - } else { - reg = reg.as_xlen(xlen) & ~Status_EXL; - } + Q_UNUSED(act_privlev) + Q_UNUSED(to_privlev) + + write_field(Field::mstatus::MPIE, read_field(Field::mstatus::MIE).as_u32()); + write_field(Field::mstatus::MIE, (uint64_t)0); + + write_field(Field::mstatus::MPP, static_cast(act_privlev)); + emit write_signal(reg_id, reg); } + PrivilegeLevel ControlState::exception_return(enum PrivilegeLevel act_privlev) { + size_t reg_id = Id::MSTATUS; + RegisterValue ® = register_data[reg_id]; + PrivilegeLevel restored_privlev; + Q_UNUSED(act_privlev) + + write_field(Field::mstatus::MIE, read_field(Field::mstatus::MPIE).as_u32()); + + restored_privlev = static_cast(read_field(Field::mstatus::MPP).as_u32()); + write_field(Field::mstatus::MPP, (uint64_t)0); + + emit write_signal(reg_id, reg); + + return restored_privlev; + } + machine::Address ControlState::exception_pc_address() { return machine::Address(register_data[Id::MTVEC].as_u64()); } diff --git a/src/machine/csr/controlstate.h b/src/machine/csr/controlstate.h index fb758f5c..f5346643 100644 --- a/src/machine/csr/controlstate.h +++ b/src/machine/csr/controlstate.h @@ -133,7 +133,8 @@ namespace machine { namespace CSR { public slots: void set_interrupt_signal(uint irq_num, bool active); - void set_status_exl(bool value); + void exception_initiate(PrivilegeLevel act_privlev, PrivilegeLevel to_privlev); + PrivilegeLevel exception_return(enum PrivilegeLevel act_privlev); private: static size_t get_register_internal_id(Address address); diff --git a/src/machine/pipeline.h b/src/machine/pipeline.h index 4da7029a..92b1a74b 100644 --- a/src/machine/pipeline.h +++ b/src/machine/pipeline.h @@ -116,6 +116,7 @@ struct DecodeInterstage { bool csr = false; // Zicsr, implies csr read and csr write bool csr_to_alu = false; bool csr_write = false; + bool xret = false; // Return from exception, MRET and SRET bool insert_stall_before = false; public: @@ -177,6 +178,7 @@ struct ExecuteInterstage { bool alu_zero = false; bool csr = false; bool csr_write = false; + bool xret = false; public: /** Reset to value corresponding to NOP. */ @@ -261,6 +263,7 @@ struct MemoryInternalState { bool branch_outcome = false; bool branch_jalx = false; bool branch_jalr = false; + bool xret = false; }; struct MemoryState {