-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCacheSimulator.py
More file actions
234 lines (223 loc) · 7.51 KB
/
Copy pathCacheSimulator.py
File metadata and controls
234 lines (223 loc) · 7.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
from __future__ import division
import math as m
import sys
import random
class Cache:
'''
initializes cache size, way and block size
n = way
num_set = number of set
'''
def __init__(self, cache_size, block_size, n, debug, policy):
self.cache_size = cache_size
self.n = n
self.block_size = block_size
self.num_sets = int(cache_size /(block_size * n))
self.hit_count = 0
self.total_count = 0
self.debug = debug
self.policy = policy
cache, queue = {}, {}
for i in range(self.num_sets):
cache[i], queue[i] = [], [-1] * self.n
for _ in range(n):
cache[i].append({'tag': None, 'valid': 0, 'dirty' : 0})
self.cache, self.queue = cache, queue
def log(self, ob, msg = ""):
if self.debug:
print(msg, ob)
'''
places an entry in a free frame
'''
def place_in_cache(self, address, free_frame, op):
tag, set_idx, offset = address
entry = self.cache[set_idx][free_frame]
entry['tag'] = tag
entry['valid'] = 1
entry['dirty'] = 0
if op == 'W':
entry['dirty'] = 1
'''
Translates the hex to binary
returns tag, set index and offset
'''
def translate(self, address):
binary_string = bin(int(address, 16))[2:]
gap = "".join("0" for _ in range(48 - len(binary_string)))
binary_string = gap + binary_string
offset_size = int(m.log(self.block_size, 2))
offset = binary_string[-offset_size:]
binary_string = binary_string[:-offset_size]
set_length = int(m.log(self.num_sets, 2))
if set_length > 0:
set_idx = int(binary_string[ -set_length: ], 2)
tag = binary_string[0 : -set_length]
else:
set_idx = 0
tag = binary_string
tag = int(tag, 2)
return tag, set_idx, offset
'''
returns empty frame slot
If no free frame then return None
'''
def get_free_frame(self, address):
_, set_idx, _ = address
for i in range(len(self.cache[set_idx])):
if self.cache[set_idx][i]['tag'] == None:
return i
return -1
'''
returns a frame that needs to be overwritten
'''
def get_overwrite_frame(self, address):
if self.policy == 'lru':
_, set_idx, _ = address
queue = self.queue[set_idx]
tag = queue[-1]
self.log(tag, " tag")
for i in range(self.n):
if tag == self.cache[set_idx][i]['tag']:
self.log(i, " get overwrite frame")
return i
else:
return random.randint(0, self.n)
def record(self, address):
if self.policy != 'lru':
return
tag, set_idx, _ = address
queue = self.queue[set_idx]
if tag not in queue:
queue = [tag] + queue[:-1]
self.queue[set_idx] = queue
return
for i in range(len(queue)):
if tag == queue[i]:
queue.remove(tag)
queue = [tag] + queue
self.queue[set_idx] = queue
break
'''
writes the value back into memory
'''
def write_back(self, frame, address):
tag, set_idx, offset = address
old_tag = self.cache[set_idx][frame]['tag']
'''
returns true if given address is available in cache
'''
def is_hit(self, address, op):
tag, set_idx, offset = address
for entry in self.cache[set_idx]:
if tag == entry['tag']:
self.log("hit")
if op == 'W':
entry['dirty'] = 1
return True
return False
'''
returns hit rate and miss rate
'''
def get_stats(self):
hit_rate = (self.hit_count/self.total_count) * 100
miss_rate = 100 - hit_rate
return round(hit_rate, 2), round(miss_rate, 2)
'''
Formats the output
'''
def tostring(self):
output = "Queue: " + str(self.queue) + "\n"
for key in self.cache.keys():
output += str(key) + ": " + str(self.cache[key]) + "\n"
return output
'''
places the next frame where it should be gone
'''
def next_frame(self, address, op):
address = self.translate(address)
tag, set_idx, offset = address
self.log((tag, set_idx, offset), " Next frame")
self.total_count += 1
if self.is_hit(address, op):
self.hit_count += 1
else:
free_frame = self.get_free_frame(address)
if free_frame == -1:
free_frame = self.get_overwrite_frame(address)
if self.cache[set_idx][free_frame]['dirty'] == 1:
self.write_back(free_frame, address)
self.place_in_cache(address, free_frame, op)
self.record(address)
def __main__():
args_len = len(sys.argv)
i = 1
multiplier = {'kb': 1024, 'mb': 1048576}
commands = {"block_size" : 64, "way" : 16, "cache_memory": 1048576, "debug": False}
while(i < args_len):
arg = sys.argv[i]
if i == 1:
if arg in ["-help", "--help", "-h"]:
help_text = '''
Usage:
run_sim.sh <file_name> [--option <arg>]
Options:
-h --help Show this screen.
--cache_memory <size>[B|KB|MB] Memory size in B, KB or MB
--block_size <size>[B|KB|MB] Memory size in B, KB or MB
--way <n> Number of ways in set associative cache
Example:
./run_sim.sh 1KB_64B --cache_memory 1KB --block_size 64B --way 16
'''
print(help_text)
return
file_name = arg
i = i + 1
elif arg[0] == "-":
arg_old = arg + ""
arg = arg.replace("-", "")
if arg not in commands:
print("Unrecognized argument: ", arg_old)
return
if arg == "debug":
commands["debug"] = True
i = i + 1
continue
i = i + 1
if i > args_len:
print("No value found for argument bal: ", arg_old)
return
try:
val = sys.argv[i]
if arg in ["cache_memory", "block_size"]:
if val[-2:].lower() in multiplier:
val = int(val[:-2]) * multiplier[val[-2:].lower()]
elif val[-1].lower() == 'b':
val = int(val[:-1])
else:
val = int(val)
commands[arg] = int(val)
i = i + 1
except :
print("Value Error!: ", val)
return
else:
print("Unrecognized argument:", arg)
return
block_size = commands["block_size"]
way = commands["way"]
cache_size = commands["cache_memory"]
debug = commands["debug"]
if block_size > cache_size:
print("Block size can't be greater than cache size.")
return
cacheob = Cache(cache_size, block_size, way, debug, 'lru')
with open(file_name, 'r') as file:
for line in file.readlines():
try:
pc, op, add = line.split(' ')
cacheob.next_frame(add.strip(), op.strip())
except:
continue
result = cacheob.get_stats()[1]
print("Cache miss rate: " + "{0:.2f}".format(result) + "%")
__main__()