selectconcat)
from soc.decoder.helpers import exts, gtu, ltu, undefined
from soc.decoder.isa.mem import Mem
+from soc.consts import MSRb # big-endian (PowerISA versions)
import math
import sys
+import unittest
# very quick, TODO move to SelectableInt utils later
def genmask(shift, size):
res[size-1-i] = SelectableInt(1, 1)
return res
+# NOTE: POWER 3.0B annotation order! see p4 1.3.2
+# MSB is indexed **LOWEST** (sigh)
+# from gem5 radixwalk.hh
+# Bitfield<63> valid; 64 - (63 + 1) = 0
+# Bitfield<62> leaf; 64 - (62 + 1) = 1
+
+def rpte_valid(r):
+ return bool(r[0])
+
+def rpte_leaf(r):
+ return bool(r[1])
+
+## Shift address bits 61--12 right by 0--47 bits and
+## supply the least significant 16 bits of the result.
+def addrshift(addr,shift):
+ x = addr.value >> shift.value
+ return SelectableInt(x,16)
+
+def NLB(x):
+ """
+ Next Level Base
+ right shifted by 8
+ """
+ return x[4:55]
+
+def NLS(x):
+ """
+ Next Level Size
+ NLS >= 5
+ """
+ return x[59:63]
+
"""
Get Root Page
"""
+testaddr = 0x10000
+testmem = {
+
+ 0x10000: # PARTITION_TABLE_2 (not implemented yet)
+ # PATB_GR=1 PRTB=0x1000 PRTS=0xb
+ 0x800000000100000b,
+
+ 0x30000: # RADIX_ROOT_PTE
+ # V = 1 L = 0 NLB = 0x400 NLS = 9
+ 0x8000000000040009,
+######## 0x4000000 #### wrong address calculated by _get_pgtable_addr
+ 0x40000: # RADIX_SECOND_LEVEL
+ # V = 1 L = 1 SW = 0 RPN = 0
+ # R = 1 C = 1 ATT = 0 EAA 0x7
+ 0xc000000000000187,
+
+ 0x1000000: # PROCESS_TABLE_3
+ # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 13
+ 0x40000000000300ad,
+ }
+
+
+
# see qemu/target/ppc/mmu-radix64.c for reference
class RADIX:
def __init__(self, mem, caller):
self.mem = mem
self.caller = caller
- #TODO move to lookup
- #self.dsisr = self.caller.spr["DSISR"]
- #self.dar = self.caller.spr["DAR"]
- #self.pidr = self.caller.spr["PIDR"]
- #self.prtbl = self.caller.spr["PRTBL"]
+ if caller is not None:
+ self.dsisr = self.caller.spr["DSISR"]
+ self.dar = self.caller.spr["DAR"]
+ self.pidr = self.caller.spr["PIDR"]
+ self.prtbl = self.caller.spr["PRTBL"]
+ self.msr = self.caller.msr
# cached page table stuff
self.pgtbl0 = 0
self.pgtbl3 = 0
self.pt3_valid = False
- def __call__(self,*args, **kwargs):
- print("TODO: implement RADIX.__call__()")
- print(args)
- print(kwargs)
- return None
+ def __call__(self, addr, sz):
+ val = self.ld(addr.value, sz, swap=False)
+ print("RADIX memread", addr, sz, val)
+ return SelectableInt(val, sz*8)
- def ld(self, address, width=8, swap=True, check_in_mem=False):
+ def ld(self, address, width=8, swap=True, check_in_mem=False,
+ instr_fetch=False):
print("RADIX: ld from addr 0x%x width %d" % (address, width))
- pte = self._walk_tree()
+ priv = ~(self.msr(MSR_PR).value) # problem-state ==> privileged
+ if instr_fetch:
+ mode = 'EXECUTE'
+ else:
+ mode = 'LOAD'
+ addr = SelectableInt(address, 64)
+ (shift, mbits, pgbase) = self._decode_prte(addr)
+ #shift = SelectableInt(0, 32)
+
+ pte = self._walk_tree(addr, pgbase, mode, mbits, shift, priv)
# use pte to caclculate phys address
return self.mem.ld(address, width, swap, check_in_mem)
# XXX set SPRs on error
# TODO implement
- def st(self, addr, v, width=8, swap=True):
- print("RADIX: st to addr 0x%x width %d data %x" % (addr, width, v))
+ def st(self, address, v, width=8, swap=True):
+ print("RADIX: st to addr 0x%x width %d data %x" % (address, width, v))
+
+ priv = ~(self.msr(MSR_PR).value) # problem-state ==> privileged
+ mode = 'STORE'
+ addr = SelectableInt(address, 64)
+ (shift, mbits, pgbase) = self._decode_prte(addr)
+ pte = self._walk_tree(addr, pgbase, mode, mbits, shift, priv)
# use pte to caclculate phys address (addr)
- return self.mem.st(addr, v, width, swap)
+ return self.mem.st(addr.value, v, width, swap)
# XXX set SPRs on error
print("memassign", addr, sz, val)
self.st(addr.value, val.value, sz, swap=False)
- def _next_level(self):
- return True
- ## DSISR_R_BADCONFIG
- ## read_entry
- ## DSISR_NOPTE
- ## Prepare for next iteration
+ def _next_level(self, addr, entry_width, swap, check_in_mem):
+ # implement read access to mmu mem here
+
+ value = 0
+ if addr.value in testmem:
+ value = testmem[addr.value]
+ else:
+ print("not found")
+
+ ##value = self.mem.ld(addr.value, entry_width, swap, check_in_mem)
+ print("addr", hex(addr.value))
+ data = SelectableInt(value, 64) # convert to SelectableInt
+ print("value", hex(value))
+ # index += 1
+ return data;
- def _walk_tree(self):
+ def _walk_tree(self, addr, pgbase, mode, mbits, shift, priv=1):
"""walk tree
// vaddr 64 Bit
"""
# get sprs
print("_walk_tree")
- pidr = self.caller.spr[DEC_SPR.PIDR.value]
- prtbl = self.caller.spr[DEC_SPR.PRTBL.value]
+ pidr = self.caller.spr["PIDR"]
+ prtbl = self.caller.spr["PRTBL"]
print(pidr)
print(prtbl)
+ p = addr[55:63]
+ print("last 8 bits ----------")
+ print
+
+ # get address of root entry
+ addr_next = self._get_prtable_addr(shift, prtbl, addr, pidr)
- # TODO read root entry from process table first
+ addr_next = SelectableInt(0x30000,64) # radix root for testing
# walk tree starts on prtbl
while True:
- ret = self._next_level()
- if ret: return ret
+ print("nextlevel----------------------------")
+ # read an entry
+ swap = False
+ check_in_mem = False
+ entry_width = 8
+
+ data = self._next_level(addr_next, entry_width, swap, check_in_mem)
+ valid = rpte_valid(data)
+ leaf = rpte_leaf(data)
+
+ print(" valid, leaf", valid, leaf)
+ if not valid:
+ return "invalid" # TODO: return error
+ if leaf:
+ ok = self._check_perms(data, priv, mode)
+ if ok == True: # data was ok, found phys address, return it?
+ return addr_next
+ return ok # return the error code
+ else:
+ newlookup = self._new_lookup(data, mbits, shift)
+ if newlookup == 'badtree':
+ return newlookup
+ shift, mask, pgbase = newlookup
+ print (" next level", shift, mask, pgbase)
+ shift = SelectableInt(shift.value,16) #THIS is wrong !!!
+ print("calling _get_pgtable_addr")
+ print(mask) #SelectableInt(value=0x9, bits=4)
+ print(pgbase) #SelectableInt(value=0x40000, bits=56)
+ print(shift) #SelectableInt(value=0x4, bits=16) #FIXME
+ pgbase = SelectableInt(pgbase.value,64)
+ addrsh = addrshift(addr,shift)
+ addr_next = self._get_pgtable_addr(mask, pgbase, addrsh)
+ print("addr_next",addr_next)
+ print("addrsh",addrsh)
+ assert(addr_next == 0x40000)
+ return "TODO verify next level"
+
+ def _new_lookup(self, data, mbits, shift):
+ """
+ mbits := unsigned('0' & data(4 downto 0));
+ if mbits < 5 or mbits > 16 or mbits > r.shift then
+ v.state := RADIX_FINISH;
+ v.badtree := '1'; -- throw error
+ else
+ v.shift := v.shift - mbits;
+ v.mask_size := mbits(4 downto 0);
+ v.pgbase := data(55 downto 8) & x"00"; NLB?
+ v.state := RADIX_LOOKUP; --> next level
+ end if;
+ """
+ mbits = data[59:64]
+ print("mbits=", mbits)
+ if mbits < 5 or mbits > 16: #fixme compare with r.shift
+ print("badtree")
+ return "badtree"
+ # reduce shift (has to be done at same bitwidth)
+ shift = shift - selectconcat(SelectableInt(0, 1), mbits)
+ mask_size = mbits[1:5] # get 4 LSBs
+ pgbase = selectconcat(data[8:56], SelectableInt(0, 8)) # shift up 8
+ return shift, mask_size, pgbase
def _decode_prte(self, data):
"""PRTE0 Layout
new_shift = shift + (31 - 12) - mbits
return new_shift
- def _check_perms(self, data, priv, iside, store):
+ def _check_perms(self, data, priv, mode):
"""check page permissions
// Leaf PDE |
// |------------------------------| |----------------|
v.rc_error := perm_ok;
end if;
"""
+ # decode mode into something that matches microwatt equivalent code
+ instr_fetch, store = 0, 0
+ if mode == 'STORE':
+ store = 1
+ if mode == 'EXECUTE':
+ inst_fetch = 1
+
# check permissions and RC bits
perm_ok = 0
if priv == 1 or data[60] == 0:
- if iside == 0:
+ if instr_fetch == 0:
perm_ok = data[62] | (data[61] & (store == 0))
# no IAMR, so no KUEP support for now
# deny execute permission if cache inhibited
rc_ok = data[55] & (data[56] | (store == 0))
if perm_ok == 1 and rc_ok == 1:
return True
+
return "perm_err" if perm_ok == 0 else "rc_err"
def _get_prtable_addr(self, shift, prtbl, addr, pid):
(effpid(31 downto 8) and finalmask(23 downto 0))) &
effpid(7 downto 0) & "0000";
"""
+ print ("_get_prtable_addr_", shift, prtbl, addr, pid)
finalmask = genmask(shift, 44)
finalmask24 = finalmask[20:44]
if addr[0].value == 1:
effpid = SelectableInt(0, 32)
else:
- effpid = self.pid[32:64] # TODO, check on this
+ effpid = pid #self.pid # TODO, check on this
zero16 = SelectableInt(0, 16)
zero4 = SelectableInt(0, 4)
res = selectconcat(zero16,
zero3 = SelectableInt(0, 3)
res = selectconcat(zero8,
pgbase[8:45], #
- (prtbl[45:61] & ~mask16) | #
+ (pgbase[45:61] & ~mask16) | #
(addrsh & mask16), #
zero3
)
return res
-# very quick test of maskgen function (TODO, move to util later)
+class TestRadixMMU(unittest.TestCase):
+
+ def test_genmask(self):
+ shift = SelectableInt(5, 6)
+ mask = genmask(shift, 43)
+ print (" mask", bin(mask.value))
+
+ self.assertEqual(sum([1, 2, 3]), 6, "Should be 6")
+
+ def test_get_pgtable_addr(self):
+
+ mem = None
+ caller = None
+ dut = RADIX(mem, caller)
+
+ mask_size=4
+ pgbase = SelectableInt(0,64)
+ addrsh = SelectableInt(0,16)
+ ret = dut._get_pgtable_addr(mask_size, pgbase, addrsh)
+ print("ret=",ret)
+ assert(ret==0)
+
+ def test_walk_tree(self):
+ # set up dummy minimal ISACaller
+ spr = {'DSISR': SelectableInt(0, 64),
+ 'DAR': SelectableInt(0, 64),
+ 'PIDR': SelectableInt(0, 64),
+ 'PRTBL': SelectableInt(0, 64)
+ }
+ # set problem state == 0 (other unit tests, set to 1)
+ msr = SelectableInt(0, 64)
+ msr[MSRb.PR] = 0
+ class ISACaller: pass
+ caller = ISACaller()
+ caller.spr = spr
+ caller.msr = msr
+
+ shift = SelectableInt(5, 6)
+ mask = genmask(shift, 43)
+ print (" mask", bin(mask.value))
+
+ mem = Mem(row_bytes=8)
+ mem = RADIX(mem, caller)
+ # -----------------------------------------------
+ # |/|RTS1|/| RPDB | RTS2 | RPDS |
+ # -----------------------------------------------
+ # |0|1 2|3|4 55|56 58|59 63|
+ data = SelectableInt(0, 64)
+ data[1:3] = 0b01
+ data[56:59] = 0b11
+ data[59:64] = 0b01101 # mask
+ data[55] = 1
+ (rts, mbits, pgbase) = mem._decode_prte(data)
+ print (" rts", bin(rts.value), rts.bits)
+ print (" mbits", bin(mbits.value), mbits.bits)
+ print (" pgbase", hex(pgbase.value), pgbase.bits)
+ addr = SelectableInt(0x1000, 64)
+ check = mem._segment_check(addr, mbits, shift)
+ print (" segment check", check)
+
+ print("walking tree")
+ addr = SelectableInt(testaddr,64)
+ # pgbase = None
+ mode = None
+ #mbits = None
+ shift = rts
+ result = mem._walk_tree(addr, pgbase, mode, mbits, shift)
+ print(" walking tree result", result)
+
+
if __name__ == '__main__':
- # set up dummy minimal ISACaller
- spr = {'DSISR': SelectableInt(0, 64),
- 'DAR': SelectableInt(0, 64),
- 'PIDR': SelectableInt(0, 64),
- 'PRTBL': SelectableInt(0, 64)
- }
- class ISACaller: pass
- caller = ISACaller()
- caller.spr = spr
-
- shift = SelectableInt(5, 6)
- mask = genmask(shift, 43)
- print (" mask", bin(mask.value))
-
- mem = Mem(row_bytes=8)
- mem = RADIX(mem, caller)
- # -----------------------------------------------
- # |/|RTS1|/| RPDB | RTS2 | RPDS |
- # -----------------------------------------------
- # |0|1 2|3|4 55|56 58|59 63|
- data = SelectableInt(0, 64)
- data[1:3] = 0b01
- data[56:59] = 0b11
- data[59:64] = 0b01101 # mask
- data[55] = 1
- (rts, mbits, pgbase) = mem._decode_prte(data)
- print (" rts", bin(rts.value), rts.bits)
- print (" mbits", bin(mbits.value), mbits.bits)
- print (" pgbase", hex(pgbase.value), pgbase.bits)
- addr = SelectableInt(0x1000, 64)
- check = mem._segment_check(addr, mbits, shift)
- print (" segment check", check)
+ unittest.main()