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

Change subject: arch-riscv: added fullsystem support.
......................................................................

arch-riscv: added fullsystem support.

That is, RISC-V has now a TLB and page table walker for Sv39 paging
according to the privileged ISA 1.11. Additionally, the instruction
sfence.vma is implemented to invalidate TLB entries accordingly.

The page table walker and parts of the TLB are based on x86 (the code
duplication of the page table walkers will be reduced by a separate
commit).

Change-Id: I5e29683bdd40c0d32c06e4d75a8382bf313f2086
---
M configs/common/CacheConfig.py
M configs/common/Caches.py
M configs/common/FSConfig.py
M configs/example/fs.py
M src/arch/riscv/RiscvTLB.py
M src/arch/riscv/SConscript
M src/arch/riscv/insts/standard.cc
M src/arch/riscv/insts/standard.hh
M src/arch/riscv/isa.cc
M src/arch/riscv/isa.hh
M src/arch/riscv/isa/decoder.isa
M src/arch/riscv/pagetable.cc
M src/arch/riscv/pagetable.hh
A src/arch/riscv/pagetable_walker.cc
A src/arch/riscv/pagetable_walker.hh
M src/arch/riscv/remote_gdb.cc
M src/arch/riscv/tlb.cc
M src/arch/riscv/tlb.hh
A src/arch/riscv/vtophys.cc
M src/arch/riscv/vtophys.hh
M src/cpu/BaseCPU.py
21 files changed, 1,488 insertions(+), 424 deletions(-)



diff --git a/configs/common/CacheConfig.py b/configs/common/CacheConfig.py
index ff2f0da..bf8e376 100644
--- a/configs/common/CacheConfig.py
+++ b/configs/common/CacheConfig.py
@@ -1,4 +1,5 @@
 # Copyright (c) 2012-2013, 2015-2016 ARM Limited
+# Copyright (c) 2020 Barkhausen Institut
 # All rights reserved
 #
 # The license below extends only to copyright in the software and shall
@@ -79,7 +80,7 @@
         dcache_class, icache_class, l2_cache_class, walk_cache_class = \
             L1_DCache, L1_ICache, L2Cache, None

-        if buildEnv['TARGET_ISA'] == 'x86':
+        if buildEnv['TARGET_ISA'] in ['x86', 'riscv']:
             walk_cache_class = PageTableWalkerCache

     # Set the cache line size of the system
@@ -181,7 +182,7 @@
             # on these names.  For simplicity, we would advise configuring
             # it to use this naming scheme; if this isn't possible, change
             # the names below.
-            if buildEnv['TARGET_ISA'] in ['x86', 'arm']:
+            if buildEnv['TARGET_ISA'] in ['x86', 'arm', 'riscv']:
                 system.cpu[i].addPrivateSplitL1Caches(
                         ExternalCache("cpu%d.icache" % i),
                         ExternalCache("cpu%d.dcache" % i),
diff --git a/configs/common/Caches.py b/configs/common/Caches.py
index 123fea4..77213e8 100644
--- a/configs/common/Caches.py
+++ b/configs/common/Caches.py
@@ -1,4 +1,5 @@
 # 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
@@ -92,7 +93,7 @@
     tgts_per_mshr = 12

     # the x86 table walker actually writes to the table-walker cache
-    if buildEnv['TARGET_ISA'] == 'x86':
+    if buildEnv['TARGET_ISA'] in ['x86', 'riscv']:
         is_read_only = False
     else:
         is_read_only = True
diff --git a/configs/common/FSConfig.py b/configs/common/FSConfig.py
index c200e61..fa7a86d 100644
--- a/configs/common/FSConfig.py
+++ b/configs/common/FSConfig.py
@@ -1,4 +1,5 @@
 # Copyright (c) 2010-2012, 2015-2019 ARM Limited
+# Copyright (c) 2020 Barkhausen Institut
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -48,6 +49,7 @@

 # Populate to reflect supported os types per target ISA
 os_types = { 'mips'  : [ 'linux' ],
+             'riscv' : [ 'linux' ], # TODO that's a lie
              'sparc' : [ 'linux' ],
              'x86'   : [ 'linux' ],
              'arm'   : [ 'linux',
@@ -604,6 +606,26 @@
     self.boot_osflags = fillInCmdline(mdesc, cmdline)
     return self

+def makeBareMetalRiscvSystem(mem_mode, mdesc=None, cmdline=None):
+    self = BareMetalRiscvSystem()
+    if not mdesc:
+        # generic system
+        mdesc = SysConfig()
+    self.mem_mode = mem_mode
+    self.mem_ranges = [AddrRange(mdesc.mem())]
+
+    self.iobus = IOXBar()
+    self.membus = MemBus()
+
+    self.bridge = Bridge(delay='50ns')
+    self.bridge.master = self.iobus.slave
+    self.bridge.slave = self.membus.master
+ # Sv39 has 56 bit physical addresses; use the upper 8 bit for the IO space
+    IO_address_space_base = 0x00FF000000000000
+    self.bridge.ranges = [AddrRange(IO_address_space_base, Addr.max)]
+
+    self.system_port = self.membus.slave
+    return self

 def makeDualRoot(full_system, testSystem, driveSystem, dumpfile):
     self = Root(full_system = full_system)
diff --git a/configs/example/fs.py b/configs/example/fs.py
index bf68325..f3627f8 100644
--- a/configs/example/fs.py
+++ b/configs/example/fs.py
@@ -1,4 +1,5 @@
 # Copyright (c) 2010-2013, 2016, 2019 ARM Limited
+# Copyright (c) 2020 Barkhausen Institut
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -82,6 +83,8 @@
test_sys = makeLinuxMipsSystem(test_mem_mode, bm[0], cmdline=cmdline)
     elif buildEnv['TARGET_ISA'] == "sparc":
         test_sys = makeSparcSystem(test_mem_mode, bm[0], cmdline=cmdline)
+    elif buildEnv['TARGET_ISA'] == "riscv":
+ test_sys = makeBareMetalRiscvSystem(test_mem_mode, bm[0], cmdline=cmdline)
     elif buildEnv['TARGET_ISA'] == "x86":
test_sys = makeLinuxX86System(test_mem_mode, np, bm[0], options.ruby,
                                       cmdline=cmdline)
@@ -123,7 +126,9 @@
                                              voltage_domain =
                                              test_sys.cpu_voltage_domain)

-    if options.kernel is not None:
+    if buildEnv['TARGET_ISA'] == 'riscv':
+        test_sys.bootloader = options.kernel
+    elif options.kernel is not None:
         test_sys.kernel = binary(options.kernel)

     if options.script is not None:
diff --git a/src/arch/riscv/RiscvTLB.py b/src/arch/riscv/RiscvTLB.py
index 4bcaf67..884b71f 100644
--- a/src/arch/riscv/RiscvTLB.py
+++ b/src/arch/riscv/RiscvTLB.py
@@ -1,6 +1,7 @@
 # -*- mode:python -*-

 # Copyright (c) 2007 MIPS Technologies, Inc.
+# Copyright (c) 2020 Barkhausen Institut
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -26,13 +27,25 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-from m5.SimObject import SimObject
 from m5.params import *
+from m5.proxy import *

 from m5.objects.BaseTLB import BaseTLB
+from m5.objects.ClockedObject import ClockedObject
+
+class RiscvPagetableWalker(ClockedObject):
+    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'
     cxx_class = 'RiscvISA::TLB'
     cxx_header = 'arch/riscv/tlb.hh'
     size = Param.Int(64, "TLB size")
+    walker = Param.RiscvPagetableWalker(\
+            RiscvPagetableWalker(), "page table walker")
diff --git a/src/arch/riscv/SConscript b/src/arch/riscv/SConscript
index 21c084b..a706079 100644
--- a/src/arch/riscv/SConscript
+++ b/src/arch/riscv/SConscript
@@ -2,6 +2,7 @@

 # Copyright (c) 2013 ARM Limited
 # Copyright (c) 2014 Sven Karlsson
+# Copyright (c) 2020 Barkhausen Institut
 # All rights reserved
 #
 # The license below extends only to copyright in the software and shall
@@ -49,10 +50,12 @@
     Source('locked_mem.cc')
     Source('process.cc')
     Source('pagetable.cc')
+    Source('pagetable_walker.cc')
     Source('remote_gdb.cc')
     Source('stacktrace.cc')
     Source('tlb.cc')
     Source('system.cc')
+    Source('vtophys.cc')

     Source('linux/process.cc')
     Source('linux/linux.cc')
@@ -65,7 +68,9 @@
     SimObject('RiscvSystem.py')

     DebugFlag('RiscvMisc')
-    DebugFlag('RiscvTLB')
+    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/insts/standard.cc b/src/arch/riscv/insts/standard.cc
index 1e7d22e..166501f 100644
--- a/src/arch/riscv/insts/standard.cc
+++ b/src/arch/riscv/insts/standard.cc
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2015 RISC-V Foundation
  * Copyright (c) 2017 The University of Virginia
+ * Copyright (c) 2020 Barkhausen Institut
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -66,4 +67,17 @@
     return ss.str();
 }

+string
+SystemOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const
+{
+    if (strcmp(mnemonic, "fence_vma") == 0) {
+        stringstream ss;
+        ss << mnemonic << ' ' << registerName(_srcRegIdx[0]) << ", " <<
+            registerName(_srcRegIdx[1]);
+        return ss.str();
+    }
+
+    return mnemonic;
+}
+
 }
