From 04b6ed4c4bc98fe76292ca1d8530b077c8c9741f Mon Sep 17 00:00:00 2001
From: Brandon Rodriguez <brodriguez8774@gmail.com>
Date: Sat, 29 Feb 2020 22:17:43 -0500
Subject: [PATCH] Update project to work with direct mapped class video example

---
 documents/references.md |  15 ++++
 main.py                 | 149 +++++++++++++++++++++++++++++++++++++---
 2 files changed, 155 insertions(+), 9 deletions(-)

diff --git a/documents/references.md b/documents/references.md
index 31804cf..2ebef18 100644
--- a/documents/references.md
+++ b/documents/references.md
@@ -8,6 +8,21 @@ Includes anything from stack overflow links to notes about logic from previous w
 ### Parsing Command Line Args with ArgParse
 <https://docs.python.org/3/library/argparse.html>
 
+### Display Int as Binary
+<https://stackoverflow.com/a/699891>
+
+### Exponents and Logarithms
+<https://docs.python.org/3.8/library/math.html#power-and-logarithmic-functions>
+
+### Splitting a String on Regex Match
+<https://stackoverflow.com/a/13209313>
+
+### Dynamically Creating Bit Masks
+<https://stackoverflow.com/a/26303439>
+
+### Bitwise Operations
+<https://www.tutorialspoint.com/python/bitwise_operators_example.htm>
+
 
 ## Makefile
 * Purpose of ".PHONY" - <https://stackoverflow.com/a/2145605>
diff --git a/main.py b/main.py
index 14eb0de..f2eb6c9 100644
--- a/main.py
+++ b/main.py
@@ -10,25 +10,156 @@ Simulates system use of a cache for data storage.
 Cache Settings Arguments:
     * S: Number of sets in cache.
     * E: Lines per set.
