Cache simulator (Graduate Course)

profileveryex0418
finalprojtemplatev51.zip

finalprojtemplate/.gitignore

# Ignore the build directory build # Ignore any executables bin/* # Ignore Mac specific files .DS_Store

finalprojtemplate/Makefile

# Script adapted from https://hiltmon.com/blog/2013/07/03/a-simple-c-plus-plus-project-structure/ CC := /usr/local/classes/eecs/spring2021/cs472/public/gcc/bin/g++ STRIPUTIL = strip SRCDIR = src BUILDDIR = build TARGET = bin/cache_sim # Handle debug case DEBUG ?= 1 ifeq ($(DEBUG), 1) CFLAGS += -g -Wall else CFLAGS += -DNDEBUG -O3 endif SRCEXT = cpp SOURCES = $(shell find $(SRCDIR) -type f -name *.$(SRCEXT)) OBJECTS = $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o)) LDFLAGS += -Wl,-rpath,/usr/local/classes/eecs/spring2021/cs472/public/gcc/lib64 LIB += -pthread INC += -I $(SRCDIR) $(TARGET): $(OBJECTS) @echo "Linking..." @echo " $(CC) $^ -o $(TARGET) $(LIB) $(LDFLAGS)"; $(CC) $^ -o $(TARGET) $(LIB) $(LDFLAGS) $(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT) @mkdir -p $(BUILDDIR) @echo " $(CC) $(CFLAGS) $(INC) -c -o $@ $<"; $(CC) $(CFLAGS) $(INC) -c -o $@ $< clean: @echo "Cleaning..."; @echo " $(RM) -r $(BUILDDIR) $(TARGET)"; $(RM) -r $(BUILDDIR) $(TARGET) .PHONY: clean

finalprojtemplate/resources/simpletracefile

==This is a simple tracefile for testing purposes
L 0 8
L 80 8
L 100 6
L 0 8
S 180 5
L 100 2
L 80 8
M 180 6
L 100 8
S 100 5
S 180 8
L 80 8
M 0 8
M 0 4
L 280 4
M 80 8

finalprojtemplate/resources/testconfig

1 230 8 16 3 1 0 13

finalprojtemplate/src/CacheController.cpp

finalprojtemplate/src/CacheController.cpp

/*
    Cache Simulator (Starter Code) by Justin Goins
    Oregon State University
    Spring Term 2021
*/

#include   "CacheController.h"
#include   < iostream >
#include   < fstream >
#include   < regex >
#include   < cmath >

using   namespace  std ;

CacheController :: CacheController ( CacheInfo  ci ,  string tracefile )   {
     // store the configuration info
     this -> ci  =  ci ;
     this -> inputFile  =  tracefile ;
     this -> outputFile  =   this -> inputFile  +   ".out" ;
     // compute the other cache parameters
     this -> ci . numByteOffsetBits  =  log2 ( ci . blockSize );
     this -> ci . numSetIndexBits  =  log2 ( ci . numberSets );
     // initialize the counters
     this -> globalCycles  =   0 ;
     this -> globalHits  =   0 ;
     this -> globalMisses  =   0 ;
     this -> globalEvictions  =   0 ;
    
     // create your cache structure
     // ...

     // manual test code to see if the cache is behaving properly
     // will need to be changed slightly to match the function prototype
     /*
    cacheAccess(false, 0);
    cacheAccess(false, 128);
    cacheAccess(false, 256);

    cacheAccess(false, 0);
    cacheAccess(false, 128);
    cacheAccess(false, 256);
    */
}

