Nils Asmussen has uploaded this change for review. ( https://gem5-review.googlesource.com/c/public/gem5/+/25660 )

Change subject: arch-riscv,arch-x86: moved common code into generic walker class.
......................................................................

arch-riscv,arch-x86: moved common code into generic walker class.

The basic structure of the page table walker, that is, loading PTEs in
multiple steps until the leaf PTE is found, support multiple walks at
the same time, handling sends and receives of packets, supporting timing
and functional walks, etc. is generic. Therefore, I've moved this code
into the generic ISA as a base class for the RISC-V and X86 page table
walker. Unfortunately, the ARM PT walker is very different, so that it
does not take advantage of the generic PT walker.

The ISA-specific page table walker only needs to implement the setup
of the walk, each step of the walk, and a couple of helper methods.

Change-Id: I45b7e8a365b32b49f1c34a5501f6b4b0d015d74e
---
M src/arch/generic/BaseTLB.py
M src/arch/generic/SConscript
A src/arch/generic/pagetable_walker.cc
A src/arch/generic/pagetable_walker.hh
M src/arch/riscv/RiscvTLB.py
M src/arch/riscv/SConscript
M src/arch/riscv/pagetable_walker.cc
M src/arch/riscv/pagetable_walker.hh
M src/arch/riscv/tlb.cc
M src/arch/x86/SConscript
M src/arch/x86/X86TLB.py
M src/arch/x86/pagetable_walker.cc
M src/arch/x86/pagetable_walker.hh
13 files changed, 820 insertions(+), 992 deletions(-)



diff --git a/src/arch/generic/BaseTLB.py b/src/arch/generic/BaseTLB.py
index 02776e6..7f5f53e 100644
--- a/src/arch/generic/BaseTLB.py
+++ b/src/arch/generic/BaseTLB.py
@@ -1,5 +1,6 @@
 # Copyright (c) 2008 The Hewlett-Packard Development Company
 # Copyright (c) 2018 Metempsy Technology Consulting
+# Copyright (c) 2020 Barkhausen Institut
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -26,7 +27,20 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 from m5.params import *
+from m5.proxy import *
+
 from m5.SimObject import SimObject
+from m5.objects.ClockedObject import ClockedObject
+
+class BasePagetableWalker(ClockedObject):
+    type = 'BasePagetableWalker'
+    cxx_class = 'BaseWalker'
+    cxx_header = 'arch/generic/pagetable_walker.hh'
+    abstract = True
+    port = MasterPort("Port for the hardware table walker")
+    system = Param.System(Parent.any, "system object")
+    num_squash_per_cycle = Param.Unsigned(4,
+            "Number of outstanding walks that can be squashed per cycle")

 class BaseTLB(SimObject):
     type = 'BaseTLB'
diff --git a/src/arch/generic/SConscript b/src/arch/generic/SConscript
index e3c2567..bc8811e 100644
--- a/src/arch/generic/SConscript
+++ b/src/arch/generic/SConscript
@@ -1,4 +1,5 @@
 # Copyright (c) 2016 ARM Limited
+# Copyright (c) 2020 Barkhausen Institut
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -48,5 +49,7 @@
 SimObject('BaseTLB.py')
 SimObject('ISACommon.py')

+DebugFlag('PageTableWalker', "Page table walker state machine debugging")
 DebugFlag('TLB')
 Source('pseudo_inst.cc')
+Source('pagetable_walker.cc')
diff --git a/src/arch/generic/pagetable_walker.cc b/src/arch/generic/pagetable_walker.cc
new file mode 100644
index 0000000..e6266b6
--- /dev/null
+++ b/src/arch/generic/pagetable_walker.cc
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 2012 ARM Limited
+ * Copyright (c) 2020 Barkhausen Institut
+ * 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.
+ *
+ * Copyright (c) 2007 The Hewlett-Packard Development Company
+ * 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 "arch/generic/pagetable_walker.hh"
+
+#include <memory>
+
+#include "base/bitfield.hh"
+#include "base/trie.hh"
+#include "cpu/base.hh"
+#include "cpu/thread_context.hh"
+#include "debug/PageTableWalker.hh"
+#include "mem/packet_access.hh"
+#include "mem/request.hh"
+
+Fault
+BaseWalker::start(ThreadContext * _tc, BaseTLB::Translation *_translation,
+                  const RequestPtr &_req, BaseTLB::Mode _mode)
+{
+    // TODO: in timing mode, instead of blocking when there are other
+    // outstanding requests, see if this request can be coalesced with
+    // another one (i.e. either coalesce or start walk)
+ BaseWalkerState * newState = createState(this, _translation, _req, false);
+    newState->initState(_tc, _mode, sys->isTimingMode());
+    if (currStates.size()) {
+        assert(newState->isTiming());
+ DPRINTF(PageTableWalker, "Walks in progress: %d\n", currStates.size());
+        currStates.push_back(newState);
+        return NoFault;
+    } else {
+        currStates.push_back(newState);
+        Fault fault = newState->startWalk();
+        if (!newState->isTiming()) {
+            currStates.pop_front();
+            delete newState;
+        }
+        return fault;
+    }
+}
+
+Fault
+BaseWalker::startFunctional(ThreadContext * _tc, Addr &addr,
+                            unsigned &logBytes, BaseTLB::Mode _mode)
+{
+    if (!funcState)
+    {
+        funcState = std::unique_ptr<BaseWalkerState>(
+            createState(this, NULL, NULL, true));
+    }
+    funcState->initState(_tc, _mode);
+    return funcState->startFunctional(addr, logBytes);
+}
+
+bool
+BaseWalker::BaseWalkerPort::recvTimingResp(PacketPtr pkt)
+{
+    return walker->recvTimingResp(pkt);
+}
+
+bool
+BaseWalker::recvTimingResp(PacketPtr pkt)
+{
+    WalkerSenderState * senderState =
+        dynamic_cast<WalkerSenderState *>(pkt->popSenderState());
+    BaseWalkerState * senderWalk = senderState->senderWalk;
+    bool walkComplete = senderWalk->recvPacket(pkt);
+    delete senderState;
+    if (walkComplete) {
+        std::list<BaseWalkerState *>::iterator iter;
+        for (iter = currStates.begin(); iter != currStates.end(); iter++) {
+            BaseWalkerState * walkerState = *(iter);
+            if (walkerState == senderWalk) {
+                iter = currStates.erase(iter);
+                break;
+            }
+        }
+        delete senderWalk;
+        // Since we block requests when another is outstanding, we
+        // need to check if there is a waiting request to be serviced
+        if (currStates.size() && !startWalkWrapperEvent.scheduled())
+            // delay sending any new requests until we are finished
+            // with the responses
+            schedule(startWalkWrapperEvent, clockEdge());
+    }
+    return true;
+}
+
+void
+BaseWalker::BaseWalkerPort::recvReqRetry()
+{
+    walker->recvReqRetry();
+}
+
+void
+BaseWalker::recvReqRetry()
+{
+    std::list<BaseWalkerState *>::iterator iter;
+    for (iter = currStates.begin(); iter != currStates.end(); iter++) {
+        BaseWalkerState * walkerState = *(iter);
+        if (walkerState->isRetrying()) {
+            walkerState->retry();
+        }
+    }
+}
+
+bool BaseWalker::sendTiming(BaseWalkerState* sendingState, PacketPtr pkt)
+{
+    WalkerSenderState* walker_state = new WalkerSenderState(sendingState);
+    pkt->pushSenderState(walker_state);
+    if (port.sendTimingReq(pkt)) {
+        return true;
+    } else {
+        // undo the adding of the sender state and delete it, as we
+        // will do it again the next time we attempt to send it
+        pkt->popSenderState();
+        delete walker_state;
+        return false;
+    }
+
+}
+
+Port &
+BaseWalker::getPort(const std::string &if_name, PortID idx)
+{
+    if (if_name == "port")
+        return port;
+    else
+        return ClockedObject::getPort(if_name, idx);
+}
+
+void
+BaseWalker::BaseWalkerState::initState(ThreadContext * _tc,
+                                       BaseTLB::Mode _mode, bool _isTiming)
+{
+    assert(state == Ready);
+    started = false;
+    tc = _tc;
+    mode = _mode;
+    timing = _isTiming;
+}
+
+void
+BaseWalker::startWalkWrapper()
+{
+    unsigned num_squashed = 0;
+    BaseWalkerState *currState = currStates.front();
+    while ((num_squashed < numSquashable) && currState &&
+        currState->translation->squashed()) {
+        currStates.pop_front();
+        num_squashed++;
+
+        DPRINTF(PageTableWalker, "Squashing table walk for address %#x\n",
+            currState->req->getVaddr());
+
+        // finish the translation which will delete the translation object
+        currState->translation->finish(
+            std::make_shared<UnimpFault>("Squashed Inst"),
+            currState->req, currState->tc, currState->mode);
+
+        // delete the current request if there are no inflight packets.
+        // if there is something in flight, delete when the packets are
+        // received and inflight is zero.
+        if (currState->numInflight() == 0) {
+            delete currState;
+        } else {
+            currState->squash();
+        }
+
+        // check the next translation request, if it exists
+        if (currStates.size())
+            currState = currStates.front();
+        else
+            currState = NULL;
+    }
+    if (currState && !currState->wasStarted())
+        currState->startWalk();
+}
+
+Fault
+BaseWalker::BaseWalkerState::startWalk()
+{
+    Fault fault = NoFault;
+    assert(!started);
+    started = true;
+    state = Translate;
+    nextState = Ready;
+    setupWalk(req->getVaddr());
+    if (timing) {
+        nextState = state;
+        state = Waiting;
+        timingFault = NoFault;
+        sendPackets();
+    } else {
+        do {
+            walker->port.sendAtomic(read);
+            PacketPtr write = NULL;
+            fault = stepWalk(write);
+            assert(fault == NoFault || read == NULL);
+            state = nextState;
+            nextState = Ready;
+            if (write)
+                walker->port.sendAtomic(write);
+        } while (read);
+        state = Ready;
+        nextState = Waiting;
+    }
+    return fault;
+}
+
+Fault
+BaseWalker::BaseWalkerState::startFunctional(Addr &addr, unsigned &logBytes)
+{
+    Fault fault = NoFault;
+    assert(!started);
+    started = true;
+    state = Translate;
+    nextState = Ready;
+    setupWalk(addr);
+
+    do {
+        walker->port.sendFunctional(read);
+        // On a functional access (page table lookup), writes should
+        // not happen so this pointer is ignored after stepWalk
+        PacketPtr write = NULL;
+        fault = stepWalk(write);
+        assert(fault == NoFault || read == NULL);
+        state = nextState;
+        nextState = Ready;
+    } while (read);
+    finishFunctional(addr, logBytes);
+
+    return fault;
+}
+
+void
+BaseWalker::BaseWalkerState::endWalk()
+{
+    nextState = Ready;
+    delete read;
+    read = NULL;
+}
+
+bool
+BaseWalker::BaseWalkerState::recvPacket(PacketPtr pkt)
+{
+    assert(pkt->isResponse());
+    assert(inflight);
+    assert(state == Waiting);
+    inflight--;
+    if (squashed) {
+        // if were were squashed, return true once inflight is zero and
+        // this WalkerState will be freed there.
+        return (inflight == 0);
+    }
+    if (pkt->isRead()) {
+        // should not have a pending read it we also had one outstanding
+        assert(!read);
+
+        // @todo someone should pay for this
+        pkt->headerDelay = pkt->payloadDelay = 0;
+
+        state = nextState;
+        nextState = Ready;
+        PacketPtr write = NULL;
+        read = pkt;
+        timingFault = stepWalk(write);
+        state = Waiting;
+        assert(timingFault == NoFault || read == NULL);
+        if (write) {
+            writes.push_back(write);
+        }
+        sendPackets();
+    } else {
+        sendPackets();
+    }
+    if (inflight == 0 && read == NULL && writes.size() == 0) {
+        state = Ready;
+        nextState = Waiting;
+        if (timingFault == NoFault) {
+            /*
+             * Finish the translation. Now that we know the right entry is
+             * in the TLB, this should work with no memory accesses.
+             * There could be new faults unrelated to the table walk like
+             * permissions violations, so we'll need the return value as
+             * well.
+             */
+            bool delayedResponse;
+            Fault fault = walker->translateWithTLB(req, tc, NULL, mode,
+                                                   delayedResponse);
+            assert(!delayedResponse);
+            // Let the CPU continue.
+            translation->finish(fault, req, tc, mode);
+        } else {
+            // There was a fault during the walk. Let the CPU know.
+            translation->finish(timingFault, req, tc, mode);
+        }
+        return true;
+    }
+
+    return false;
+}
+
+void
+BaseWalker::BaseWalkerState::sendPackets()
+{
+ //If we're already waiting for the port to become available, just return.
+    if (retrying)
+        return;
+
+    //Reads always have priority
+    if (read) {
+        PacketPtr pkt = read;
+        read = NULL;
+        inflight++;
+        if (!walker->sendTiming(this, pkt)) {
+            retrying = true;
+            read = pkt;
+            inflight--;
+            return;
+        }
+    }
+    //Send off as many of the writes as we can.
+    while (writes.size()) {
+        PacketPtr write = writes.back();
+        writes.pop_back();
+        inflight++;
+        if (!walker->sendTiming(this, write)) {
+            retrying = true;
+            writes.push_back(write);
+            inflight--;
+            return;
+        }
+    }
+}
+
+unsigned
+BaseWalker::BaseWalkerState::numInflight() const
+{
+    return inflight;
+}
+
+bool
+BaseWalker::BaseWalkerState::isRetrying()
+{
+    return retrying;
+}
+
+bool
+BaseWalker::BaseWalkerState::isTiming()
+{
+    return timing;
+}
+
+bool
+BaseWalker::BaseWalkerState::wasStarted()
+{
+    return started;
+}
+
+void
+BaseWalker::BaseWalkerState::squash()
+{
+    squashed = true;
+}
+
+void
+BaseWalker::BaseWalkerState::retry()
+{
+    retrying = false;
+    sendPackets();
+}
diff --git a/src/arch/generic/pagetable_walker.hh b/src/arch/generic/pagetable_walker.hh
new file mode 100644
index 0000000..56dd0ef
--- /dev/null
+++ b/src/arch/generic/pagetable_walker.hh
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2007 The Hewlett-Packard Development Company
+ * Copyright (c) 2020 Barkhausen Institut
+ * 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 __ARCH_GENERIC_WALKER_HH__
+#define __ARCH_GENERIC_WALKER_HH__
+
+#include <memory>
+#include <vector>
+
+#include "arch/generic/tlb.hh"
+#include "base/types.hh"
+#include "mem/packet.hh"
+#include "params/BasePagetableWalker.hh"
+#include "sim/clocked_object.hh"
+#include "sim/faults.hh"
+#include "sim/system.hh"
+
+class ThreadContext;
+
+class BaseWalker : public ClockedObject
+{
+  protected:
+    // Port for accessing memory
+    class BaseWalkerPort : public MasterPort
+    {
+      public:
+        BaseWalkerPort(const std::string &_name, BaseWalker * _walker) :
+              MasterPort(_name, _walker), walker(_walker)
+        {}
+
+      protected:
+        BaseWalker *walker;
+
+        bool recvTimingResp(PacketPtr pkt);
+        void recvReqRetry();
+    };
+
+    friend class BaseWalkerPort;
+    BaseWalkerPort port;
+
+    // State to track each walk of the page table
+    class BaseWalkerState
+    {
+      friend class BaseWalker;
+
+      protected:
+        enum State {
+            Ready,
+            Waiting,
+            Translate,
+        };
+
+        BaseWalker *walker;
+        ThreadContext *tc;
+        RequestPtr req;
+        State state;
+        State nextState;
+        unsigned inflight;
+        PacketPtr read;
+        std::vector<PacketPtr> writes;
+        Fault timingFault;
+        BaseTLB::Translation * translation;
+        BaseTLB::Mode mode;
+        bool functional;
+        bool timing;
+        bool retrying;
+        bool started;
+        bool squashed;
+      public:
+        BaseWalkerState(BaseWalker * _walker,
+                        BaseTLB::Translation *_translation,
+ const RequestPtr &_req, bool _isFunctional = false) :
+            walker(_walker), req(_req), state(Ready),
+            nextState(Ready), inflight(0),
+            translation(_translation),
+            functional(_isFunctional), timing(false),
+            retrying(false), started(false), squashed(false)
+        {
+        }
+        virtual ~BaseWalkerState() {}
+        void initState(ThreadContext * _tc, BaseTLB::Mode _mode,
+                       bool _isTiming = false);
+        Fault startWalk();
+        Fault startFunctional(Addr &addr, unsigned &logBytes);
+        bool recvPacket(PacketPtr pkt);
+        unsigned numInflight() const;
+        bool isRetrying();
+        bool wasStarted();
+        bool isTiming();
+        void retry();
+        void squash();
+        std::string name() const {return walker->name();}
+
+      protected:
+        virtual void setupWalk(Addr vaddr) = 0;
+        virtual Fault stepWalk(PacketPtr &write) = 0;
+        virtual void endWalk();
+        virtual void finishFunctional(Addr &addr, unsigned &logBytes) = 0;
+
+      private:
+        void sendPackets();
+    };
+
+    friend class BaseWalkerState;
+    // State for timing and atomic accesses (need multiple per walker in
+    // the case of multiple outstanding requests in timing mode)
+    std::list<BaseWalkerState *> currStates;
+    // State for functional accesses (only need one of these per walker)
+    std::unique_ptr<BaseWalkerState> funcState;
+
+    struct WalkerSenderState : public Packet::SenderState
+    {
+        BaseWalkerState * senderWalk;
+        WalkerSenderState(BaseWalkerState * _senderWalk) :
+            senderWalk(_senderWalk) {}
+    };
+
+  public:
+    // Kick off the state machine.
+    Fault start(ThreadContext * _tc, BaseTLB::Translation *translation,
+            const RequestPtr &req, BaseTLB::Mode mode);
+    Fault startFunctional(ThreadContext * _tc, Addr &addr,
+            unsigned &logBytes, BaseTLB::Mode mode);
+    Port &getPort(const std::string &if_name,
+                  PortID idx=InvalidPortID) override;
+
+  protected:
+    System * sys;
+
+    // The number of outstanding walks that can be squashed per cycle.
+    unsigned numSquashable;
+
+    // Wrapper for checking for squashes before starting a translation.
+    void startWalkWrapper();
+
+    /**
+     * Event used to call startWalkWrapper.
+     **/
+    EventFunctionWrapper startWalkWrapperEvent;
+
+    // Functions for dealing with packets.
+    bool recvTimingResp(PacketPtr pkt);
+    void recvReqRetry();
+    bool sendTiming(BaseWalkerState * sendingState, PacketPtr pkt);
+
+    // Creates the initial walker state for the given translation
+    virtual BaseWalkerState *createState(BaseWalker *walker,
+ BaseTLB::Translation *_translation,
+                                         const RequestPtr &req,
+                                         bool isFunctional) = 0;
+
+ // Performs a TLB lookup for the given translation, assuming that the TLB
+    // entry has been created before.
+ virtual Fault translateWithTLB(const RequestPtr &req, ThreadContext *tc,
+                                   BaseTLB::Translation *translation,
+                                   BaseTLB::Mode mode, bool &delayed) = 0;
+
+  public:
+
+    typedef BasePagetableWalkerParams Params;
+
+    const Params *
+    params() const
+    {
+        return static_cast<const Params *>(_params);
+    }
+
+    BaseWalker(const Params *params) :
+        ClockedObject(params), port(name() + ".port", this),
+        funcState(), sys(params->system),
+        numSquashable(params->num_squash_per_cycle),
+        startWalkWrapperEvent([this]{ startWalkWrapper(); }, name())
+    {
+    }
+    virtual ~BaseWalker() {}
+};
+
+#endif // __ARCH_GENERIC_ABLE_WALKER_HH__
\ No newline at end of file
diff --git a/src/arch/riscv/RiscvTLB.py b/src/arch/riscv/RiscvTLB.py
index 884b71f..14ba36e 100644
--- a/src/arch/riscv/RiscvTLB.py
+++ b/src/arch/riscv/RiscvTLB.py
@@ -30,17 +30,12 @@
 from m5.params import *
 from m5.proxy import *

-from m5.objects.BaseTLB import BaseTLB
-from m5.objects.ClockedObject import ClockedObject
+from m5.objects.BaseTLB import BaseTLB, BasePagetableWalker

-class RiscvPagetableWalker(ClockedObject):
+class RiscvPagetableWalker(BasePagetableWalker):
     type = 'RiscvPagetableWalker'
     cxx_class = 'RiscvISA::Walker'
     cxx_header = 'arch/riscv/pagetable_walker.hh'
-    port = MasterPort("Port for the hardware table walker")
-    system = Param.System(Parent.any, "system object")
-    num_squash_per_cycle = Param.Unsigned(4,
-            "Number of outstanding walks that can be squashed per cycle")

 class RiscvTLB(BaseTLB):
     type = 'RiscvTLB'
diff --git a/src/arch/riscv/SConscript b/src/arch/riscv/SConscript
index a706079..402107c 100644
--- a/src/arch/riscv/SConscript
+++ b/src/arch/riscv/SConscript
@@ -69,8 +69,6 @@

     DebugFlag('RiscvMisc')
     DebugFlag('TLBVerbose')
-    DebugFlag('PageTableWalker', \
-              "Page table walker state machine debugging")

     # Add in files generated by the ISA description.
     ISADesc('isa/main.isa')
diff --git a/src/arch/riscv/pagetable_walker.cc b/src/arch/riscv/pagetable_walker.cc
index 0af12ca..1d28580 100644
--- a/src/arch/riscv/pagetable_walker.cc
+++ b/src/arch/riscv/pagetable_walker.cc
@@ -66,215 +66,46 @@

 namespace RiscvISA {

-Fault
-Walker::start(ThreadContext * _tc, BaseTLB::Translation *_translation,
-              const RequestPtr &_req, BaseTLB::Mode _mode)
+BaseWalker::BaseWalkerState *
+Walker::createState(BaseWalker *walker, BaseTLB::Translation *translation,
+                    const RequestPtr &req, bool isFunctional)
 {
-    // TODO: in timing mode, instead of blocking when there are other
-    // outstanding requests, see if this request can be coalesced with
-    // another one (i.e. either coalesce or start walk)
-    WalkerState * newState = new WalkerState(this, _translation, _req);
-    newState->initState(_tc, _mode, sys->isTimingMode());
-    if (currStates.size()) {
-        assert(newState->isTiming());
- DPRINTF(PageTableWalker, "Walks in progress: %d\n", currStates.size());
-        currStates.push_back(newState);
-        return NoFault;
-    } else {
-        currStates.push_back(newState);
-        Fault fault = newState->startWalk();
-        if (!newState->isTiming()) {
-            currStates.pop_front();
-            delete newState;
-        }
-        return fault;
-    }
+    return new WalkerState(walker, translation, req, isFunctional);
 }

 Fault
-Walker::startFunctional(ThreadContext * _tc, Addr &addr, unsigned &logBytes,
-              BaseTLB::Mode _mode)
+Walker::translateWithTLB(const RequestPtr &req, ThreadContext *tc,
+                         BaseTLB::Translation *translation,
+                         BaseTLB::Mode mode, bool &delayed)
 {
-    funcState.initState(_tc, _mode);
-    return funcState.startFunctional(addr, logBytes);
-}
-
-bool
-Walker::WalkerPort::recvTimingResp(PacketPtr pkt)
-{
-    return walker->recvTimingResp(pkt);
-}
-
-bool
-Walker::recvTimingResp(PacketPtr pkt)
-{
-    WalkerSenderState * senderState =
-        dynamic_cast<WalkerSenderState *>(pkt->popSenderState());
-    WalkerState * senderWalk = senderState->senderWalk;
-    bool walkComplete = senderWalk->recvPacket(pkt);
-    delete senderState;
-    if (walkComplete) {
-        std::list<WalkerState *>::iterator iter;
-        for (iter = currStates.begin(); iter != currStates.end(); iter++) {
-            WalkerState * walkerState = *(iter);
-            if (walkerState == senderWalk) {
-                iter = currStates.erase(iter);
-                break;
-            }
-        }
-        delete senderWalk;
-        // Since we block requests when another is outstanding, we
-        // need to check if there is a waiting request to be serviced
-        if (currStates.size() && !startWalkWrapperEvent.scheduled())
-            // delay sending any new requests until we are finished
-            // with the responses
-            schedule(startWalkWrapperEvent, clockEdge());
-    }
-    return true;
+    return tlb->doTranslate(req, tc, NULL, mode, delayed);
 }

 void
-Walker::WalkerPort::recvReqRetry()
+Walker::WalkerState::setupWalk(Addr vaddr)
 {
-    walker->recvReqRetry();
-}
+    vaddr &= ((static_cast<Addr>(1) << VADDR_BITS) - 1);

-void
-Walker::recvReqRetry()
-{
-    std::list<WalkerState *>::iterator iter;
-    for (iter = currStates.begin(); iter != currStates.end(); iter++) {
-        WalkerState * walkerState = *(iter);
-        if (walkerState->isRetrying()) {
-            walkerState->retry();
-        }
-    }
-}
+    SATP satp = tc->readMiscReg(MISCREG_SATP);
+    assert(satp.mode == AddrXlateMode::SV39);

-bool Walker::sendTiming(WalkerState* sendingState, PacketPtr pkt)
-{
-    WalkerSenderState* walker_state = new WalkerSenderState(sendingState);
-    pkt->pushSenderState(walker_state);
-    if (port.sendTimingReq(pkt)) {
-        return true;
-    } else {
-        // undo the adding of the sender state and delete it, as we
-        // will do it again the next time we attempt to send it
-        pkt->popSenderState();
-        delete walker_state;
-        return false;
-    }
+    Addr shift = PageShift + LEVEL_BITS * 2;
+    Addr idx = (vaddr >> shift) & LEVEL_MASK;
+    Addr topAddr = (satp.ppn << PageShift) + (idx * sizeof(PTESv39));
+    level = 2;

-}
+ DPRINTF(PageTableWalker, "Performing table walk for address %#x\n", vaddr); + DPRINTF(PageTableWalker, "Loading level%d PTE from %#x\n", level, topAddr);

-Port &
-Walker::getPort(const std::string &if_name, PortID idx)
-{
-    if (if_name == "port")
-        return port;
-    else
-        return ClockedObject::getPort(if_name, idx);
-}
+    entry.vaddr = vaddr;
+    entry.asid = satp.asid;

-void
-Walker::WalkerState::initState(ThreadContext * _tc,
-        BaseTLB::Mode _mode, bool _isTiming)
-{
-    assert(state == Ready);
-    started = false;
-    tc = _tc;
-    mode = _mode;
-    timing = _isTiming;
-}
+    Request::Flags flags = Request::PHYSICAL;
+    RequestPtr request = std::make_shared<Request>(
+        topAddr, sizeof(PTESv39), flags, ourWalker()->masterId);

-void
-Walker::startWalkWrapper()
-{
-    unsigned num_squashed = 0;
-    WalkerState *currState = currStates.front();
-    while ((num_squashed < numSquashable) && currState &&
-        currState->translation->squashed()) {
-        currStates.pop_front();
-        num_squashed++;
-
-        DPRINTF(PageTableWalker, "Squashing table walk for address %#x\n",
-            currState->req->getVaddr());
-
-        // finish the translation which will delete the translation object
-        currState->translation->finish(
-            std::make_shared<UnimpFault>("Squashed Inst"),
-            currState->req, currState->tc, currState->mode);
-
-        // delete the current request if there are no inflight packets.
-        // if there is something in flight, delete when the packets are
-        // received and inflight is zero.
-        if (currState->numInflight() == 0) {
-            delete currState;
-        } else {
-            currState->squash();
-        }
-
-        // check the next translation request, if it exists
-        if (currStates.size())
-            currState = currStates.front();
-        else
-            currState = NULL;
-    }
-    if (currState && !currState->wasStarted())
-        currState->startWalk();
-}
-
-Fault
-Walker::WalkerState::startWalk()
-{
-    Fault fault = NoFault;
-    assert(!started);
-    started = true;
-    setupWalk(req->getVaddr());
-    if (timing) {
-        nextState = state;
-        state = Waiting;
-        timingFault = NoFault;
-        sendPackets();
-    } else {
-        do {
-            walker->port.sendAtomic(read);
-            PacketPtr write = NULL;
-            fault = stepWalk(write);
-            assert(fault == NoFault || read == NULL);
-            state = nextState;
-            nextState = Ready;
-            if (write)
-                walker->port.sendAtomic(write);
-        } while (read);
-        state = Ready;
-        nextState = Waiting;
-    }
-    return fault;
-}
-
-Fault
-Walker::WalkerState::startFunctional(Addr &addr, unsigned &logBytes)
-{
-    Fault fault = NoFault;
-    assert(!started);
-    started = true;
-    setupWalk(addr);
-
-    do {
-        walker->port.sendFunctional(read);
-        // On a functional access (page table lookup), writes should
-        // not happen so this pointer is ignored after stepWalk
-        PacketPtr write = NULL;
-        fault = stepWalk(write);
-        assert(fault == NoFault || read == NULL);
-        state = nextState;
-        nextState = Ready;
-    } while (read);
-    logBytes = entry.logBytes;
-    addr = entry.paddr << PageShift;
-
-    return fault;
+    read = new Packet(request, MemCmd::ReadReq);
+    read->allocate();
 }

 Fault
@@ -297,26 +128,27 @@
     if (!pte.v || (!pte.r && pte.w)) {
         doEndWalk = true;
         DPRINTF(PageTableWalker, "PTE invalid, raising PF\n");
-        fault = pageFault(pte.v);
+        fault = pageFault();
     }
     else {
         // step 4:
         if (pte.r || pte.x) {
             // step 5: leaf PTE
             doEndWalk = true;
- fault = walker->tlb->checkPermissions(tc, entry.vaddr, mode, pte);
+            fault = ourWalker()->tlb->checkPermissions(tc, entry.vaddr,
+                                                       mode, pte);

             // step 6
             if (fault == NoFault) {
                 if (level >= 1 && pte.ppn0 != 0) {
                     DPRINTF(PageTableWalker,
                             "PTE has misaligned PPN, raising PF\n");
-                    fault = pageFault(true);
+                    fault = pageFault();
                 }
                 else if (level == 2 && pte.ppn1 != 0) {
                     DPRINTF(PageTableWalker,
                             "PTE has misaligned PPN, raising PF\n");
-                    fault = pageFault(true);
+                    fault = pageFault();
                 }
             }

@@ -349,7 +181,7 @@
             if (level < 0) {
DPRINTF(PageTableWalker, "No leaf PTE found, raising PF\n");
                 doEndWalk = true;
-                fault = pageFault(true);
+                fault = pageFault();
             }
             else {
                 Addr shift = (PageShift + LEVEL_BITS * level);
@@ -379,7 +211,7 @@

         if (doTLBInsert) {
             if (!functional)
-                walker->tlb->insert(entry.vaddr, entry);
+                ourWalker()->tlb->insert(entry.vaddr, entry);
             else {
                 Addr offset = entry.vaddr & mask(entry.logBytes);
                 Addr paddr = entry.paddr << PageShift | offset;
@@ -392,7 +224,7 @@
     else {
         //If we didn't return, we're setting up another read.
         RequestPtr request = std::make_shared<Request>(
-            nextRead, oldRead->getSize(), flags, walker->masterId);
+            nextRead, oldRead->getSize(), flags, ourWalker()->masterId);
         read = new Packet(request, MemCmd::ReadReq);
         read->allocate();

@@ -404,177 +236,17 @@
 }

 void
-Walker::WalkerState::endWalk()
+Walker::WalkerState::finishFunctional(Addr &addr, unsigned &logBytes)
 {
-    nextState = Ready;
-    delete read;
-    read = NULL;
-}
-
-void
-Walker::WalkerState::setupWalk(Addr vaddr)
-{
-    vaddr &= ((static_cast<Addr>(1) << VADDR_BITS) - 1);
-
-    SATP satp = tc->readMiscReg(MISCREG_SATP);
-    assert(satp.mode == AddrXlateMode::SV39);
-
-    Addr shift = PageShift + LEVEL_BITS * 2;
-    Addr idx = (vaddr >> shift) & LEVEL_MASK;
-    Addr topAddr = (satp.ppn << PageShift) + (idx * sizeof(PTESv39));
-    level = 2;
-
- DPRINTF(PageTableWalker, "Performing table walk for address %#x\n", vaddr); - DPRINTF(PageTableWalker, "Loading level%d PTE from %#x\n", level, topAddr);
-
-    state = Translate;
-    nextState = Ready;
-    entry.vaddr = vaddr;
-    entry.asid = satp.asid;
-
-    Request::Flags flags = Request::PHYSICAL;
-    RequestPtr request = std::make_shared<Request>(
-        topAddr, sizeof(PTESv39), flags, walker->masterId);
-
-    read = new Packet(request, MemCmd::ReadReq);
-    read->allocate();
-}
-
-bool
-Walker::WalkerState::recvPacket(PacketPtr pkt)
-{
-    assert(pkt->isResponse());
-    assert(inflight);
-    assert(state == Waiting);
-    inflight--;
-    if (squashed) {
-        // if were were squashed, return true once inflight is zero and
-        // this WalkerState will be freed there.
-        return (inflight == 0);
-    }
-    if (pkt->isRead()) {
-        // should not have a pending read it we also had one outstanding
-        assert(!read);
-
-        // @todo someone should pay for this
-        pkt->headerDelay = pkt->payloadDelay = 0;
-
-        state = nextState;
-        nextState = Ready;
-        PacketPtr write = NULL;
-        read = pkt;
-        timingFault = stepWalk(write);
-        state = Waiting;
-        assert(timingFault == NoFault || read == NULL);
-        if (write) {
-            writes.push_back(write);
-        }
-        sendPackets();
-    } else {
-        sendPackets();
-    }
-    if (inflight == 0 && read == NULL && writes.size() == 0) {
-        state = Ready;
-        nextState = Waiting;
-        if (timingFault == NoFault) {
-            /*
-             * Finish the translation. Now that we know the right entry is
-             * in the TLB, this should work with no memory accesses.
-             * There could be new faults unrelated to the table walk like
-             * permissions violations, so we'll need the return value as
-             * well.
-             */
-            bool delayedResponse;
-            Fault fault = walker->tlb->doTranslate(req, tc, NULL, mode,
-                                                   delayedResponse);
-            assert(!delayedResponse);
-            // Let the CPU continue.
-            translation->finish(fault, req, tc, mode);
-        } else {
-            // There was a fault during the walk. Let the CPU know.
-            translation->finish(timingFault, req, tc, mode);
-        }
-        return true;
-    }
-
-    return false;
-}
-
-void
-Walker::WalkerState::sendPackets()
-{
- //If we're already waiting for the port to become available, just return.
-    if (retrying)
-        return;
-
-    //Reads always have priority
-    if (read) {
-        PacketPtr pkt = read;
-        read = NULL;
-        inflight++;
-        if (!walker->sendTiming(this, pkt)) {
-            retrying = true;
-            read = pkt;
-            inflight--;
-            return;
-        }
-    }
-    //Send off as many of the writes as we can.
-    while (writes.size()) {
-        PacketPtr write = writes.back();
-        writes.pop_back();
-        inflight++;
-        if (!walker->sendTiming(this, write)) {
-            retrying = true;
-            writes.push_back(write);
-            inflight--;
-            return;
-        }
-    }
-}
-
-unsigned
-Walker::WalkerState::numInflight() const
-{
-    return inflight;
-}
-
-bool
-Walker::WalkerState::isRetrying()
-{
-    return retrying;
-}
-
-bool
-Walker::WalkerState::isTiming()
-{
-    return timing;
-}
-
-bool
-Walker::WalkerState::wasStarted()
-{
-    return started;
-}
-
-void
-Walker::WalkerState::squash()
-{
-    squashed = true;
-}
-
-void
-Walker::WalkerState::retry()
-{
-    retrying = false;
-    sendPackets();
+    logBytes = entry.logBytes;
+    addr = entry.paddr << PageShift;
 }

 Fault
-Walker::WalkerState::pageFault(bool present)
+Walker::WalkerState::pageFault()
 {
     DPRINTF(PageTableWalker, "Raising page fault.\n");
-    return walker->tlb->createPagefault(entry.vaddr, mode);
+    return ourWalker()->tlb->createPagefault(entry.vaddr, mode);
 }

 } /* end namespace RiscvISA */
diff --git a/src/arch/riscv/pagetable_walker.hh b/src/arch/riscv/pagetable_walker.hh
index 2d50928..04caec0 100644
--- a/src/arch/riscv/pagetable_walker.hh
+++ b/src/arch/riscv/pagetable_walker.hh
@@ -41,6 +41,7 @@

 #include <vector>

+#include "arch/generic/pagetable_walker.hh"
 #include "arch/riscv/pagetable.hh"
 #include "arch/riscv/tlb.hh"
 #include "base/types.hh"
@@ -54,133 +55,40 @@

 namespace RiscvISA
 {
-    class Walker : public ClockedObject
+    class Walker : public BaseWalker
     {
       protected:
-        // Port for accessing memory
-        class WalkerPort : public MasterPort
-        {
-          public:
-            WalkerPort(const std::string &_name, Walker * _walker) :
-                  MasterPort(_name, _walker), walker(_walker)
-            {}
-
-          protected:
-            Walker *walker;
-
-            bool recvTimingResp(PacketPtr pkt);
-            void recvReqRetry();
-        };
-
-        friend class WalkerPort;
-        WalkerPort port;
-
         // State to track each walk of the page table
-        class WalkerState
+        class WalkerState : public BaseWalkerState
         {
-          friend class Walker;
-          private:
-            enum State {
-                Ready,
-                Waiting,
-                Translate,
-            };
-
           protected:
-            Walker *walker;
-            ThreadContext *tc;
-            RequestPtr req;
-            State state;
-            State nextState;
             int level;
-            unsigned inflight;
             TlbEntry entry;
-            PacketPtr read;
-            std::vector<PacketPtr> writes;
-            Fault timingFault;
-            TLB::Translation * translation;
-            BaseTLB::Mode mode;
-            bool functional;
-            bool timing;
-            bool retrying;
-            bool started;
-            bool squashed;
           public:
- WalkerState(Walker * _walker, BaseTLB::Translation *_translation,
+            WalkerState(BaseWalker * _walker,
+                        BaseTLB::Translation *_translation,
const RequestPtr &_req, bool _isFunctional = false) :
-                walker(_walker), req(_req), state(Ready),
-                nextState(Ready), level(0), inflight(0),
-                translation(_translation),
-                functional(_isFunctional), timing(false),
-                retrying(false), started(false), squashed(false)
+ BaseWalkerState(_walker, _translation, _req, _isFunctional),
+                level(0), entry()
             {
             }
-            void initState(ThreadContext * _tc, BaseTLB::Mode _mode,
-                           bool _isTiming = false);
-            Fault startWalk();
-            Fault startFunctional(Addr &addr, unsigned &logBytes);
-            bool recvPacket(PacketPtr pkt);
-            unsigned numInflight() const;
-            bool isRetrying();
-            bool wasStarted();
-            bool isTiming();
-            void retry();
-            void squash();
-            std::string name() const {return walker->name();}

-          private:
-            void setupWalk(Addr vaddr);
-            Fault stepWalk(PacketPtr &write);
-            void sendPackets();
-            void endWalk();
-            Fault pageFault(bool present);
+          protected:
+            Walker *ourWalker()
+            {
+                return static_cast<Walker*>(walker);
+            }
+
+            void setupWalk(Addr vaddr) override;
+            Fault stepWalk(PacketPtr &write) override;
+            void finishFunctional(Addr &addr, unsigned &logBytes) override;
+            Fault pageFault();
         };

-        friend class WalkerState;
- // State for timing and atomic accesses (need multiple per walker in
-        // the case of multiple outstanding requests in timing mode)
-        std::list<WalkerState *> currStates;
- // State for functional accesses (only need one of these per walker)
-        WalkerState funcState;
-
-        struct WalkerSenderState : public Packet::SenderState
-        {
-            WalkerState * senderWalk;
-            WalkerSenderState(WalkerState * _senderWalk) :
-                senderWalk(_senderWalk) {}
-        };
-
-      public:
-        // Kick off the state machine.
-        Fault start(ThreadContext * _tc, BaseTLB::Translation *translation,
-                const RequestPtr &req, BaseTLB::Mode mode);
-        Fault startFunctional(ThreadContext * _tc, Addr &addr,
-                unsigned &logBytes, BaseTLB::Mode mode);
-        Port &getPort(const std::string &if_name,
-                      PortID idx=InvalidPortID) override;
-
-      protected:
         // The TLB we're supposed to load.
         TLB * tlb;
-        System * sys;
         MasterID masterId;

-        // The number of outstanding walks that can be squashed per cycle.
-        unsigned numSquashable;
-
-        // Wrapper for checking for squashes before starting a translation.
-        void startWalkWrapper();
-
-        /**
-         * Event used to call startWalkWrapper.
-         **/
-        EventFunctionWrapper startWalkWrapperEvent;
-
-        // Functions for dealing with packets.
-        bool recvTimingResp(PacketPtr pkt);
-        void recvReqRetry();
-        bool sendTiming(WalkerState * sendingState, PacketPtr pkt);
-
       public:

         void setTLB(TLB * _tlb)
@@ -188,6 +96,15 @@
             tlb = _tlb;
         }

+        BaseWalkerState *createState(BaseWalker *walker,
+                                     BaseTLB::Translation *translation,
+                                     const RequestPtr &req,
+                                     bool isFunctional) override;
+
+        Fault translateWithTLB(const RequestPtr &req, ThreadContext *tc,
+                               BaseTLB::Translation *translation,
+                               BaseTLB::Mode mode, bool &delayed) override;
+
         typedef RiscvPagetableWalkerParams Params;

         const Params *
@@ -197,11 +114,8 @@
         }

         Walker(const Params *params) :
-            ClockedObject(params), port(name() + ".port", this),
- funcState(this, NULL, NULL, true), tlb(NULL), sys(params->system),
-            masterId(sys->getMasterId(this)),
-            numSquashable(params->num_squash_per_cycle),
-            startWalkWrapperEvent([this]{ startWalkWrapper(); }, name())
+            BaseWalker(params), tlb(NULL),
+            masterId(params->system->getMasterId(this))
         {
         }
     };
diff --git a/src/arch/riscv/tlb.cc b/src/arch/riscv/tlb.cc
index febfc16..9296a84 100644
--- a/src/arch/riscv/tlb.cc
+++ b/src/arch/riscv/tlb.cc
@@ -76,7 +76,7 @@
     walker->setTLB(this);
 }

-Walker *
+RiscvISA::Walker *
 TLB::getWalker()
 {
     return walker;
diff --git a/src/arch/x86/SConscript b/src/arch/x86/SConscript
index 3e53228..12dda46 100644
--- a/src/arch/x86/SConscript
+++ b/src/arch/x86/SConscript
@@ -1,6 +1,7 @@
 # -*- mode:python -*-

 # Copyright (c) 2007-2008 The Hewlett-Packard Development Company
+# Copyright (c) 2020 Barkhausen Institut
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -79,8 +80,6 @@

     DebugFlag('Faults', "Trace all faults/exceptions/traps")
     DebugFlag('LocalApic', "Local APIC debugging")
-    DebugFlag('PageTableWalker', \
-              "Page table walker state machine debugging")
     DebugFlag('Decoder', "Decoder debug output")
     DebugFlag('X86', "Generic X86 ISA debugging")

diff --git a/src/arch/x86/X86TLB.py b/src/arch/x86/X86TLB.py
index bb35526..8811afd 100644
--- a/src/arch/x86/X86TLB.py
+++ b/src/arch/x86/X86TLB.py
@@ -1,4 +1,5 @@
 # Copyright (c) 2007 The Hewlett-Packard Development Company
+# Copyright (c) 2020 Barkhausen Institut
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -36,17 +37,13 @@
 from m5.params import *
 from m5.proxy import *

-from m5.objects.BaseTLB import BaseTLB
+from m5.objects.BaseTLB import BaseTLB, BasePagetableWalker
 from m5.objects.ClockedObject import ClockedObject

-class X86PagetableWalker(ClockedObject):
+class X86PagetableWalker(BasePagetableWalker):
     type = 'X86PagetableWalker'
     cxx_class = 'X86ISA::Walker'
     cxx_header = 'arch/x86/pagetable_walker.hh'
-    port = MasterPort("Port for the hardware table walker")
-    system = Param.System(Parent.any, "system object")
-    num_squash_per_cycle = Param.Unsigned(4,
-            "Number of outstanding walks that can be squashed per cycle")

 class X86TLB(BaseTLB):
     type = 'X86TLB'
diff --git a/src/arch/x86/pagetable_walker.cc b/src/arch/x86/pagetable_walker.cc
index d655fa6..65a22cb 100644
--- a/src/arch/x86/pagetable_walker.cc
+++ b/src/arch/x86/pagetable_walker.cc
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2012 ARM Limited
+ * Copyright (c) 2020 Barkhausen Institut
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -65,215 +66,68 @@

 namespace X86ISA {

-Fault
-Walker::start(ThreadContext * _tc, BaseTLB::Translation *_translation,
-              const RequestPtr &_req, BaseTLB::Mode _mode)
+BaseWalker::BaseWalkerState *
+Walker::createState(BaseWalker *walker, BaseTLB::Translation *translation,
+                    const RequestPtr &req, bool isFunctional)
 {
-    // TODO: in timing mode, instead of blocking when there are other
-    // outstanding requests, see if this request can be coalesced with
-    // another one (i.e. either coalesce or start walk)
-    WalkerState * newState = new WalkerState(this, _translation, _req);
-    newState->initState(_tc, _mode, sys->isTimingMode());
-    if (currStates.size()) {
-        assert(newState->isTiming());
- DPRINTF(PageTableWalker, "Walks in progress: %d\n", currStates.size());
-        currStates.push_back(newState);
-        return NoFault;
-    } else {
-        currStates.push_back(newState);
-        Fault fault = newState->startWalk();
-        if (!newState->isTiming()) {
-            currStates.pop_front();
-            delete newState;
-        }
-        return fault;
-    }
+    return new WalkerState(walker, translation, req, isFunctional);
 }

 Fault
-Walker::startFunctional(ThreadContext * _tc, Addr &addr, unsigned &logBytes,
-              BaseTLB::Mode _mode)
+Walker::translateWithTLB(const RequestPtr &req, ThreadContext *tc,
+                         BaseTLB::Translation *translation,
+                         BaseTLB::Mode mode, bool &delayed)
 {
-    funcState.initState(_tc, _mode);
-    return funcState.startFunctional(addr, logBytes);
-}
-
-bool
-Walker::WalkerPort::recvTimingResp(PacketPtr pkt)
-{
-    return walker->recvTimingResp(pkt);
-}
-
-bool
-Walker::recvTimingResp(PacketPtr pkt)
-{
-    WalkerSenderState * senderState =
-        dynamic_cast<WalkerSenderState *>(pkt->popSenderState());
-    WalkerState * senderWalk = senderState->senderWalk;
-    bool walkComplete = senderWalk->recvPacket(pkt);
-    delete senderState;
-    if (walkComplete) {
-        std::list<WalkerState *>::iterator iter;
-        for (iter = currStates.begin(); iter != currStates.end(); iter++) {
-            WalkerState * walkerState = *(iter);
-            if (walkerState == senderWalk) {
-                iter = currStates.erase(iter);
-                break;
-            }
-        }
-        delete senderWalk;
-        // Since we block requests when another is outstanding, we
-        // need to check if there is a waiting request to be serviced
-        if (currStates.size() && !startWalkWrapperEvent.scheduled())
-            // delay sending any new requests until we are finished
-            // with the responses
-            schedule(startWalkWrapperEvent, clockEdge());
-    }
-    return true;
+    return tlb->translate(req, tc, NULL, mode, delayed, true);
 }

 void
-Walker::WalkerPort::recvReqRetry()
+Walker::WalkerState::setupWalk(Addr vaddr)
 {
-    walker->recvReqRetry();
-}
-
-void
-Walker::recvReqRetry()
-{
-    std::list<WalkerState *>::iterator iter;
-    for (iter = currStates.begin(); iter != currStates.end(); iter++) {
-        WalkerState * walkerState = *(iter);
-        if (walkerState->isRetrying()) {
-            walkerState->retry();
-        }
-    }
-}
-
-bool Walker::sendTiming(WalkerState* sendingState, PacketPtr pkt)
-{
-    WalkerSenderState* walker_state = new WalkerSenderState(sendingState);
-    pkt->pushSenderState(walker_state);
-    if (port.sendTimingReq(pkt)) {
-        return true;
+    VAddr addr = vaddr;
+    CR3 cr3 = tc->readMiscRegNoEffect(MISCREG_CR3);
+    // Check if we're in long mode or not
+    Efer efer = tc->readMiscRegNoEffect(MISCREG_EFER);
+    dataSize = 8;
+    Addr topAddr;
+    if (efer.lma) {
+        // Do long mode.
+        xlState = LongPML4;
+        topAddr = (cr3.longPdtb << 12) + addr.longl4 * dataSize;
+        enableNX = efer.nxe;
     } else {
-        // undo the adding of the sender state and delete it, as we
-        // will do it again the next time we attempt to send it
-        pkt->popSenderState();
-        delete walker_state;
-        return false;
-    }
-
-}
-
-Port &
-Walker::getPort(const std::string &if_name, PortID idx)
-{
-    if (if_name == "port")
-        return port;
-    else
-        return ClockedObject::getPort(if_name, idx);
-}
-
-void
-Walker::WalkerState::initState(ThreadContext * _tc,
-        BaseTLB::Mode _mode, bool _isTiming)
-{
-    assert(state == Ready);
-    started = false;
-    tc = _tc;
-    mode = _mode;
-    timing = _isTiming;
-}
-
-void
-Walker::startWalkWrapper()
-{
-    unsigned num_squashed = 0;
-    WalkerState *currState = currStates.front();
-    while ((num_squashed < numSquashable) && currState &&
-        currState->translation->squashed()) {
-        currStates.pop_front();
-        num_squashed++;
-
-        DPRINTF(PageTableWalker, "Squashing table walk for address %#x\n",
-            currState->req->getVaddr());
-
-        // finish the translation which will delete the translation object
-        currState->translation->finish(
-            std::make_shared<UnimpFault>("Squashed Inst"),
-            currState->req, currState->tc, currState->mode);
-
-        // delete the current request if there are no inflight packets.
-        // if there is something in flight, delete when the packets are
-        // received and inflight is zero.
-        if (currState->numInflight() == 0) {
-            delete currState;
+        // We're in some flavor of legacy mode.
+        CR4 cr4 = tc->readMiscRegNoEffect(MISCREG_CR4);
+        if (cr4.pae) {
+            // Do legacy PAE.
+            xlState = PAEPDP;
+            topAddr = (cr3.paePdtb << 5) + addr.pael3 * dataSize;
+            enableNX = efer.nxe;
         } else {
-            currState->squash();
+            dataSize = 4;
+            topAddr = (cr3.pdtb << 12) + addr.norml2 * dataSize;
+            if (cr4.pse) {
+                // Do legacy PSE.
+                xlState = PSEPD;
+            } else {
+                // Do legacy non PSE.
+                xlState = PD;
+            }
+            enableNX = false;
         }
-
-        // check the next translation request, if it exists
-        if (currStates.size())
-            currState = currStates.front();
-        else
-            currState = NULL;
     }
-    if (currState && !currState->wasStarted())
-        currState->startWalk();
-}

-Fault
-Walker::WalkerState::startWalk()
-{
-    Fault fault = NoFault;
-    assert(!started);
-    started = true;
-    setupWalk(req->getVaddr());
-    if (timing) {
-        nextState = state;
-        state = Waiting;
-        timingFault = NoFault;
-        sendPackets();
-    } else {
-        do {
-            walker->port.sendAtomic(read);
-            PacketPtr write = NULL;
-            fault = stepWalk(write);
-            assert(fault == NoFault || read == NULL);
-            state = nextState;
-            nextState = Ready;
-            if (write)
-                walker->port.sendAtomic(write);
-        } while (read);
-        state = Ready;
-        nextState = Waiting;
-    }
-    return fault;
-}
+    entry.vaddr = vaddr;

-Fault
-Walker::WalkerState::startFunctional(Addr &addr, unsigned &logBytes)
-{
-    Fault fault = NoFault;
-    assert(!started);
-    started = true;
-    setupWalk(addr);
+    Request::Flags flags = Request::PHYSICAL;
+    if (cr3.pcd)
+        flags.set(Request::UNCACHEABLE);

-    do {
-        walker->port.sendFunctional(read);
-        // On a functional access (page table lookup), writes should
-        // not happen so this pointer is ignored after stepWalk
-        PacketPtr write = NULL;
-        fault = stepWalk(write);
-        assert(fault == NoFault || read == NULL);
-        state = nextState;
-        nextState = Ready;
-    } while (read);
-    logBytes = entry.logBytes;
-    addr = entry.paddr;
+    RequestPtr request = std::make_shared<Request>(
+        topAddr, dataSize, flags, ourWalker()->masterId);

-    return fault;
+    read = new Packet(request, MemCmd::ReadReq);
+    read->allocate();
 }

 Fault
@@ -282,6 +136,7 @@
     assert(state != Ready && state != Waiting);
     Fault fault = NoFault;
     write = NULL;
+    nextXlState = Idle;
     PageTableEntry pte;
     if (dataSize == 8)
         pte = read->getLE<uint64_t>();
@@ -294,7 +149,7 @@
     bool doTLBInsert = false;
     bool doEndWalk = false;
     bool badNX = pte.nx && mode == BaseTLB::Execute && enableNX;
-    switch(state) {
+    switch(xlState) {
       case LongPML4:
         DPRINTF(PageTableWalker,
                 "Got long mode PML4 entry %#016x.\n", (uint64_t)pte);
@@ -309,7 +164,7 @@
             break;
         }
         entry.noExec = pte.nx;
-        nextState = LongPDP;
+        nextXlState = LongPDP;
         break;
       case LongPDP:
         DPRINTF(PageTableWalker,
@@ -324,7 +179,7 @@
             fault = pageFault(pte.p);
             break;
         }
-        nextState = LongPD;
+        nextXlState = LongPD;
         break;
       case LongPD:
         DPRINTF(PageTableWalker,
@@ -343,7 +198,7 @@
             entry.logBytes = 12;
             nextRead =
((uint64_t)pte & (mask(40) << 12)) + vaddr.longl1 * dataSize;
-            nextState = LongPTE;
+            nextXlState = LongPTE;
             break;
         } else {
             // 2 MB page
@@ -386,7 +241,7 @@
             fault = pageFault(pte.p);
             break;
         }
-        nextState = PAEPD;
+        nextXlState = PAEPD;
         break;
       case PAEPD:
         DPRINTF(PageTableWalker,
@@ -404,7 +259,7 @@
             // 4 KB page
             entry.logBytes = 12;
nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.pael1 * dataSize;
-            nextState = PAEPTE;
+            nextXlState = PAEPTE;
             break;
         } else {
             // 2 MB page
@@ -455,7 +310,7 @@
             entry.logBytes = 12;
             nextRead =
((uint64_t)pte & (mask(20) << 12)) + vaddr.norml2 * dataSize;
-            nextState = PTE;
+            nextXlState = PTE;
             break;
         } else {
             // 4 MB page
@@ -484,7 +339,7 @@
         // 4 KB page
         entry.logBytes = 12;
nextRead = ((uint64_t)pte & (mask(20) << 12)) + vaddr.norml2 * dataSize;
-        nextState = PTE;
+        nextXlState = PTE;
         break;
       case PTE:
         DPRINTF(PageTableWalker,
@@ -509,10 +364,15 @@
       default:
         panic("Unknown page table walker state %d!\n");
     }
+
+    if (nextXlState != Idle)
+        nextState = Translate;
+    xlState = nextXlState;
+
     if (doEndWalk) {
         if (doTLBInsert)
             if (!functional)
-                walker->tlb->insert(entry.vaddr, entry);
+                ourWalker()->tlb->insert(entry.vaddr, entry);
         endWalk();
     } else {
         PacketPtr oldRead = read;
@@ -520,7 +380,7 @@
         Request::Flags flags = oldRead->req->getFlags();
         flags.set(Request::UNCACHEABLE, uncacheable);
         RequestPtr request = std::make_shared<Request>(
-            nextRead, oldRead->getSize(), flags, walker->masterId);
+            nextRead, oldRead->getSize(), flags, ourWalker()->masterId);
         read = new Packet(request, MemCmd::ReadReq);
         read->allocate();
// If we need to write, adjust the read packet to write the modified
@@ -538,191 +398,10 @@
 }

 void
-Walker::WalkerState::endWalk()
+Walker::WalkerState::finishFunctional(Addr &addr, unsigned &logBytes)
 {
-    nextState = Ready;
-    delete read;
-    read = NULL;
-}
-
-void
-Walker::WalkerState::setupWalk(Addr vaddr)
-{
-    VAddr addr = vaddr;
-    CR3 cr3 = tc->readMiscRegNoEffect(MISCREG_CR3);
-    // Check if we're in long mode or not
-    Efer efer = tc->readMiscRegNoEffect(MISCREG_EFER);
-    dataSize = 8;
-    Addr topAddr;
-    if (efer.lma) {
-        // Do long mode.
-        state = LongPML4;
-        topAddr = (cr3.longPdtb << 12) + addr.longl4 * dataSize;
-        enableNX = efer.nxe;
-    } else {
-        // We're in some flavor of legacy mode.
-        CR4 cr4 = tc->readMiscRegNoEffect(MISCREG_CR4);
-        if (cr4.pae) {
-            // Do legacy PAE.
-            state = PAEPDP;
-            topAddr = (cr3.paePdtb << 5) + addr.pael3 * dataSize;
-            enableNX = efer.nxe;
-        } else {
-            dataSize = 4;
-            topAddr = (cr3.pdtb << 12) + addr.norml2 * dataSize;
-            if (cr4.pse) {
-                // Do legacy PSE.
-                state = PSEPD;
-            } else {
-                // Do legacy non PSE.
-                state = PD;
-            }
-            enableNX = false;
-        }
-    }
-
-    nextState = Ready;
-    entry.vaddr = vaddr;
-
-    Request::Flags flags = Request::PHYSICAL;
-    if (cr3.pcd)
-        flags.set(Request::UNCACHEABLE);
-
-    RequestPtr request = std::make_shared<Request>(
-        topAddr, dataSize, flags, walker->masterId);
-
-    read = new Packet(request, MemCmd::ReadReq);
-    read->allocate();
-}
-
-bool
-Walker::WalkerState::recvPacket(PacketPtr pkt)
-{
-    assert(pkt->isResponse());
-    assert(inflight);
-    assert(state == Waiting);
-    inflight--;
-    if (squashed) {
-        // if were were squashed, return true once inflight is zero and
-        // this WalkerState will be freed there.
-        return (inflight == 0);
-    }
-    if (pkt->isRead()) {
-        // should not have a pending read it we also had one outstanding
-        assert(!read);
-
-        // @todo someone should pay for this
-        pkt->headerDelay = pkt->payloadDelay = 0;
-
-        state = nextState;
-        nextState = Ready;
-        PacketPtr write = NULL;
-        read = pkt;
-        timingFault = stepWalk(write);
-        state = Waiting;
-        assert(timingFault == NoFault || read == NULL);
-        if (write) {
-            writes.push_back(write);
-        }
-        sendPackets();
-    } else {
-        sendPackets();
-    }
-    if (inflight == 0 && read == NULL && writes.size() == 0) {
-        state = Ready;
-        nextState = Waiting;
-        if (timingFault == NoFault) {
-            /*
-             * Finish the translation. Now that we know the right entry is
-             * in the TLB, this should work with no memory accesses.
-             * There could be new faults unrelated to the table walk like
-             * permissions violations, so we'll need the return value as
-             * well.
-             */
-            bool delayedResponse;
-            Fault fault = walker->tlb->translate(req, tc, NULL, mode,
-                                                 delayedResponse, true);
-            assert(!delayedResponse);
-            // Let the CPU continue.
-            translation->finish(fault, req, tc, mode);
-        } else {
-            // There was a fault during the walk. Let the CPU know.
-            translation->finish(timingFault, req, tc, mode);
-        }
-        return true;
-    }
-
-    return false;
-}
-
-void
-Walker::WalkerState::sendPackets()
-{
- //If we're already waiting for the port to become available, just return.
-    if (retrying)
-        return;
-
-    //Reads always have priority
-    if (read) {
-        PacketPtr pkt = read;
-        read = NULL;
-        inflight++;
-        if (!walker->sendTiming(this, pkt)) {
-            retrying = true;
-            read = pkt;
-            inflight--;
-            return;
-        }
-    }
-    //Send off as many of the writes as we can.
-    while (writes.size()) {
-        PacketPtr write = writes.back();
-        writes.pop_back();
-        inflight++;
-        if (!walker->sendTiming(this, write)) {
-            retrying = true;
-            writes.push_back(write);
-            inflight--;
-            return;
-        }
-    }
-}
-
-unsigned
-Walker::WalkerState::numInflight() const
-{
-    return inflight;
-}
-
-bool
-Walker::WalkerState::isRetrying()
-{
-    return retrying;
-}
-
-bool
-Walker::WalkerState::isTiming()
-{
-    return timing;
-}
-
-bool
-Walker::WalkerState::wasStarted()
-{
-    return started;
-}
-
-void
-Walker::WalkerState::squash()
-{
-    squashed = true;
-}
-
-void
-Walker::WalkerState::retry()
-{
-    retrying = false;
-    sendPackets();
+    logBytes = entry.logBytes;
+    addr = entry.paddr;
 }

 Fault
diff --git a/src/arch/x86/pagetable_walker.hh b/src/arch/x86/pagetable_walker.hh
index a269426..edba325 100644
--- a/src/arch/x86/pagetable_walker.hh
+++ b/src/arch/x86/pagetable_walker.hh
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2007 The Hewlett-Packard Development Company
+ * Copyright (c) 2020 Barkhausen Institut
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -40,6 +41,7 @@

 #include <vector>

+#include "arch/generic/pagetable_walker.hh"
 #include "arch/x86/pagetable.hh"
 #include "arch/x86/tlb.hh"
 #include "base/types.hh"
@@ -53,35 +55,16 @@

 namespace X86ISA
 {
-    class Walker : public ClockedObject
+    class Walker : public BaseWalker
     {
       protected:
-        // Port for accessing memory
-        class WalkerPort : public MasterPort
-        {
-          public:
-            WalkerPort(const std::string &_name, Walker * _walker) :
-                  MasterPort(_name, _walker), walker(_walker)
-            {}
-
-          protected:
-            Walker *walker;
-
-            bool recvTimingResp(PacketPtr pkt);
-            void recvReqRetry();
-        };
-
-        friend class WalkerPort;
-        WalkerPort port;
-
         // State to track each walk of the page table
-        class WalkerState
+        class WalkerState : public BaseWalkerState
         {
           friend class Walker;
           private:
-            enum State {
-                Ready,
-                Waiting,
+            enum TranslateState {
+                Idle,
                 // Long mode
                 LongPML4, LongPDP, LongPD, LongPTE,
                 // PAE legacy mode
@@ -91,101 +74,37 @@
             };

           protected:
-            Walker *walker;
-            ThreadContext *tc;
-            RequestPtr req;
-            State state;
-            State nextState;
+            TranslateState xlState;
+            TranslateState nextXlState;
             int dataSize;
             bool enableNX;
-            unsigned inflight;
             TlbEntry entry;
-            PacketPtr read;
-            std::vector<PacketPtr> writes;
-            Fault timingFault;
-            TLB::Translation * translation;
-            BaseTLB::Mode mode;
-            bool functional;
-            bool timing;
-            bool retrying;
-            bool started;
-            bool squashed;
           public:
- WalkerState(Walker * _walker, BaseTLB::Translation *_translation,
+            WalkerState(BaseWalker * _walker,
+                        BaseTLB::Translation *_translation,
const RequestPtr &_req, bool _isFunctional = false) :
-                walker(_walker), req(_req), state(Ready),
-                nextState(Ready), inflight(0),
-                translation(_translation),
-                functional(_isFunctional), timing(false),
-                retrying(false), started(false), squashed(false)
+ BaseWalkerState(_walker, _translation, _req, _isFunctional),
+                xlState(Idle), nextXlState(Idle), dataSize(0), enableNX(),
+                entry()
             {
             }
-            void initState(ThreadContext * _tc, BaseTLB::Mode _mode,
-                           bool _isTiming = false);
-            Fault startWalk();
-            Fault startFunctional(Addr &addr, unsigned &logBytes);
-            bool recvPacket(PacketPtr pkt);
-            unsigned numInflight() const;
-            bool isRetrying();
-            bool wasStarted();
-            bool isTiming();
-            void retry();
-            void squash();
-            std::string name() const {return walker->name();}

-          private:
-            void setupWalk(Addr vaddr);
-            Fault stepWalk(PacketPtr &write);
-            void sendPackets();
-            void endWalk();
+          protected:
+            Walker *ourWalker()
+            {
+                return static_cast<Walker*>(walker);
+            }
+
+            void setupWalk(Addr vaddr) override;
+            Fault stepWalk(PacketPtr &write) override;
+            void finishFunctional(Addr &addr, unsigned &logBytes) override;
             Fault pageFault(bool present);
         };

-        friend class WalkerState;
- // State for timing and atomic accesses (need multiple per walker in
-        // the case of multiple outstanding requests in timing mode)
-        std::list<WalkerState *> currStates;
- // State for functional accesses (only need one of these per walker)
-        WalkerState funcState;
-
-        struct WalkerSenderState : public Packet::SenderState
-        {
-            WalkerState * senderWalk;
-            WalkerSenderState(WalkerState * _senderWalk) :
-                senderWalk(_senderWalk) {}
-        };
-
-      public:
-        // Kick off the state machine.
-        Fault start(ThreadContext * _tc, BaseTLB::Translation *translation,
-                const RequestPtr &req, BaseTLB::Mode mode);
-        Fault startFunctional(ThreadContext * _tc, Addr &addr,
-                unsigned &logBytes, BaseTLB::Mode mode);
-        Port &getPort(const std::string &if_name,
-                      PortID idx=InvalidPortID) override;
-
-      protected:
         // The TLB we're supposed to load.
         TLB * tlb;
-        System * sys;
         MasterID masterId;

-        // The number of outstanding walks that can be squashed per cycle.
-        unsigned numSquashable;
-
-        // Wrapper for checking for squashes before starting a translation.
-        void startWalkWrapper();
-
-        /**
-         * Event used to call startWalkWrapper.
-         **/
-        EventFunctionWrapper startWalkWrapperEvent;
-
-        // Functions for dealing with packets.
-        bool recvTimingResp(PacketPtr pkt);
-        void recvReqRetry();
-        bool sendTiming(WalkerState * sendingState, PacketPtr pkt);
-
       public:

         void setTLB(TLB * _tlb)
@@ -193,6 +112,15 @@
             tlb = _tlb;
         }

+        BaseWalkerState *createState(BaseWalker *walker,
+                                     BaseTLB::Translation *translation,
+                                     const RequestPtr &req,
+                                     bool isFunctional) override;
+
+        Fault translateWithTLB(const RequestPtr &req, ThreadContext *tc,
+                               BaseTLB::Translation *translation,
+                               BaseTLB::Mode mode, bool &delayed) override;
+
         typedef X86PagetableWalkerParams Params;

         const Params *
@@ -202,13 +130,11 @@
         }

         Walker(const Params *params) :
-            ClockedObject(params), port(name() + ".port", this),
- funcState(this, NULL, NULL, true), tlb(NULL), sys(params->system),
-            masterId(sys->getMasterId(this)),
-            numSquashable(params->num_squash_per_cycle),
-            startWalkWrapperEvent([this]{ startWalkWrapper(); }, name())
+            BaseWalker(params), tlb(NULL),
+            masterId(params->system->getMasterId(this))
         {
         }
     };
 }
+
 #endif // __ARCH_X86_PAGE_TABLE_WALKER_HH__

--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/25660
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: I45b7e8a365b32b49f1c34a5501f6b4b0d015d74e
Gerrit-Change-Number: 25660
Gerrit-PatchSet: 1
Gerrit-Owner: Nils Asmussen <nils.asmus...@barkhauseninstitut.org>
Gerrit-MessageType: newchange
_______________________________________________
gem5-dev mailing list
gem5-dev@gem5.org
http://m5sim.org/mailman/listinfo/gem5-dev

Reply via email to