diff --git a/src/arch/riscv/insts/standard.hh b/src/arch/riscv/insts/standard.hh
index a956d77..a1c5911 100644
--- a/src/arch/riscv/insts/standard.hh
+++ b/src/arch/riscv/insts/standard.hh
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2015 RISC-V Foundation
  * Copyright (c) 2017 The University of Virginia
+ * Copyright (c) 2020 Barkhausen Institut
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -75,10 +76,7 @@
     using RiscvStaticInst::RiscvStaticInst;

     std::string
-    generateDisassembly(Addr pc, const SymbolTable *symtab) const override
-    {
-        return mnemonic;
-    }
+    generateDisassembly(Addr pc, const SymbolTable *symtab) const override;
 };

 /**
diff --git a/src/arch/riscv/isa.cc b/src/arch/riscv/isa.cc
index ca3358e..861b19c 100644
--- a/src/arch/riscv/isa.cc
+++ b/src/arch/riscv/isa.cc
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2016 RISC-V Foundation
  * Copyright (c) 2016 The University of Virginia
+ * Copyright (c) 2020 Barkhausen Institut
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,6 +35,7 @@
 #include <sstream>

 #include "arch/riscv/interrupts.hh"
+#include "arch/riscv/pagetable.hh"
 #include "arch/riscv/registers.hh"
 #include "base/bitfield.hh"
 #include "cpu/base.hh"
@@ -204,6 +206,18 @@
                 ic->setIE(val);
             }
             break;
+          case MISCREG_SATP:
+            {
+ // we only support bare and Sv39 mode; setting a different mode
+                // shall have no effect (see page 64 in priv ISA manual)
+                SATP cur_val = readMiscRegNoEffect(misc_reg);
+                SATP new_val = val;
+                if (new_val.mode != AddrXlateMode::BARE &&
+                    new_val.mode != AddrXlateMode::SV39)
+                    new_val.mode = cur_val.mode;
+                setMiscRegNoEffect(misc_reg, new_val);
+            }
+            break;
           default:
             setMiscRegNoEffect(misc_reg, val);
         }
diff --git a/src/arch/riscv/isa.hh b/src/arch/riscv/isa.hh
index 4bf0fdb..9d34242 100644
--- a/src/arch/riscv/isa.hh
+++ b/src/arch/riscv/isa.hh
@@ -4,6 +4,7 @@
  * Copyright (c) 2014 Sven Karlsson
  * Copyright (c) 2016 RISC-V Foundation
  * Copyright (c) 2016 The University of Virginia
+ * Copyright (c) 2020 Barkhausen Institut
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -52,7 +53,8 @@
 namespace RiscvISA
 {

-enum PrivilegeMode {
+enum PrivilegeMode
+{
     PRV_U = 0,
     PRV_S = 1,
     PRV_M = 3
diff --git a/src/arch/riscv/isa/decoder.isa b/src/arch/riscv/isa/decoder.isa
index 65e8409..1e726a3 100644
--- a/src/arch/riscv/isa/decoder.isa
+++ b/src/arch/riscv/isa/decoder.isa
@@ -1758,38 +1758,48 @@

         0x1c: decode FUNCT3 {
             format SystemOp {
-                0x0: decode FUNCT12 {
-                    0x0: ecall({{
-                        fault = make_shared<SyscallFault>(
+                0x0: decode FUNCT7 {
+                    0x0: decode RS2 {
+                        0x0: ecall({{
+                            fault = make_shared<SyscallFault>(
(PrivilegeMode)xc->readMiscReg(MISCREG_PRV));
-                    }}, IsSerializeAfter, IsNonSpeculative, IsSyscall,
-                        No_OpClass);
-                    0x1: ebreak({{
- fault = make_shared<BreakpointFault>(xc->pcState());
-                    }}, IsSerializeAfter, IsNonSpeculative, No_OpClass);
-                    0x2: uret({{
-                        STATUS status = xc->readMiscReg(MISCREG_STATUS);
-                        status.uie = status.upie;
-                        status.upie = 1;
-                        xc->setMiscReg(MISCREG_STATUS, status);
-                        NPC = xc->readMiscReg(MISCREG_UEPC);
-                    }}, IsReturn);
-                    0x102: sret({{
-                        if (xc->readMiscReg(MISCREG_PRV) == PRV_U) {
-                            fault = make_shared<IllegalInstFault>(
-                                        "sret in user mode", machInst);
-                            NPC = NPC;
-                        } else {
+                        }}, IsSerializeAfter, IsNonSpeculative, IsSyscall,
+                            No_OpClass);
+                        0x1: ebreak({{
+                            fault = make_shared<BreakpointFault>(
+                                xc->pcState());
+ }}, IsSerializeAfter, IsNonSpeculative, No_OpClass);
+                        0x2: uret({{
STATUS status = xc->readMiscReg(MISCREG_STATUS);
-                            xc->setMiscReg(MISCREG_PRV, status.spp);
-                            status.sie = status.spie;
-                            status.spie = 1;
-                            status.spp = PRV_U;
+                            status.uie = status.upie;
+                            status.upie = 1;
                             xc->setMiscReg(MISCREG_STATUS, status);
-                            NPC = xc->readMiscReg(MISCREG_SEPC);
-                        }
-                    }}, IsReturn);
-                    0x302: mret({{
+                            NPC = xc->readMiscReg(MISCREG_UEPC);
+                        }}, IsReturn);
+                    }
+                    0x8: decode RS2 {
+                        0x2: sret({{
+                            if (xc->readMiscReg(MISCREG_PRV) == PRV_U) {
+                                fault = make_shared<IllegalInstFault>(
+                                            "sret in user mode", machInst);
+                                NPC = NPC;
+                            } else {
+                                STATUS status = xc->readMiscReg(
+                                    MISCREG_STATUS);
+                                xc->setMiscReg(MISCREG_PRV, status.spp);
+                                status.sie = status.spie;
+                                status.spie = 1;
+                                status.spp = PRV_U;
+                                xc->setMiscReg(MISCREG_STATUS, status);
+                                NPC = xc->readMiscReg(MISCREG_SEPC);
+                            }
+                        }}, IsReturn);
+                    }
+                    0x9: sfence_vma({{
+                        xc->tcBase()->getITBPtr()->demapPage(Rs1, Rs2);
+                        xc->tcBase()->getDTBPtr()->demapPage(Rs1, Rs2);
+                    }}, IsNonSpeculative, IsSerializeAfter, No_OpClass);
+                    0x18: mret({{
                         if (xc->readMiscReg(MISCREG_PRV) != PRV_M) {
                             fault = make_shared<IllegalInstFault>(
"mret at lower privilege", machInst);
diff --git a/src/arch/riscv/pagetable.cc b/src/arch/riscv/pagetable.cc
index 3291d8a..9e4fef0 100644
--- a/src/arch/riscv/pagetable.cc
+++ b/src/arch/riscv/pagetable.cc
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2002-2005 The Regents of The University of Michigan
  * Copyright (c) 2007 MIPS Technologies, Inc.
+ * Copyright (c) 2020 Barkhausen Institut
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,47 +30,32 @@

 #include "arch/riscv/pagetable.hh"

+#include "arch/riscv/isa_traits.hh"
 #include "sim/serialize.hh"

 namespace RiscvISA
 {

 void
-PTE::serialize(CheckpointOut &cp) const
+TlbEntry::serialize(CheckpointOut &cp) const
 {
-    SERIALIZE_SCALAR(Mask);
-    SERIALIZE_SCALAR(VPN);
+    SERIALIZE_SCALAR(paddr);
+    SERIALIZE_SCALAR(vaddr);
+    SERIALIZE_SCALAR(logBytes);
     SERIALIZE_SCALAR(asid);
-    SERIALIZE_SCALAR(G);
-    SERIALIZE_SCALAR(PFN0);
-    SERIALIZE_SCALAR(D0);
-    SERIALIZE_SCALAR(V0);
-    SERIALIZE_SCALAR(C0);
-    SERIALIZE_SCALAR(PFN1);
-    SERIALIZE_SCALAR(D1);
-    SERIALIZE_SCALAR(V1);
-    SERIALIZE_SCALAR(C1);
-    SERIALIZE_SCALAR(AddrShiftAmount);
-    SERIALIZE_SCALAR(OffsetMask);
+    SERIALIZE_SCALAR(pte);
+    SERIALIZE_SCALAR(lruSeq);
 }

 void
-PTE::unserialize(CheckpointIn &cp)
+TlbEntry::unserialize(CheckpointIn &cp)
 {
-    UNSERIALIZE_SCALAR(Mask);
-    UNSERIALIZE_SCALAR(VPN);
+    UNSERIALIZE_SCALAR(paddr);
+    UNSERIALIZE_SCALAR(vaddr);
+    UNSERIALIZE_SCALAR(logBytes);
     UNSERIALIZE_SCALAR(asid);
-    UNSERIALIZE_SCALAR(G);
-    UNSERIALIZE_SCALAR(PFN0);
-    UNSERIALIZE_SCALAR(D0);
-    UNSERIALIZE_SCALAR(V0);
-    UNSERIALIZE_SCALAR(C0);
-    UNSERIALIZE_SCALAR(PFN1);
-    UNSERIALIZE_SCALAR(D1);
-    UNSERIALIZE_SCALAR(V1);
-    UNSERIALIZE_SCALAR(C1);
-    UNSERIALIZE_SCALAR(AddrShiftAmount);
-    UNSERIALIZE_SCALAR(OffsetMask);
+    UNSERIALIZE_SCALAR(pte);
+    UNSERIALIZE_SCALAR(lruSeq);
 }

 }
diff --git a/src/arch/riscv/pagetable.hh b/src/arch/riscv/pagetable.hh
index 45d53f1..bb65805 100644
--- a/src/arch/riscv/pagetable.hh
+++ b/src/arch/riscv/pagetable.hh
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2002-2005 The Regents of The University of Michigan
  * Copyright (c) 2007 MIPS Technologies, Inc.
+ * Copyright (c) 2020 Barkhausen Institut
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,81 +32,80 @@
 #define __ARCH_RISCV_PAGETABLE_H__

 #include "base/logging.hh"
+#include "base/trie.hh"
 #include "base/types.hh"
 #include "sim/serialize.hh"

 namespace RiscvISA {

-struct VAddr
+BitUnion64(SATP)
+    Bitfield<63, 60> mode;
+    Bitfield<59, 44> asid;
+    Bitfield<43, 0> ppn;
+EndBitUnion(SATP)
+
+enum AddrXlateMode
 {
+    BARE = 0,
+    SV39 = 8,
+    SV48 = 9,
 };

-// ITB/DTB page table entry
-struct PTE
+// Sv39 paging
+const Addr VADDR_BITS  = 39;
+const Addr LEVEL_BITS  = 9;
+const Addr LEVEL_MASK  = (1 << LEVEL_BITS) - 1;
+
+BitUnion64(PTESv39)
+    Bitfield<53, 10> ppn;
+    Bitfield<53, 28> ppn2;
+    Bitfield<27, 19> ppn1;
+    Bitfield<18, 10> ppn0;
+    Bitfield<7> d;
+    Bitfield<6> a;
+    Bitfield<5> g;
+    Bitfield<4> u;
+    Bitfield<3, 1> perm;
+    Bitfield<3> x;
+    Bitfield<2> w;
+    Bitfield<1> r;
+    Bitfield<0> v;
+EndBitUnion(PTESv39)
+
+struct TlbEntry;
+typedef Trie<Addr, TlbEntry> TlbEntryTrie;
+
+struct TlbEntry : public Serializable
 {
-    Addr Mask;
-    Addr VPN;
-    uint8_t asid;
+    // The base of the physical page.
+    Addr paddr;

-    bool G;
+    // The beginning of the virtual page this entry maps.
+    Addr vaddr;
+    // The size of the page this represents, in address bits.
+    unsigned logBytes;

-    /* Contents of Entry Lo0 */
-    Addr PFN0;  // Physical Frame Number - Even
-    bool D0;    // Even entry Dirty Bit
-    bool V0;    // Even entry Valid Bit
-    uint8_t C0; // Cache Coherency Bits - Even
+    uint16_t asid;

