more optimizations; Enum is too slow for critical path
This commit is contained in:
parent
ccfab58040
commit
a050c0d29d
1 changed files with 27 additions and 25 deletions
52
mmu.py
52
mmu.py
|
@ -24,13 +24,6 @@ from functools import partial
|
||||||
from pdptraps import PDPTraps
|
from pdptraps import PDPTraps
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
|
|
||||||
# used internally to represent reads vs writes
|
|
||||||
class _CYCLE(Enum):
|
|
||||||
READ = 'r'
|
|
||||||
WRITE = 'w'
|
|
||||||
|
|
||||||
|
|
||||||
class MemoryMgmt:
|
class MemoryMgmt:
|
||||||
|
@ -68,7 +61,7 @@ class MemoryMgmt:
|
||||||
# memory control (parity, etc) is not implemented but needs to respond
|
# memory control (parity, etc) is not implemented but needs to respond
|
||||||
MCR_OFFS = 0o17746
|
MCR_OFFS = 0o17746
|
||||||
|
|
||||||
TransKey = namedtuple('TransKey', ('segno', 'mode', 'space', 'cycle'))
|
TransKey = namedtuple('TransKey', ('segno', 'mode', 'space', 'reading'))
|
||||||
|
|
||||||
def __init__(self, cpu, /, *, nocache=False):
|
def __init__(self, cpu, /, *, nocache=False):
|
||||||
|
|
||||||
|
@ -155,14 +148,14 @@ class MemoryMgmt:
|
||||||
return aprfile[aprnum][parpdr]
|
return aprfile[aprnum][parpdr]
|
||||||
else:
|
else:
|
||||||
# dump any matching cache entries in both reading/writing form.
|
# dump any matching cache entries in both reading/writing form.
|
||||||
for rw in (_CYCLE.READ, _CYCLE.WRITE):
|
for reading in (True, False):
|
||||||
# the "space" is a dilemma because it is tied up in
|
# the "space" is a dilemma because it is tied up in
|
||||||
# the unfolding of I/D space separation. It's not hard
|
# the unfolding of I/D space separation. It's not hard
|
||||||
# to figure out what to do but its also very easy to
|
# to figure out what to do but its also very easy to
|
||||||
# just do this: nuke both I and D space cache entries.
|
# just do this: nuke both I and D space cache entries.
|
||||||
for xspc in (self.ISPACE, self.DSPACE):
|
for xspc in (self.ISPACE, self.DSPACE):
|
||||||
if (aprnum, mode, xspc, rw) in self.segcache:
|
if (aprnum, mode, xspc, reading) in self.segcache:
|
||||||
del self.segcache[(aprnum, mode, xspc, rw)]
|
del self.segcache[(aprnum, mode, xspc, reading)]
|
||||||
|
|
||||||
aprfile[aprnum][parpdr] = value
|
aprfile[aprnum][parpdr] = value
|
||||||
|
|
||||||
|
@ -265,9 +258,10 @@ class MemoryMgmt:
|
||||||
if self._22bit:
|
if self._22bit:
|
||||||
self.iopage_base |= (15 << 18) # ... and 4 more
|
self.iopage_base |= (15 << 18) # ... and 4 more
|
||||||
|
|
||||||
def v2p(self, vaddr, mode, space, cycle):
|
def v2p(self, vaddr, mode, space, reading):
|
||||||
"""Convert a 16-bit virtual address to physical.
|
"""Convert a 16-bit virtual address to physical.
|
||||||
NOTE: Raises traps, updates A/W bits, & sets straps as needed.
|
NOTE: Raises traps, updates A/W bits, & sets straps as needed.
|
||||||
|
NOTE: 'reading' MUST be True or False, not anything else.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not self._mmu_relo_enabled:
|
if not self._mmu_relo_enabled:
|
||||||
|
@ -292,7 +286,7 @@ class MemoryMgmt:
|
||||||
# a "translation key" used in several places. Unfortunately,
|
# a "translation key" used in several places. Unfortunately,
|
||||||
# the namedtuple construction is enough overhead to matter in
|
# the namedtuple construction is enough overhead to matter in
|
||||||
# this critical/fast path, so caching uses tuple key
|
# this critical/fast path, so caching uses tuple key
|
||||||
tuplexkey = (segno, mode, space, cycle)
|
tuplexkey = (segno, mode, space, reading)
|
||||||
|
|
||||||
# All this translation code takes quite some time; caching
|
# All this translation code takes quite some time; caching
|
||||||
# dramatically improves performance.
|
# dramatically improves performance.
|
||||||
|
@ -306,7 +300,17 @@ class MemoryMgmt:
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
xkey = self.TransKey(segno, mode, space, cycle)
|
# AFTER the critical path can validate 'reading' which MUST
|
||||||
|
# be either True or False, and not just 'truthy'. It's ok
|
||||||
|
# to only check this after the fast path because bad values
|
||||||
|
# can't get into the fast path (since they are filtered here).
|
||||||
|
#
|
||||||
|
# NOTE: This is a coding error if it happens. This is why it is
|
||||||
|
# not being silently corrected instead of ValueError'd.
|
||||||
|
if reading not in (True, False):
|
||||||
|
raise ValueError(f"Illegal value for reading: '{reading}'")
|
||||||
|
|
||||||
|
xkey = self.TransKey(segno, mode, space, reading)
|
||||||
|
|
||||||
# not cached; do the translation...
|
# not cached; do the translation...
|
||||||
|
|
||||||
|
@ -348,7 +352,7 @@ class MemoryMgmt:
|
||||||
# there are no further A/W bit updates to worry about (so they
|
# there are no further A/W bit updates to worry about (so they
|
||||||
# can be cached at that point).
|
# can be cached at that point).
|
||||||
|
|
||||||
W_update = 0o100 if cycle == _CYCLE.WRITE else 0o000
|
W_update = 0o000 if reading else 0o100
|
||||||
A_update = 0o200 if straps else 0o000
|
A_update = 0o200 if straps else 0o000
|
||||||
|
|
||||||
AW_update = (W_update | A_update)
|
AW_update = (W_update | A_update)
|
||||||
|
@ -442,21 +446,21 @@ class MemoryMgmt:
|
||||||
# cause no traps or aborts. So, for example, control mode 6
|
# cause no traps or aborts. So, for example, control mode 6
|
||||||
# is not in the cases; nor is control mode 5 if reading.
|
# is not in the cases; nor is control mode 5 if reading.
|
||||||
|
|
||||||
cycle = xkey.cycle
|
reading = xkey.reading
|
||||||
match pdr & 7:
|
match pdr & 7:
|
||||||
# control modes 0, 3, and 7 are always aborts
|
# control modes 0, 3, and 7 are always aborts
|
||||||
case 0 | 3 | 7:
|
case 0 | 3 | 7:
|
||||||
self.cpu.logger.debug(f"ABORT_NR trap, regs: "
|
self.cpu.logger.debug(f"ABORT_NR trap, regs: "
|
||||||
f"{list(map(oct, self.cpu.r))}"
|
f"{list(map(oct, self.cpu.r))}"
|
||||||
f", {oct(self.cpu.psw)}"
|
f", {oct(self.cpu.psw)}"
|
||||||
f", PDR={oct(pdr)} {cycle=}")
|
f", PDR={oct(pdr)} {reading=}")
|
||||||
self._raisetrap(self.MMR0_BITS.ABORT_NR, vaddr, xkey)
|
self._raisetrap(self.MMR0_BITS.ABORT_NR, vaddr, xkey)
|
||||||
|
|
||||||
# control mode 1 is an abort if writing, mgmt trap if read
|
# control mode 1 is an abort if writing, mgmt trap if read
|
||||||
case 1 if cycle == _CYCLE.READ:
|
case 1 if reading:
|
||||||
straps = self.cpu.STRAPBITS.MEMMGT
|
straps = self.cpu.STRAPBITS.MEMMGT
|
||||||
|
|
||||||
case 1 | 2 if cycle == _CYCLE.WRITE:
|
case 1 | 2 if not reading:
|
||||||
self._raisetrap(self.MMR0_BITS.ABORT_RDONLY, vaddr, xkey)
|
self._raisetrap(self.MMR0_BITS.ABORT_RDONLY, vaddr, xkey)
|
||||||
|
|
||||||
# control mode 4 is mgmt trap on any access (read or write)
|
# control mode 4 is mgmt trap on any access (read or write)
|
||||||
|
@ -464,7 +468,7 @@ class MemoryMgmt:
|
||||||
straps = self.cpu.STRAPBITS.MEMMGT
|
straps = self.cpu.STRAPBITS.MEMMGT
|
||||||
|
|
||||||
# control mode 5 is mgmt trap if WRITING
|
# control mode 5 is mgmt trap if WRITING
|
||||||
case 5 if cycle == _CYCLE.WRITE:
|
case 5 if not reading:
|
||||||
straps = self.cpu.STRAPBITS.MEMMGT
|
straps = self.cpu.STRAPBITS.MEMMGT
|
||||||
|
|
||||||
return straps
|
return straps
|
||||||
|
@ -476,8 +480,7 @@ class MemoryMgmt:
|
||||||
If value is not None, perform a write; return None.
|
If value is not None, perform a write; return None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cycle = _CYCLE.READ if value is None else _CYCLE.WRITE
|
pa = self.v2p(vaddr, mode, space, value is None)
|
||||||
pa = self.v2p(vaddr, mode, space, cycle)
|
|
||||||
if pa >= self.iopage_base:
|
if pa >= self.iopage_base:
|
||||||
return self.ub.mmio.wordRW(pa & self.cpu.IOPAGE_MASK, value)
|
return self.ub.mmio.wordRW(pa & self.cpu.IOPAGE_MASK, value)
|
||||||
else:
|
else:
|
||||||
|
@ -490,8 +493,7 @@ class MemoryMgmt:
|
||||||
If value is not None, perform a write; return None.
|
If value is not None, perform a write; return None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cycle = _CYCLE.READ if value is None else _CYCLE.WRITE
|
pa = self.v2p(vaddr, mode, space, value is None)
|
||||||
pa = self.v2p(vaddr, mode, space, cycle)
|
|
||||||
|
|
||||||
# Physical memory is represented as an array of 16-bit word
|
# Physical memory is represented as an array of 16-bit word
|
||||||
# values, and byte operations are synthesized from that in
|
# values, and byte operations are synthesized from that in
|
||||||
|
@ -571,6 +573,6 @@ class MemoryMgmt:
|
||||||
for xkey, v in self.segcache.items():
|
for xkey, v in self.segcache.items():
|
||||||
ms = "KS!U"[xkey.mode]
|
ms = "KS!U"[xkey.mode]
|
||||||
ds = "ID"[xkey.space]
|
ds = "ID"[xkey.space]
|
||||||
s += f"{oct(xkey.segno << 13)}:{ms}{ds}{xkey.cycle} :"
|
s += f"{oct(xkey.segno << 13)}:{ms}{ds}{xkey.reading} :"
|
||||||
s += f" {oct(v[0])}\n"
|
s += f" {oct(v[0])}\n"
|
||||||
return s
|
return s
|
||||||
|
|
Loading…
Add table
Reference in a new issue