×
Samples Blogs Make Payment About Us Reviews 4.9/5 Order Now

Program to Simulate the Cache Implementation in C Language Assignment Solution

July 01, 2024
Mark Davis
Mark Davis
🇦🇪 United Arab Emirates
C
Mark holds a Master of Science degree in Computer Engineering and specializes in C. With 750 assignments completed, his solutions are known for their accuracy and efficiency, helping students overcome complex Makefile challenges.
Key Topics
  • Instructions
  • Requirements and Specifications
Tip of the day
Familiarize yourself with OCaml's pattern matching; it simplifies handling recursive data structures like lists and trees, making your code concise and easier to debug.
News
In 2024, Girls Who Code introduced a Data Science + AI track in their free summer programs for high school students, fostering skills in cybersecurity and creative coding​

Instructions

Objective

Write a C assignment program to simulate the cache in C language.

Requirements and Specifications

Cache Simulation

A cache is a collection of cache lines, each of which may store a block of memory along with some additional information about the block (for example, which addresses it contains). Each block contains the same number of bytes, known as the block size. The block size will always be a power of two. The cache size is the block size multiplied by the number of cache lines (that is, the additional information is not counted in the cache size).

Consider a system with 48-bit addresses and a block size of 16 bytes. Each block will begin with an address divisible by 16. Because 16 = 24, the last 4 bits of an address will determine its offset within a particular block. For example, the address ffff0000abcd (hexadecimal) will have offset d. The remaining 44 bits of the address may be considered a block identifier.

If the block size were 8 bytes, then the last 3 bits of the address would be its block offset. The last three bits of ffff000abcd are 101 (binary) or 5 (hexadecimal). The most-significant 45 bits will be the block identifier. (Exercise: write the block identifier in hexadecimal.1)

For a cache with a single cache line, that cache line will store the 16 bytes of the block (the data) along with the validity bit and block identifier (the metadata). Each memory access will first check whether the address is part of the block currently in the cache (if any). Since we are only simulating memory reads and writes, you cache will not need to store the actual blocks.

Screenshots of output

program to simulate the cache in C language

Source Code

