| // Copyright 2023 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 |
| // |
| // https://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. |
| |
| #include "riscv/riscv_i_instructions.h" |
| |
| #include <cstdint> |
| #include <functional> |
| #include <limits> |
| #include <type_traits> |
| |
| #include "mpact/sim/generic/arch_state.h" |
| #include "mpact/sim/generic/data_buffer.h" |
| #include "mpact/sim/generic/instruction.h" |
| #include "mpact/sim/generic/register.h" |
| #include "mpact/sim/generic/type_helpers.h" |
| #include "riscv/riscv_instruction_helpers.h" |
| #include "riscv/riscv_register.h" |
| #include "riscv/riscv_state.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace riscv { |
| |
| void RiscVIllegalInstruction(const Instruction* inst) { |
| auto* state = static_cast<RiscVState*>(inst->state()); |
| state->Trap(/*is_interrupt*/ false, /*trap_value*/ 0, |
| *ExceptionCode::kIllegalInstruction, |
| /*epc*/ inst->address(), inst); |
| } |
| |
| // The following instruction semantic functions implement basic alu operations. |
| // They are used for both register-register and register-immediate versions of |
| // the corresponding instructions. |
| |
| namespace RV32 { |
| |
| using RegisterType = RV32Register; |
| |
| // Register width integer types. These are preferred to uint32_t, etc, for |
| // those instructions that operate on the entire instruction width. |
| using UIntReg = |
| typename std::make_unsigned<typename RegisterType::ValueType>::type; |
| using IntReg = typename std::make_signed<UIntReg>::type; |
| |
| void RiscVIAdd(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a + b; }); |
| } |
| |
| void RiscVISub(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a - b; }); |
| } |
| |
| void RiscVISlt(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, IntReg, IntReg>( |
| instruction, [](IntReg a, IntReg b) { return a < b; }); |
| } |
| |
| void RiscVISltu(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a < b; }); |
| } |
| |
| void RiscVIAnd(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a & b; }); |
| } |
| |
| void RiscVIOr(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a | b; }); |
| } |
| |
| void RiscVIXor(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a ^ b; }); |
| } |
| |
| void RiscVISll(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a << (b & 0x1f); }); |
| } |
| |
| void RiscVISrl(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a >> (b & 0x1f); }); |
| } |
| |
| void RiscVISra(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, IntReg, IntReg>( |
| instruction, [](IntReg a, IntReg b) { return a >> (b & 0x1f); }); |
| } |
| |
| // Load upper immediate. It is assumed that the decoder already shifted the |
| // immediate. Operates on 32 bit quantities, not XLEN bits. |
| void RiscVILui(const Instruction* instruction) { |
| RiscVUnaryOp<RegisterType, uint32_t, uint32_t>( |
| instruction, [](uint32_t lhs) { return lhs & ~0xfff; }); |
| } |
| |
| // Add upper immediate to PC (for PC relative addressing). It is assumed that |
| // the decoder already shifted the immediate. Operates on 32 bit quantities, |
| // not XLEN bits. |
| void RiscVIAuipc(const Instruction* instruction) { |
| RiscVUnaryOp<RegisterType, uint32_t, uint32_t>( |
| instruction, [instruction](uint32_t lhs) { |
| return (lhs & ~0xfff) + instruction->address(); |
| }); |
| } |
| |
| // Jump and link instructions. One using a long offset, the other offset plus |
| // base. |
| void RiscVIJal(const Instruction* instruction) { |
| UIntReg offset = generic::GetInstructionSource<UIntReg>(instruction, 0); |
| UIntReg target = (offset + instruction->address()) & ~0x1; |
| UIntReg return_address = instruction->address() + instruction->size(); |
| auto* db = instruction->Destination(0)->AllocateDataBuffer(); |
| db->SetSubmit<UIntReg>(0, target); |
| auto* state = static_cast<RiscVState*>(instruction->state()); |
| state->set_branch(true); |
| auto* reg = static_cast<generic::RegisterDestinationOperand<UIntReg>*>( |
| instruction->Destination(1)) |
| ->GetRegister(); |
| reg->data_buffer()->Set<UIntReg>(0, return_address); |
| } |
| |
| void RiscVIJalr(const Instruction* instruction) { |
| UIntReg reg_base = generic::GetInstructionSource<UIntReg>(instruction, 0); |
| UIntReg offset = generic::GetInstructionSource<UIntReg>(instruction, 1); |
| UIntReg target = (offset + reg_base) & ~0x1; |
| UIntReg return_address = instruction->address() + instruction->size(); |
| auto* db = instruction->Destination(0)->AllocateDataBuffer(); |
| db->SetSubmit<UIntReg>(0, target); |
| auto* state = static_cast<RiscVState*>(instruction->state()); |
| state->set_branch(true); |
| auto* reg = static_cast<generic::RegisterDestinationOperand<UIntReg>*>( |
| instruction->Destination(1)) |
| ->GetRegister(); |
| reg->data_buffer()->Set<UIntReg>(0, return_address); |
| } |
| |
| } // namespace RV32 |
| |
| namespace RV64 { |
| |
| using RegisterType = RV64Register; |
| |
| // Register width integer types. |
| using UIntReg = |
| typename std::make_unsigned<typename RegisterType::ValueType>::type; |
| using IntReg = typename std::make_signed<UIntReg>::type; |
| using NarrowIntReg = typename ::mpact::sim::generic::NarrowType<IntReg>::type; |
| using NarrowUIntReg = typename ::mpact::sim::generic::NarrowType<UIntReg>::type; |
| |
| void RiscVIAdd(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a + b; }); |
| } |
| |
| void RiscVIAddw(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, IntReg, NarrowIntReg>( |
| instruction, [](NarrowIntReg a, NarrowIntReg b) { |
| IntReg c = static_cast<IntReg>(a + b); |
| return c; |
| }); |
| } |
| |
| void RiscVISub(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a - b; }); |
| } |
| |
| void RiscVISubw(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, IntReg, NarrowIntReg>( |
| instruction, [](NarrowIntReg a, NarrowIntReg b) { |
| IntReg c = static_cast<IntReg>(a - b); |
| return c; |
| }); |
| } |
| |
| void RiscVISlt(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, IntReg, IntReg>( |
| instruction, [](IntReg a, IntReg b) { return a < b; }); |
| } |
| |
| void RiscVISltu(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a < b; }); |
| } |
| |
| void RiscVIAnd(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a & b; }); |
| } |
| |
| void RiscVIOr(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a | b; }); |
| } |
| |
| void RiscVIXor(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a ^ b; }); |
| } |
| |
| void RiscVISll(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a << (b & 0x3f); }); |
| } |
| |
| void RiscVISrl(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a >> (b & 0x3f); }); |
| } |
| |
| void RiscVISra(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, IntReg, IntReg>( |
| instruction, [](IntReg a, IntReg b) { return a >> (b & 0x3f); }); |
| } |
| |
| void RiscVISllw(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, IntReg, NarrowIntReg>( |
| instruction, [](NarrowIntReg a, NarrowIntReg b) -> IntReg { |
| return static_cast<IntReg>(a << (b & 0x1f)); |
| }); |
| } |
| |
| void RiscVISrlw(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, IntReg, NarrowUIntReg>( |
| instruction, [](NarrowUIntReg a, NarrowUIntReg b) -> IntReg { |
| auto c = a >> (b & 0x1f); |
| return static_cast<IntReg>(static_cast<NarrowIntReg>(c)); |
| }); |
| } |
| |
| void RiscVISraw(const Instruction* instruction) { |
| RiscVBinaryOp<RegisterType, IntReg, NarrowIntReg>( |
| instruction, [](NarrowIntReg a, NarrowIntReg b) -> IntReg { |
| return static_cast<IntReg>(a >> (b & 0x1f)); |
| }); |
| } |
| |
| // Load upper immediate. It is assumed that the decoder already shifted the |
| // immediate. |
| void RiscVILui(const Instruction* instruction) { |
| RiscVUnaryOp<RegisterType, IntReg, int32_t>( |
| instruction, [](int32_t lhs) -> IntReg { |
| auto lhs_w = static_cast<IntReg>(lhs); |
| return lhs_w & ~0xfff; |
| }); |
| } |
| |
| // Add upper immediate to PC (for PC relative addressing). It is assumed that |
| // the decoder already shifted the immediate. |
| void RiscVIAuipc(const Instruction* instruction) { |
| RiscVUnaryOp<RegisterType, uint64_t, int32_t>( |
| instruction, [instruction](int32_t lhs) { |
| auto lhs_w = static_cast<IntReg>(lhs & ~0xfff); |
| return lhs_w + instruction->address(); |
| }); |
| } |
| |
| // Jump and link instructions. One using a long offset, the other offset plus |
| // base. |
| void RiscVIJal(const Instruction* instruction) { |
| UIntReg offset = generic::GetInstructionSource<UIntReg>(instruction, 0); |
| UIntReg target = offset + instruction->address(); |
| target &= (std::numeric_limits<UIntReg>::max() << 1); |
| UIntReg return_address = instruction->address() + instruction->size(); |
| auto* db = instruction->Destination(0)->AllocateDataBuffer(); |
| db->SetSubmit<UIntReg>(0, target); |
| auto* state = static_cast<RiscVState*>(instruction->state()); |
| state->set_branch(true); |
| auto* reg = static_cast<generic::RegisterDestinationOperand<UIntReg>*>( |
| instruction->Destination(1)) |
| ->GetRegister(); |
| reg->data_buffer()->Set<UIntReg>(0, return_address); |
| } |
| |
| void RiscVIJalr(const Instruction* instruction) { |
| UIntReg reg_base = generic::GetInstructionSource<UIntReg>(instruction, 0); |
| UIntReg offset = generic::GetInstructionSource<UIntReg>(instruction, 1); |
| UIntReg target = offset + reg_base; |
| target &= (std::numeric_limits<UIntReg>::max() << 1); |
| UIntReg return_address = instruction->address() + instruction->size(); |
| auto* db = instruction->Destination(0)->AllocateDataBuffer(); |
| db->SetSubmit<UIntReg>(0, target); |
| auto* state = static_cast<RiscVState*>(instruction->state()); |
| state->set_branch(true); |
| auto* reg = static_cast<generic::RegisterDestinationOperand<UIntReg>*>( |
| instruction->Destination(1)) |
| ->GetRegister(); |
| reg->data_buffer()->Set<UIntReg>(0, return_address); |
| } |
| |
| } // namespace RV64 |
| |
| void RiscVINop(const Instruction* instruction) {} |
| |
| namespace RV32 { |
| |
| using RegisterType = RV32Register; |
| |
| // Register width integer types. |
| using UIntReg = |
| typename std::make_unsigned<typename RegisterType::ValueType>::type; |
| using IntReg = typename std::make_signed<UIntReg>::type; |
| |
| void RiscVIBeq(const Instruction* instruction) { |
| BranchConditional<RegisterType, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a == b; }); |
| } |
| |
| void RiscVIBne(const Instruction* instruction) { |
| BranchConditional<RegisterType, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a != b; }); |
| } |
| |
| void RiscVIBlt(const Instruction* instruction) { |
| BranchConditional<RegisterType, IntReg>( |
| instruction, [](IntReg a, IntReg b) { return a < b; }); |
| } |
| |
| void RiscVIBltu(const Instruction* instruction) { |
| BranchConditional<RegisterType, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a < b; }); |
| } |
| |
| void RiscVIBge(const Instruction* instruction) { |
| BranchConditional<RegisterType, IntReg>( |
| instruction, [](IntReg a, IntReg b) { return a >= b; }); |
| } |
| |
| void RiscVIBgeu(const Instruction* instruction) { |
| BranchConditional<RegisterType, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a >= b; }); |
| } |
| |
| } // namespace RV32 |
| |
| namespace RV64 { |
| |
| using RegisterType = RV64Register; |
| |
| // Register width integer types. |
| using UIntReg = |
| typename std::make_unsigned<typename RegisterType::ValueType>::type; |
| using IntReg = typename std::make_signed<UIntReg>::type; |
| |
| void RiscVIBeq(const Instruction* instruction) { |
| BranchConditional<RegisterType, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a == b; }); |
| } |
| |
| void RiscVIBne(const Instruction* instruction) { |
| BranchConditional<RegisterType, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a != b; }); |
| } |
| |
| void RiscVIBlt(const Instruction* instruction) { |
| BranchConditional<RegisterType, IntReg>( |
| instruction, [](IntReg a, IntReg b) { return a < b; }); |
| } |
| |
| void RiscVIBltu(const Instruction* instruction) { |
| BranchConditional<RegisterType, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a < b; }); |
| } |
| |
| void RiscVIBge(const Instruction* instruction) { |
| BranchConditional<RegisterType, IntReg>( |
| instruction, [](IntReg a, IntReg b) { return a >= b; }); |
| } |
| |
| void RiscVIBgeu(const Instruction* instruction) { |
| BranchConditional<RegisterType, UIntReg>( |
| instruction, [](UIntReg a, UIntReg b) { return a >= b; }); |
| } |
| |
| } // namespace RV64 |
| |
| namespace RV32 { |
| |
| using RegisterType = RV32Register; |
| |
| void RiscVILd(const Instruction* instruction) { |
| RVLoad<RegisterType, uint64_t>(instruction); |
| } |
| |
| void RiscVILw(const Instruction* instruction) { |
| RVLoad<RegisterType, int32_t>(instruction); |
| } |
| |
| void RiscVILwChild(const Instruction* instruction) { |
| RVLoadChild<RegisterType, int32_t>(instruction); |
| } |
| |
| void RiscVILh(const Instruction* instruction) { |
| RVLoad<RegisterType, int16_t>(instruction); |
| } |
| |
| void RiscVILhChild(const Instruction* instruction) { |
| RVLoadChild<RegisterType, int16_t>(instruction); |
| } |
| |
| void RiscVILhu(const Instruction* instruction) { |
| RVLoad<RegisterType, uint16_t>(instruction); |
| } |
| |
| void RiscVILhuChild(const Instruction* instruction) { |
| RVLoadChild<RegisterType, uint16_t>(instruction); |
| } |
| |
| void RiscVILb(const Instruction* instruction) { |
| RVLoad<RegisterType, int8_t>(instruction); |
| } |
| |
| void RiscVILbChild(const Instruction* instruction) { |
| RVLoadChild<RegisterType, int8_t>(instruction); |
| } |
| |
| void RiscVILbu(const Instruction* instruction) { |
| RVLoad<RegisterType, uint8_t>(instruction); |
| } |
| |
| void RiscVILbuChild(const Instruction* instruction) { |
| RVLoadChild<RegisterType, uint8_t>(instruction); |
| } |
| |
| } // namespace RV32 |
| |
| namespace RV64 { |
| |
| using RegisterType = RV64Register; |
| |
| void RiscVILd(const Instruction* instruction) { |
| RVLoad<RegisterType, uint64_t>(instruction); |
| } |
| |
| void RiscVILdChild(const Instruction* instruction) { |
| RVLoadChild<RegisterType, int64_t>(instruction); |
| } |
| |
| void RiscVILw(const Instruction* instruction) { |
| RVLoad<RegisterType, int32_t>(instruction); |
| } |
| |
| void RiscVILwChild(const Instruction* instruction) { |
| RVLoadChild<RegisterType, int32_t>(instruction); |
| } |
| |
| void RiscVILwu(const Instruction* instruction) { |
| RVLoad<RegisterType, uint32_t>(instruction); |
| } |
| |
| void RiscVILwuChild(const Instruction* instruction) { |
| RVLoadChild<RegisterType, uint32_t>(instruction); |
| } |
| |
| void RiscVILh(const Instruction* instruction) { |
| RVLoad<RegisterType, int16_t>(instruction); |
| } |
| |
| void RiscVILhChild(const Instruction* instruction) { |
| RVLoadChild<RegisterType, int16_t>(instruction); |
| } |
| |
| void RiscVILhu(const Instruction* instruction) { |
| RVLoad<RegisterType, uint16_t>(instruction); |
| } |
| |
| void RiscVILhuChild(const Instruction* instruction) { |
| RVLoadChild<RegisterType, uint16_t>(instruction); |
| } |
| |
| void RiscVILb(const Instruction* instruction) { |
| RVLoad<RegisterType, int8_t>(instruction); |
| } |
| |
| void RiscVILbChild(const Instruction* instruction) { |
| RVLoadChild<RegisterType, int8_t>(instruction); |
| } |
| |
| void RiscVILbu(const Instruction* instruction) { |
| RVLoad<RegisterType, uint8_t>(instruction); |
| } |
| |
| void RiscVILbuChild(const Instruction* instruction) { |
| RVLoadChild<RegisterType, uint8_t>(instruction); |
| } |
| |
| } // namespace RV64 |
| |
| namespace RV32 { |
| |
| using RegisterType = RV32Register; |
| |
| void RiscVISd(const Instruction* instruction) { |
| RVStore<RegisterType, uint64_t>(instruction); |
| } |
| |
| void RiscVISw(const Instruction* instruction) { |
| RVStore<RegisterType, uint32_t>(instruction); |
| } |
| |
| void RiscVISh(const Instruction* instruction) { |
| RVStore<RegisterType, uint16_t>(instruction); |
| } |
| |
| void RiscVISb(const Instruction* instruction) { |
| RVStore<RegisterType, uint8_t>(instruction); |
| } |
| |
| } // namespace RV32 |
| |
| namespace RV64 { |
| |
| using RegisterType = RV64Register; |
| |
| void RiscVISd(const Instruction* instruction) { |
| RVStore<RegisterType, uint64_t>(instruction); |
| } |
| |
| void RiscVISw(const Instruction* instruction) { |
| RVStore<RegisterType, uint32_t>(instruction); |
| } |
| |
| void RiscVISh(const Instruction* instruction) { |
| RVStore<RegisterType, uint16_t>(instruction); |
| } |
| |
| void RiscVISb(const Instruction* instruction) { |
| RVStore<RegisterType, uint8_t>(instruction); |
| } |
| |
| } // namespace RV64 |
| |
| void RiscVIUnimplemented(const Instruction* instruction) { |
| auto* state = static_cast<RiscVState*>(instruction->state()); |
| // Get instruction word, as it needs to be used as trap value. |
| uint64_t address = instruction->address(); |
| auto db = state->db_factory()->Allocate<uint32_t>(1); |
| state->LoadMemory(instruction, address, db, nullptr, nullptr); |
| uint32_t inst_word = db->Get<uint32_t>(0); |
| db->DecRef(); |
| // See if the instruction is interpreted as 32 or 16 bit instruction. |
| if ((inst_word & 0b11) != 0b11) inst_word &= 0xffff; |
| state->Trap(/*is_interrupt=*/false, /*trap_value=*/inst_word, |
| *ExceptionCode::kIllegalInstruction, |
| /*epc=*/instruction->address(), instruction); |
| } |
| |
| void RiscVIFence(const Instruction* instruction) { |
| int pred = generic::GetInstructionSource<uint32_t>(instruction, 0) & 0xf; |
| int succ = generic::GetInstructionSource<uint32_t>(instruction, 1) & 0xf; |
| auto* state = static_cast<RiscVState*>(instruction->state()); |
| // Fence mode is 0x0 |
| state->Fence(instruction, /*fence_mode=*/0x0, pred, succ); |
| } |
| |
| void RiscVIFenceTso(const Instruction* instruction) { |
| auto* state = static_cast<RiscVState*>(instruction->state()); |
| state->Fence(instruction, /*fence_mode=*/0b1000, /*pred=*/0b0011, |
| /*succ=*/0b0011); |
| } |
| |
| void RiscVIEcall(const Instruction* instruction) { |
| auto* state = static_cast<RiscVState*>(instruction->state()); |
| state->ECall(instruction); |
| } |
| |
| void RiscVIEbreak(const Instruction* instruction) { |
| auto* state = static_cast<RiscVState*>(instruction->state()); |
| state->EBreak(instruction); |
| } |
| |
| void RiscVWFI(const Instruction* instruction) { |
| auto* state = static_cast<RiscVState*>(instruction->state()); |
| state->WFI(instruction); |
| } |
| |
| } // namespace riscv |
| } // namespace sim |
| } // namespace mpact |