-    /* Contents of Entry Lo1 */
-    Addr PFN1;  // Physical Frame Number - Odd
-    bool D1;    // Odd entry Dirty Bit
-    bool V1;    // Odd entry Valid Bit
-    uint8_t C1; // Cache Coherency Bits (3 bits)
+    PTESv39 pte;

-    /*
-     * The next few variables are put in as optimizations to reduce
-     * TLB lookup overheads. For a given Mask, what is the address shift
-     * amount, and what is the OffsetMask
-     */
-    int AddrShiftAmount;
-    int OffsetMask;
+    TlbEntryTrie::Handle trieHandle;

-    bool Valid() { return (V0 | V1); };
-    void serialize(CheckpointOut &cp) const;
-    void unserialize(CheckpointIn &cp);
-};
+    // A sequence number to keep track of LRU.
+    uint64_t lruSeq;

-// WARN: This particular TLB entry is not necessarily conformed to RISC-V ISA
-struct TlbEntry
-{
-    Addr _pageStart;
-    TlbEntry() {}
-    TlbEntry(Addr asn, Addr vaddr, Addr paddr,
-             bool uncacheable, bool read_only)
-        : _pageStart(paddr)
+    TlbEntry()
+        : paddr(0), vaddr(0), logBytes(0), pte(), lruSeq(0)
+    {}
+
+    // Return the page size in bytes
+    Addr size() const
     {
-        if (uncacheable || read_only)
-            warn("RISC-V TlbEntry does not support uncacheable"
-                 " or read-only mappings\n");
+        return (static_cast<Addr>(1) << logBytes);
     }

-    Addr pageStart()
-    {
-        return _pageStart;
-    }
-
-    void
-    updateVaddr(Addr new_vaddr) {}
-
-    void serialize(CheckpointOut &cp) const
-    {
-        SERIALIZE_SCALAR(_pageStart);
-    }
-
-    void unserialize(CheckpointIn &cp)
-    {
-        UNSERIALIZE_SCALAR(_pageStart);
-    }
-
+    void serialize(CheckpointOut &cp) const override;
+    void unserialize(CheckpointIn &cp) override;
 };

 };
