/* A simple cache simulation program, myCache is designed to allow testing cache hits and misses with a cache based on #define-d parameters. To change these, simply modify the constants in myCache.h */ #include #include #include #include "myCache.h" // Global variables for our cache. If this was a real-world // program, the cache would be an instance of a myCache class // and these variables would be members of the class. // A 2D array of cache_line structs. // 1st dimension: set number // 2nd dimension: set members (associativity) cache_line *myCache; int cache_set_mask, cache_tag_mask, cache_offset_mask, cache_b_bits, cache_set_shift_bits, cache_tag_shift_bits, cache_num_sets, cache_num_bytes_per_line, cache_next_line_to_evict; // Global test array to get the array off the stack so gcc will // actually run the test :P int myLargeArray[ARRAY_WIDTH][ARRAY_WIDTH]; // Function declarations void pretendGetFromCache(int *address); int runArrayTest(); void startCache(); void stopCache(); void main (int argc, char* argv[]){ // variables for checking the loop execution time struct timeval start_time, end_time; struct timezone dummy_timezone; time_t sec_diff; suseconds_t usec_diff; int m, sum_result; #ifdef PRINT_CACHE // Set up the cache now startCache(); #endif //PRINT_CACHE // Print our test parameters if(TEST_RUN_MODE == ROW_MAJOR_ORDER){ printf("Running %d tests on a %d x %d array in ROW major order.\n", NUM_TESTS, ARRAY_WIDTH, ARRAY_WIDTH); } else { printf("Running %d tests on a %d x %d array in COL major order.\n", NUM_TESTS, ARRAY_WIDTH, ARRAY_WIDTH); } // Check the start time and handle the return value if(gettimeofday(&start_time, &dummy_timezone) == -1){ printf("Error getting start time\n"); return; } for(m = 0; m < NUM_TESTS; m++){ // Run the test loop sum_result = runArrayTest(); } // Check the end time and handle the return value if(gettimeofday(&end_time, &dummy_timezone) == -1){ printf("Error getting end time\n"); return; } // Calculate the time and report our findings sec_diff = end_time.tv_sec - start_time.tv_sec; usec_diff = end_time.tv_usec - start_time.tv_usec; long long total_usec = ((long) sec_diff * 1000000LL) + (long)usec_diff; printf("Start %d.%d\n",start_time.tv_sec, start_time.tv_usec); printf("End %d.%d\n",end_time.tv_sec, end_time.tv_usec); printf("Sum of last array was %d\n", sum_result); printf("This run took %lld seconds and %lld microseconds\n", total_usec/1000000LL, total_usec%1000000LL); #ifdef PRINT_CACHE // Clean up and return stopCache(); #endif //PRINT_CACHE return; } // runArrayTest cycles through a 2D array to add all of its elements, // optionally simulating our dummy cache. // // arrayWidth determines the width and length of our MxM test array // // runMode indicates whether we should run in row or col order // use ROW_MAJOR_ORDER or COL_MAJOR_ORDER int runArrayTest(){ // Test array and indices for a simple nested loop // Changing the order of the index traversal in the // loop changes how quickly the program runs and how // well we use the cache. int i,j; int *myPointer; volatile int mySum = 0; // The boolean is outside the loop so we avoid the overhead // of running the row-order test inside the loop. if (TEST_RUN_MODE == ROW_MAJOR_ORDER){ for(i = 0; i < ARRAY_WIDTH; i++){ for(j = 0; j < ARRAY_WIDTH; j++){ // If PRINT_CACHE is defined (top of file), // pretend to access the cache and print a hit // or miss. #ifdef PRINT_CACHE myPointer = &myLargeArray[i][j]; pretendGetFromCache(myPointer); #endif //PRINT_CACHE // This is our array access, which uses the real // per-computer cache mySum += myLargeArray[i][j]; } } } else { // COL_MAJOR_ORDER for(i = 0; i < ARRAY_WIDTH; i++){ for(j = 0; j < ARRAY_WIDTH; j++){ // If PRINT_CACHE is defined (top of file), // pretend to access the cache and print a hit // or miss. #ifdef PRINT_CACHE myPointer = &myLargeArray[j][i]; pretendGetFromCache(myPointer); #endif //PRINT_CACHE // This is our array access, which uses the real // per-computer cache mySum += myLargeArray[j][i]; } } } return mySum; } // Set up the globals and big array we use to track the dummy cache void startCache(){ // Set up global cache parameters cache_b_bits = CACHE_ADDRESS_LENGTH - CACHE_S_BITS - CACHE_T_BITS; cache_set_shift_bits = cache_b_bits; cache_tag_shift_bits = cache_b_bits + CACHE_S_BITS; // Build the set bits mask signed int left_one = 1 << (CACHE_ADDRESS_LENGTH - 1); signed int inverse_mask = left_one >> (CACHE_ADDRESS_LENGTH - CACHE_S_BITS - 1); unsigned int offset_mask = ~inverse_mask; cache_set_mask = offset_mask << cache_set_shift_bits; // Build the tag bits mask cache_tag_mask = left_one >> (CACHE_T_BITS - 1); // Build the offset bits mask unsigned int tag_and_set_mask = cache_tag_mask | cache_set_mask; cache_offset_mask = ~tag_and_set_mask; // Check to be sure we masked all of the bits unsigned int all_mask_xor = cache_tag_mask ^ cache_set_mask ^ cache_offset_mask; if((all_mask_xor ^ (-1)) != 0) { printf("MASK CHECK FAILED!\n"); printf("Size of int: %d\n",sizeof(int)); printf("Size of int *: %d\n",sizeof(int *)); printf("Size of long: %d\n",sizeof(long)); printf("Size of long long: %d\n",sizeof(long long)); printf("set_mask\t\t0x%08x\n",cache_set_mask); printf("tag_mask\t\t0x%08x\n",cache_tag_mask); printf("cache_offset_mask\t0x%08x\n",cache_offset_mask); printf("all_mask_xor\t\t0x%08x\n",all_mask_xor); exit(1); } #ifdef CACHE_VERBOSE_DEBUG printf("CACHE_ADDRESS_LENGTH:\t%d\n",CACHE_ADDRESS_LENGTH); printf("CACHE_T_BITS:\t\t%d\n",CACHE_T_BITS); printf("CACHE_S_BITS:\t\t%d\n",CACHE_S_BITS); printf("CACHE_ASSOCIATIVITY_E:\t%d\n",CACHE_ASSOCIATIVITY_E); printf("b_bits\t\t\t%d\n",cache_b_bits); printf("set_shift_bits\t\t%d\n",cache_set_shift_bits); printf("tag_shift_bits\t\t%d\n",cache_tag_shift_bits); printf("inverse_mask\t\t0x%x\n",inverse_mask); printf("offset_mask\t\t0x%x\n",offset_mask); printf("set_mask\t\t0x%08x\n",cache_set_mask); printf("left_one\t\t0x%08x\n",left_one); printf("tag_mask\t\t0x%08x\n",cache_tag_mask); printf("tag_and_set_mask\t0x%08x\n",tag_and_set_mask); printf("cache_offset_mask\t0x%08x\n",cache_offset_mask); printf("all_mask_xor\t\t0x%08x\n",all_mask_xor); printf("num_sets\t\t%d\n",cache_num_sets); printf("num_bytes_per_line\t%d\n",cache_num_bytes_per_line); printf("cache_next_line_to_evict\t%d\n",cache_next_line_to_evict); #endif //CACHE_VERBOSE_DEBUG cache_num_sets = 1 << CACHE_S_BITS; cache_num_bytes_per_line = 1 << cache_b_bits; cache_next_line_to_evict = 0; // Set up our giant cache array int structs_needed = cache_num_sets * CACHE_ASSOCIATIVITY_E; printf("For cache, allocating %d structs for %d sets of %d lines each.\n", structs_needed, cache_num_sets, CACHE_ASSOCIATIVITY_E); printf("Each line holds %d bytes.\n", cache_num_bytes_per_line); // Allocate as a 1-D array but treat as a 2-D array // Use calloc so we clear out our structs myCache = (cache_line *) calloc(structs_needed, sizeof(cache_line)); if (myCache == NULL){ printf("Error allocating myCache!\n"); exit(1); } } // Note: Assumes the entire element is in the cache if // the first byte is in the cache. void pretendGetFromCache(int *address){ int i; unsigned int intAddr = (unsigned int) address; int setNumber = (signed int)(intAddr & cache_set_mask) >> cache_set_shift_bits; int tagNumber = (signed int)(intAddr & cache_tag_mask) >> cache_tag_shift_bits; int lineNumberFound = -1; #ifdef CACHE_VERBOSE_DEBUG printf("\nLookup address:\t%08x\n", intAddr); printf("Looking for tag %d in set %d\n", tagNumber, setNumber); printf("TEST\ts:%08x[%08x]\tt:%08x\taddr: %08x\n",setNumber,i,tagNumber,intAddr); #endif //CACHE_VERBOSE_DEBUG for(i = 0; i < CACHE_ASSOCIATIVITY_E; i++){ // Fetch the next cache line in the set cache_line *nextElem = &myCache[(setNumber * CACHE_ASSOCIATIVITY_E) + i]; cache_line thisLine = *nextElem; #ifdef CACHE_VERBOSE_DEBUG printf("Checking element %d at %p\n", i, nextElem); printf("Tag:\t%d Valid:\t%d\n", thisLine.tag, thisLine.valid); #endif //CACHE_VERBOSE_DEBUG if(thisLine.tag == tagNumber && thisLine.valid == 1){ // We got a hit! lineNumberFound = i; printf("HIT\ts:%d[%d]\tt:%d\taddr: 0x%08x\n",setNumber,i,tagNumber,intAddr); break; } } if(lineNumberFound == -1){ // We got a miss, so add this to the cache // Create the line we'll copy into the cache cache_line toCopy = {tagNumber,1}; // Figure out the address we're overwriting cache_line *overwrite_loc = &myCache[(setNumber * CACHE_ASSOCIATIVITY_E) + cache_next_line_to_evict]; // Save to the cache *overwrite_loc = toCopy; // Print out our miss printf("MISS\ts:%d[%d]\tt:%d\taddr: 0x%08x\n",setNumber, cache_next_line_to_evict,tagNumber,intAddr); #ifdef CACHE_VERBOSE_DEBUG printf("Will save to location 0x%08x\n", overwrite_loc); printf("Will save:\n%d[%d] Tag: %d Valid: %d\n\n", setNumber, cache_next_line_to_evict, toCopy.tag, toCopy.valid); #endif //CACHE_VERBOSE_DEBUG // Choose next line to evict (not set-specific) cache_next_line_to_evict = (cache_next_line_to_evict + 1) % CACHE_ASSOCIATIVITY_E; } } void stopCache(){ printf("Freeing dummy cache...\n"); free(myCache); }