From 8fb2ee6e86ad31723f3939eb98586ae148e86142 Mon Sep 17 00:00:00 2001
From: Kip Macsai-Goren <kipmacsaigoren@github.com>
Date: Thu, 3 Jun 2021 14:32:12 -0400
Subject: [PATCH] added support for sv48 and some docs on how to use these
 files

---
 .../testgen/virtual_memory_util.py            | 101 +++++++++++++++---
 1 file changed, 88 insertions(+), 13 deletions(-)

diff --git a/wally-pipelined/testgen/virtual_memory_util.py b/wally-pipelined/testgen/virtual_memory_util.py
index 27ca742b..0aafb3a1 100644
--- a/wally-pipelined/testgen/virtual_memory_util.py
+++ b/wally-pipelined/testgen/virtual_memory_util.py
@@ -5,9 +5,54 @@
 # Jessica Torrey <jtorrey@hmc.edu>  01 March 2021
 # Thomas Fleming <tfleming@hmc.edu> 01 March 2021
 #
-# Utility functions for simulating and testing virtual memory on RISC-V.
+# Modified kmacsaigoren@hmc.edu 2 June 2021
+#             file now reflects actual use to generate assembly code pagetable.
+#             file now also includes small guide on how it can be used.
+#
+# Utility for generating the pagetable for any test assembly code where virtual memory is needed.
 ##################################
 
+######################################################
+""" HOW TO USE THIS FILE
+    
+This is all assuming you are writing code very similar to the WALLY-VIRTUALMEMORY tests and would like your own virtual memory map.
+This guide is also stored in the WALLY-VIRTUALMEMORY.S file as well.
+
+Begin by copying an existing page directory over to your test and running make (it'll be wrong, but we'll fix it in a second).
+  Make may hang or give you an error because the reference outputs may be wrong, but all we're trying to do is get an elf file.
+Simulate the test code on your favorite riscv processor simulator with a debugger that will show you internal state/register values.
+  I used OVPsimPlus with the command 'riscvOVPsimPlus.exe --variant <Variant name, ex: RV64I> --program <path to elf file> --gdbconsole'
+Run through the simulation until it has written to satp and read the bottom 60 bits of it. 
+  Assuming you're a test with the same setup code, this should be the value of the base ppn.
+
+Near the top of the python file you're reading right now, edit the value of 'INITIAL_PPN' to be the base PPN you just found in hex.
+
+Now find the mappings at the very bottom of the python file.
+Each of these loops is adding a mapping from each virtual page in the loop to a physical page somewhere in RAM. 
+
+add or remove mappings as you see fit. the first loop maps VPNs of 0x80000 to 0x80014 onto PPNs of 0x80000 to 0x80014
+  you can map single pages or ranges of pages. you can also map multiple VPNs onto the same PPN.
+  Make sure NOT to include the final VPN that causes the page fault in your test or your program will hang on the j loop instruction (unless you change the end condition).
+
+double check that you're using the right architecture/svmode in the 'arch' variable
+
+then run this python file and paste its output at the bottom of your assembly code. Be sure not to delete the signature fills.
+
+email kmacsaigoren@hmc.edu if you have any questions and he might be able to remember the answers.
+
+*** Currently doesn't work: mapping something with nonzeros in the VPN[3] feild onto any physical aderss. 
+    It'll produce a page table, but writing to those virtual adresses will not correspond to the correctly mapped physical adresses.
+
+    additionally, the expected behaviour doesn't really work when we try to map to a ram afress that starts with something larger than 000000008
+    This could be ebcause of the 32 bit adress space for physical memory.
+
+    remember that these things are broken with this program that generates page tables for test code. it does not say whether the test or module
+    itself works or not.
+*/
+
+"""
+######################################################
+
 ##################################
 # libraries
 ##################################
@@ -38,13 +83,18 @@ testcase_num = 0
 signature_len = 2000
 signature = [0xff for _ in range(signature_len)]
 
+# Base PPN, comes after 2 pages of test code and 2 pages of filler signature output space.
+# depending on your test however, this value may be different. You can use OVPsimPlus or QEMU with your testcode to find it. 
+INITIAL_PPN = 0x80005
+
+
 ##################################
 # classes
 ##################################
 class Architecture:
-  def __init__(self, xlen):
+  def __init__(self, xlen, svMode):
     if (xlen == 32):
-      self.PTESIZE = 4
+      self.PTESIZE = 4 # bytes
       self.PTE_BITS = 32
 
       self.VPN_BITS = 20
@@ -54,15 +104,28 @@ class Architecture:
 
       self.LEVELS = 2
     elif (xlen == 64):
-      self.PTESIZE = 8
-      self.PTE_BITS = 54
+      if (svMode == 39): 
+        self.PTESIZE = 8
+        self.PTE_BITS = 54
 
-      self.VPN_BITS = 27
-      self.VPN_SEGMENT_BITS = 9
+        self.VPN_BITS = 27
+        self.VPN_SEGMENT_BITS = 9
 
-      self.PPN_BITS = 44
+        self.PPN_BITS = 44
 
-      self.LEVELS = 3
+        self.LEVELS = 3
+      elif (svMode == 48):
+        self.PTESIZE = 8
+        self.PTE_BITS = 54
+
+        self.VPN_BITS = 36
+        self.VPN_SEGMENT_BITS = 9
+
+        self.PPN_BITS = 44
+
+        self.LEVELS = 4
+      else:
+        raise ValueError('Only Sv39 and Sv48 are implemented')
     else:
       raise ValueError('Only rv32 and rv64 are allowed.')
 
@@ -179,7 +242,6 @@ def virtual_to_vpn(vaddr, arch):
 
   return page_number
 
-INITIAL_PPN = 0x80002
 next_free_ppn = INITIAL_PPN
 def next_ppn():
   global next_free_ppn
@@ -194,11 +256,12 @@ def print_pages():
 ##################################
 # helper variables
 ##################################
-rv32 = Architecture(32)
-rv64 = Architecture(64)
+sv32 = Architecture(32, 32)
+sv39 = Architecture(64, 39)
+sv48 = Architecture(64, 48)
 
 if __name__ == "__main__":
-  arch = rv64 
+  arch = sv39
   pgdir = PageTable("page_directory", next_ppn(), arch)
 
   # Directly map the first 20 pages of RAM
@@ -206,6 +269,18 @@ if __name__ == "__main__":
     vaddr = 0x80000000 + (arch.PGSIZE * page)
     paddr = 0x80000000 + (arch.PGSIZE * page)
     pgdir.add_mapping(vaddr, paddr, PTE_D | PTE_A | PTE_R | PTE_W | PTE_U | PTE_X | PTE_V)
+
+  # Map Vpn of of the offset below and the 20 pages after it mapped onto the same 20 pages of ram.
+  # the first two of these are also the location of the output for each test.
+  for page in range(40):
+    vaddr = 0x00000000 + (arch.PGSIZE * page)
+    paddr = 0x80000000 + (arch.PGSIZE * page)
+    if page >= 20:
+      pgdir.add_mapping(vaddr, paddr, PTE_D | PTE_A | PTE_R | PTE_W | PTE_U | PTE_X | 0) # gives me an invalid mapping where I can try to store/read to force a page fault.
+    else:
+      pgdir.add_mapping(vaddr, paddr, PTE_D | PTE_A | PTE_R | PTE_W | PTE_U | PTE_X | PTE_V)
+
+
   """
   supervisor_pgdir = PageTable("sdir", next_ppn(), rv64)
   supervisor_pgdir.add_mapping(0x80000000, 0x80000000, PTE_R | PTE_W | PTE_X)