HW 4
HW 4
1
t bits s bits b bits
The cache field is an array of array of cache lines. Each array of cache lines can be thought of as a
cache set, and so cache is simply an array of sets. Each cache line t has a valid bit (ignore the fact that
it is of course actually a byte), its frequency, its tag (which should be t bits long, but we made it a long to
make your (my) life easier), and the block of memory.
The cache you implement should replace lines using the LFU (least frequently used) method, meaning
if it tries to read in memory to a set with no available lines then it replaces the line whose frequency is
minimal. If two lines both have the same minimal frequency, then the first line is chosen (ie. if line 3 and
line 10 both have frequency 1 which is the smallest, then line 3 is chosen).
The read byte and write byte functions accept as inputs start as well as off. You should act as if
start is the zero address and off is the address that you insert into your cache. More specifically, insert
the contents of start[off] into your cache, while using the bits of off as your offset.
Your cache is write-through, meaning that when write byte is called, it writes new both to the cache
and to memory.
The read byte function should return eventually the asked byte from the cache, after making sure that
the cache is updated as we saw in class.
We also provide you with the print cache function (that you must use, for proper formatting), so you
don’t need to worry about whitespace errors But fair warning: it’s going to paste weirdly into your text
editor, sorry. You win some, you lose some.
1 void print_cache(cache_t cache) {
2 int S = 1 << cache.s;
3 int B = 1 << cache.b;
4
5 for (int i = 0; i < S; i++) {
6 printf("Set %d\n", i);
7 for (int j = 0; j < cache.E; j++) {
8 printf("%1d %d 0x%0*lx ", cache.cache[i][j].valid,
9 cache.cache[i][j].frequency, cache.t, cache.cache[i][j].tag);
10 for (int k = 0; k < B; k++) {
11 printf("%02x ", cache.cache[i][j].block[k]);
12 }
13 puts("");
14 }
15 }
16 }
So for example,
1 int main() {
2 uchar arr[] = {1, 2, 3, 4, 5, 6, 7, 8};
3 cache_t cache = initialize_cache(1, 1, 1, 2);
4 read_byte(cache, arr, 0);
5 read_byte(cache, arr, 1);
6 read_byte(cache, arr, 2);
7 read_byte(cache, arr, 6);
8 read_byte(cache, arr, 7);
9 print_cache(cache);
10 }
Should print
2
1 Set 0
2 1 2 0x0 01 02
3 0 0 0x0 00 00
4 Set 1
5 1 1 0x0 03 04
6 1 2 0x1 07 08
We supply you here your main function (that must be included in your final code!). It simply gets as
input the size of your data (which simulates RAM), the actual data to store in “RAM”, the cache parameters,
and then the bytes it should read (which it reads until it gets a negative value):
1 int main() {
2 int n;
3 printf("Size of data: ");
4 scanf("%d", &n);
5 uchar* mem = malloc(n);
6 printf("Input data >> ");
7 for (int i = 0; i < n; i++)
8 scanf("%hhd", mem + i);
9
10 int s, t, b, E;
11 printf("s t b E: ");
12 scanf("%d %d %d %d", &s, &t, &b, &E);
13 cache_t cache = initialize_cache(s, t, b, E);
14
15 while (1) {
16 scanf("%d", &n);
17 if (n < 0) break;
18 read_byte(cache, mem, n);
19 }
20
21 puts("");
22 print_cache(cache);
23
24 free(mem);
25 }
So for example, the previous example can be equivalently run like so:
1 Size of data: 8
2 Input data >> 1 2 3 4 5 6 7 8
3 s t b E: 1 1 1 2
4 0 1 2 6 7 -1
5
6 Set 0
7 1 2 0x0 01 02
8 0 0 0x0 00 00
9 Set 1
10 1 1 0x0 03 04
11 1 2 0x1 07 08
3
What to Submit
You must submit using the submit system all your code plus a makefile that compiles your code to an
executable named cache. Notice that the files containing your code can be named whatever you like, just
make sure that the result of the makefile is named as mentioned and is compiled without any warnings or
errors!