-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/tobyatgithub/cs5600_compute…
- Loading branch information
Showing
22 changed files
with
1,344 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,6 @@ | |
.vs_code | ||
*/.idea | ||
*/cmake-build-* | ||
ls-trace.txt | ||
vpn.txt | ||
result.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": "Python: Current File", | ||
"type": "python", | ||
"request": "launch", | ||
"program": "${file}", | ||
"console": "integratedTerminal", | ||
"args": ["-s", "0", "-c"] | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# Homework Simulation Chapter 20 Smaller Pages | ||
|
||
## Question 1 | ||
|
||
> With a linear page table, you need a single register to locate the page table, assuming that hardware does the lookup upon a TLB miss. How many registers do you need to locate a two-level page table? A three-level table? | ||
**Answer:** | ||
|
||
Based on figure 20.3, I believe only the top level page directory base registor is required (PDBR). | ||
|
||
## Question 2 | ||
|
||
> Use the simulator to perform translations given random seeds 0, 1, and 2, and check your answers using the -c flag. How many memory references are needed to perform each lookup? | ||
**Answer:** | ||
|
||
`python paging-multilevel-translate.py -s 0 -c` | ||
|
||
Here I used a python debugger with config file to investigate the process (which is pretty much the same calculation as the textbook.) | ||
|
||
As described in the function: | ||
|
||
```python | ||
def translate(self, pid, virtualAddr): | ||
(valid, ptPtr, pdeAddr) = self.getPageDirEntry(pid, virtualAddr, True) | ||
if valid == 1: | ||
ptePage = ptPtr | ||
(valid, pfn, pteAddr) = self.getPageTableEntry(virtualAddr, ptePage, True) | ||
if valid == 1: | ||
offset = (virtualAddr & self.OFFSET_MASK) | ||
paddr = (pfn << self.pageBits) | offset | ||
# print(' --> pfn: %02x offset: %x' % (pfn, offset)) | ||
return paddr | ||
else: | ||
return -2 | ||
return -1 | ||
``` | ||
|
||
here we first call `self.getPageDirEntry`, if valid then call the `self.getPageTableEntry`, and if valid, calculate the offset and actually page address in the end. | ||
|
||
And the memory involved methods are: | ||
```python | ||
pageDir = self.pdbr[pid] | ||
pde = self.memory[pdeAddr] | ||
pte = self.memory[pteAddr] | ||
``` | ||
so three times. | ||
|
||
## Question 3 | ||
|
||
> Given your understanding of how cache memory works, how do you think memory references to the page table will behave in the cache? Will they lead to lots of cache hits (and thus fast accesses?) Or lots of misses (and thus slow accesses)? | ||
**Answer:** | ||
By the figure 20.3, the top level page directory lookup will work well with temporal locality cache because we will revisit each top-level PFN multiple times. And the page table level lookup will be a little bit harder to cache (i.e. probably more cache misses) and will suit spatial locality cache beter because each PFN is not likely to be revisited but revisit will happen to other PFNs that is close to each other in the memory. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,272 @@ | ||
#! /usr/bin/env python | ||
|
||
from __future__ import print_function | ||
import sys | ||
from optparse import OptionParser | ||
import random | ||
import math | ||
|
||
# to make Python2 and Python3 act the same -- how dumb | ||
def random_seed(seed): | ||
try: | ||
random.seed(seed, version=1) | ||
except: | ||
random.seed(seed) | ||
return | ||
|
||
def convert(size): | ||
length = len(size) | ||
lastchar = size[length-1] | ||
if (lastchar == 'k') or (lastchar == 'K'): | ||
m = 1024 | ||
nsize = int(size[0:length-1]) * m | ||
elif (lastchar == 'm') or (lastchar == 'M'): | ||
m = 1024*1024 | ||
nsize = int(size[0:length-1]) * m | ||
elif (lastchar == 'g') or (lastchar == 'G'): | ||
m = 1024*1024*1024 | ||
nsize = int(size[0:length-1]) * m | ||
else: | ||
nsize = int(size) | ||
return nsize | ||
|
||
def roundup(size): | ||
value = 1.0 | ||
while value < size: | ||
value = value * 2.0 | ||
return value | ||
|
||
|
||
class OS: | ||
def __init__(self): | ||
# 4k phys memory (128 pages) | ||
self.pageSize = 32 | ||
self.physPages = 128 | ||
self.physMem = self.pageSize * self.physPages | ||
self.vaPages = 1024 | ||
self.vaSize = self.pageSize * self.vaPages | ||
self.pteSize = 1 | ||
self.pageBits = 5 # log of page size | ||
|
||
# os tracks | ||
self.usedPages = [] | ||
self.usedPagesCount = 0 | ||
self.maxPageCount = int(self.physMem / self.pageSize) | ||
|
||
# no pages used (yet) | ||
for i in range(0, self.maxPageCount): | ||
self.usedPages.append(0) | ||
|
||
# set contents of memory to 0, too | ||
self.memory = [] | ||
for i in range(0, self.physMem): | ||
self.memory.append(0) | ||
|
||
# associative array of pdbr's (indexed by PID) | ||
self.pdbr = {} | ||
|
||
# mask is 11111 00000 00000 --> 0111 1100 0000 0000 | ||
self.PDE_MASK = 0x7c00 | ||
self.PDE_SHIFT = 10 | ||
|
||
# 00000 11111 00000 -> 000 0011 1110 0000 | ||
self.PTE_MASK = 0x03e0 | ||
self.PTE_SHIFT = 5 | ||
|
||
self.VPN_MASK = self.PDE_MASK | self.PTE_MASK | ||
self.VPN_SHIFT = self.PTE_SHIFT | ||
|
||
# grabs the last five bits of a virtual address | ||
self.OFFSET_MASK = 0x1f | ||
|
||
def findFree(self): | ||
assert(self.usedPagesCount < self.maxPageCount) | ||
look = int(random.random() * self.maxPageCount) | ||
while self.usedPages[look] == 1: | ||
look = int(random.random() * self.maxPageCount) | ||
self.usedPagesCount = self.usedPagesCount + 1 | ||
self.usedPages[look] = 1 | ||
return look | ||
|
||
def initPageDir(self, whichPage): | ||
whichByte = whichPage << self.pageBits | ||
for i in range(whichByte, whichByte + self.pageSize): | ||
self.memory[i] = 0x7f | ||
|
||
def initPageTablePage(self, whichPage): | ||
self.initPageDir(whichPage) | ||
|
||
def getPageTableEntry(self, virtualAddr, ptePage, printStuff): | ||
pteBits = (virtualAddr & self.PTE_MASK) >> self.PTE_SHIFT | ||
pteAddr = (ptePage << self.pageBits) | pteBits | ||
pte = self.memory[pteAddr] | ||
valid = (pte & 0x80) >> 7 | ||
pfn = (pte & 0x7f) | ||
if printStuff == True: | ||
print(' --> pte index:0x%x [decimal %d] pte contents:0x%x (valid %d, pfn 0x%02x [decimal %d])' % (pteBits, pteBits, pte, valid, pfn, pfn)) | ||
return (valid, pfn, pteAddr) | ||
|
||
def getPageDirEntry(self, pid, virtualAddr, printStuff): | ||
pageDir = self.pdbr[pid] | ||
pdeBits = (virtualAddr & self.PDE_MASK) >> self.PDE_SHIFT | ||
pdeAddr = (pageDir << self.pageBits) | pdeBits | ||
pde = self.memory[pdeAddr] | ||
valid = (pde & 0x80) >> 7 | ||
ptPtr = (pde & 0x7f) | ||
if printStuff == True: | ||
print(' --> pde index:0x%x [decimal %d] pde contents:0x%x (valid %d, pfn 0x%02x [decimal %d])' % | ||
(pdeBits, pdeBits, pde, valid, ptPtr, ptPtr)) | ||
return (valid, ptPtr, pdeAddr) | ||
|
||
def setPageTableEntry(self, pteAddr, physicalPage): | ||
self.memory[pteAddr] = 0x80 | physicalPage | ||
|
||
def setPageDirEntry(self, pdeAddr, physicalPage): | ||
self.memory[pdeAddr] = 0x80 | physicalPage | ||
|
||
def allocVirtualPage(self, pid, virtualPage, physicalPage): | ||
# make it into a virtual address, as everything uses this (and not VPN) | ||
virtualAddr = virtualPage << self.pageBits | ||
(valid, ptPtr, pdeAddr) = self.getPageDirEntry(pid, virtualAddr, False) | ||
if valid == 0: | ||
# must allocate a page of the page table now, and have the PD point to it | ||
assert(ptPtr == 127) | ||
ptePage = self.findFree() | ||
self.setPageDirEntry(pdeAddr, ptePage) | ||
self.initPageTablePage(ptePage) | ||
else: | ||
# otherwise, just extract page number of page table page | ||
ptePage = ptPtr | ||
# now, look up page table entry too, and mark it valid and fill in translation | ||
(valid, pfn, pteAddr) = self.getPageTableEntry(virtualAddr, ptePage, False) | ||
assert(valid == 0) | ||
assert(pfn == 127) | ||
self.setPageTableEntry(pteAddr, physicalPage) | ||
|
||
# -2 -> PTE fault, -1 means PDE fault | ||
def translate(self, pid, virtualAddr): | ||
(valid, ptPtr, pdeAddr) = self.getPageDirEntry(pid, virtualAddr, True) | ||
if valid == 1: | ||
ptePage = ptPtr | ||
(valid, pfn, pteAddr) = self.getPageTableEntry(virtualAddr, ptePage, True) | ||
if valid == 1: | ||
offset = (virtualAddr & self.OFFSET_MASK) | ||
paddr = (pfn << self.pageBits) | offset | ||
# print(' --> pfn: %02x offset: %x' % (pfn, offset)) | ||
return paddr | ||
else: | ||
return -2 | ||
return -1 | ||
|
||
def fillPage(self, whichPage): | ||
for j in range(0, self.pageSize): | ||
self.memory[(whichPage * self.pageSize) + j] = int(random.random() * 31) | ||
|
||
def procAlloc(self, pid, numPages): | ||
# need a PDBR: find one somewhere in memory | ||
pageDir = self.findFree() | ||
# print('**ALLOCATE** page dir', pageDir) | ||
self.pdbr[pid] = pageDir | ||
self.initPageDir(pageDir) | ||
|
||
used = {} | ||
for vp in range(0, self.vaPages): | ||
used[vp] = 0 | ||
allocatedVPs = [] | ||
|
||
for vp in range(0, numPages): | ||
vp = int(random.random() * self.vaPages) | ||
while used[vp] == 1: | ||
vp = int(random.random() * self.vaPages) | ||
assert(used[vp] == 0) | ||
used[vp] = 1 | ||
allocatedVPs.append(vp) | ||
pp = self.findFree() | ||
# print('**ALLOCATE** page', pp) | ||
# print(' trying to map vp:%08x to pp:%08x' % (vp, pp)) | ||
self.allocVirtualPage(pid, vp, pp) | ||
self.fillPage(pp) | ||
return allocatedVPs | ||
|
||
def dumpPage(self, whichPage): | ||
i = whichPage | ||
for j in range(0, self.pageSize): | ||
print(self.memory[(i * self.pageSize) + j], end='') | ||
print('') | ||
|
||
def memoryDump(self): | ||
for i in range(0, int(self.physMem / self.pageSize)): | ||
print('page %3d:' % i, end='') | ||
for j in range(0, self.pageSize): | ||
print('%02x' % self.memory[(i * self.pageSize) + j], end='') | ||
print('') | ||
|
||
def getPDBR(self, pid): | ||
return self.pdbr[pid] | ||
|
||
def getValue(self, addr): | ||
return self.memory[addr] | ||
|
||
# allocate some processes in memory | ||
# allocate some multi-level page tables in memory | ||
# make a bit of a mystery: | ||
# can examine PDBR (PFN of current proc's page directory) | ||
# can examine contents of any page | ||
# fill pages with values too | ||
# ask: when given | ||
# LOAD VA, R1 | ||
# what will final value will be loaded into R1? | ||
|
||
# | ||
# main program | ||
# | ||
parser = OptionParser() | ||
parser.add_option('-s', '--seed', default=0, help='the random seed', action='store', type='int', dest='seed') | ||
parser.add_option('-a', '--allocated', default=64, help='number of virtual pages allocated', | ||
action='store', type='int', dest='allocated') | ||
parser.add_option('-n', '--addresses', default=10, help='number of virtual addresses to generate', | ||
action='store', type='int', dest='num') | ||
parser.add_option('-c', '--solve', help='compute answers for me', action='store_true', default=False, dest='solve') | ||
|
||
|
||
(options, args) = parser.parse_args() | ||
|
||
print('ARG seed', options.seed) | ||
print('ARG allocated', options.allocated) | ||
print('ARG num', options.num) | ||
print('') | ||
|
||
random_seed(options.seed) | ||
|
||
# do the work now | ||
os = OS() | ||
used = os.procAlloc(1, options.allocated) | ||
|
||
os.memoryDump() | ||
|
||
print('\nPDBR:', os.getPDBR(1), ' (decimal) [This means the page directory is held in this page]\n') | ||
|
||
for i in range(0, options.num): | ||
if (random.random() * 100) > 50.0 or i >= len(used): | ||
vaddr = int(random.random() * 1024 * 32) | ||
else: | ||
vaddr = (used[i] << 5) | int(random.random() * 32) | ||
if options.solve == True: | ||
print('Virtual Address 0x%04x:' % vaddr) | ||
r = os.translate(1, vaddr) | ||
if r > -1: | ||
print(' --> Translates to Physical Address 0x%03x --> Value: 0x%02x' % (r, os.getValue(r))) | ||
elif r == -1: | ||
print(' --> Fault (page directory entry not valid)') | ||
else: | ||
print(' --> Fault (page table entry not valid)') | ||
else: | ||
print('Virtual Address %04x: Translates To What Physical Address (And Fetches what Value)? Or Fault?' % vaddr) | ||
|
||
print('') | ||
|
||
exit(0) | ||
|
||
|
||
|
||
|
Oops, something went wrong.