From f24059005f1b4bc36f3925b7d7f58d3d6ba6ebc0 Mon Sep 17 00:00:00 2001 From: Gedare Bloom Date: Thu, 23 Feb 2017 15:51:49 -0500 Subject: [PATCH] dev, arm: add a9mpcore global timer device Change-Id: I6d8a5e3795291b2a4cce022f555cf4b04f997538 Signed-off-by: Gedare Bloom Reviewed-on: https://gem5-review.googlesource.com/3262 Reviewed-by: Andreas Sandberg Maintainer: Andreas Sandberg --- src/dev/arm/RealView.py | 11 +- src/dev/arm/SConscript | 1 + src/dev/arm/timer_a9global.cc | 319 ++++++++++++++++++++++++++++++++++ src/dev/arm/timer_a9global.hh | 178 +++++++++++++++++++ 4 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 src/dev/arm/timer_a9global.cc create mode 100644 src/dev/arm/timer_a9global.hh diff --git a/src/dev/arm/RealView.py b/src/dev/arm/RealView.py index 3d4b0e583..323c14c7a 100644 --- a/src/dev/arm/RealView.py +++ b/src/dev/arm/RealView.py @@ -233,6 +233,12 @@ class Sp804(AmbaPioDevice): clock1 = Param.Clock('1MHz', "Clock speed of the input") amba_id = 0x00141804 +class A9GlobalTimer(BasicPioDevice): + type = 'A9GlobalTimer' + cxx_header = "dev/arm/timer_a9global.hh" + gic = Param.BaseGic(Parent.any, "Gic to use for interrupting") + int_num = Param.UInt32("Interrrupt number that connects to GIC") + class CpuLocalTimer(BasicPioDevice): type = 'CpuLocalTimer' cxx_header = "dev/arm/timer_cpulocal.hh" @@ -377,7 +383,9 @@ class RealViewPBX(RealView): pci_pio_base=0) timer0 = Sp804(int_num0=36, int_num1=36, pio_addr=0x10011000) timer1 = Sp804(int_num0=37, int_num1=37, pio_addr=0x10012000) - local_cpu_timer = CpuLocalTimer(int_num_timer=29, int_num_watchdog=30, pio_addr=0x1f000600) + global_timer = A9GlobalTimer(int_num=27, pio_addr=0x1f000200) + local_cpu_timer = CpuLocalTimer(int_num_timer=29, int_num_watchdog=30, + pio_addr=0x1f000600) clcd = Pl111(pio_addr=0x10020000, int_num=55) kmi0 = Pl050(pio_addr=0x10006000, int_num=52) kmi1 = Pl050(pio_addr=0x10007000, int_num=53, is_mouse=True) @@ -416,6 +424,7 @@ class RealViewPBX(RealView): self.gic.pio = bus.master self.l2x0_fake.pio = bus.master self.a9scu.pio = bus.master + self.global_timer.pio = bus.master self.local_cpu_timer.pio = bus.master # Bridge ranges based on excluding what is part of on-chip I/O # (gic, l2x0, a9scu, local_cpu_timer) diff --git a/src/dev/arm/SConscript b/src/dev/arm/SConscript index 7a559928b..ae3ed7194 100644 --- a/src/dev/arm/SConscript +++ b/src/dev/arm/SConscript @@ -67,6 +67,7 @@ if env['TARGET_ISA'] == 'arm': Source('realview.cc') Source('rtc_pl031.cc') Source('timer_cpulocal.cc') + Source('timer_a9global.cc') Source('vgic.cc') Source('ufs_device.cc') Source('energy_ctrl.cc') diff --git a/src/dev/arm/timer_a9global.cc b/src/dev/arm/timer_a9global.cc new file mode 100644 index 000000000..742ed76cd --- /dev/null +++ b/src/dev/arm/timer_a9global.cc @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2017 Gedare Bloom + * Copyright (c) 2010 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: Ali Saidi + * Gedare Bloom + */ + +#include "dev/arm/timer_a9global.hh" + +#include "base/intmath.hh" +#include "base/trace.hh" +#include "debug/Checkpoint.hh" +#include "debug/Timer.hh" +#include "dev/arm/base_gic.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" + +A9GlobalTimer::A9GlobalTimer(Params *p) + : BasicPioDevice(p, 0x1C), gic(p->gic), + global_timer(name() + ".globaltimer", this, p->int_num) +{ +} + +A9GlobalTimer::Timer::Timer(std::string __name, A9GlobalTimer *_parent, + int int_num) + : _name(__name), parent(_parent), intNum(int_num), control(0x0), + rawInt(false), pendingInt(false), autoIncValue(0x0), cmpValEvent(this) +{ +} + +Tick +A9GlobalTimer::read(PacketPtr pkt) +{ + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + assert(pkt->getSize() == 4); + Addr daddr = pkt->getAddr() - pioAddr; + + if (daddr < Timer::Size) + global_timer.read(pkt, daddr); + else + panic("Tried to read A9GlobalTimer at offset %#x that doesn't exist\n", + daddr); + pkt->makeAtomicResponse(); + return pioDelay; +} + +uint64_t +A9GlobalTimer::Timer::getTimeCounterFromTicks(Tick ticks) +{ + return ticks / parent->clockPeriod() / (control.prescalar + 1) - 1; +} + +void +A9GlobalTimer::Timer::read(PacketPtr pkt, Addr daddr) +{ + DPRINTF(Timer, "Reading from A9GlobalTimer at offset: %#x\n", daddr); + uint64_t time; + + switch(daddr) { + case CounterRegLow32: + time = getTimeCounterFromTicks(curTick()); + DPRINTF(Timer, "-- returning lower 32-bits of counter: %u\n", time); + pkt->set(time); + break; + case CounterRegHigh32: + time = getTimeCounterFromTicks(curTick()); + time >>= 32; + DPRINTF(Timer, "-- returning upper 32-bits of counter: %u\n", time); + pkt->set(time); + break; + case ControlReg: + pkt->set(control); + break; + case IntStatusReg: + pkt->set(rawInt); + break; + case CmpValRegLow32: + DPRINTF(Timer, "Event schedule for %d, clock=%d, prescale=%d\n", + cmpValEvent.when(), parent->clockPeriod(), control.prescalar); + if (cmpValEvent.scheduled()) { + time = getTimeCounterFromTicks(cmpValEvent.when() - curTick()); + } else { + time = 0; + } + DPRINTF(Timer, "-- returning lower 32-bits of comparator: %u\n", time); + pkt->set(time); + break; + case CmpValRegHigh32: + DPRINTF(Timer, "Event schedule for %d, clock=%d, prescale=%d\n", + cmpValEvent.when(), parent->clockPeriod(), control.prescalar); + if (cmpValEvent.scheduled()) { + time = getTimeCounterFromTicks(cmpValEvent.when() - curTick()); + time >>= 32; + } else { + time = 0; + } + DPRINTF(Timer, "-- returning upper 32-bits of comparator: %u\n", time); + pkt->set(time); + break; + case AutoIncrementReg: + pkt->set(autoIncValue); + break; + default: + panic("Tried to read A9GlobalTimer at offset %#x\n", daddr); + break; + } + DPRINTF(Timer, "Reading %#x from A9GlobalTimer at offset: %#x\n", + pkt->get(), daddr); +} + +Tick +A9GlobalTimer::write(PacketPtr pkt) +{ + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + assert(pkt->getSize() == 4); + Addr daddr = pkt->getAddr() - pioAddr; + DPRINTF(Timer, "Writing to A9GlobalTimer at offset: %#x\n", daddr); + + warn_once("A9 Global Timer doesn't support banked per-cpu registers\n"); + + if (daddr < Timer::Size) + global_timer.write(pkt, daddr); + else + panic("Tried to write A9GlobalTimer at offset %#x doesn't exist\n", + daddr); + pkt->makeAtomicResponse(); + return pioDelay; +} + +void +A9GlobalTimer::Timer::write(PacketPtr pkt, Addr daddr) +{ + DPRINTF(Timer, "Writing %#x to A9GlobalTimer at offset: %#x\n", + pkt->get(), daddr); + switch (daddr) { + case CounterRegLow32: + case CounterRegHigh32: + DPRINTF(Timer, "Ignoring unsupported write to Global Timer Counter\n"); + break; + case ControlReg: + bool old_enable; + bool old_cmpEnable; + old_enable = control.enable; + old_cmpEnable = control.cmpEnable; + control = pkt->get(); + if ((old_enable == 0) && control.enable) + restartCounter(); + if ((old_cmpEnable == 0) && control.cmpEnable) + restartCounter(); + break; + case IntStatusReg: + /* TODO: should check that '1' was written. */ + rawInt = false; + if (pendingInt) { + pendingInt = false; + DPRINTF(Timer, "Clearing interrupt\n"); + parent->gic->clearInt(intNum); + } + break; + case CmpValRegLow32: + cmpVal &= 0xFFFFFFFF00000000ULL; + cmpVal |= (uint64_t)pkt->get(); + break; + case CmpValRegHigh32: + cmpVal &= 0x00000000FFFFFFFFULL; + cmpVal |= ((uint64_t)pkt->get() << 32); + break; + case AutoIncrementReg: + autoIncValue = pkt->get(); + break; + default: + panic("Tried to write A9GlobalTimer at offset %#x\n", daddr); + break; + } +} + +void +A9GlobalTimer::Timer::restartCounter() +{ + if (!control.enable) + return; + DPRINTF(Timer, "Restarting counter with value %#x\n", cmpVal); + + Tick time = parent->clockPeriod() * (control.prescalar + 1) * (cmpVal + 1); + + if (time < curTick()) { + DPRINTF(Timer, "-- Event time %#x < curTick %#x\n", time, curTick()); + return; + } + if (cmpValEvent.scheduled()) { + DPRINTF(Timer, "-- Event was already schedule, de-scheduling\n"); + parent->deschedule(cmpValEvent); + } + parent->schedule(cmpValEvent, time); + DPRINTF(Timer, "-- Scheduling new event for: %d\n", time); +} + +void +A9GlobalTimer::Timer::counterAtCmpVal() +{ + if (!control.enable) + return; + + DPRINTF(Timer, "Counter reached cmpVal\n"); + + rawInt = true; + bool old_pending = pendingInt; + if (control.intEnable) + pendingInt = true; + if (pendingInt && !old_pending) { + DPRINTF(Timer, "-- Causing interrupt\n"); + parent->gic->sendPPInt(intNum, 0); /* FIXME: cpuNum */ + } + + if (control.autoIncrement == 0) // one-shot + return; + + cmpVal += (uint64_t)autoIncValue; + restartCounter(); +} + +void +A9GlobalTimer::Timer::serialize(CheckpointOut &cp) const +{ + DPRINTF(Checkpoint, "Serializing Arm A9GlobalTimer\n"); + + uint32_t control_serial = control; + SERIALIZE_SCALAR(control_serial); + + SERIALIZE_SCALAR(rawInt); + SERIALIZE_SCALAR(pendingInt); + SERIALIZE_SCALAR(cmpVal); + SERIALIZE_SCALAR(autoIncValue); + + bool is_in_event = cmpValEvent.scheduled(); + SERIALIZE_SCALAR(is_in_event); + + Tick event_time; + if (is_in_event){ + event_time = cmpValEvent.when(); + SERIALIZE_SCALAR(event_time); + } +} + +void +A9GlobalTimer::Timer::unserialize(CheckpointIn &cp) +{ + DPRINTF(Checkpoint, "Unserializing Arm A9GlobalTimer\n"); + + uint32_t control_serial; + UNSERIALIZE_SCALAR(control_serial); + control = control_serial; + + UNSERIALIZE_SCALAR(rawInt); + UNSERIALIZE_SCALAR(pendingInt); + UNSERIALIZE_SCALAR(cmpVal); + UNSERIALIZE_SCALAR(autoIncValue); + + bool is_in_event; + UNSERIALIZE_SCALAR(is_in_event); + + Tick event_time; + if (is_in_event){ + UNSERIALIZE_SCALAR(event_time); + parent->schedule(cmpValEvent, event_time); + } +} + +void +A9GlobalTimer::serialize(CheckpointOut &cp) const +{ + global_timer.serialize(cp); +} + +void +A9GlobalTimer::unserialize(CheckpointIn &cp) +{ + global_timer.unserialize(cp); +} + +A9GlobalTimer * +A9GlobalTimerParams::create() +{ + return new A9GlobalTimer(this); +} diff --git a/src/dev/arm/timer_a9global.hh b/src/dev/arm/timer_a9global.hh new file mode 100644 index 000000000..0bb5259c2 --- /dev/null +++ b/src/dev/arm/timer_a9global.hh @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2017 Gedare Bloom + * Copyright (c) 2010 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: Ali Saidi + * Gedare Bloom + */ + +#ifndef __DEV_ARM_GLOBAL_TIMER_HH__ +#define __DEV_ARM_GLOBAL_TIMER_HH__ + +#include "dev/io_device.hh" +#include "params/A9GlobalTimer.hh" + +/** @file + * This implements the Cortex A9-MPCore global timer from TRM rev r4p1. + * The global timer is an incrementing timer. + */ + +class BaseGic; + +class A9GlobalTimer : public BasicPioDevice +{ + protected: + class Timer : public Serializable + { + + public: + /* TODO: IntStatusReg, CmpValRegLow32, CmpValRegHigh32, + * and AutoIncrementReg are banked per-cpu. Some bits of + * ControlReg are also banked per-cpu, see below. */ + enum { + CounterRegLow32 = 0x00, + CounterRegHigh32 = 0x04, + ControlReg = 0x08, + IntStatusReg = 0x0C, + CmpValRegLow32 = 0x10, + CmpValRegHigh32 = 0x14, + AutoIncrementReg = 0x18, + Size = 0x1C + }; + + /* TODO: bits 1--3 are banked per-cpu */ + BitUnion32(CTRL) + Bitfield<0> enable; + Bitfield<1> cmpEnable; + Bitfield<2> intEnable; + Bitfield<3> autoIncrement; + Bitfield<7,4> reserved; + Bitfield<15,8> prescalar; + EndBitUnion(CTRL) + + protected: + std::string _name; + + /** Pointer to parent class */ + A9GlobalTimer *parent; + + /** Number of interrupt to cause/clear */ + const uint32_t intNum; + + /** Control register as specified above */ + /* TODO: one per-cpu? */ + CTRL control; + + /** If timer has caused an interrupt. This is irrespective of + * interrupt enable */ + /* TODO: one per-cpu */ + bool rawInt; + + /** If an interrupt is currently pending. Logical and of CTRL.intEnable + * and rawInt */ + bool pendingInt; + + /** Value of the comparator */ + uint64_t cmpVal; + + /** Value to add to comparator when counter reaches comparator */ + /* TODO: one per-cpu */ + uint32_t autoIncValue; + + /** Called when the counter reaches the comparator */ + void counterAtCmpVal(); + EventWrapper cmpValEvent; + + public: + /** Restart the counter ticking */ + void restartCounter(); + /** + * Convert a number of ticks into the time counter format + * @param ticks number of ticks + */ + uint64_t getTimeCounterFromTicks(Tick ticks); + Timer(std::string __name, A9GlobalTimer *parent, int int_num); + + std::string name() const { return _name; } + + /** Handle read for a single timer */ + void read(PacketPtr pkt, Addr daddr); + + /** Handle write for a single timer */ + void write(PacketPtr pkt, Addr daddr); + + void serialize(CheckpointOut &cp) const override; + void unserialize(CheckpointIn &cp) override; + }; + + /** Pointer to the GIC for causing an interrupt */ + BaseGic *gic; + + /** Timer that does the actual work */ + Timer global_timer; + + public: + typedef A9GlobalTimerParams Params; + const Params * + params() const + { + return dynamic_cast(_params); + } + /** + * The constructor for RealView just registers itself with the MMU. + * @param p params structure + */ + A9GlobalTimer(Params *p); + + /** + * Handle a read to the device + * @param pkt The memory request. + * @return Returns latency of device read + */ + Tick read(PacketPtr pkt) override; + + /** + * Handle a write to the device. + * @param pkt The memory request. + * @return Returns latency of device write + */ + Tick write(PacketPtr pkt) override; + + void serialize(CheckpointOut &cp) const override; + void unserialize(CheckpointIn &cp) override; +}; + +#endif // __DEV_ARM_GLOBAL_TIMER_HH__ -- 2.30.2