diff --git a/src/arch/riscv/pagetable_walker.cc b/src/arch/riscv/pagetable_walker.cc
new file mode 100644
index 0000000..0af12ca
--- /dev/null
+++ b/src/arch/riscv/pagetable_walker.cc
@@ -0,0 +1,586 @@
+/*
+ * 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/riscv/pagetable_walker.hh"
+
+#include <memory>
+
+#include "arch/riscv/faults.hh"
+#include "arch/riscv/pagetable.hh"
+#include "arch/riscv/tlb.hh"
+#include "arch/riscv/vtophys.hh"
+#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"
+
+namespace RiscvISA {
+
+Fault
+Walker::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)
+    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;
+    }
+}
+
+Fault
+Walker::startFunctional(ThreadContext * _tc, Addr &addr, unsigned &logBytes,
+              BaseTLB::Mode _mode)
+{
+    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;
+}
+
+void
+Walker::WalkerPort::recvReqRetry()
+{
+    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;
+    } 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;
+        } 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;
+}
+
+Fault
+Walker::WalkerState::stepWalk(PacketPtr &write)
+{
+    assert(state != Ready && state != Waiting);
+    Fault fault = NoFault;
+    write = NULL;
+    PTESv39 pte = read->getLE<uint64_t>();
+    Addr nextRead = 0;
+    bool doWrite = false;
+    bool doTLBInsert = false;
+    bool doEndWalk = false;
+
+    DPRINTF(PageTableWalker, "Got level%d PTE: %#x\n", level, pte);
+
+    // step 2: TODO check PMA and PMP
+
+    // step 3:
+    if (!pte.v || (!pte.r && pte.w)) {
+        doEndWalk = true;
+        DPRINTF(PageTableWalker, "PTE invalid, raising PF\n");
+        fault = pageFault(pte.v);
+    }
+    else {
+        // step 4:
+        if (pte.r || pte.x) {
+            // step 5: leaf PTE
+            doEndWalk = true;
+ fault = walker->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);
+                }
+                else if (level == 2 && pte.ppn1 != 0) {
+                    DPRINTF(PageTableWalker,
+                            "PTE has misaligned PPN, raising PF\n");
+                    fault = pageFault(true);
+                }
+            }
+
+            if (fault == NoFault) {
+                // step 7
+                if (!pte.a) {
+                    pte.a = 1;
+                    doWrite = true;
+                }
+                if (!pte.d && mode == TLB::Write) {
+                    pte.d = 1;
+                    doWrite = true;
+                }
+                // TODO check if this violates a PMA or PMP
+
+                // step 8
+                entry.logBytes = PageShift + (level * LEVEL_BITS);
+                entry.paddr = pte.ppn;
+                entry.vaddr &= ~((1 << entry.logBytes) - 1);
+                entry.pte = pte;
+ // put it non-writable into the TLB to detect writes and redo
+                // the page table walk in order to update the dirty flag.
+                if (!pte.d && mode != TLB::Write)
+                    entry.pte.w = 0;
+                doTLBInsert = true;
+            }
+        }
+        else {
+            level--;
+            if (level < 0) {
+ DPRINTF(PageTableWalker, "No leaf PTE found, raising PF\n");
+                doEndWalk = true;
+                fault = pageFault(true);
+            }
+            else {
+                Addr shift = (PageShift + LEVEL_BITS * level);
+                Addr idx = (entry.vaddr >> shift) & LEVEL_MASK;
+                nextRead = (pte.ppn << PageShift) + (idx * sizeof(pte));
+                nextState = Translate;
+            }
+        }
+    }
+
+    PacketPtr oldRead = read;
+    Request::Flags flags = oldRead->req->getFlags();
+
+    if (doEndWalk) {
+ // If we need to write, adjust the read packet to write the modified
+        // value back to memory.
+        if (!functional && doWrite) {
+            DPRINTF(PageTableWalker, "Writing level%d PTE to %#x: %#x\n",
+                level, oldRead->getAddr(), pte);
+            write = oldRead;
+            write->setLE<uint64_t>(pte);
+            write->cmd = MemCmd::WriteReq;
+            read = NULL;
+        } else {
+            write = NULL;
+        }
+
+        if (doTLBInsert) {
+            if (!functional)
+                walker->tlb->insert(entry.vaddr, entry);
+            else {
+                Addr offset = entry.vaddr & mask(entry.logBytes);
+                Addr paddr = entry.paddr << PageShift | offset;
+                DPRINTF(PageTableWalker, "Translated %#x -> %#x\n",
+                        entry.vaddr, paddr);
+            }
+        }
+        endWalk();
+    }
+    else {
+        //If we didn't return, we're setting up another read.
+        RequestPtr request = std::make_shared<Request>(
+            nextRead, oldRead->getSize(), flags, walker->masterId);
+        read = new Packet(request, MemCmd::ReadReq);
+        read->allocate();
+
+        DPRINTF(PageTableWalker,
+                "Loading level%d PTE from %#x\n", level, nextRead);
+    }
+
+    return fault;
+}
+
+void
+Walker::WalkerState::endWalk()
+{
+    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();
+}
+
+Fault
+Walker::WalkerState::pageFault(bool present)
+{
+    DPRINTF(PageTableWalker, "Raising page fault.\n");
+    return walker->tlb->createPagefault(entry.vaddr, mode);
+}
+
+} /* end namespace RiscvISA */
+
+RiscvISA::Walker *
+RiscvPagetableWalkerParams::create()
+{
+    return new RiscvISA::Walker(this);
+}
diff --git a/src/arch/riscv/pagetable_walker.hh b/src/arch/riscv/pagetable_walker.hh
new file mode 100644
index 0000000..2d50928
--- /dev/null
+++ b/src/arch/riscv/pagetable_walker.hh
@@ -0,0 +1,210 @@
+/*
+ * 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_RISCV_TABLE_WALKER_HH__
+#define __ARCH_RISCV_TABLE_WALKER_HH__
+
+#include <vector>
+
+#include "arch/riscv/pagetable.hh"
+#include "arch/riscv/tlb.hh"
+#include "base/types.hh"
+#include "mem/packet.hh"
+#include "params/RiscvPagetableWalker.hh"
+#include "sim/clocked_object.hh"
+#include "sim/faults.hh"
+#include "sim/system.hh"
+
+class ThreadContext;
+
+namespace RiscvISA
+{
+    class Walker : public ClockedObject
+    {
+      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
+        {
+          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, + 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)
+            {
+            }
+            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);
+        };
+
+        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)
+        {
+            tlb = _tlb;
+        }
+
+        typedef RiscvPagetableWalkerParams Params;
+
+        const Params *
+        params() const
+        {
+            return static_cast<const Params *>(_params);
+        }
+
+        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())
+        {
+        }
+    };
+}
+
+#endif // __ARCH_RISCV_PAGE_TABLE_WALKER_HH__
diff --git a/src/arch/riscv/remote_gdb.cc b/src/arch/riscv/remote_gdb.cc
index 16b4585..7da666d 100644
--- a/src/arch/riscv/remote_gdb.cc
+++ b/src/arch/riscv/remote_gdb.cc
@@ -2,6 +2,7 @@
  * Copyright 2015 LabWare
  * Copyright 2014 Google, Inc.
  * Copyright (c) 2010 ARM Limited
+ * Copyright (c) 2020 Barkhausen Institut
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -133,7 +134,9 @@

 #include <string>

+#include "arch/riscv/pagetable_walker.hh"
 #include "arch/riscv/registers.hh"
+#include "arch/riscv/tlb.hh"
 #include "cpu/thread_state.hh"
 #include "debug/GDBAcc.hh"
 #include "mem/page_table.hh"
@@ -150,7 +153,25 @@
 bool
 RemoteGDB::acc(Addr va, size_t len)
 {
-    panic_if(FullSystem, "acc not implemented for RISCV FS!");
+    if (FullSystem)
+    {
+        TLB *tlb = dynamic_cast<TLB *>(context()->getDTBPtr());
+        unsigned logBytes;
+        Addr paddr = va;
+
+        PrivilegeMode pmode = tlb->getMemPriv(context(), BaseTLB::Read);
+        SATP satp = context()->readMiscReg(MISCREG_SATP);
+        if (pmode != PrivilegeMode::PRV_M &&
+            satp.mode != AddrXlateMode::BARE) {
+            Walker *walker = tlb->getWalker();
+            Fault fault = walker->startFunctional(
+                    context(), paddr, logBytes, BaseTLB::Read);
+            if (fault != NoFault)
+                return false;
+        }
+        return true;
+    }
+
     return context()->getProcessPtr()->pTable->lookup(va) != nullptr;
 }

diff --git a/src/arch/riscv/tlb.cc b/src/arch/riscv/tlb.cc
index 014e369..febfc16 100644
--- a/src/arch/riscv/tlb.cc
+++ b/src/arch/riscv/tlb.cc
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2001-2005 The Regents of The University of Michigan
  * Copyright (c) 2007 MIPS Technologies, Inc.
+ * Copyright (c) 2020 Barkhausen Institut
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,6 +35,7 @@

 #include "arch/riscv/faults.hh"
 #include "arch/riscv/pagetable.hh"
+#include "arch/riscv/pagetable_walker.hh"
 #include "arch/riscv/pra_constants.hh"
 #include "arch/riscv/system.hh"
 #include "arch/riscv/utility.hh"
@@ -41,8 +43,8 @@
 #include "base/str.hh"
 #include "base/trace.hh"
 #include "cpu/thread_context.hh"
-#include "debug/RiscvTLB.hh"
 #include "debug/TLB.hh"
+#include "debug/TLBVerbose.hh"
 #include "mem/page_table.hh"
 #include "params/RiscvTLB.hh"
 #include "sim/full_system.hh"
@@ -56,167 +58,384 @@
 //  RISC-V TLB
 //

+static Addr
+buildKey(Addr vpn, uint16_t asid)
+{
+    return (static_cast<Addr>(asid) << 48) | vpn;
+}
+
 TLB::TLB(const Params *p)
-    : BaseTLB(p), size(p->size), nlu(0)
+    : BaseTLB(p), size(p->size), tlb(size), lruSeq(0)
 {
-    table = new PTE[size];
-    memset(table, 0, sizeof(PTE[size]));
-    smallPages = 0;
-}
-
-TLB::~TLB()
-{
-    if (table)
-        delete [] table;
-}
-
-// look up an entry in the TLB
-RiscvISA::PTE *
-TLB::lookup(Addr vpn, uint8_t asn) const
-{
-    // assume not found...
-    PTE *retval = nullptr;
-    PageTable::const_iterator i = lookupTable.find(vpn);
-    if (i != lookupTable.end()) {
-        while (i->first == vpn) {
-            int index = i->second;
-            PTE *pte = &table[index];
-
- /* 1KB TLB Lookup code - from MIPS ARM Volume III - Rev. 2.50 */
-            Addr Mask = pte->Mask;
-            Addr InvMask = ~Mask;
-            Addr VPN  = pte->VPN;
-            if (((vpn & InvMask) == (VPN & InvMask)) &&
-                    (pte->G  || (asn == pte->asid))) {
-                // We have a VPN + ASID Match
-                retval = pte;
-                break;
-            }
-            ++i;
-        }
+    for (size_t x = 0; x < size; x++) {
+        tlb[x].trieHandle = NULL;
+        freeList.push_back(&tlb[x]);
     }

