1 /* Copyright (C) 2007-2008 The Android Open Source Project 2 ** 3 ** This software is licensed under the terms of the GNU General Public 4 ** License version 2, as published by the Free Software Foundation, and 5 ** may be copied, distributed, and modified under those terms. 6 ** 7 ** This program is distributed in the hope that it will be useful, 8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 ** GNU General Public License for more details. 11 */ 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include "dcache.h" 16 #include "cpu.h" 17 #include "exec-all.h" 18 #include "trace.h" 19 #include "varint.h" 20 21 extern FILE *ftrace_debug; 22 23 int dcache_size = 16 * 1024; 24 int dcache_ways = 4; 25 int dcache_line_size = 32; 26 int dcache_replace_policy = kPolicyRandom; 27 int dcache_load_miss_penalty = 30; 28 int dcache_store_miss_penalty = 5; 29 30 typedef struct Dcache { 31 int size; 32 int ways; 33 int line_size; 34 int log_line_size; 35 int rows; 36 uint32_t addr_mask; 37 int replace_policy; 38 int next_way; 39 int extra_increment_counter; 40 int *replace; 41 uint32_t **table; 42 int load_miss_penalty; 43 int store_miss_penalty; 44 uint64_t load_hits; 45 uint64_t load_misses; 46 uint64_t store_hits; 47 uint64_t store_misses; 48 } Dcache; 49 50 Dcache dcache; 51 52 void dcache_cleanup(); 53 54 // Returns the log2 of "num" rounded up to the nearest integer. 55 int log2_roundup(int num) 56 { 57 int power2; 58 int exp; 59 60 for (exp = 0, power2 = 1; power2 < num; power2 <<= 1) { 61 exp += 1; 62 } 63 return exp; 64 } 65 66 void dcache_init(int size, int ways, int line_size, int replace_policy, 67 int load_miss_penalty, int store_miss_penalty) 68 { 69 int ii; 70 71 // Compute the logs of the params, rounded up 72 int log_size = log2_roundup(size); 73 int log_ways = log2_roundup(ways); 74 int log_line_size = log2_roundup(line_size); 75 76 // The number of rows in the table = size / (line_size * ways) 77 int log_rows = log_size - log_line_size - log_ways; 78 79 dcache.size = 1 << log_size; 80 dcache.ways = 1 << log_ways; 81 dcache.line_size = 1 << log_line_size; 82 dcache.log_line_size = log_line_size; 83 dcache.rows = 1 << log_rows; 84 dcache.addr_mask = (1 << log_rows) - 1; 85 86 // Allocate an array of pointers, one for each row 87 uint32_t **table = malloc(sizeof(uint32_t *) << log_rows); 88 89 // Allocate the data for the whole cache in one call to malloc() 90 int data_size = sizeof(uint32_t) << (log_rows + log_ways); 91 uint32_t *data = malloc(data_size); 92 93 // Fill the cache with invalid addresses 94 memset(data, ~0, data_size); 95 96 // Assign the pointers into the data array 97 int rows = dcache.rows; 98 for (ii = 0; ii < rows; ++ii) { 99 table[ii] = &data[ii << log_ways]; 100 } 101 dcache.table = table; 102 dcache.replace_policy = replace_policy; 103 dcache.next_way = 0; 104 dcache.extra_increment_counter = 0; 105 106 dcache.replace = NULL; 107 if (replace_policy == kPolicyRoundRobin) { 108 dcache.replace = malloc(sizeof(int) << log_rows); 109 memset(dcache.replace, 0, sizeof(int) << log_rows); 110 } 111 dcache.load_miss_penalty = load_miss_penalty; 112 dcache.store_miss_penalty = store_miss_penalty; 113 dcache.load_hits = 0; 114 dcache.load_misses = 0; 115 dcache.store_hits = 0; 116 dcache.store_misses = 0; 117 118 atexit(dcache_cleanup); 119 } 120 121 void dcache_stats() 122 { 123 uint64_t hits = dcache.load_hits + dcache.store_hits; 124 uint64_t misses = dcache.load_misses + dcache.store_misses; 125 uint64_t total = hits + misses; 126 double hit_per = 0; 127 double miss_per = 0; 128 if (total) { 129 hit_per = 100.0 * hits / total; 130 miss_per = 100.0 * misses / total; 131 } 132 printf("\n"); 133 printf("Dcache hits %10llu %6.2f%%\n", hits, hit_per); 134 printf("Dcache misses %10llu %6.2f%%\n", misses, miss_per); 135 printf("Dcache total %10llu\n", hits + misses); 136 } 137 138 void dcache_free() 139 { 140 free(dcache.table[0]); 141 free(dcache.table); 142 free(dcache.replace); 143 dcache.table = NULL; 144 } 145 146 void dcache_cleanup() 147 { 148 dcache_stats(); 149 dcache_free(); 150 } 151 152 void compress_trace_addresses(TraceAddr *trace_addr) 153 { 154 AddrRec *ptr; 155 char *comp_ptr = trace_addr->compressed_ptr; 156 uint32_t prev_addr = trace_addr->prev_addr; 157 uint64_t prev_time = trace_addr->prev_time; 158 AddrRec *last = &trace_addr->buffer[kMaxNumAddrs]; 159 for (ptr = trace_addr->buffer; ptr != last; ++ptr) { 160 if (comp_ptr >= trace_addr->high_water_ptr) { 161 uint32_t size = comp_ptr - trace_addr->compressed; 162 fwrite(trace_addr->compressed, sizeof(char), size, trace_addr->fstream); 163 comp_ptr = trace_addr->compressed; 164 } 165 166 int addr_diff = ptr->addr - prev_addr; 167 uint64_t time_diff = ptr->time - prev_time; 168 prev_addr = ptr->addr; 169 prev_time = ptr->time; 170 171 comp_ptr = varint_encode_signed(addr_diff, comp_ptr); 172 comp_ptr = varint_encode(time_diff, comp_ptr); 173 } 174 trace_addr->compressed_ptr = comp_ptr; 175 trace_addr->prev_addr = prev_addr; 176 trace_addr->prev_time = prev_time; 177 } 178 179 // This function is called by the generated code to simulate 180 // a dcache load access. 181 void dcache_load(uint32_t addr) 182 { 183 int ii; 184 int ways = dcache.ways; 185 uint32_t cache_addr = addr >> dcache.log_line_size; 186 int row = cache_addr & dcache.addr_mask; 187 //printf("ld %lld 0x%x\n", sim_time, addr); 188 for (ii = 0; ii < ways; ++ii) { 189 if (cache_addr == dcache.table[row][ii]) { 190 dcache.load_hits += 1; 191 #if 0 192 printf("dcache load hit addr: 0x%x cache_addr: 0x%x row %d way %d\n", 193 addr, cache_addr, row, ii); 194 #endif 195 // If we are tracing all addresses, then include this in the trace. 196 if (trace_all_addr) { 197 AddrRec *next = trace_load.next; 198 next->addr = addr; 199 next->time = sim_time; 200 next += 1; 201 if (next == &trace_load.buffer[kMaxNumAddrs]) { 202 // Compress the trace 203 compress_trace_addresses(&trace_load); 204 next = &trace_load.buffer[0]; 205 } 206 trace_load.next = next; 207 } 208 return; 209 } 210 } 211 // This is a cache miss 212 213 #if 0 214 if (ftrace_debug) 215 fprintf(ftrace_debug, "t%lld %08x\n", sim_time, addr); 216 #endif 217 if (trace_load.fstream) { 218 AddrRec *next = trace_load.next; 219 next->addr = addr; 220 next->time = sim_time; 221 next += 1; 222 if (next == &trace_load.buffer[kMaxNumAddrs]) { 223 // Compress the trace 224 compress_trace_addresses(&trace_load); 225 next = &trace_load.buffer[0]; 226 } 227 trace_load.next = next; 228 } 229 230 dcache.load_misses += 1; 231 sim_time += dcache.load_miss_penalty; 232 233 // Pick a way to replace 234 int way; 235 if (dcache.replace_policy == kPolicyRoundRobin) { 236 // Round robin replacement policy 237 way = dcache.replace[row]; 238 int next_way = way + 1; 239 if (next_way == dcache.ways) 240 next_way = 0; 241 dcache.replace[row] = next_way; 242 } else { 243 // Random replacement policy 244 way = dcache.next_way; 245 dcache.next_way += 1; 246 if (dcache.next_way >= dcache.ways) 247 dcache.next_way = 0; 248 249 // Every 13 replacements, add an extra increment to the next way 250 dcache.extra_increment_counter += 1; 251 if (dcache.extra_increment_counter == 13) { 252 dcache.extra_increment_counter = 0; 253 dcache.next_way += 1; 254 if (dcache.next_way >= dcache.ways) 255 dcache.next_way = 0; 256 } 257 } 258 #if 0 259 printf("dcache load miss addr: 0x%x cache_addr: 0x%x row %d replacing way %d\n", 260 addr, cache_addr, row, way); 261 #endif 262 dcache.table[row][way] = cache_addr; 263 } 264 265 // This function is called by the generated code to simulate 266 // a dcache store access. 267 void dcache_store(uint32_t addr, uint32_t val) 268 { 269 //printf("st %lld 0x%08x val 0x%x\n", sim_time, addr, val); 270 271 int ii; 272 int ways = dcache.ways; 273 uint32_t cache_addr = addr >> dcache.log_line_size; 274 int row = cache_addr & dcache.addr_mask; 275 for (ii = 0; ii < ways; ++ii) { 276 if (cache_addr == dcache.table[row][ii]) { 277 dcache.store_hits += 1; 278 #if 0 279 printf("dcache store hit addr: 0x%x cache_addr: 0x%x row %d way %d\n", 280 addr, cache_addr, row, ii); 281 #endif 282 // If we are tracing all addresses, then include this in the trace. 283 if (trace_all_addr) { 284 AddrRec *next = trace_store.next; 285 next->addr = addr; 286 next->time = sim_time; 287 next += 1; 288 if (next == &trace_store.buffer[kMaxNumAddrs]) { 289 // Compress the trace 290 compress_trace_addresses(&trace_store); 291 next = &trace_store.buffer[0]; 292 } 293 trace_store.next = next; 294 } 295 return; 296 } 297 } 298 // This is a cache miss 299 #if 0 300 printf("dcache store miss addr: 0x%x cache_addr: 0x%x row %d\n", 301 addr, cache_addr, row); 302 #endif 303 304 #if 0 305 if (ftrace_debug) 306 fprintf(ftrace_debug, "t%lld %08x\n", sim_time, addr); 307 #endif 308 309 if (trace_store.fstream) { 310 AddrRec *next = trace_store.next; 311 next->addr = addr; 312 next->time = sim_time; 313 next += 1; 314 if (next == &trace_store.buffer[kMaxNumAddrs]) { 315 // Compress the trace 316 compress_trace_addresses(&trace_store); 317 next = &trace_store.buffer[0]; 318 } 319 trace_store.next = next; 320 } 321 322 dcache.store_misses += 1; 323 sim_time += dcache.store_miss_penalty; 324 325 // Assume no write-allocate for now 326 } 327 328 // This function is called by the generated code to simulate 329 // a dcache load and store (swp) access. 330 void dcache_swp(uint32_t addr) 331 { 332 dcache_load(addr); 333 dcache_store(addr, 0); 334 } 335