/*
 * Copyright (c) 2011-2014, 2017-2018 ARM Limited
 * 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.
 *
 * Authors: Dam Sunwoo
 *          Matt Horsnell
 *          Andreas Sandberg
 *          Jose Marinho
 */

#include "arch/arm/pmu.hh"

#include "arch/arm/isa.hh"
#include "arch/arm/utility.hh"
#include "base/trace.hh"
#include "cpu/base.hh"
#include "debug/Checkpoint.hh"
#include "debug/PMUVerbose.hh"
#include "dev/arm/base_gic.hh"
#include "dev/arm/generic_timer.hh"
#include "params/ArmPMU.hh"

namespace ArmISA {

const RegVal PMU::reg_pmcr_wr_mask = 0x39;

PMU::PMU(const ArmPMUParams *p)
    : SimObject(p), BaseISADevice(),
      reg_pmcnten(0), reg_pmcr(0),
      reg_pmselr(0), reg_pminten(0), reg_pmovsr(0),
      reg_pmceid0(0),reg_pmceid1(0),
      clock_remainder(0),
      maximumCounterCount(p->eventCounters),
      cycleCounter(*this, maximumCounterCount),
      cycleCounterEventId(p->cycleEventId),
      swIncrementEvent(nullptr),
      reg_pmcr_conf(0),
      interrupt(p->interrupt->get())
{
    DPRINTF(PMUVerbose, "Initializing the PMU.\n");

    if (maximumCounterCount > 31) {
        fatal("The PMU can only accept 31 counters, %d counters requested.\n",
              maximumCounterCount);
    }

    warn_if(!interrupt, "ARM PMU: No interrupt specified, interrupt " \
            "delivery disabled.\n");

    /* Setup the performance counter ID registers */
    reg_pmcr_conf.imp = 0x41;    // ARM Ltd.
    reg_pmcr_conf.idcode = 0x00;
    reg_pmcr_conf.n = p->eventCounters;

    // Setup the hard-coded cycle counter, which is equivalent to
    // architected counter event type 0x11.
    cycleCounter.eventId = 0x11;
}

PMU::~PMU()
{
}

void
PMU::setThreadContext(ThreadContext *tc)
{
    DPRINTF(PMUVerbose, "Assigning PMU to ContextID %i.\n", tc->contextId());
    if (interrupt)
        interrupt->setThreadContext(tc);
}

void
PMU::addSoftwareIncrementEvent(unsigned int id)
{
    auto old_event = eventMap.find(id);
    DPRINTF(PMUVerbose, "PMU: Adding SW increment event with id '0x%x'\n", id);

    if (swIncrementEvent) {
        fatal_if(old_event == eventMap.end() ||
                 old_event->second != swIncrementEvent,
                 "Trying to add a software increment event with multiple"
                 "IDs. This is not supported.\n");
        return;
    }

    fatal_if(old_event != eventMap.end(), "An event with id %d has "
             "been previously defined\n", id);

    swIncrementEvent = new SWIncrementEvent();
    eventMap[id] = swIncrementEvent;
    registerEvent(id);
}

void
PMU::addEventProbe(unsigned int id, SimObject *obj, const char *probe_name)
{

    DPRINTF(PMUVerbose, "PMU: Adding Probe Driven event with id '0x%x'"
        "as probe %s:%s\n",id, obj->name(), probe_name);

    RegularEvent *event = nullptr;
    auto event_entry = eventMap.find(id);
    if (event_entry == eventMap.end()) {

        event = new RegularEvent();
        eventMap[id] = event;

    } else {
        event = dynamic_cast<RegularEvent*>(event_entry->second);
        if (!event) {
            fatal("Event with id %d is not probe driven\n", id);
        }
    }
    event->addMicroarchitectureProbe(obj, probe_name);

    registerEvent(id);

}

void
PMU::registerEvent(uint32_t id)
{
    // Flag the event as available in the corresponding PMCEID register if it
    // is an architected event.
    if (id < 0x20) {
        reg_pmceid0 |= ((uint64_t)1) << id;
    } else if (id > 0x20 && id < 0x40) {
        reg_pmceid1 |= ((uint64_t)1) << (id - 0x20);
    } else if (id >= 0x4000 && id < 0x4020) {
        reg_pmceid0 |= ((uint64_t)1) << (id - 0x4000 + 32);
    } else if (id >= 0x4020 && id < 0x4040) {
        reg_pmceid1 |= ((uint64_t)1) << (id - 0x4020 + 32);
    }
}

void
PMU::drainResume()
{
    // Re-attach enabled counters after a resume in case they changed.
    updateAllCounters();
}

void
PMU::regProbeListeners()
{

    // at this stage all probe configurations are done
    // counters can be configured
    for (uint32_t index = 0; index < maximumCounterCount-1; index++) {
        counters.emplace_back(*this, index);
    }

    PMUEvent *event = getEvent(cycleCounterEventId);
    panic_if(!event, "core cycle event is not present\n");
    cycleCounter.enabled = true;
    cycleCounter.attach(event);
}

void
PMU::setMiscReg(int misc_reg, RegVal val)
{
    DPRINTF(PMUVerbose, "setMiscReg(%s, 0x%x)\n",
            miscRegName[unflattenMiscReg(misc_reg)], val);

    switch (unflattenMiscReg(misc_reg)) {
      case MISCREG_PMCR_EL0:
      case MISCREG_PMCR:
        setControlReg(val);
        return;

      case MISCREG_PMCNTENSET_EL0:
      case MISCREG_PMCNTENSET:
        reg_pmcnten |= val;
        updateAllCounters();
        return;

      case MISCREG_PMCNTENCLR_EL0:
      case MISCREG_PMCNTENCLR:
        reg_pmcnten &= ~val;
        updateAllCounters();
        return;

      case MISCREG_PMOVSCLR_EL0:
      case MISCREG_PMOVSR:
        setOverflowStatus(reg_pmovsr & ~val);
        return;

      case MISCREG_PMSWINC_EL0:
      case MISCREG_PMSWINC:
        if (swIncrementEvent) {
            swIncrementEvent->write(val);
        }
        return;

      case MISCREG_PMCCNTR_EL0:
      case MISCREG_PMCCNTR:
        cycleCounter.setValue(val);
        return;

      case MISCREG_PMSELR_EL0:
      case MISCREG_PMSELR:
        reg_pmselr = val;
        return;
      //TODO: implement MISCREF_PMCEID{2,3}
      case MISCREG_PMCEID0_EL0:
      case MISCREG_PMCEID0:
      case MISCREG_PMCEID1_EL0:
      case MISCREG_PMCEID1:
        // Ignore writes
        return;

      case MISCREG_PMEVTYPER0_EL0...MISCREG_PMEVTYPER5_EL0:
        setCounterTypeRegister(misc_reg - MISCREG_PMEVTYPER0_EL0, val);
        return;

      case MISCREG_PMCCFILTR:
      case MISCREG_PMCCFILTR_EL0:
        DPRINTF(PMUVerbose, "Setting PMCCFILTR: 0x%x\n", val);
        setCounterTypeRegister(PMCCNTR, val);
        return;

      case MISCREG_PMXEVTYPER_PMCCFILTR:
      case MISCREG_PMXEVTYPER_EL0:
      case MISCREG_PMXEVTYPER:
        DPRINTF(PMUVerbose, "Setting counter type: "
                "[PMSELR: 0x%x, PMSELER.sel: 0x%x, EVTYPER: 0x%x]\n",
                reg_pmselr, reg_pmselr.sel, val);
        setCounterTypeRegister(reg_pmselr.sel, val);
        return;

      case MISCREG_PMEVCNTR0_EL0...MISCREG_PMEVCNTR5_EL0:
        setCounterValue(misc_reg - MISCREG_PMEVCNTR0_EL0, val);
        return;

      case MISCREG_PMXEVCNTR_EL0:
      case MISCREG_PMXEVCNTR:
        setCounterValue(reg_pmselr.sel, val);
        return;

      case MISCREG_PMUSERENR_EL0:
      case MISCREG_PMUSERENR:
        // TODO
        break;

      case MISCREG_PMINTENSET_EL1:
      case MISCREG_PMINTENSET:
        reg_pminten |= val;
        return;

      case MISCREG_PMINTENCLR_EL1:
      case MISCREG_PMINTENCLR:
        reg_pminten &= ~val;
        return;

      case MISCREG_PMOVSSET_EL0:
      case MISCREG_PMOVSSET:
        setOverflowStatus(reg_pmovsr | val);
        return;

      default:
        panic("Unexpected PMU register: %i\n", miscRegName[misc_reg]);
    }

    warn("Not doing anything for write to miscreg %s\n",
         miscRegName[misc_reg]);
}

RegVal
PMU::readMiscReg(int misc_reg)
{
    RegVal val(readMiscRegInt(misc_reg));
    DPRINTF(PMUVerbose, "readMiscReg(%s): 0x%x\n",
            miscRegName[unflattenMiscReg(misc_reg)], val);
    return val;
}

RegVal
PMU::readMiscRegInt(int misc_reg)
{
    misc_reg = unflattenMiscReg(misc_reg);
    switch (misc_reg) {
      case MISCREG_PMCR_EL0:
      case MISCREG_PMCR:
        return reg_pmcr_conf | (reg_pmcr & reg_pmcr_wr_mask);

      case MISCREG_PMCNTENSET_EL0:
      case MISCREG_PMCNTENCLR_EL0:
      case MISCREG_PMCNTENSET:
      case MISCREG_PMCNTENCLR:
        return reg_pmcnten;

      case MISCREG_PMOVSCLR_EL0:
      case MISCREG_PMOVSSET_EL0:
      case MISCREG_PMOVSR:  // Overflow Status Register
      case MISCREG_PMOVSSET:
        return reg_pmovsr;

      case MISCREG_PMSWINC_EL0:
      case MISCREG_PMSWINC: // Software Increment Register (RAZ)
        return 0;

      case MISCREG_PMSELR:
        return reg_pmselr;

      case MISCREG_PMCEID0_EL0:
        return reg_pmceid0;

      case MISCREG_PMCEID1_EL0:
        return reg_pmceid1;

      //TODO: implement MISCREF_PMCEID{2,3}
      case MISCREG_PMCEID0: // Common Event ID register
        return reg_pmceid0 & 0xFFFFFFFF;

      case MISCREG_PMCEID1: // Common Event ID register
        return reg_pmceid1 & 0xFFFFFFFF;

      case MISCREG_PMCCNTR_EL0:
        return cycleCounter.getValue();

      case MISCREG_PMCCNTR:
        return cycleCounter.getValue() & 0xFFFFFFFF;

      case MISCREG_PMEVTYPER0_EL0...MISCREG_PMEVTYPER5_EL0:
        return getCounterTypeRegister(misc_reg - MISCREG_PMEVTYPER0_EL0);

      case MISCREG_PMCCFILTR:
      case MISCREG_PMCCFILTR_EL0:
        return getCounterTypeRegister(PMCCNTR);

      case MISCREG_PMXEVTYPER_PMCCFILTR:
      case MISCREG_PMXEVTYPER_EL0:
      case MISCREG_PMXEVTYPER:
        return getCounterTypeRegister(reg_pmselr.sel);

      case MISCREG_PMEVCNTR0_EL0...MISCREG_PMEVCNTR5_EL0: {
            return getCounterValue(misc_reg - MISCREG_PMEVCNTR0_EL0) &
                0xFFFFFFFF;

        }

      case MISCREG_PMXEVCNTR_EL0:
      case MISCREG_PMXEVCNTR:
        return getCounterValue(reg_pmselr.sel) & 0xFFFFFFFF;

      case MISCREG_PMUSERENR_EL0:
      case MISCREG_PMUSERENR:
        // TODO
        return 0;

      case MISCREG_PMINTENSET_EL1:
      case MISCREG_PMINTENCLR_EL1:
      case MISCREG_PMINTENSET:
      case MISCREG_PMINTENCLR:
        return reg_pminten;

      default:
        panic("Unexpected PMU register: %i\n", miscRegName[misc_reg]);
    }

    warn("Not doing anything for read from miscreg %s\n",
         miscRegName[misc_reg]);
    return 0;
}

void
PMU::setControlReg(PMCR_t val)
{
    DPRINTF(PMUVerbose, "Set Control Reg 0x%08x.\n", val);

    if (val.p) {
        DPRINTF(PMUVerbose, "PMU reset all events to zero.\n");
        resetEventCounts();
    }

    if (val.c) {
        DPRINTF(PMUVerbose, "PMU reset cycle counter to zero.\n");
        cycleCounter.setValue(0);
    }

    // Reset the clock remainder if divide by 64-mode is toggled.
    if (reg_pmcr.d != val.d)
        clock_remainder = 0;

    reg_pmcr = val & reg_pmcr_wr_mask;
    updateAllCounters();
}

void
PMU::updateAllCounters()
{
    const bool global_enable(reg_pmcr.e);

    for (int i = 0; i < counters.size(); ++i) {
        CounterState &ctr(counters[i]);
        const bool enable(global_enable && (reg_pmcnten & (1 << i)));
        if (ctr.enabled != enable) {
            ctr.enabled = enable;
            updateCounter(ctr);
        }
    }

    const bool ccntr_enable(global_enable && (reg_pmcnten & (1 << PMCCNTR)));
    if (cycleCounter.enabled != ccntr_enable) {
        cycleCounter.enabled = ccntr_enable;
        updateCounter(cycleCounter);
    }
}

void
PMU::PMUEvent::attachEvent(PMU::CounterState *user)
{
    if (userCounters.empty()) {
        enable();
    }
    userCounters.insert(user);
    updateAttachedCounters();
}

void
PMU::PMUEvent::increment(const uint64_t val)
{
    for (auto& counter: userCounters) {
        counter->add(val);
    }
}

void
PMU::PMUEvent::detachEvent(PMU::CounterState *user)
{
    userCounters.erase(user);

    if (userCounters.empty()) {
        disable();
    }
}

void
PMU::RegularEvent::RegularProbe::notify(const uint64_t &val)
{
    parentEvent->increment(val);
}

void
PMU::RegularEvent::enable()
{
    for (auto& subEvents: microArchitectureEventSet) {
        attachedProbePointList.emplace_back(
            new RegularProbe(this, subEvents.first, subEvents.second));
    }
}

void
PMU::RegularEvent::disable()
{
    attachedProbePointList.clear();
}

bool
PMU::CounterState::isFiltered() const
{
    assert(pmu.isa);

    const PMEVTYPER_t filter(this->filter);
    const SCR scr(pmu.isa->readMiscRegNoEffect(MISCREG_SCR));
    const CPSR cpsr(pmu.isa->readMiscRegNoEffect(MISCREG_CPSR));
    const ExceptionLevel el(opModeToEL((OperatingMode)(uint8_t)cpsr.mode));
    const bool secure(inSecureState(scr, cpsr));

    switch (el) {
      case EL0:
        return secure ? filter.u : (filter.u != filter.nsu);

      case EL1:
        return secure ? filter.p : (filter.p != filter.nsk);

      case EL2:
        return !filter.nsh;

      case EL3:
        return filter.p != filter.m;

      default:
        panic("Unexpected execution level in PMU::isFiltered.\n");
    }
}

void
PMU::CounterState::detach()
{
    if (sourceEvent) {
        sourceEvent->detachEvent(this);
        sourceEvent = nullptr;
    } else {
        debugCounter("detaching event not currently attached"
            " to any event\n");
    }
}

void
PMU::CounterState::attach(PMUEvent* event)
{
    value = 0;
    sourceEvent = event;
    sourceEvent->attachEvent(this);
}

uint64_t
PMU::CounterState::getValue() const
{
    if (sourceEvent) {
        sourceEvent->updateAttachedCounters();
    } else {
        debugCounter("attempted to get value from a counter without"
            " an associated event\n");
    }
    return value;
}

void
PMU::CounterState::setValue(uint64_t val)
{
    value = val;
    resetValue = true;

    if (sourceEvent) {
        sourceEvent->updateAttachedCounters();
    } else {
        debugCounter("attempted to set value from a counter without"
            " an associated event\n");
    }
}

void
PMU::updateCounter(CounterState &ctr)
{
    if (!ctr.enabled) {
        DPRINTF(PMUVerbose, "updateCounter(%i): Disabling counter\n",
            ctr.getCounterId());
        ctr.detach();

    } else {
        DPRINTF(PMUVerbose, "updateCounter(%i): Enable event id 0x%x\n",
                ctr.getCounterId(), ctr.eventId);

        auto sourceEvent = eventMap.find(ctr.eventId);
        if (sourceEvent == eventMap.end()) {
            warn("Can't enable PMU counter of type '0x%x': "
                 "No such event type.\n", ctr.eventId);
        } else {
            ctr.attach(sourceEvent->second);
        }
    }
}


void
PMU::resetEventCounts()
{
    for (CounterState &ctr : counters)
        ctr.setValue(0);
}

void
PMU::setCounterValue(CounterId id, uint64_t val)
{
    if (!isValidCounter(id)) {
        warn_once("Can't change counter value: Counter %i does not exist.\n",
                  id);
        return;
    }

    CounterState &ctr(getCounter(id));
    ctr.setValue(val);
}

PMU::PMEVTYPER_t
PMU::getCounterTypeRegister(CounterId id) const
{
    if (!isValidCounter(id))
        return 0;

    const CounterState &cs(getCounter(id));
    PMEVTYPER_t type(cs.filter);

    type.evtCount = cs.eventId;

    return type;
}

void
PMU::setCounterTypeRegister(CounterId id, PMEVTYPER_t val)
{
    DPRINTF(PMUVerbose, "Set Event [%d] = 0x%08x\n", id, val);
    if (!isValidCounter(id)) {
        warn_once("Can't change counter type: Counter %i does not exist.\n",
                  id);
        return;
    }

    CounterState &ctr(getCounter(id));
    const EventTypeId old_event_id(ctr.eventId);

    ctr.filter = val;

    // If PMCCNTR Register, do not change event type. PMCCNTR can
    // count processor cycles only. If we change the event type, we
    // need to update the probes the counter is using.
    if (id != PMCCNTR && old_event_id != val.evtCount) {
        ctr.eventId = val.evtCount;
        updateCounter(ctr);
    }
}

void
PMU::setOverflowStatus(RegVal new_val)
{
    const bool int_old = reg_pmovsr != 0;
    const bool int_new = new_val != 0;

    reg_pmovsr = new_val;
    if (int_old && !int_new) {
        clearInterrupt();
    } else if (!int_old && int_new && (reg_pminten & reg_pmovsr)) {
        raiseInterrupt();
    }
}

void
PMU::raiseInterrupt()
{
    if (interrupt) {
        DPRINTF(PMUVerbose, "Delivering PMU interrupt.\n");
        interrupt->raise();
    } else {
        warn_once("Dropping PMU interrupt as no interrupt has "
                  "been specified\n");
    }
}

void
PMU::clearInterrupt()
{
    if (interrupt) {
        DPRINTF(PMUVerbose, "Clearing PMU interrupt.\n");
        interrupt->clear();
    } else {
        warn_once("Dropping PMU interrupt as no interrupt has "
                  "been specified\n");
    }
}

void
PMU::serialize(CheckpointOut &cp) const
{
    DPRINTF(Checkpoint, "Serializing Arm PMU\n");

    SERIALIZE_SCALAR(reg_pmcr);
    SERIALIZE_SCALAR(reg_pmcnten);
    SERIALIZE_SCALAR(reg_pmselr);
    SERIALIZE_SCALAR(reg_pminten);
    SERIALIZE_SCALAR(reg_pmovsr);
    SERIALIZE_SCALAR(reg_pmceid0);
    SERIALIZE_SCALAR(reg_pmceid1);
    SERIALIZE_SCALAR(clock_remainder);

    for (size_t i = 0; i < counters.size(); ++i)
        counters[i].serializeSection(cp, csprintf("counters.%i", i));

    cycleCounter.serializeSection(cp, "cycleCounter");
}

void
PMU::unserialize(CheckpointIn &cp)
{
    DPRINTF(Checkpoint, "Unserializing Arm PMU\n");

    UNSERIALIZE_SCALAR(reg_pmcr);
    UNSERIALIZE_SCALAR(reg_pmcnten);
    UNSERIALIZE_SCALAR(reg_pmselr);
    UNSERIALIZE_SCALAR(reg_pminten);
    UNSERIALIZE_SCALAR(reg_pmovsr);

    // Old checkpoints used to store the entire PMCEID value in a
    // single 64-bit entry (reg_pmceid). The register was extended in
    // ARMv8.1, so we now need to store it as two 64-bit registers.
    if (!UNSERIALIZE_OPT_SCALAR(reg_pmceid0))
        paramIn(cp, "reg_pmceid", reg_pmceid0);

    if (!UNSERIALIZE_OPT_SCALAR(reg_pmceid1))
        reg_pmceid1 = 0;

    UNSERIALIZE_SCALAR(clock_remainder);

    for (size_t i = 0; i < counters.size(); ++i)
        counters[i].unserializeSection(cp, csprintf("counters.%i", i));

    cycleCounter.unserializeSection(cp, "cycleCounter");
}

PMU::PMUEvent*
PMU::getEvent(uint64_t eventId)
{
    auto entry = eventMap.find(eventId);

    if (entry == eventMap.end()) {
        warn("event %d does not exist\n", eventId);
        return nullptr;
    } else {
        return entry->second;
    }
}

void
PMU::CounterState::serialize(CheckpointOut &cp) const
{
    SERIALIZE_SCALAR(eventId);
    SERIALIZE_SCALAR(value);
    SERIALIZE_SCALAR(overflow64);
}

void
PMU::CounterState::unserialize(CheckpointIn &cp)
{
    UNSERIALIZE_SCALAR(eventId);
    UNSERIALIZE_SCALAR(value);
    UNSERIALIZE_SCALAR(overflow64);
}

uint64_t
PMU::CounterState::add(uint64_t delta)
{
    uint64_t value_until_overflow;
    if (overflow64) {
        value_until_overflow = UINT64_MAX - value;
    } else {
        value_until_overflow = UINT32_MAX - (uint32_t)value;
    }

    if (isFiltered())
        return value_until_overflow;

    if (resetValue) {
        delta = 0;
        resetValue = false;
    } else {
        value += delta;
    }

    if (delta > value_until_overflow) {

        // overflow situation detected
        // flag the overflow occurence
        pmu.reg_pmovsr |= (1 << counterId);

        // Deliver a PMU interrupt if interrupt delivery is enabled
        // for this counter.
        if (pmu.reg_pminten  & (1 << counterId)) {
            pmu.raiseInterrupt();
        }
        return overflow64 ? UINT64_MAX : UINT32_MAX;
    }
    return value_until_overflow - delta + 1;
}

void
PMU::SWIncrementEvent::write(uint64_t val)
{
    for (auto& counter: userCounters) {
        if (val & (0x1 << counter->getCounterId())) {
            counter->add(1);
        }
    }
}

} // namespace ArmISA

ArmISA::PMU *
ArmPMUParams::create()
{
    return new ArmISA::PMU(this);
}