#include #include #include #include #include // associativity type enum {DIRECT, NWAY}; // replacement policy type enum {FIFO, LRU}; // Definition of a single line in the cache typedef struct { unsigned long tag; // tag of the cached block bool valid; // valid flag unsigned time; // time of entry for fifo, time of last access for lru }CacheLine; // gets the log 2 of the number, x must be a power of 2. It simply gets the // position of the first 1 int log_2(unsigned int x) { int n = 0; while ((x & 1) == 0) { x >>= 1; n++; } return n; } // simulate the cache using the given arguments and reading from the given file void simulate(FILE *f, int cachesize, int blksize, int nways, int assoc, int policy, int prefetch) { char buffer[50]; int cache_misses = 0; int cache_hits = 0; int memory_reads = 0; int memory_writes = 0; // number of lines in cache int nlines = cachesize / blksize; // create the cache CacheLine *cache = (CacheLine *) malloc(nlines * sizeof(CacheLine)); // clear cache entries memset(cache, 0, nlines * sizeof(CacheLine)); // calculate the offset bits int obits = log_2(blksize); // calculate the set bits int sbits = log_2(nlines / nways); // get the bitmasks for the set and the tag fields unsigned long setmsk = (1 << sbits) - 1; unsigned long tagmsk = ((unsigned long) 1 << (48 - sbits - obits)) - 1; fseek(f, 0, SEEK_SET); // rewind file pointer bool halt = false; unsigned time = 0; while (!halt) { if (fgets(buffer, 50, f) != NULL) { if (!strncmp(buffer, "#eof", 4) || !strncmp(buffer, "#end", 4)) halt = true; else if (buffer[0] != 0) // avoid empty lines { char rw; unsigned long address; // read trace line sscanf(buffer, "%*x: %c 0x%lx", &rw, &address); // get set and tag fields from address unsigned long set = (address >> obits) & setmsk; unsigned long tag = (address >> (sbits + obits)) & tagmsk; if (assoc == DIRECT) { // if tag was cached if (cache[set].valid && cache[set].tag == tag) { cache_hits++; if (rw == 'W') // a write to cache will write memory memory_writes++; } else // tag not found in cache { cache_misses++; memory_reads++; // reads the block from memory if (rw == 'W') memory_writes++; // the write first reads, then writes cache[set].tag = tag; // update tag cache[set].valid = true; // mark line as valid if (prefetch) { // increment tag if set + 1 overflows the number of set bits tag += (set + 1) >> sbits; // leave only the correct number of bits for set + 1 set = (set + 1) & setmsk; if (!cache[set].valid || cache[set].tag != tag) { memory_reads++; // if set + 1 is not loaded, read memory cache[set].tag = tag; cache[set].valid = true; } } } } else { bool found = false; // search tag in all ways for the selected set for (int i = 0; i < nways && !found; i++) { // if we found the tag in one of the ways if (cache[set * nways + i].valid && cache[set * nways + i].tag == tag) { cache_hits++; if (rw == 'W') // a write to cache will write memory memory_writes++; if (policy == LRU) // if LRU, any access updates the line time cache[set * nways + i].time = time++; found = true; } } if (!found) // tag not found in cache { // set minimum to current time + 1 (above any time in cache) int min_t = time + 1; int sel_i = -1; // no selected way // search for an empty line and, if all are full, // find the line with the minimum time for (int i = 0; i < nways && !found; i++) { // if free line if (!cache[set * nways + i].valid) { sel_i = i; // select this line found = true; } // if not free, see if we need to update the minimum else if (cache[set * nways + i].time < min_t) { min_t = cache[set * nways + i].time; sel_i = i; } } // get the position of the selected line in the cache int pos = set * nways + sel_i; cache_misses++; memory_reads++; // read the block from memory if (rw == 'W') memory_writes++; // the write first reads, then writes cache[pos].tag = tag; // update tag cache[pos].valid = true; // mark line as valid cache[pos].time = time++; // update its time of entry/access if (prefetch) { // increment tag if set + 1 overflows the number of set bits tag += (set + 1) >> sbits; // leave only the correct number of bits for set + 1 set = (set + 1) & setmsk; found = false; // search tag in all ways for set + 1 for (int i = 0; i < nways && !found; i++) { if (cache[set * nways + i].valid && cache[set * nways + i].tag == tag) found = true; // if found, we don't need to prefetch } if (!found) // if tag is not in set + 1 { // set minimum to current time + 1 (above any time in cache) min_t = time + 1; sel_i = -1; // search for an empty line and, if all are full, // find the line with the minimum time for (int i = 0; i < nways && !found; i++) { // if free line if (!cache[set * nways + i].valid) { sel_i = i; // select this line found = true; // break loop } // if not free, see if we need to update the minimum else if (cache[set * nways + i].time < min_t) { min_t = cache[set * nways + i].time; sel_i = i; } } // get the position of the selected line in the cache pos = set * nways + sel_i; memory_reads++; // read the block from memory cache[pos].tag = tag; // update tag cache[pos].valid = true; // mark line as valid cache[pos].time = time++; // update its time of entry/access } } } } } } else halt = true; } // free the allocated memory free(cache); // print the statistics printf("Prefetch %d\n", prefetch); printf("Memory reads: %d\n", memory_reads); printf("Memory writes: %d\n", memory_writes); printf("Cache hits: %d\n", cache_hits); printf("Cache misses: %d\n", cache_misses); } int main(int argc, char **argv) { int cache_size; int assoc; int block_size; int nways; int policy; FILE *f; if (argc != 6) { printf("Usage:\n"); printf(" %s (direct | assoc[:n]) (fifo | lru) \n", argv[0]); return 0; } cache_size = atoi(argv[1]); if (cache_size <= 0 || (cache_size & (cache_size - 1)) != 0) { printf("Error: Invalid cache size.\n"); return 1; } block_size = atoi(argv[4]); if (block_size <= 0 || (block_size & (block_size - 1)) != 0) { printf("Error: Invalid block size.\n"); return 1; } if (strcmp(argv[2], "direct") == 0) { assoc = DIRECT; nways = 1; } else if (strcmp(argv[2], "assoc") == 0) { assoc = NWAY; // we treat full as n-way with n = number of lines // set number of ways to number of lines nways = cache_size/block_size; } else if (strncmp(argv[2], "assoc:", 6) == 0) { assoc = NWAY; if (sscanf(argv[2], "assoc:%d", &nways) != 1) { printf("Error: Invalid number of ways.\n"); return 1; } } else { printf("Error: Invalid associativity.\n"); return 1; } if (strcmp(argv[3], "fifo") == 0) policy = FIFO; else if (strcmp(argv[3], "lru") == 0) policy = LRU; else { printf("Error: Invalid replacement policy.\n"); return 1; } f = fopen(argv[5], "rt"); if (f == NULL) { printf("Error: unable to open trace file.\n"); return 1; } // simulate without prefetch simulate(f, cache_size, block_size, nways, assoc, policy, 0); // simulate using prefetch simulate(f, cache_size, block_size, nways, assoc, policy, 1); fclose(f); return 0; }

Similar Samples

Discover high-quality sample programming assignments at ProgrammingHomeworkHelp.com. Our examples cover various languages and complexity levels, demonstrating our expertise and commitment to excellence. Use these samples to gauge our capabilities and see how we can help you achieve your academic goals.