-    * B: Block size (number of bits per block).
+    * B: Block size (number of bytes per block).
+    * s: Bit count to identify set.
+    * e: Bit count to identify line.
+    * b: Bit count to identify block.
 """
 
 # System Imports.
-import argparse
+import argparse, math, re
 
 # User Imports.
 
 
-def main(set_count, line_count, block_size, file_name, verbose):
+def main(set_identifier_bit_count, line_count, block_identifier_bit_count, file_name, verbose):
     """
     Program main.
     """
     print('main():')
-    print('    set_count: {0}'.format(set_count))
+    print('    set_identifier_bit_count: {0}'.format(set_identifier_bit_count))
     print('    line_count: {0}'.format(line_count))
-    print('    block_size: {0}'.format(block_size))
+    print('    block_identifier_bit_count: {0}'.format(block_identifier_bit_count))
     print('    file_name: {0}'.format(file_name))
     print('    verbose: {0}'.format(verbose))
+    print('')
+
+    # Calculate total sizes of cache structures.
+    s = set_identifier_bit_count
+    S = set_count = int(math.pow(2, s))
+    E = line_count
+    e = line_identifier_bit_count =  math.log2(E)
+    b = block_identifier_bit_count
+    B = block_size = int(math.pow(2, b))
+    m = address_identifier_bit_count = E * S
+    M = total_bytes = int(math.pow(2, m))
+    C = capacity = S * E * B
+    tag_identifier_bit_count = m - (b + s)
+
+    # Create cache object.
+    cache = []
+    for set in range(set_count):
+        cache.append([])
+
+        # Create lines in set.
+        for line in range(line_count):
+            cache[len(cache) - 1].append({
+                'valid': False,
+                'tag': 0,
+                'block': 0,
+            })
+
+    # print('Address Bit Count: {0}'.format(address_identifier_bit_count))
+    # print('B: {0}'.format(B))
+    # print('S: {0}'.format(S))
+    # print('E: {0}'.format(E))
+    # print('Tag Bits: {0}'.format(tag_identifier_bit_count))
+    # print('Cache: {0}'.format(cache))
+
+    run_simulation(cache, file_name, M, B, S)
+
+
+def run_simulation(cache, file_name, M, B, S):
+    """
+
+    :param cache:
+    :return:
+    """
+    hits = 0
+    misses = 0
+    evictions = 0
+
+    # Open file to read in values.
+    with open(file_name) as file:
+        # Read in line.
+        for line in file:
+            # print('Line: {0}'.format(line))
+            cache, hits, misses, evictions = handle_line(cache, line, M, B, S, hits, misses, evictions)
+
+    # print('M: {0}   As Bits: {1:b}'.format(M, M - 1))
+    # print('B: {0}   As Bits: {1:b}'.format(B, B - 1))
+    # print('S: {0}   As Bits: {1:b}'.format(S, S - 1))
+
+    print('hits:{0} misses:{1} evictions:{2}'.format(hits, misses, evictions))
+
+
+def handle_line(cache, line, M, B, S, hits, misses, evictions):
+    """
+
+    :param cache:
+    :param line:
+    :return:
+    """
+    split_line = re.split(r'[ ,\n]', line)
+    # print('split_line: {0}'.format(split_line))
+
+    # Check if instruction load. If so, we can skip line.
+    if split_line[0] == 'I':
+        # Found instruction load. Returning.
+        return
+    else:
+        line = line.strip()
+
+        # Not an instruction load. Keep processing. Start by getting address.
+        address = int(split_line[2]) % M
+        value_size = int(split_line[3])
+
+        # Get mask values.
+        full_mask = M - 1
+        block_mask = B - 1
+        set_mask = S + B
+        tag_mask = set_mask + block_mask + 1
+
+        # print('Address: {0}     As Bits: {0:b}'.format(address))
+        # print('full_mask: {0}   As Bits: {0:b}'.format(full_mask))
+        # print('block_mask: {0}  As Bits: {0:b}'.format(block_mask))
+        # print('set_mask: {0}    As Bits: {0:b}'.format(set_mask))
+        # print('tag_mask: {0}    As Bits: {0:b}'.format(tag_mask))
+
+        # Use masks to get address chunks.
+        block = address & block_mask
+        set = (address & set_mask) - B - 1
+        tag = address & tag_mask
+
+        # print('block: {0:b}'.format(block))
+        # print('set: {0:b}'.format(set))
+        # print('tag: {0:b}'.format(tag))
+
+        # We have our values. Now compare against the cache.
+        # print('set: {0}'.format(set))
+        # print('cache[set]: {0}'.format(cache[set]))
+        if cache[set][0]['valid'] == True and cache[set][0]['tag'] == tag:
+            # Match found.
+            print('{0} hit'.format(line))
+            hits += 1
+        else:
+            # Not a match. Examine why.
+            misses += 1
+            if cache[set][0]['valid'] == True:
+                # Cache location was set, but tag did not match. Conflict miss.
+                print('{0} miss eviction'.format(line))
+                cache[set][0]['tag'] = tag
+            else:
+                # Cache location was not set. Cold miss.
+                print('{0} miss'.format(line))
+                cache[set][0]['valid'] = True
+                cache[set][0]['tag'] = tag
+                evictions += 1
+        # print('cache: {0}'.format(cache))
+
+    return (cache, hits, misses, evictions)
+
+
 
 
 if __name__ == '__main__':
@@ -37,13 +168,13 @@ if __name__ == '__main__':
     # Define our argparser and get command line args.
     parser = argparse.ArgumentParser(description='Cache Simulator')
     parser.add_argument('-v', '-V', action='store_true', default=False, help='Flag for verbose mode.', dest='verbose')
-    parser.add_argument('-s', '-S', action='store', required=True, help='Total sets.', dest='set_count')
-    parser.add_argument('-e', '-E', action='store', required=True, help='Lines per set.', dest='line_count')
-    parser.add_argument('-b', '-B', action='store', required=True, help='Bits per block.', dest='block_size')
+    parser.add_argument('-s', action='store', required=True, help='Total sets.', dest='set_count')
+    parser.add_argument('-E', action='store', required=True, help='Lines per set.', dest='line_count')
+    parser.add_argument('-b', action='store', required=True, help='Bytes per block.', dest='block_size')
     parser.add_argument('-t', '-T', action='store', required=True, help='File to read in.', dest='file_name')
     args = parser.parse_args()
 
     # If args parsed properly, run program main.
-    main(args.set_count, args.line_count, args.block_size, args.file_name, args.verbose)
+    main(int(args.set_count), int(args.line_count), int(args.block_size), args.file_name, args.verbose)
 
     print('Terminating program.')
-- 
GitLab