Regarding the programming assignment, I've attached two simplified workloads that should help you test whether you implemented cache replacement correctly. This is what you should get when running 1 process on 1 core for 1,000,000 references with wrap-around.
cache lines used: 4, unused: 65532 pid hits misses eviction trace file 1 999996 4 0 data/simple-cache-addrs cache lines used: 40, unused: 65496 pid hits misses eviction trace file 1 999936 64 24 data/synthetic-l2-cache-addrs
The simple-cache-addrs should be easy enough to see what's going on. The four addresses are mapped to four distinct sets, so there should be no eviction. The synthetic workload is slightly more involved. It is generated using the following for-loop:
for (int msb = 0; msb < 16; msb++) for (int index = 0; index < 4; index++) printf("%#x\n", (msb << 18 | index << 6));
The purpose is that, for each of the index 0 through 3, each of which map to a cache set, we want to populate that cache set 16 times. Since cache replacement is uniform random, eviction can occur quite often.