-    DPRINTF(TLB, "lookup %#x, asn %#x -> %s ppn %#x\n", vpn, (int)asn,
-            retval ? "hit" : "miss", retval ? retval->PFN1 : 0);
-    return retval;
+    walker = p->walker;
+    walker->setTLB(this);
 }

-RiscvISA::PTE*
-TLB::getEntry(unsigned Index) const
+Walker *
+TLB::getWalker()
 {
-    // Make sure that Index is valid
-    assert(Index<size);
-    return &table[Index];
-}
-
-int
-TLB::probeEntry(Addr vpn, uint8_t asn) const
-{
-    // assume not found...
-    int Ind = -1;
-    PageTable::const_iterator i = lookupTable.find(vpn);
-    if (i != lookupTable.end()) {
-        while (i->first == vpn) {
-            int index = i->second;
-            PTE *pte = &table[index];
-
- /* 1KB TLB Lookup code - from MIPS ARM Volume III - Rev. 2.50 */
-            Addr Mask = pte->Mask;
-            Addr InvMask = ~Mask;
-            Addr VPN = pte->VPN;
-            if (((vpn & InvMask) == (VPN & InvMask)) &&
-                    (pte->G  || (asn == pte->asid))) {
-                // We have a VPN + ASID Match
-                Ind = index;
-                break;
-            }
-            ++i;
-        }
-    }
- DPRINTF(RiscvTLB,"VPN: %x, asid: %d, Result of TLBP: %d\n",vpn,asn,Ind);
-    return Ind;
-}
-
-inline Fault
-TLB::checkCacheability(const RequestPtr &req)
-{
-    Addr VAddrUncacheable = 0xA0000000;
-    // In MIPS, cacheability is controlled by certain bits of the virtual
-    // address or by the TLB entry
-    if ((req->getVaddr() & VAddrUncacheable) == VAddrUncacheable) {
-        // mark request as uncacheable
-        req->setFlags(Request::UNCACHEABLE | Request::STRICT_ORDER);
-    }
-    return NoFault;
+    return walker;
 }

 void
