blob: 5c68132d939161e57b8b58d7a252c33820e3b299 [file]
// Copyright 2024 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_clint.h"
#include <cstdint>
#include <cstring>
#include "absl/log/log.h"
#include "mpact/sim/generic/data_buffer.h"
#include "mpact/sim/generic/instruction.h"
#include "mpact/sim/generic/ref_count.h"
#include "riscv/riscv_state.h"
#include "riscv/riscv_xip_xie.h"
namespace mpact {
namespace sim {
namespace riscv {
using ::mpact::sim::generic::DataBuffer;
using ::mpact::sim::generic::Instruction;
using ::mpact::sim::generic::ReferenceCount;
using EC = ::mpact::sim::riscv::ExceptionCode;
RiscVClint::RiscVClint(int period, MipExternalWriteInterface* mip_interface)
: mip_interface_(mip_interface), period_(period) {
// Set the initial values.
SetValue(0);
mip_interface_->set_mtip(mtip_);
mip_interface->set_msip(msip_ & 0b1);
int bit = mtime_ >= mtimecmp_;
if (bit == mtip_) return;
mip_interface_->set_mtip(bit);
mtip_ = bit;
}
// Reset of the clint block.
void RiscVClint::Reset() {
// Reset clears mtime and msip.
WriteMTimeLow(0);
WriteMTimeHigh(0);
WriteMSip(0);
}
// Called by the counter whenever its value is changed.
void RiscVClint::SetValue(const uint64_t& val) {
update_counter_++;
if (update_counter_ >= period_) {
update_counter_ = 0;
mtime_++;
int bit = mtime_ >= mtimecmp_;
if (bit == mtip_) return;
mip_interface_->set_mtip(bit);
mtip_ = bit;
}
}
// Implementation of the memory load interface for reading memory mapped
// registers.
void RiscVClint::Load(uint64_t address, DataBuffer* db, Instruction* inst,
ReferenceCount* context) {
uint32_t offset = address & 0xffff;
switch (db->size<uint8_t>()) {
case 1:
db->Set<uint8_t>(0, static_cast<uint8_t>(Read(offset)));
break;
case 2:
db->Set<uint16_t>(0, static_cast<uint16_t>(Read(offset)));
break;
case 4:
db->Set<uint32_t>(0, static_cast<uint32_t>(Read(offset)));
break;
case 8:
db->Set<uint32_t>(0, static_cast<uint32_t>(Read(offset)));
db->Set<uint32_t>(1, static_cast<uint32_t>(Read(offset + 4)));
break;
default:
::memset(db->raw_ptr(), 0, sizeof(db->size<uint8_t>()));
break;
}
// Execute the instruction to process and write back the load data.
if (nullptr != inst) {
if (db->latency() > 0) {
inst->IncRef();
if (context != nullptr) context->IncRef();
inst->state()->function_delay_line()->Add(db->latency(),
[inst, context]() {
inst->Execute(context);
if (context != nullptr)
context->DecRef();
inst->DecRef();
});
} else {
inst->Execute(context);
}
}
}
// No support for vector loads.
void RiscVClint::Load(DataBuffer* address_db, DataBuffer* mask_db, int el_size,
DataBuffer* db, Instruction* inst,
ReferenceCount* context) {
LOG(FATAL) << "RiscVClint does not support vector loads";
}
// Implementation of memory store interface to support writes to memory mapped
// registers.
void RiscVClint::Store(uint64_t address, DataBuffer* db) {
uint32_t offset = address & 0xffff;
switch (db->size<uint8_t>()) {
case 1:
return Write(offset, static_cast<uint32_t>(db->Get<uint8_t>(0)));
case 2:
return Write(offset, static_cast<uint32_t>(db->Get<uint16_t>(0)));
case 4:
return Write(offset, static_cast<uint32_t>(db->Get<uint32_t>(0)));
case 8:
return Write(offset, static_cast<uint32_t>(db->Get<uint32_t>(0)));
return Write(offset + 4, static_cast<uint32_t>(db->Get<uint32_t>(1)));
default:
return;
}
}
// No support for vector stores.
void RiscVClint::Store(DataBuffer* address, DataBuffer* mask, int el_size,
DataBuffer* db) {
LOG(FATAL) << "RiscVClint does not support vector stores";
}
uint32_t RiscVClint::Read(uint32_t offset) {
switch (offset) {
case 0x0000: // msip
return msip_;
case 0x4000: // mtimecmp low
return static_cast<uint32_t>(mtimecmp_ & 0xffff'ffffULL);
case 0x4004: // mtimecmp high
return static_cast<uint32_t>(mtimecmp_ >> 32);
case 0xbff8: // mtime low
return ReadMTimeLow();
case 0xbffc: // mtime high
return ReadMTimeHigh();
default:
return 0;
}
}
void RiscVClint::Write(uint32_t offset, uint32_t value) {
switch (offset) {
case 0x0000: // msip
WriteMSip(value);
return;
case 0x4000: // mtimecmp low
WriteMTimeCmpLow(value);
return;
case 0x4004: // mtimecmp high
WriteMTimeCmpHigh(value);
return;
case 0xbff8: // mtime low
WriteMTimeLow(value);
update_counter_ = 0;
return;
case 0xbffc: // mtime high
WriteMTimeHigh(value);
return;
default:
// Ignore write.
return;
}
}
// Reading mtime is equivalent to reading the cycle counter, extracting the
// right half, and adding any offset produced by a previous write to the
// register.
uint32_t RiscVClint::ReadMTimeLow() {
return static_cast<uint32_t>(mtime_ & 0xffff'ffffULL);
}
uint32_t RiscVClint::ReadMTimeHigh() {
return static_cast<uint32_t>(mtime_ >> 32);
}
// Write to msip updates the msip bit in the mip register.
void RiscVClint::WriteMSip(uint32_t value) {
msip_ = value & 0b1;
mip_interface_->set_msip(msip_ & 0b1);
}
// Update the time compare registers.
void RiscVClint::WriteMTimeCmpLow(uint32_t value) {
mtimecmp_ =
(mtimecmp_ & 0xffff'ffff'0000'0000ULL) | static_cast<uint64_t>(value);
int bit = mtime_ >= mtimecmp_;
if (bit == mtip_) return;
mip_interface_->set_mtip(bit);
mtip_ = bit;
}
void RiscVClint::WriteMTimeCmpHigh(uint32_t value) {
mtimecmp_ = (mtimecmp_ & 0x0000'0000'ffff'ffffULL) |
(static_cast<uint64_t>(value) << 32);
int bit = mtime_ >= mtimecmp_;
if (bit == mtip_) return;
mip_interface_->set_mtip(bit);
mtip_ = bit;
}
// Update the mtime register.
void RiscVClint::WriteMTimeLow(uint32_t value) {
mtime_ = (mtime_ & 0xffff'ffff'0000'0000ULL) | static_cast<uint64_t>(value);
}
void RiscVClint::WriteMTimeHigh(uint32_t value) {
mtime_ = (mtime_ & 0xffff'ffffULL) | (static_cast<uint64_t>(value) << 32);
}
} // namespace riscv
} // namespace sim
} // namespace mpact