/*
    Starts reading the tracefile and processing memory operations.
*/
void   CacheController :: runTracefile ()   {
    cout  <<   "Input tracefile: "   <<  inputFile  <<  endl ;
    cout  <<   "Output file name: "   <<  outputFile  <<  endl ;
    
     // process each input line
    string line ;
     // define regular expressions that are used to locate commands
    regex commentPattern ( "==.*" );
    regex instructionPattern ( "I .*" );
    regex loadPattern ( " (L )(.*)(,)([[:digit:]]+)$" );
    regex storePattern ( " (S )(.*)(,)([[:digit:]]+)$" );
    regex modifyPattern ( " (M )(.*)(,)([[:digit:]]+)$" );

     // open the output file
    ofstream outfile ( outputFile );
     // open the output file
    ifstream infile ( inputFile );
     // parse each line of the file and look for commands
     while   ( getline ( infile ,  line ))   {
         // these strings will be used in the file output
        string opString ,  activityString ;
        smatch match ;   // will eventually hold the hexadecimal address string
         unsigned   long   int  address ;
         // create a struct to track cache responses
         CacheResponse  response ;

         // ignore comments
         if   ( std :: regex_match ( line ,  commentPattern )   ||  std :: regex_match ( line ,  instructionPattern ))   {
             // skip over comments and CPU instructions
             continue ;
         }   else   if   ( std :: regex_match ( line ,  match ,  loadPattern ))   {
            cout  <<   "Found a load op!"   <<  endl ;
            istringstream hexStream ( match . str ( 2 ));
            hexStream  >>  std :: hex  >>  address ;
            outfile  <<  match . str ( 1 )   <<  match . str ( 2 )   <<  match . str ( 3 )   <<  match . str ( 4 );
            cacheAccess ( & response ,   false ,  address ,  stoi ( match . str ( 4 )));
            logEntry ( outfile ,   & response );
            
         }   else   if   ( std :: regex_match ( line ,  match ,  storePattern ))   {
            cout  <<   "Found a store op!"   <<  endl ;
            istringstream hexStream ( match . str ( 2 ));
            hexStream  >>  std :: hex  >>  address ;
            outfile  <<  match . str ( 1 )   <<  match . str ( 2 )   <<  match . str ( 3 )   <<  match . str ( 4 );
            cacheAccess ( & response ,   true ,  address ,  stoi ( match . str ( 4 )));
            logEntry ( outfile ,   & response );
         }   else   if   ( std :: regex_match ( line ,  match ,  modifyPattern ))   {
            cout  <<   "Found a modify op!"   <<  endl ;
            istringstream hexStream ( match . str ( 2 ));
             // first process the read operation
            hexStream  >>  std :: hex  >>  address ;
            outfile  <<  match . str ( 1 )   <<  match . str ( 2 )   <<  match . str ( 3 )   <<  match . str ( 4 );
            cacheAccess ( & response ,   false ,  address ,  stoi ( match . str ( 4 )));
            logEntry ( outfile ,   & response );
            outfile  <<  endl ;
             // now process the write operation
            hexStream  >>  std :: hex  >>  address ;
            outfile  <<  match . str ( 1 )   <<  match . str ( 2 )   <<  match . str ( 3 )   <<  match . str ( 4 );
            cacheAccess ( & response ,   true ,  address ,  stoi ( match . str ( 4 )));
            logEntry ( outfile ,   & response );
         }   else   {
             throw  runtime_error ( "Encountered unknown line format in tracefile." );
         }
        outfile  <<  endl ;
     }
     // add the final cache statistics
    outfile  <<   "Hits: "   <<  globalHits  <<   " Misses: "   <<  globalMisses  <<   " Evictions: "   <<  globalEvictions  <<  endl ;
    outfile  <<   "Cycles: "   <<  globalCycles  <<  endl ;

    infile . close ();
    outfile . close ();
}

/*
    Report the results of a memory access operation.
*/
void   CacheController :: logEntry ( ofstream &  outfile ,   CacheResponse *  response )   {
    outfile  <<   " "   <<  response -> cycles ;
     if   ( response -> hits  >   0 )
        outfile  <<   " hit" ;
     if   ( response -> misses  >   0 )
        outfile  <<   " miss" ;
     if   ( response -> evictions  >   0 )
        outfile  <<   " eviction" ;
}

/*
    Calculate the block index and tag for a specified address.
*/
CacheController :: AddressInfo   CacheController :: getAddressInfo ( unsigned   long   int  address )   {
     AddressInfo  ai ;
     // this code should be changed to assign the proper index and tag
     return  ai ;
}

/*
    This function allows us to read or write to the cache.
    The read or write is indicated by isWrite.
    address is the initial memory address
    numByte is the number of bytes involved in the access
*/
void   CacheController :: cacheAccess ( CacheResponse *  response ,   bool  isWrite ,   unsigned   long   int  address ,   int  numBytes )   {
     // determine the index and tag
     AddressInfo  ai  =  getAddressInfo ( address );

    cout  <<   "\tSet index: "   <<  ai . setIndex  <<   ", tag: "   <<  ai . tag  <<  endl ;
    
     // your code should also calculate the proper number of cycles that were used for the operation
    response -> cycles  =   0 ;
    
     // your code needs to update the global counters that track the number of hits, misses, and evictions

     if   ( response -> hits  >   0 )
        cout  <<   "Operation at address "   <<  std :: hex  <<  address  <<   " caused "   <<  response -> hits  <<   " hit(s)."   <<  std :: dec  <<  endl ;
     if   ( response -> misses  >   0 )
        cout  <<   "Operation at address "   <<  std :: hex  <<  address  <<   " caused "   <<  response -> misses  <<   " miss(es)."   <<  std :: dec  <<  endl ;

    cout  <<   "-----------------------------------------"   <<  endl ;

     return ;
}

finalprojtemplate/src/CacheController.h

