dev-arm: add FVP Base Power Controller model

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>
This commit is contained in:
Adrian Herrera
2019-10-25 10:49:23 +01:00
committed by Giacomo Travaglini
parent 8cfa988335
commit 5719da9fff
8 changed files with 518 additions and 4 deletions

View File

@@ -762,6 +762,7 @@ let {{
fault = trapWFx(tc, cpsr, scr, false);
if (fault == NoFault) {
PseudoInst::quiesce(tc);
ArmSystem::callSetStandByWfi(tc);
} else {
PseudoInst::quiesceSkip(tc);
}

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -47,6 +47,7 @@
#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"
@@ -62,6 +63,7 @@ ArmSystem::ArmSystem(Params *p)
_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),
@@ -197,6 +199,20 @@ ArmSystem::callSemihosting32(ThreadContext *tc,
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()
{

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -53,6 +53,7 @@
class GenericTimer;
class BaseGic;
class FVPBasePwrCtrl;
class ThreadContext;
class ArmSystem : public System
@@ -84,6 +85,11 @@ class ArmSystem : public System
GenericTimer *_genericTimer;
BaseGic *_gic;
/**
* Pointer to the Power Controller (if any)
*/
FVPBasePwrCtrl *_pwrCtrl;
/**
* Reset address (ARMv8)
*/
@@ -175,12 +181,21 @@ class ArmSystem : public System
/** 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; }
@@ -305,6 +320,12 @@ class ArmSystem : public System
/** 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

View File

@@ -1159,3 +1159,14 @@ class VExpress_GEM5_V2(VExpress_GEM5_V2_Base):
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'

View File

@@ -1,6 +1,6 @@
# -*- 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
@@ -92,6 +92,7 @@ if env['TARGET_ISA'] == 'arm':
Source('vio_mmio.cc')
Source('ufs_device.cc')
Source('energy_ctrl.cc')
Source('fvp_base_pwr_ctrl.cc')
DebugFlag('AMBA')
DebugFlag('FlashDevice')
@@ -106,6 +107,7 @@ if env['TARGET_ISA'] == 'arm':
DebugFlag('SMMUv3Hazard')
DebugFlag('Sp805')
DebugFlag('EnergyCtrl')
DebugFlag('FVPBasePwrCtrl')
DebugFlag('UFSHostDevice')
DebugFlag('VGIC')
DebugFlag('NoMali')

View File

@@ -0,0 +1,289 @@
/*
* 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);
}

View File

@@ -0,0 +1,173 @@
/*
* 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__

View File

@@ -1,5 +1,5 @@
/*
* 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
@@ -205,6 +205,7 @@ void
Gicv3::postInt(uint32_t cpu, ArmISA::InterruptTypes int_type)
{
platform->intrctrl->post(cpu, int_type, 0);
ArmSystem::callClearStandByWfi(sys->getThreadContext(cpu));
}
bool