blob: a93700db098b423dfe0253d5320e48cc05e52e95 [file]
// 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
//
// 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 <cstdint>
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "mpact/sim/generic/data_buffer.h"
#include "mpact/sim/generic/decoder_interface.h"
#include "mpact/sim/generic/instruction.h"
#include "mpact/sim/generic/type_helpers.h"
#include "mpact/sim/util/memory/memory_interface.h"
#include "riscv/riscv_csr.h"
#include "riscv/riscv_state.h"
#ifndef THIRD_PARTY_MPACT_RISCV_RISCV_GENERIC_DECODER_H_
#define THIRD_PARTY_MPACT_RISCV_RISCV_GENERIC_DECODER_H_
namespace mpact::sim::riscv {
using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive.
using ::mpact::sim::generic::Instruction;
template <typename State, typename OpcodeEnum, typename Encoding, typename Isa>
class RiscVGenericDecoder {
public:
RiscVGenericDecoder(State* state, util::MemoryInterface* memory,
Encoding* encoding, Isa* isa)
: state_(state), memory_(memory), encoding_(encoding), isa_(isa) {
auto res =
state_->csr_set()->GetCsr(static_cast<uint64_t>(RiscVCsrEnum::kMIsa));
if (res.ok()) {
isa_csr_ = res.value();
} else {
isa_csr_ = nullptr;
LOG(FATAL) << "Failed to get misa CSR: " << res.status();
}
// Need a data buffer to load instructions from memory. Allocate a single
// buffer that can be reused for each instruction word.
inst_db_ = state_->db_factory()->Allocate<uint32_t>(1);
}
~RiscVGenericDecoder() { inst_db_->DecRef(); }
Instruction* DecodeInstruction(uint64_t address) {
Instruction* instruction = nullptr;
auto status = CheckAddress(address, instruction);
if (!status.ok()) {
LOG(FATAL) << "Decoder error check failed.";
return nullptr;
}
if (instruction != nullptr) return instruction;
// Read the instruction word from memory and parse it in the encoding
// parser.
memory_->Load(address, inst_db_, nullptr, nullptr);
uint32_t iword = inst_db_->Get<uint32_t>(0);
encoding_->ParseInstruction(iword);
// Call the isa decoder to obtain a new instruction object for the
// instruction word that was parsed above.
instruction = isa_->Decode(address, encoding_);
return instruction;
}
private:
// Check that the address is valid. If not, return an instruction object that
// will raise an exception.
absl::Status CheckAddress(uint64_t address, Instruction*& inst) {
if (isa_csr_ == nullptr) {
return absl::InvalidArgumentError("MISA CSR is null");
}
if (inst != nullptr) {
return absl::InvalidArgumentError("Instruction is not null");
}
// First check that the address is aligned properly. If not, create and
// return an instruction object that will raise an exception.
if (address & 0x1) {
inst = new generic::Instruction(0, state_);
inst->set_size(1);
inst->SetDisassemblyString("Misaligned instruction address");
inst->set_opcode(*OpcodeEnum::kNone);
inst->set_address(address);
inst->set_semantic_function([this](generic::Instruction* inst) {
state_->Trap(/*is_interrupt*/ false, inst->address(),
*ExceptionCode::kInstructionAddressMisaligned,
inst->address() ^ 0x1, inst);
});
return absl::OkStatus();
}
// If the C extension is not supported and the address is not 4 byte
// aligned, return an instruction that will raise an exception.
if (((isa_csr_->GetUint64() &
static_cast<uint64_t>(IsaExtensions::kCompressed)) == 0) &&
(address & 0x3)) {
inst = new generic::Instruction(0, state_);
inst->set_size(1);
inst->SetDisassemblyString("Misaligned instruction address");
inst->set_opcode(*OpcodeEnum::kNone);
inst->set_address(address);
inst->set_semantic_function([this](generic::Instruction* inst) {
state_->Trap(/*is_interrupt*/ false, inst->address(),
*ExceptionCode::kInstructionAddressMisaligned,
inst->address() & ~0x3, inst);
});
return absl::OkStatus();
}
// If the address is greater than the max address, return an instruction
// that will raise an exception.
if (address > state_->max_physical_address()) {
inst = new generic::Instruction(0, state_);
inst->set_size(0);
inst->SetDisassemblyString("Instruction access fault");
inst->set_opcode(*OpcodeEnum::kNone);
inst->set_address(address);
inst->set_semantic_function([this](generic::Instruction* inst) {
state_->Trap(/*is_interrupt*/ false, inst->address(),
*ExceptionCode::kInstructionAccessFault, inst->address(),
nullptr);
});
return absl::OkStatus();
}
return absl::OkStatus();
}
RiscVState* state_;
util::MemoryInterface* memory_;
Encoding* encoding_;
Isa* isa_;
generic::DataBuffer* inst_db_;
RiscVCsrInterface* isa_csr_;
};
} // namespace mpact::sim::riscv
#endif // THIRD_PARTY_MPACT_RISCV_RISCV_GENERIC_DECODER_H_