/* Cache Simulator (Starter Code) by Justin Goins Oregon State University Spring Term 2021 */ #ifndef _CACHECONTROLLER_H_ #define _CACHECONTROLLER_H_ #include "CacheStuff.h" #include <string> #include <fstream> class CacheController { private: struct AddressInfo { unsigned long int tag; unsigned int setIndex; }; unsigned int globalCycles; unsigned int globalHits; unsigned int globalMisses; unsigned int globalEvictions; std::string inputFile, outputFile; CacheInfo ci; // function to allow read or write access to the cache void cacheAccess(CacheResponse*, bool, unsigned long int, int); // function that can compute the index and tag matching a specific address AddressInfo getAddressInfo(unsigned long int); // function to add entry into output file void logEntry(std::ofstream&, CacheResponse*); public: CacheController(CacheInfo, std::string); void runTracefile(); }; #endif //CACHECONTROLLER

finalprojtemplate/src/CacheSimulator.cpp

finalprojtemplate/src/CacheSimulator.cpp

/*
    Cache Simulator (Starter Code) by Justin Goins
    Oregon State University
    Spring Term 2021
*/

#include   "CacheSimulator.h"
#include   "CacheStuff.h"
#include   "CacheController.h"

#include   < iostream >
#include   < fstream >
#include   < thread >

using   namespace  std ;

/*
    This function creates the cache and starts the simulator.
    Accepts core ID number, configuration info, and the name of the tracefile to read.
*/
void  initializeCache ( int  id ,   CacheInfo  config ,  string tracefile )   {
     CacheController  singlecore  =   CacheController ( config ,  tracefile );
    singlecore . runTracefile ();
}

/*
    This function accepts a configuration file and a trace file on the command line.
    The code then initializes a cache simulator and reads the requested trace file(s).
*/
int  main ( int  argc ,   char *  argv [])   {
     CacheInfo  config ;
     if   ( argc  <   3 )   {
        cerr  <<   "You need two command line arguments. You should provide a configuration file and a trace file."   <<  endl ;
         return   1 ;
     }

     // determine how many cache levels the system is using
     unsigned   int  numCacheLevels ;

     // read the configuration file
    cout  <<   "Reading config file: "   <<  argv [ 1 ]   <<  endl ;
    ifstream infile ( argv [ 1 ]);
     unsigned   int  tmp ;
    infile  >>  numCacheLevels ;
    infile  >>  config . memoryAccessCycles ;
    infile  >>  config . numberSets ;
    infile  >>  config . blockSize ;
    infile  >>  config . associativity ;
    infile  >>  tmp ;
    config . rp  =   static_cast < ReplacementPolicy > ( tmp );
    infile  >>  tmp ;
    config . wp  =   static_cast < WritePolicy > ( tmp );
    infile  >>  config . cacheAccessCycles ;
    infile . close ();
    
     // Examples of how you can access the configuration file information
    cout  <<   "System has "   <<  numCacheLevels  <<   " cache(s)."   <<  endl ;
    cout  <<  config . numberSets  <<   " sets with "   <<  config . blockSize  <<   " bytes in each block. N = "   <<  config . associativity  <<  endl ;

     if   ( config . rp  ==   ReplacementPolicy :: Random )
        cout  <<   "Using random replacement protocol"   <<  endl ;
     else
        cout  <<   "Using LRU protocol"   <<  endl ;
    
     if   ( config . wp  ==   WritePolicy :: WriteThrough )
        cout  <<   "Using write-through policy"   <<  endl ;
     else
        cout  <<   "Using write-back policy"   <<  endl ;

     // start the cache operation...
    string tracefile ( argv [ 2 ]);
    initializeCache ( 0 ,  config ,  tracefile );

     return   0 ;
}

finalprojtemplate/src/CacheSimulator.h

/* Cache Simulator (Starter Code) by Justin Goins Oregon State University Spring Term 2021 */ #ifndef _CACHESIMULATOR_H_ #define _CACHESIMULATOR_H_ #endif //CACHESIMULATOR

finalprojtemplate/src/CacheStuff.h

/* Cache Simulator (Starter Code) by Justin Goins Oregon State University Spring Term 2021 */ #ifndef _CACHESTUFF_H_ #define _CACHESTUFF_H_ enum class ReplacementPolicy { Random, LRU }; enum class WritePolicy { WriteThrough, WriteBack }; // structure to hold information about a particular cache struct CacheInfo { unsigned int numByteOffsetBits; unsigned int numSetIndexBits; unsigned int numberSets; // how many sets are in the cache unsigned int blockSize; // size of each block in bytes unsigned int associativity; // the level of associativity (N) ReplacementPolicy rp; WritePolicy wp; unsigned int cacheAccessCycles; unsigned int memoryAccessCycles; }; // this structure can filled with information about each memory operation struct CacheResponse { int hits; // how many caches did this memory operation hit? int misses; // how many caches did this memory operation miss? int evictions; // did this memory operation involve one or more evictions? int dirtyEvictions; // were any evicted blocks marked as dirty? (relevant for write-back cache) unsigned int cycles; // how many clock cycles did this operation take? }; #endif //CACHESTUFF