/* Copyright (C) 2007-2008 The Android Open Source Project ** ** This software is licensed under the terms of the GNU General Public ** License version 2, as published by the Free Software Foundation, and ** may be copied, distributed, and modified under those terms. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. */ #include #include #include #include "dcache.h" #include "cpu.h" #include "exec-all.h" #include "trace.h" #include "varint.h" extern FILE *ftrace_debug; int dcache_size = 16 * 1024; int dcache_ways = 4; int dcache_line_size = 32; int dcache_replace_policy = kPolicyRandom; int dcache_load_miss_penalty = 30; int dcache_store_miss_penalty = 5; typedef struct Dcache { int size; int ways; int line_size; int log_line_size; int rows; uint32_t addr_mask; int replace_policy; int next_way; int extra_increment_counter; int *replace; uint32_t **table; int load_miss_penalty; int store_miss_penalty; uint64_t load_hits; uint64_t load_misses; uint64_t store_hits; uint64_t store_misses; } Dcache; Dcache dcache; void dcache_cleanup(); // Returns the log2 of "num" rounded up to the nearest integer. int log2_roundup(int num) { int power2; int exp; for (exp = 0, power2 = 1; power2 < num; power2 <<= 1) { exp += 1; } return exp; } void dcache_init(int size, int ways, int line_size, int replace_policy, int load_miss_penalty, int store_miss_penalty) { int ii; // Compute the logs of the params, rounded up int log_size = log2_roundup(size); int log_ways = log2_roundup(ways); int log_line_size = log2_roundup(line_size); // The number of rows in the table = size / (line_size * ways) int log_rows = log_size - log_line_size - log_ways; dcache.size = 1 << log_size; dcache.ways = 1 << log_ways; dcache.line_size = 1 << log_line_size; dcache.log_line_size = log_line_size; dcache.rows = 1 << log_rows; dcache.addr_mask = (1 << log_rows) - 1; // Allocate an array of pointers, one for each row uint32_t **table = malloc(sizeof(uint32_t *) << log_rows); // Allocate the data for the whole cache in one call to malloc() int data_size = sizeof(uint32_t) << (log_rows + log_ways); uint32_t *data = malloc(data_size); // Fill the cache with invalid addresses memset(data, ~0, data_size); // Assign the pointers into the data array int rows = dcache.rows; for (ii = 0; ii < rows; ++ii) { table[ii] = &data[ii << log_ways]; } dcache.table = table; dcache.replace_policy = replace_policy; dcache.next_way = 0; dcache.extra_increment_counter = 0; dcache.replace = NULL; if (replace_policy == kPolicyRoundRobin) { dcache.replace = malloc(sizeof(int) << log_rows); memset(dcache.replace, 0, sizeof(int) << log_rows); } dcache.load_miss_penalty = load_miss_penalty; dcache.store_miss_penalty = store_miss_penalty; dcache.load_hits = 0; dcache.load_misses = 0; dcache.store_hits = 0; dcache.store_misses = 0; atexit(dcache_cleanup); } void dcache_stats() { uint64_t hits = dcache.load_hits + dcache.store_hits; uint64_t misses = dcache.load_misses + dcache.store_misses; uint64_t total = hits + misses; double hit_per = 0; double miss_per = 0; if (total) { hit_per = 100.0 * hits / total; miss_per = 100.0 * misses / total; } printf("\n"); printf("Dcache hits %10llu %6.2f%%\n", hits, hit_per); printf("Dcache misses %10llu %6.2f%%\n", misses, miss_per); printf("Dcache total %10llu\n", hits + misses); } void dcache_free() { free(dcache.table[0]); free(dcache.table); free(dcache.replace); dcache.table = NULL; } void dcache_cleanup() { dcache_stats(); dcache_free(); } void compress_trace_addresses(TraceAddr *trace_addr) { AddrRec *ptr; char *comp_ptr = trace_addr->compressed_ptr; uint32_t prev_addr = trace_addr->prev_addr; uint64_t prev_time = trace_addr->prev_time; AddrRec *last = &trace_addr->buffer[kMaxNumAddrs]; for (ptr = trace_addr->buffer; ptr != last; ++ptr) { if (comp_ptr >= trace_addr->high_water_ptr) { uint32_t size = comp_ptr - trace_addr->compressed; fwrite(trace_addr->compressed, sizeof(char), size, trace_addr->fstream); comp_ptr = trace_addr->compressed; } int addr_diff = ptr->addr - prev_addr; uint64_t time_diff = ptr->time - prev_time; prev_addr = ptr->addr; prev_time = ptr->time; comp_ptr = varint_encode_signed(addr_diff, comp_ptr); comp_ptr = varint_encode(time_diff, comp_ptr); } trace_addr->compressed_ptr = comp_ptr; trace_addr->prev_addr = prev_addr; trace_addr->prev_time = prev_time; } // This function is called by the generated code to simulate // a dcache load access. void dcache_load(uint32_t addr) { int ii; int ways = dcache.ways; uint32_t cache_addr = addr >> dcache.log_line_size; int row = cache_addr & dcache.addr_mask; //printf("ld %lld 0x%x\n", sim_time, addr); for (ii = 0; ii < ways; ++ii) { if (cache_addr == dcache.table[row][ii]) { dcache.load_hits += 1; #if 0 printf("dcache load hit addr: 0x%x cache_addr: 0x%x row %d way %d\n", addr, cache_addr, row, ii); #endif // If we are tracing all addresses, then include this in the trace. if (trace_all_addr) { AddrRec *next = trace_load.next; next->addr = addr; next->time = sim_time; next += 1; if (next == &trace_load.buffer[kMaxNumAddrs]) { // Compress the trace compress_trace_addresses(&trace_load); next = &trace_load.buffer[0]; } trace_load.next = next; } return; } } // This is a cache miss #if 0 if (ftrace_debug) fprintf(ftrace_debug, "t%lld %08x\n", sim_time, addr); #endif if (trace_load.fstream) { AddrRec *next = trace_load.next; next->addr = addr; next->time = sim_time; next += 1; if (next == &trace_load.buffer[kMaxNumAddrs]) { // Compress the trace compress_trace_addresses(&trace_load); next = &trace_load.buffer[0]; } trace_load.next = next; } dcache.load_misses += 1; sim_time += dcache.load_miss_penalty; // Pick a way to replace int way; if (dcache.replace_policy == kPolicyRoundRobin) { // Round robin replacement policy way = dcache.replace[row]; int next_way = way + 1; if (next_way == dcache.ways) next_way = 0; dcache.replace[row] = next_way; } else { // Random replacement policy way = dcache.next_way; dcache.next_way += 1; if (dcache.next_way >= dcache.ways) dcache.next_way = 0; // Every 13 replacements, add an extra increment to the next way dcache.extra_increment_counter += 1; if (dcache.extra_increment_counter == 13) { dcache.extra_increment_counter = 0; dcache.next_way += 1; if (dcache.next_way >= dcache.ways) dcache.next_way = 0; } } #if 0 printf("dcache load miss addr: 0x%x cache_addr: 0x%x row %d replacing way %d\n", addr, cache_addr, row, way); #endif dcache.table[row][way] = cache_addr; } // This function is called by the generated code to simulate // a dcache store access. void dcache_store(uint32_t addr, uint32_t val) { // Check for a write to a magic address (this is a virtual address) //printf("st %lld 0x%08x val 0x%x\n", sim_time, addr, val); if ((addr & kMagicBaseMask) == kMagicBaseAddr) { uint32_t offset = addr & kMagicOffsetMask; switch (offset) { case kMethodTraceEnterOffset: trace_interpreted_method(val, kMethodEnter); break; case kMethodTraceExitOffset: trace_interpreted_method(val, kMethodExit); break; case kMethodTraceExceptionOffset: trace_interpreted_method(val, kMethodException); break; } } int ii; int ways = dcache.ways; uint32_t cache_addr = addr >> dcache.log_line_size; int row = cache_addr & dcache.addr_mask; for (ii = 0; ii < ways; ++ii) { if (cache_addr == dcache.table[row][ii]) { dcache.store_hits += 1; #if 0 printf("dcache store hit addr: 0x%x cache_addr: 0x%x row %d way %d\n", addr, cache_addr, row, ii); #endif // If we are tracing all addresses, then include this in the trace. if (trace_all_addr) { AddrRec *next = trace_store.next; next->addr = addr; next->time = sim_time; next += 1; if (next == &trace_store.buffer[kMaxNumAddrs]) { // Compress the trace compress_trace_addresses(&trace_store); next = &trace_store.buffer[0]; } trace_store.next = next; } return; } } // This is a cache miss #if 0 printf("dcache store miss addr: 0x%x cache_addr: 0x%x row %d\n", addr, cache_addr, row); #endif #if 0 if (ftrace_debug) fprintf(ftrace_debug, "t%lld %08x\n", sim_time, addr); #endif if (trace_store.fstream) { AddrRec *next = trace_store.next; next->addr = addr; next->time = sim_time; next += 1; if (next == &trace_store.buffer[kMaxNumAddrs]) { // Compress the trace compress_trace_addresses(&trace_store); next = &trace_store.buffer[0]; } trace_store.next = next; } dcache.store_misses += 1; sim_time += dcache.store_miss_penalty; // Assume no write-allocate for now } // This function is called by the generated code to simulate // a dcache load and store (swp) access. void dcache_swp(uint32_t addr) { dcache_load(addr); dcache_store(addr, 0); }