blob: 0356af0398af221130ee81586e3092228e4468a0 [file] [log] [blame]
/*
* Copyright (c) 2021 Huawei International
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "dev/riscv/clint.hh"
#include "cpu/base.hh"
#include "debug/Clint.hh"
#include "mem/packet.hh"
#include "mem/packet_access.hh"
#include "params/Clint.hh"
#include "sim/system.hh"
namespace gem5
{
using namespace RiscvISA;
Clint::Clint(const Params &params) :
BasicPioDevice(params, params.pio_size),
system(params.system),
signal(params.name + ".signal", 0, this),
registers(params.name + ".registers", params.pio_addr, this)
{
}
void
Clint::raiseInterruptPin(int id)
{
// Increment mtime
uint64_t& mtime = registers.mtime.get();
mtime++;
for (int context_id = 0; context_id < nThread; context_id++) {
auto tc = system->threads[context_id];
// Update misc reg file
ISA* isa = dynamic_cast<ISA*>(tc->getIsaPtr());
isa->setMiscRegNoEffect(MISCREG_TIME, mtime);
// Post timer interrupt
uint64_t mtimecmp = registers.mtimecmp[context_id].get();
if (mtime >= mtimecmp) {
if (mtime == mtimecmp) {
DPRINTF(Clint,
"MTIP posted - thread: %d, mtime: %d, mtimecmp: %d\n",
context_id, mtime, mtimecmp);
}
tc->getCpuPtr()->postInterrupt(tc->threadId(),
ExceptionCode::INT_TIMER_MACHINE, 0);
} else {
tc->getCpuPtr()->clearInterrupt(tc->threadId(),
ExceptionCode::INT_TIMER_MACHINE, 0);
}
}
}
void
Clint::ClintRegisters::init()
{
using namespace std::placeholders;
// Calculate reserved space size
const size_t reserved0_size = mtimecmpStart - clint->nThread * 4;
reserved.emplace_back("reserved0", reserved0_size);
const size_t reserved1_size = mtimeStart
- mtimecmpStart - clint->nThread * 8;
reserved.emplace_back("reserved1", reserved1_size);
// Sanity check
assert((int) clint->pioSize <= maxBankSize);
// Initialize registers
for (int i = 0; i < clint->nThread; i++) {
msip.emplace_back(std::string("msip") + std::to_string(i), 0);
mtimecmp.emplace_back(std::string("mtimecmp") + std::to_string(i), 0);
}
// Add registers to bank
for (int i = 0; i < clint->nThread; i++) {
auto read_cb = std::bind(&Clint::readMSIP, clint, _1, i);
msip[i].reader(read_cb);
auto write_cb = std::bind(&Clint::writeMSIP, clint, _1, _2, i);
msip[i].writer(write_cb);
addRegister(msip[i]);
}
addRegister(reserved[0]);
for (int i = 0; i < clint->nThread; i++) {
addRegister(mtimecmp[i]);
}
addRegister(reserved[1]);
mtime.readonly();
addRegister(mtime);
}
uint32_t
Clint::readMSIP(Register32& reg, const int thread_id)
{
// To avoid discrepancies if mip is externally set using remote_gdb etc.
auto tc = system->threads[thread_id];
RegVal mip = tc->readMiscReg(MISCREG_IP);
uint32_t msip = bits<uint32_t>(mip, ExceptionCode::INT_SOFTWARE_MACHINE);
reg.update(msip);
return reg.get();
};
void
Clint::writeMSIP(Register32& reg, const uint32_t& data, const int thread_id)
{
reg.update(data);
assert(data <= 1);
auto tc = system->threads[thread_id];
if (data > 0) {
DPRINTF(Clint, "MSIP posted - thread: %d\n", thread_id);
tc->getCpuPtr()->postInterrupt(tc->threadId(),
ExceptionCode::INT_SOFTWARE_MACHINE, 0);
} else {
DPRINTF(Clint, "MSIP cleared - thread: %d\n", thread_id);
tc->getCpuPtr()->clearInterrupt(tc->threadId(),
ExceptionCode::INT_SOFTWARE_MACHINE, 0);
}
};
Tick
Clint::read(PacketPtr pkt)
{
// Check for atomic operation
bool is_atomic = pkt->isAtomicOp() && pkt->cmd == MemCmd::SwapReq;
DPRINTF(Clint,
"Read request - addr: %#x, size: %#x, atomic:%d\n",
pkt->getAddr(), pkt->getSize(), is_atomic);
// Perform register read
registers.read(pkt->getAddr(), pkt->getPtr<void>(), pkt->getSize());
if (is_atomic) {
// Perform atomic operation
(*(pkt->getAtomicOp()))(pkt->getPtr<uint8_t>());
return write(pkt);
} else {
pkt->makeResponse();
return pioDelay;
}
}
Tick
Clint::write(PacketPtr pkt)
{
DPRINTF(Clint,
"Write request - addr: %#x, size: %#x\n",
pkt->getAddr(), pkt->getSize());
// Perform register write
registers.write(pkt->getAddr(), pkt->getPtr<void>(), pkt->getSize());
pkt->makeResponse();
return pioDelay;
}
void
Clint::init()
{
nThread = system->threads.size();
registers.init();
BasicPioDevice::init();
}
Port &
Clint::getPort(const std::string &if_name, PortID idx)
{
if (if_name == "int_pin")
return signal;
else
return BasicPioDevice::getPort(if_name, idx);
}
void
Clint::serialize(CheckpointOut &cp) const
{
for (auto const &reg: registers.msip) {
paramOut(cp, reg.name(), reg);
}
for (auto const &reg: registers.mtimecmp) {
paramOut(cp, reg.name(), reg);
}
paramOut(cp, "mtime", registers.mtime);
}
void
Clint::unserialize(CheckpointIn &cp)
{
for (auto &reg: registers.msip) {
paramIn(cp, reg.name(), reg);
}
for (auto &reg: registers.mtimecmp) {
paramIn(cp, reg.name(), reg);
}
paramIn(cp, "mtime", registers.mtime);
}
} // namespace gem5