This is a reduced model of the FVP Base platforms Power Controller.
As of now it allows the following functions from software:
- Checking for core presence
- Reporting the power state of a core / cluster
- Explicitly powering off a core on WFI
- Explicitly powering off cores in a CPU cluster on WFI
- Explicitly powering on a core through writes to PPONR register
Change-Id: Ia1d4d3ae8e4bfb2d23b2c6077396a4d8500e2e30
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/26463
Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
fault = trapWFx(tc, cpsr, scr, false);
if (fault == NoFault) {
PseudoInst::quiesce(tc);
+ ArmSystem::callSetStandByWfi(tc);
} else {
PseudoInst::quiesceSkip(tc);
}
/*
- * Copyright (c) 2010, 2012-2013, 2015,2017-2019 ARM Limited
+ * Copyright (c) 2010, 2012-2013, 2015,2017-2020 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
#include "base/loader/object_file.hh"
#include "base/loader/symtab.hh"
#include "cpu/thread_context.hh"
+#include "dev/arm/fvp_base_pwr_ctrl.hh"
#include "dev/arm/gic_v2.hh"
#include "mem/fs_translating_port_proxy.hh"
#include "mem/physical.hh"
_haveCrypto(p->have_crypto),
_genericTimer(nullptr),
_gic(nullptr),
+ _pwrCtrl(nullptr),
_highestELIs64(p->highest_el_is_64),
_physAddrRange64(p->phys_addr_range_64),
_haveLargeAsid64(p->have_large_asid_64),
return sys->semihosting->call32(tc, op, param);
}
+void
+ArmSystem::callSetStandByWfi(ThreadContext *tc)
+{
+ if (FVPBasePwrCtrl *pwr_ctrl = getArmSystem(tc)->getPowerController())
+ pwr_ctrl->setStandByWfi(tc);
+}
+
+void
+ArmSystem::callClearStandByWfi(ThreadContext *tc)
+{
+ if (FVPBasePwrCtrl *pwr_ctrl = getArmSystem(tc)->getPowerController())
+ pwr_ctrl->clearStandByWfi(tc);
+}
+
ArmSystem *
ArmSystemParams::create()
{
/*
- * Copyright (c) 2010, 2012-2013, 2015-2019 ARM Limited
+ * Copyright (c) 2010, 2012-2013, 2015-2020 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
class GenericTimer;
class BaseGic;
+class FVPBasePwrCtrl;
class ThreadContext;
class ArmSystem : public System
GenericTimer *_genericTimer;
BaseGic *_gic;
+ /**
+ * Pointer to the Power Controller (if any)
+ */
+ FVPBasePwrCtrl *_pwrCtrl;
+
/**
* Reset address (ARMv8)
*/
/** Sets the pointer to the GIC. */
void setGIC(BaseGic *gic) { _gic = gic; }
+ /** Sets the pointer to the Power Controller */
+ void setPowerController(FVPBasePwrCtrl *pwr_ctrl)
+ {
+ _pwrCtrl = pwr_ctrl;
+ }
+
/** Get a pointer to the system's generic timer model */
GenericTimer *getGenericTimer() const { return _genericTimer; }
/** Get a pointer to the system's GIC */
BaseGic *getGIC() const { return _gic; }
+ /** Get a pointer to the system's power controller */
+ FVPBasePwrCtrl *getPowerController() const { return _pwrCtrl; }
+
/** Returns true if the register width of the highest implemented exception
* level is 64 bits (ARMv8) */
bool highestELIs64() const { return _highestELIs64; }
/** Make a Semihosting call from aarch32 */
static uint32_t callSemihosting32(ThreadContext *tc,
uint32_t op, uint32_t param);
+
+ /** Make a call to notify the power controller of STANDBYWFI assertion */
+ static void callSetStandByWfi(ThreadContext *tc);
+
+ /** Make a call to notify the power controller of STANDBYWFI deassertion */
+ static void callClearStandByWfi(ThreadContext *tc);
};
#endif
return super(VExpress_GEM5_V2,self)._on_chip_devices() + [
self.hdlcd,
]
+
+class FVPBasePwrCtrl(BasicPioDevice):
+ """
+Based on Fast Models Base_PowerController v11.8
+Reference:
+ Fast Models Reference Manual - Section 7.7.2 - Version 11.8
+ Document ID: 100964_1180_00_en
+ """
+
+ type = 'FVPBasePwrCtrl'
+ cxx_header = 'dev/arm/fvp_base_pwr_ctrl.hh'
# -*- mode:python -*-
-# Copyright (c) 2009, 2012-2013 ARM Limited
+# Copyright (c) 2009, 2012-2013, 2020 ARM Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
Source('vio_mmio.cc')
Source('ufs_device.cc')
Source('energy_ctrl.cc')
+ Source('fvp_base_pwr_ctrl.cc')
DebugFlag('AMBA')
DebugFlag('FlashDevice')
DebugFlag('SMMUv3Hazard')
DebugFlag('Sp805')
DebugFlag('EnergyCtrl')
+ DebugFlag('FVPBasePwrCtrl')
DebugFlag('UFSHostDevice')
DebugFlag('VGIC')
DebugFlag('NoMali')
--- /dev/null
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include "dev/arm/fvp_base_pwr_ctrl.hh"
+
+#include "arch/arm/faults.hh"
+#include "arch/arm/system.hh"
+#include "arch/arm/utility.hh"
+#include "cpu/base.hh"
+#include "cpu/thread_context.hh"
+#include "debug/FVPBasePwrCtrl.hh"
+#include "mem/packet_access.hh"
+#include "params/FVPBasePwrCtrl.hh"
+#include "sim/system.hh"
+
+FVPBasePwrCtrl::FVPBasePwrCtrl(FVPBasePwrCtrlParams *const params)
+ : BasicPioDevice(params, 0x1000),
+ regs(),
+ system(*static_cast<ArmSystem *>(sys))
+{
+ warn_if(sys->multiThread,
+ "Base Power Controller does not support multi-threaded systems\n");
+ system.setPowerController(this);
+}
+
+void
+FVPBasePwrCtrl::init()
+{
+ // All cores are ON by default (PwrStatus.{l0,l1} = 0b1)
+ corePwrStatus.resize(sys->numContexts(), 0x60000000);
+ for (const auto &tc : sys->threadContexts)
+ poweredCoresPerCluster[tc->socketId()] += 1;
+ BasicPioDevice::init();
+}
+
+void
+FVPBasePwrCtrl::setStandByWfi(ThreadContext *const tc)
+{
+ PwrStatus *pwrs = getCorePwrStatus(tc);
+
+ if (!pwrs->pwfi)
+ DPRINTF(FVPBasePwrCtrl, "FVPBasePwrCtrl::setStandByWfi: STANDBYWFI "
+ "asserted for core %d\n", tc->contextId());
+ pwrs->pwfi = 1;
+ if (pwrs->l0 && (pwrs->pp || pwrs->pc))
+ powerCoreOff(tc, pwrs);
+}
+
+void
+FVPBasePwrCtrl::clearStandByWfi(ThreadContext *const tc)
+{
+ PwrStatus *pwrs = getCorePwrStatus(tc);
+
+ if (pwrs->pwfi)
+ DPRINTF(FVPBasePwrCtrl, "FVPBasePwrCtrl::clearStandByWfi: STANDBYWFI "
+ "deasserted for core %d\n", tc->contextId());
+ pwrs->pwfi = 0;
+}
+
+Tick
+FVPBasePwrCtrl::read(PacketPtr pkt)
+{
+ const Addr addr = pkt->getAddr() - pioAddr;
+ const size_t size = pkt->getSize();
+ panic_if(size != 4, "FVPBasePwrCtrl::read: Invalid size %i\n", size);
+
+ uint64_t resp = 0;
+ switch (addr) {
+ case PPOFFR:
+ resp = regs.ppoffr;
+ break;
+ case PPONR:
+ resp = regs.pponr;
+ break;
+ case PCOFFR:
+ resp = regs.pcoffr;
+ break;
+ case PWKUPR:
+ resp = regs.pwkupr;
+ break;
+ case PSYSR:
+ resp = regs.psysr;
+ break;
+ default:
+ warn("FVPBasePwrCtrl::read: Unexpected address (0x%x:%i), "
+ "assuming RAZ\n", addr, size);
+ }
+
+ DPRINTF(FVPBasePwrCtrl, "FVPBasePwrCtrl::read: 0x%x<-0x%x(%i)\n", resp,
+ addr, size);
+
+ pkt->setUintX(resp, LittleEndianByteOrder);
+ pkt->makeResponse();
+ return pioDelay;
+}
+
+Tick
+FVPBasePwrCtrl::write(PacketPtr pkt)
+{
+ const Addr addr = pkt->getAddr() - pioAddr;
+ const size_t size = pkt->getSize();
+ panic_if(size != 4, "FVPBasePwrCtrl::write: Invalid size %i\n", size);
+
+ uint64_t data = pkt->getUintX(LittleEndianByteOrder);
+
+ // Software may use the power controller to check for core presence
+ // If core is not present, return an invalid MPID as notification
+ ThreadContext *tc = getThreadContextByMPID(data & MPID_MSK);
+ PwrStatus *pwrs = tc ? getCorePwrStatus(tc) : nullptr;
+ switch (addr) {
+ case PPOFFR:
+ if (!tc) {
+ regs.ppoffr = ~0;
+ } else if (pwrs->l0) {
+ // Set pending power off
+ pwrs->pp = 1;
+ regs.ppoffr = data & MPID_MSK;
+ } else {
+ regs.ppoffr = ~0 & MPID_MSK;
+ }
+ break;
+ case PPONR:
+ if (!tc) {
+ regs.pponr = ~0;
+ } else {
+ if (!pwrs->l0) {
+ pwrs->wk = WK_PPONR;
+ powerCoreOn(tc, pwrs);
+ startCoreUp(tc);
+ regs.pponr = data & MPID_MSK;
+ } else {
+ regs.pponr = ~0 & MPID_MSK;
+ }
+ }
+ break;
+ case PCOFFR:
+ if (!tc) {
+ regs.pcoffr = ~0;
+ } else if (pwrs->l0) {
+ // Power off all cores in the cluster
+ for (const auto &tco : sys->threadContexts) {
+ if (tc->socketId() == tco->socketId()) {
+ PwrStatus *npwrs = getCorePwrStatus(tco);
+ // Set pending cluster power off
+ npwrs->pc = 1;
+ if (npwrs->l0 && npwrs->pwfi)
+ powerCoreOff(tco, npwrs);
+ }
+ }
+ } else {
+ regs.pcoffr = ~0 & MPID_MSK;
+ }
+ break;
+ case PWKUPR:
+ if (!tc) {
+ regs.pwkupr = ~0;
+ } else {
+ // Update WEN value
+ pwrs->wen = bits(data, 31);
+ // Power-on if there is any pending Wakeup Requests
+ if (!pwrs->l0 && pwrs->wen && pwrs->pwk) {
+ pwrs->wk = WK_GICWR;
+ powerCoreOn(tc, pwrs);
+ startCoreUp(tc);
+ }
+ regs.pwkupr = data & (MPID_MSK | (1 << 31));
+ }
+ break;
+ case PSYSR:
+ if (!tc)
+ regs.psysr = ~0;
+ else
+ regs.psysr = (data & MPID_MSK) | (((uint32_t) *pwrs) & ~MPID_MSK);
+ break;
+ default:
+ warn("FVPBasePwrCtrl::write: Unexpected address (0x%x:%i), "
+ "assuming WI\n", addr, size);
+ }
+
+ DPRINTF(FVPBasePwrCtrl, "FVPBasePwrCtrl::write: 0x%x->0x%x(%i)\n", data,
+ addr, size);
+
+ pkt->makeResponse();
+ return pioDelay;
+}
+
+FVPBasePwrCtrl::PwrStatus *
+FVPBasePwrCtrl::getCorePwrStatus(ThreadContext *const tc)
+{
+ PwrStatus *pwrs = &corePwrStatus[tc->contextId()];
+ pwrs->l1 = poweredCoresPerCluster[tc->socketId()] > 0;
+ return pwrs;
+}
+
+ThreadContext *
+FVPBasePwrCtrl::getThreadContextByMPID(uint32_t mpid) const
+{
+ for (auto &tc : sys->threadContexts) {
+ if (mpid == ArmISA::getAffinity(&system, tc))
+ return tc;
+ }
+ return nullptr;
+}
+
+void
+FVPBasePwrCtrl::powerCoreOn(ThreadContext *const tc, PwrStatus *const pwrs)
+{
+ DPRINTF(FVPBasePwrCtrl, "FVPBasePwrCtrl::powerCoreOn: Powering ON "
+ "core %d\n", tc->contextId());
+ pwrs->l0 = 1;
+ poweredCoresPerCluster[tc->socketId()]++;
+ // Clear pending power-offs to the core
+ pwrs->pp = 0;
+ // Clear pending power-offs to the core's cluster
+ for (const auto &tco : sys->threadContexts) {
+ if (tc->socketId() == tco->socketId()) {
+ PwrStatus *npwrs = getCorePwrStatus(tco);
+ npwrs->pc = 0;
+ }
+ }
+ tc->getCpuPtr()->pwrState(Enums::PwrState::ON);
+}
+
+void
+FVPBasePwrCtrl::powerCoreOff(ThreadContext *const tc, PwrStatus *const pwrs)
+{
+ DPRINTF(FVPBasePwrCtrl, "FVPBasePwrCtrl::powerCoreOff: Powering OFF "
+ "core %d\n", tc->contextId());
+ pwrs->l0 = 0;
+ poweredCoresPerCluster[tc->socketId()]--;
+ // Clear pending power-offs to the core
+ pwrs->pp = 0;
+ pwrs->pc = 0;
+ // Clear power-on reason
+ pwrs->wk = 0;
+ tc->getCpuPtr()->pwrState(Enums::PwrState::OFF);
+}
+
+void
+FVPBasePwrCtrl::startCoreUp(ThreadContext *const tc)
+{
+ DPRINTF(FVPBasePwrCtrl, "FVPBasePwrCtrl::startCoreUp: Starting core %d "
+ "from the power controller\n", tc->contextId());
+ clearStandByWfi(tc);
+
+ // InitCPU
+ Reset().invoke(tc);
+ tc->activate();
+}
+
+FVPBasePwrCtrl *
+FVPBasePwrCtrlParams::create()
+{
+ return new FVPBasePwrCtrl(this);
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#ifndef __DEV_ARM_FVP_BASE_PWR_CTRL_HH__
+#define __DEV_ARM_FVP_BASE_PWR_CTRL_HH__
+
+#include <unordered_map>
+
+#include "base/bitunion.hh"
+#include "dev/io_device.hh"
+
+class ArmSystem;
+class FVPBasePwrCtrlParams;
+class ThreadContext;
+
+/**
+ * @file
+ * This class implements the base power controller for FVP-based
+ * platforms. Based on Fast Models version 11.8.
+ *
+ * Limitations:
+ * - Level 2 affinity is not implemented -> PSYSR.L2 is RAZ
+ * - WakeRequests are not modelled by GICv3 -> PSYSR.WEN is always 0
+ * - PSYSR.WK can only be 0b00 (Cold power-on) and 0b10 (Wake by PPONR)
+ */
+class FVPBasePwrCtrl : public BasicPioDevice
+{
+ public:
+ FVPBasePwrCtrl(FVPBasePwrCtrlParams *const params);
+
+ /**
+ * Triggered by the ISA when a WFI instruction is executed and (1) there
+ * are no pending interrupts and (2) it is not trapped. Core is set
+ * to quiescent state only if there is a pending power off
+ * @param tc Thread context representing the core
+ */
+ void setStandByWfi(ThreadContext *const tc);
+
+ /**
+ * Triggered when an interrupt is posted to the core. Core is brought up
+ * from quiescent state if it is suspended. The latter is done by
+ * BaseCPU::wakeup.
+ * @param tc Thread context representing the core
+ */
+ void clearStandByWfi(ThreadContext *const tc);
+
+ void init() override;
+
+ protected:
+ Tick read(PacketPtr pkt) override;
+ Tick write(PacketPtr pkt) override;
+
+ private:
+ BitUnion32(PwrStatus)
+ Bitfield<30> l1;
+ Bitfield<29> l0;
+ Bitfield<28> wen;
+ Bitfield<27> pc;
+ Bitfield<26> pp;
+ Bitfield<25,24> wk;
+ Bitfield<1> pwfi;
+ Bitfield<0> pwk;
+ EndBitUnion(PwrStatus)
+
+ enum Offset : Addr {
+ PPOFFR = 0x00,
+ PPONR = 0x04,
+ PCOFFR = 0x08,
+ PWKUPR = 0x0c,
+ PSYSR = 0x10
+ };
+
+ struct Registers {
+ uint32_t ppoffr;
+ uint32_t pponr;
+ uint32_t pcoffr;
+ uint32_t pwkupr;
+ uint32_t psysr;
+ } regs;
+
+ /** Mask for extracting the MPID from a 32-bit value */
+ static constexpr uint32_t MPID_MSK = 0x00ffffff;
+ /** Wake-up reasons */
+ enum { WK_COLD, WK_RESET, WK_PPONR, WK_GICWR };
+
+ /**
+ * Per-core power status. This is power related information for each core
+ * that is bound to this power controller functionality
+ */
+ std::vector<PwrStatus> corePwrStatus;
+
+ /** Number of powered cores per cluster. Helps keep track of PSYSR.L1 */
+ std::unordered_map<uint32_t, size_t> poweredCoresPerCluster;
+
+ /**
+ * Retrieves the power status of a certain core and resizes the entries
+ * if needed. This is a workaround for a limitation of the System object
+ * only exposing existing thread contexts after "init()"
+ * @param Thread (HW) context in the core
+ * @return Core power status
+ */
+ PwrStatus *getCorePwrStatus(ThreadContext *const tc);
+
+ /**
+ * Retrieves the thread context reference for a CPU core by MPID
+ * @param mpid ID provided by software
+ * @return valid thread context reference if valid MPID, nullptr otherwise
+ */
+ ThreadContext *getThreadContextByMPID(uint32_t mpid) const;
+
+ /**
+ * Powers on a core. A Reset fault is invoked in the core followed by
+ * an activation
+ * @param Thread (HW) context in the core
+ * @param Core power status
+ */
+ void powerCoreOn(ThreadContext *const tc, PwrStatus *const pwrs);
+
+ /**
+ * Powers off a core. The core enters into quiescent state until
+ * an explicit PPONR write or a WakeRequest from the GIC wakes it up
+ * @param Thread (HW) context in the core
+ * @param Core power status
+ */
+ void powerCoreOff(ThreadContext *const tc, PwrStatus *const pwrs);
+
+ /**
+ * Starts a core up. This invokes the reset vector to setup the wake-up
+ * entrypoint and activates the thread context. This covers cases when
+ * PSYSR.WEN is enabled or PPONR is written
+ * @param Thread (HW) context in the core
+ */
+ void startCoreUp(ThreadContext *const tc);
+
+ /** Reference to the arm system */
+ ArmSystem &system;
+};
+
+#endif // __DEV_ARM_FVP_BASE_PWR_CTRL_HH__
/*
- * Copyright (c) 2019 ARM Limited
+ * Copyright (c) 2019-2020 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
Gicv3::postInt(uint32_t cpu, ArmISA::InterruptTypes int_type)
{
platform->intrctrl->post(cpu, int_type, 0);
+ ArmSystem::callClearStandByWfi(sys->getThreadContext(cpu));
}
bool