-TLB::insertAt(PTE &pte, unsigned Index, int _smallPages)
+TLB::evictLRU()
 {
-    smallPages = _smallPages;
-    if (Index > size) {
-        warn("Attempted to write at index (%d) beyond TLB size (%d)",
-                Index, size);
-    } else {
-        // Update TLB
-        DPRINTF(TLB, "TLB[%d]: %x %x %x %x\n",
-                Index, pte.Mask << 11,
-                ((pte.VPN << 11) | pte.asid),
-                ((pte.PFN0 << 6) | (pte.C0 << 3) |
-                 (pte.D0 << 2) | (pte.V0 <<1) | pte.G),
-                ((pte.PFN1 <<6) | (pte.C1 << 3) |
-                 (pte.D1 << 2) | (pte.V1 <<1) | pte.G));
-        if (table[Index].V0 || table[Index].V1) {
-            // Previous entry is valid
-            PageTable::iterator i = lookupTable.find(table[Index].VPN);
-            lookupTable.erase(i);
-        }
-        table[Index]=pte;
-        // Update fast lookup table
-        lookupTable.insert(make_pair(table[Index].VPN, Index));
+    // Find the entry with the lowest (and hence least recently updated)
+    // sequence number.
+
+    size_t lru = 0;
+    for (size_t i = 1; i < size; i++) {
+        if (tlb[i].lruSeq < tlb[lru].lruSeq)
+            lru = i;
     }
+
+    remove(lru);
 }

-// insert a new TLB entry
-void
-TLB::insert(Addr addr, PTE &pte)
+TlbEntry *
+TLB::lookup(Addr vpn, uint16_t asid, Mode mode, bool hidden)
 {
-    fatal("TLB Insert not yet implemented\n");
+    TlbEntry *entry = trie.lookup(buildKey(vpn, asid));
+
+    if (!hidden) {
+        if (entry)
+            entry->lruSeq = nextSeq();
+
+        if (mode == Write)
+            write_accesses++;
+        else
+            read_accesses++;
+
+        if (!entry) {
+            if (mode == Write)
+                write_misses++;
+            else
+                read_misses++;
+        }
+        else {
+            if (mode == Write)
+                write_hits++;
+            else
+                read_hits++;
+        }
+
+        DPRINTF(TLBVerbose, "lookup(vpn=%#x, asid=%#x): %s ppn %#x\n",
+ vpn, asid, entry ? "hit" : "miss", entry ? entry->paddr : 0);
+    }
+
+    return entry;
+}
+
+TlbEntry *
+TLB::insert(Addr vpn, const TlbEntry &entry)
+{
+    DPRINTF(TLB, "insert(vpn=%#x, asid=%#x): ppn=%#x pte=%#x size=%#x\n",
+        vpn, entry.asid, entry.paddr, entry.pte, entry.size());
+
+    // If somebody beat us to it, just use that existing entry.
+    TlbEntry *newEntry = lookup(vpn, entry.asid, Mode::Read, true);
+    if (newEntry) {
+        // update PTE flags (maybe we set the dirty/writable flag)
+        newEntry->pte = entry.pte;
+        assert(newEntry->vaddr == vpn);
+        return newEntry;
+    }
+
+    if (freeList.empty())
+        evictLRU();
+
+    newEntry = freeList.front();
+    freeList.pop_front();
+
+    Addr key = buildKey(vpn, entry.asid);
+    *newEntry = entry;
+    newEntry->lruSeq = nextSeq();
+    newEntry->vaddr = vpn;
+    newEntry->trieHandle =
+    trie.insert(key, TlbEntryTrie::MaxBits - entry.logBytes, newEntry);
+    return newEntry;
+}
+
+void
+TLB::demapPage(Addr vpn, uint64_t asid)
+{
+    asid &= 0xFFFF;
+
+    if (vpn == 0 && asid == 0)
+        flushAll();
+    else {
+        DPRINTF(TLB, "flush(vpn=%#x, asid=%#x)\n", vpn, asid);
+        if (vpn != 0 && asid != 0) {
+            TlbEntry *newEntry = lookup(vpn, asid, Mode::Read, true);
+            if (newEntry)
+                remove(newEntry - tlb.data());
+        }
+        else {
+            for (size_t i = 0; i < size; i++) {
+                if (tlb[i].trieHandle) {
+                    Addr mask = ~(tlb[i].size() - 1);
+                    if ((vpn == 0 || (vpn & mask) == tlb[i].vaddr) &&
+                        (asid == 0 || tlb[i].asid == asid))
+                        remove(i);
+                }
+            }
+        }
+    }
 }

 void
 TLB::flushAll()
 {
-    DPRINTF(TLB, "flushAll\n");
-    memset(table, 0, sizeof(PTE[size]));
-    lookupTable.clear();
-    nlu = 0;
+    DPRINTF(TLB, "flushAll()\n");
+    for (size_t i = 0; i < size; i++) {
+        if (tlb[i].trieHandle)
+            remove(i);
+    }
+}
+
+void
+TLB::remove(size_t idx)
+{
+    DPRINTF(TLB, "remove(vpn=%#x, asid=%#x): ppn=%#x pte=%#x size=%#x\n",
+        tlb[idx].vaddr, tlb[idx].asid, tlb[idx].paddr, tlb[idx].pte,
+        tlb[idx].size());
+
+    assert(tlb[idx].trieHandle);
+    trie.remove(tlb[idx].trieHandle);
+    tlb[idx].trieHandle = NULL;
+    freeList.push_back(&tlb[idx]);
+}
+
+Fault
+TLB::checkPermissions(ThreadContext *tc, Addr vaddr, Mode mode, PTESv39 pte)
+{
+    Fault fault = NoFault;
+
+    if (mode == TLB::Read && !pte.r) {
+        DPRINTF(TLB, "PTE has no read perm, raising PF\n");
+        fault = createPagefault(vaddr, mode);
+    }
+    else if (mode == TLB::Write && !pte.w) {
+        DPRINTF(TLB, "PTE has no write perm, raising PF\n");
+        fault = createPagefault(vaddr, mode);
+    }
+    else if (mode == TLB::Execute && !pte.x) {
+        DPRINTF(TLB, "PTE has no exec perm, raising PF\n");
+        fault = createPagefault(vaddr, mode);
+    }
+
+    if (fault == NoFault) {
+        // check pte.u
+        STATUS status = tc->readMiscReg(MISCREG_STATUS);
+        PrivilegeMode pmode = getMemPriv(tc, mode);
+        if (pmode == PrivilegeMode::PRV_U && !pte.u) {
+            DPRINTF(TLB, "PTE is not user accessible, raising PF\n");
+            fault = createPagefault(vaddr, mode);
+        }
+ else if (pmode == PrivilegeMode::PRV_S && pte.u && status.sum == 0) {
+            DPRINTF(TLB, "PTE is only user accessible, raising PF\n");
+            fault = createPagefault(vaddr, mode);
+        }
+    }
+
+    return fault;
+}
+
+Fault
+TLB::createPagefault(Addr vaddr, Mode mode)
+{
+    ExceptionCode code;
+    if (mode == TLB::Read)
+        code = ExceptionCode::LOAD_PAGE;
+    else if (mode == TLB::Write)
+        code = ExceptionCode::STORE_PAGE;
+    else
+        code = ExceptionCode::INST_PAGE;
+    return std::make_shared<AddressFault>(vaddr, code);
+}
+
+Fault
+TLB::doTranslate(const RequestPtr &req, ThreadContext *tc,
+                 Translation *translation, Mode mode, bool &delayed)
+{
+    delayed = false;
+
+ Addr vaddr = req->getVaddr() & ((static_cast<Addr>(1) << VADDR_BITS) - 1);
+    SATP satp = tc->readMiscReg(MISCREG_SATP);
+
+    TlbEntry *e = lookup(vaddr, satp.asid, mode, false);
+    if (!e) {
+        Fault fault = walker->start(tc, translation, req, mode);
+        if (translation != nullptr || fault != NoFault) {
+            // This gets ignored in atomic mode.
+            delayed = true;
+            return fault;
+        }
+        e = lookup(vaddr, satp.asid, mode, false);
+        assert(e != nullptr);
+    }
+
+    Fault fault = checkPermissions(tc, vaddr, mode, e->pte);
+    if (fault != NoFault) {
+        // if we want to write and it isn't writable, do a page table walk
+        // again to update the dirty flag.
+        if (mode == TLB::Write && !e->pte.w) {
+            DPRINTF(TLB, "Dirty bit not set, repeating PT walk\n");
+            fault = walker->start(tc, translation, req, mode);
+            if (translation != nullptr || fault != NoFault) {
+                delayed = true;
+                return fault;
+            }
+        }
+        if (fault != NoFault)
+            return fault;
+    }
+
+    Addr paddr = e->paddr << PageShift | (vaddr & mask(e->logBytes));
+    DPRINTF(TLBVerbose, "translate(vpn=%#x, asid=%#x): %#x\n",
+            vaddr, satp.asid, paddr);
+    req->setPaddr(paddr);
+
+    return NoFault;
+}
+
+PrivilegeMode
+TLB::getMemPriv(ThreadContext *tc, Mode mode)
+{
+    STATUS status = (STATUS)tc->readMiscReg(MISCREG_STATUS);
+    PrivilegeMode pmode = (PrivilegeMode)tc->readMiscReg(MISCREG_PRV);
+    if (mode != Mode::Execute && status.mprv == 1)
+        pmode = (PrivilegeMode)(RegVal)status.mpp;
+    return pmode;
+}
+
+Fault
+TLB::translate(const RequestPtr &req, ThreadContext *tc,
+               Translation *translation, Mode mode, bool &delayed)
+{
+    delayed = false;
+
+    if (FullSystem) {
+        PrivilegeMode pmode = getMemPriv(tc, mode);
+        SATP satp = tc->readMiscReg(MISCREG_SATP);
+ if (pmode == PrivilegeMode::PRV_M || satp.mode == AddrXlateMode::BARE)
+            req->setFlags(Request::PHYSICAL);
+
+        Fault fault;
+        if (req->getFlags() & Request::PHYSICAL) {
+            /**
+             * we simply set the virtual address to physical address
+             */
+            req->setPaddr(req->getVaddr());
+            fault = NoFault;
+        } else {
+            fault = doTranslate(req, tc, translation, mode, delayed);
+        }
+
+ // according to the RISC-V tests, negative physical addresses trigger
+        // an illegal address exception.
+        // TODO where is that written in the manual?
+ if (!delayed && fault == NoFault && (req->getPaddr() & (1ULL << 63))) {
+            ExceptionCode code;
+            if (mode == TLB::Read)
+                code = ExceptionCode::LOAD_ACCESS;
+            else if (mode == TLB::Write)
+                code = ExceptionCode::STORE_ACCESS;
+            else
+                code = ExceptionCode::INST_ACCESS;
+            fault = make_shared<AddressFault>(req->getVaddr(), code);
+        }
+
+        return fault;
+    } else {
+ // In the O3 CPU model, sometimes a memory access will be speculatively + // executed along a branch that will end up not being taken where the + // address is invalid. In that case, return a fault rather than trying
+        // to translate it (which will cause a panic).  Since RISC-V allows
+ // unaligned memory accesses, this should only happen if the request's + // length is long enough to wrap around from the end of the memory to
+        // the start.
+        assert(req->getSize() > 0);
+        if (req->getVaddr() + req->getSize() - 1 < req->getVaddr())
+            return make_shared<GenericPageTableFault>(req->getVaddr());
+
+        Process * p = tc->getProcessPtr();
+
+        Fault fault = p->pTable->translate(req);
+        if (fault != NoFault)
+            return fault;
+
+        return NoFault;
+    }
+}
+
+Fault
+TLB::translateAtomic(const RequestPtr &req, ThreadContext *tc, Mode mode)
+{
+    bool delayed;
+    return translate(req, tc, nullptr, mode, delayed);
+}
+
+void
+TLB::translateTiming(const RequestPtr &req, ThreadContext *tc,
+        Translation *translation, Mode mode)
+{
+    bool delayed;
+    assert(translation);
+    Fault fault = translate(req, tc, translation, mode, delayed);
+    if (!delayed)
+        translation->finish(fault, req, tc, mode);
+    else
+        translation->markDelayed();
+}
+
+Fault
+TLB::finalizePhysical(const RequestPtr &req,
+                      ThreadContext *tc, Mode mode) const
+{
+    return NoFault;
 }

 void
 TLB::serialize(CheckpointOut &cp) const
 {
-    SERIALIZE_SCALAR(size);
-    SERIALIZE_SCALAR(nlu);
+    // Only store the entries in use.
+    uint32_t _size = size - freeList.size();
+    SERIALIZE_SCALAR(_size);
+    SERIALIZE_SCALAR(lruSeq);

-    for (int i = 0; i < size; i++) {
-        ScopedCheckpointSection sec(cp, csprintf("PTE%d", i));
-        table[i].serialize(cp);
+    uint32_t _count = 0;
+    for (uint32_t x = 0; x < size; x++) {
+        if (tlb[x].trieHandle != NULL)
+            tlb[x].serializeSection(cp, csprintf("Entry%d", _count++));
     }
 }

 void
 TLB::unserialize(CheckpointIn &cp)
 {
-    UNSERIALIZE_SCALAR(size);
-    UNSERIALIZE_SCALAR(nlu);
+    // Do not allow to restore with a smaller tlb.
+    uint32_t _size;
+    UNSERIALIZE_SCALAR(_size);
+    if (_size > size) {
+        fatal("TLB size less than the one in checkpoint!");
+    }

-    for (int i = 0; i < size; i++) {
-        ScopedCheckpointSection sec(cp, csprintf("PTE%d", i));
-        table[i].unserialize(cp);
-        if (table[i].V0 || table[i].V1) {
-            lookupTable.insert(make_pair(table[i].VPN, i));
-        }
+    UNSERIALIZE_SCALAR(lruSeq);
+
+    for (uint32_t x = 0; x < _size; x++) {
+        TlbEntry *newEntry = freeList.front();
+        freeList.pop_front();
+
+        newEntry->unserializeSection(cp, csprintf("Entry%d", x));
+        Addr key = buildKey(newEntry->vaddr, newEntry->asid);
+        newEntry->trieHandle = trie.insert(key,
+            TlbEntryTrie::MaxBits - newEntry->logBytes, newEntry);
     }
 }

@@ -277,123 +496,6 @@
     accesses = read_accesses + write_accesses;
 }

-Fault
-TLB::translateInst(const RequestPtr &req, ThreadContext *tc)
-{
-    if (FullSystem) {
-        /**
-         * check if we simulate a bare metal system
-         * if so, we have no tlb, phys addr == virt addr
-         */
-        if (static_cast<RiscvSystem *>(tc->getSystemPtr())->isBareMetal())
-            req->setFlags(Request::PHYSICAL);
-
-        if (req->getFlags() & Request::PHYSICAL) {
-            /**
-             * we simply set the virtual address to physical address
-             */
-            req->setPaddr(req->getVaddr());
-            return checkCacheability(req);
-        } else {
-            /**
-             * as we currently support bare metal only, we throw a panic,
-             * if it is not a bare metal system
-             */
-            panic("translateInst not implemented in RISC-V.\n");
-        }
-    } else {
-        Process * p = tc->getProcessPtr();
-
-        Fault fault = p->pTable->translate(req);
-        if (fault != NoFault)
-            return fault;
-
-        return NoFault;
-    }
-}
-
-Fault
-TLB::translateData(const RequestPtr &req, ThreadContext *tc, bool write)
-{
-    if (FullSystem) {
-        /**
-         * check if we simulate a bare metal system
-         * if so, we have no tlb, phys addr == virt addr
-         */
-        if (static_cast<RiscvSystem *>(tc->getSystemPtr())->isBareMetal())
-            req->setFlags(Request::PHYSICAL);
-
-        if (req->getFlags() & Request::PHYSICAL) {
-            /**
-             * we simply set the virtual address to physical address
-             */
-            req->setPaddr(req->getVaddr());
-            return checkCacheability(req);
-        } else {
-            /**
-             * as we currently support bare metal only, we throw a panic,
-             * if it is not a bare metal system
-             */
-            panic("translateData not implemented in RISC-V.\n");
-        }
-    } else {
- // In the O3 CPU model, sometimes a memory access will be speculatively - // executed along a branch that will end up not being taken where the - // address is invalid. In that case, return a fault rather than trying
-        // to translate it (which will cause a panic).  Since RISC-V allows
- // unaligned memory accesses, this should only happen if the request's - // length is long enough to wrap around from the end of the memory to
-        // the start.
-        assert(req->getSize() > 0);
-        if (req->getVaddr() + req->getSize() - 1 < req->getVaddr())
-            return make_shared<GenericPageTableFault>(req->getVaddr());
-
-        Process * p = tc->getProcessPtr();
-
-        Fault fault = p->pTable->translate(req);
-        if (fault != NoFault)
-            return fault;
-
-        return NoFault;
-    }
-}
-
-Fault
-TLB::translateAtomic(const RequestPtr &req, ThreadContext *tc, Mode mode)
-{
-    if (mode == Execute)
-        return translateInst(req, tc);
-    else
-        return translateData(req, tc, mode == Write);
-}
-
-void
-TLB::translateTiming(const RequestPtr &req, ThreadContext *tc,
-        Translation *translation, Mode mode)
-{
-    assert(translation);
-    translation->finish(translateAtomic(req, tc, mode), req, tc, mode);
-}
-
-Fault
-TLB::finalizePhysical(const RequestPtr &req,
-                      ThreadContext *tc, Mode mode) const
-{
-    return NoFault;
-}
-
-
-RiscvISA::PTE &
-TLB::index(bool advance)
-{
-    PTE *pte = &table[nlu];
-
-    if (advance)
-        nextnlu();
-
-    return *pte;
-}
-
 RiscvISA::TLB *
 RiscvTLBParams::create()
 {
diff --git a/src/arch/riscv/tlb.hh b/src/arch/riscv/tlb.hh
index bdd3181..6271bec 100644
--- a/src/arch/riscv/tlb.hh
+++ b/src/arch/riscv/tlb.hh
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2001-2005 The Regents of The University of Michigan
  * Copyright (c) 2007 MIPS Technologies, Inc.
+ * Copyright (c) 2020 Barkhausen Institut
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -30,9 +31,10 @@
 #ifndef __ARCH_RISCV_TLB_HH__
 #define __ARCH_RISCV_TLB_HH__

-#include <map>
+#include <list>

 #include "arch/generic/tlb.hh"
+#include "arch/riscv/isa.hh"
 #include "arch/riscv/isa_traits.hh"
 #include "arch/riscv/pagetable.hh"
 #include "arch/riscv/utility.hh"
@@ -48,18 +50,20 @@
    simply create an ITLB and DTLB that will point to the real TLB */
 namespace RiscvISA {

+class Walker;
+
 class TLB : public BaseTLB
 {
+    typedef std::list<TlbEntry *> EntryList;
+
   protected:
-    typedef std::multimap<Addr, int> PageTable;
-    PageTable lookupTable;      // Quick lookup into page table
+    size_t size;
+    std::vector<TlbEntry> tlb;  // our TLB
+    TlbEntryTrie trie;          // for quick access
+    EntryList freeList;         // free entries
+    uint64_t lruSeq;

-    RiscvISA::PTE *table;        // the Page Table
-    int size;                   // TLB Size
-    int nlu;                    // not last used entry (for replacement)
-
-    void nextnlu() { if (++nlu >= size) nlu = 0; }
-    RiscvISA::PTE *lookup(Addr vpn, uint8_t asn) const;
+    Walker *walker;

     mutable Stats::Scalar read_hits;
     mutable Stats::Scalar read_misses;
@@ -77,28 +81,19 @@
     typedef RiscvTLBParams Params;
     TLB(const Params *p);

-    int probeEntry(Addr vpn,uint8_t) const;
-    RiscvISA::PTE *getEntry(unsigned) const;
-    virtual ~TLB();
+    Walker *getWalker();

     void takeOverFrom(BaseTLB *otlb) override {}

-    int smallPages;
-    int getsize() const { return size; }
-
-    RiscvISA::PTE &index(bool advance = true);
-    void insert(Addr vaddr, RiscvISA::PTE &pte);
-    void insertAt(RiscvISA::PTE &pte, unsigned Index, int _smallPages);
+    TlbEntry *insert(Addr vpn, const TlbEntry &entry);
     void flushAll() override;
-    void demapPage(Addr vaddr, uint64_t asn) override
-    {
-        panic("demapPage unimplemented.\n");
-    }
+    void demapPage(Addr vaddr, uint64_t asn) override;

-    // static helper functions... really
-    static bool validVirtualAddress(Addr vaddr);
+    Fault checkPermissions(ThreadContext *tc, Addr vaddr,
+                           Mode mode, PTESv39 pte);
+    Fault createPagefault(Addr vaddr, Mode mode);

-    static Fault checkCacheability(const RequestPtr &req);
+    PrivilegeMode getMemPriv(ThreadContext *tc, Mode mode);

     // Checkpointing
     void serialize(CheckpointOut &cp) const override;
@@ -106,22 +101,28 @@

     void regStats() override;

-    Fault translateAtomic(
-            const RequestPtr &req, ThreadContext *tc, Mode mode) override;
-    void translateTiming(
-            const RequestPtr &req, ThreadContext *tc,
-            Translation *translation, Mode mode) override;
-    Fault finalizePhysical(
-            const RequestPtr &req,
-            ThreadContext *tc, Mode mode) const override;
+    Fault doTranslate(const RequestPtr &req, ThreadContext *tc,
+                      Translation *translation, Mode mode, bool &delayed);
+
+    Fault translateAtomic(const RequestPtr &req,
+                          ThreadContext *tc, Mode mode) override;
+    void translateTiming(const RequestPtr &req, ThreadContext *tc,
+                         Translation *translation, Mode mode) override;
+    Fault finalizePhysical(const RequestPtr &req,
+                           ThreadContext *tc, Mode mode) const override;

   private:
-    Fault translateInst(const RequestPtr &req, ThreadContext *tc);
- Fault translateData(const RequestPtr &req, ThreadContext *tc, bool write);
+    uint64_t nextSeq() { return ++lruSeq; }
+
+    TlbEntry *lookup(Addr vpn, uint16_t asid, Mode mode, bool hidden);
+
+    void evictLRU();
+    void remove(size_t idx);
+
+    Fault translate(const RequestPtr &req, ThreadContext *tc,
+                    Translation *translation, Mode mode, bool &delayed);
 };

 }

-
-
 #endif // __RISCV_MEMORY_HH__
diff --git a/src/arch/riscv/vtophys.cc b/src/arch/riscv/vtophys.cc
new file mode 100644
index 0000000..941ccdf
--- /dev/null
+++ b/src/arch/riscv/vtophys.cc
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#include "arch/riscv/vtophys.hh"
+
+#include <string>
+
+#include "arch/riscv/pagetable_walker.hh"
+#include "arch/riscv/tlb.hh"
+#include "base/trace.hh"
+#include "cpu/thread_context.hh"
+#include "debug/VtoPhys.hh"
+
+using namespace std;
+
+namespace RiscvISA
+{
+    Addr
+    vtophys(Addr vaddr)
+    {
+        panic("Need access to page tables\n");
+    }
+
+    Addr
+    vtophys(ThreadContext *tc, Addr vaddr)
+    {
+        TLB *tlb = dynamic_cast<TLB *>(tc->getDTBPtr());
+        unsigned logBytes;
+        Addr paddr = vaddr;
+
+        PrivilegeMode pmode = tlb->getMemPriv(tc, BaseTLB::Read);
+        SATP satp = tc->readMiscReg(MISCREG_SATP);
+        if (pmode != PrivilegeMode::PRV_M &&
+            satp.mode != AddrXlateMode::BARE) {
+            Walker *walker = tlb->getWalker();
+            Fault fault = walker->startFunctional(
+                    tc, paddr, logBytes, BaseTLB::Read);
+            if (fault != NoFault)
+                panic("vtophys page walk returned fault\n");
+            Addr masked_addr = vaddr & mask(logBytes);
+            paddr |= masked_addr;
+        }
+
+        DPRINTF(VtoPhys, "vtophys(%#x) -> %#x\n", vaddr, paddr);
+        return paddr;
+    }
+}
diff --git a/src/arch/riscv/vtophys.hh b/src/arch/riscv/vtophys.hh
index 29d9f4f..a2a1da5 100644
--- a/src/arch/riscv/vtophys.hh
+++ b/src/arch/riscv/vtophys.hh
@@ -3,6 +3,7 @@
  * Copyright (c) 2007-2008 The Florida State University
  * Copyright (c) 2009 The University of Edinburgh
  * Copyright (c) 2014-2015 Sven Karlsson
+ * Copyright (c) 2020 Barkhausen Institut
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -39,19 +40,8 @@

 namespace RiscvISA {

-inline Addr
-vtophys(Addr vaddr)
-{
-    fatal("VTOPHYS: Unimplemented on RISC-V\n");
-    return vaddr;
-}
-
-inline Addr
-vtophys(ThreadContext *tc, Addr vaddr)
-{
-    fatal("VTOPHYS: Unimplemented on RISC-V\n");
-    return vtophys(vaddr);
-}
+Addr vtophys(Addr vaddr);
+Addr vtophys(ThreadContext *tc, Addr vaddr);

 } // namespace RiscvISA

diff --git a/src/cpu/BaseCPU.py b/src/cpu/BaseCPU.py
index 53652bf..67d95d0 100644
--- a/src/cpu/BaseCPU.py
+++ b/src/cpu/BaseCPU.py
@@ -1,4 +1,5 @@
 # Copyright (c) 2012-2013, 2015-2017 ARM Limited
+# Copyright (c) 2020 Barkhausen Institut
 # All rights reserved.
 #
 # The license below extends only to copyright in the software and shall
@@ -181,7 +182,7 @@
     dcache_port = MasterPort("Data Port")
     _cached_ports = ['icache_port', 'dcache_port']

-    if buildEnv['TARGET_ISA'] in ['x86', 'arm']:
+    if buildEnv['TARGET_ISA'] in ['x86', 'arm', 'riscv']:
         _cached_ports += ["itb.walker.port", "dtb.walker.port"]

     _uncached_slave_ports = []
@@ -216,7 +217,7 @@
         self.icache_port = ic.cpu_side
         self.dcache_port = dc.cpu_side
         self._cached_ports = ['icache.mem_side', 'dcache.mem_side']
-        if buildEnv['TARGET_ISA'] in ['x86', 'arm']:
+        if buildEnv['TARGET_ISA'] in ['x86', 'arm', 'riscv']:
             if iwc and dwc:
                 self.itb_walker_cache = iwc
                 self.dtb_walker_cache = dwc

--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/25647
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: I5e29683bdd40c0d32c06e4d75a8382bf313f2086
Gerrit-Change-Number: 25647
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