HomeGuidesHybrid C Assembly Cache Simulator

Hybrid C + Assembly Cache Simulator

Published Mar 1, 2023
Updated Jun 10, 2023
1 minutes read

The Engineering Challenge

Evaluate cache policies and memory hierarchy efficiency by replaying address traces at high throughput. The solution required cycle-accurate indexing, fast bitwise tag/set extraction, and low-overhead replacement policy emulation.

The Architecture & Tech Stack

Core Implementation Logic

// sim/cache_sim.c
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
 
#define LINE_SIZE 64
#define SETS 1024
#define WAYS 8
#define OFFSET_BITS 6
#define INDEX_BITS 10  // log2(SETS)
 
typedef struct {
    uint64_t tag;
    uint8_t valid;
    uint64_t last_access; // for LRU
} cache_line_t;
 
static cache_line_t cache[SETS][WAYS];
 
static inline uint64_t addr_to_tag(uint64_t addr) {
    return addr >> (OFFSET_BITS + INDEX_BITS);
}
 
static inline uint32_t addr_to_index(uint64_t addr) {
    // extract index by shifting and masking
    return (addr >> OFFSET_BITS) & ((1u << INDEX_BITS) - 1);
}
 
void access_address(uint64_t addr, uint64_t cycle) {
    uint32_t idx = addr_to_index(addr);
    uint64_t tag = addr_to_tag(addr);
    for (int w = 0; w < WAYS; ++w) {
        if (cache[idx][w].valid && cache[idx][w].tag == tag) {
            cache[idx][w].last_access = cycle;
            return; // hit
        }
    }
    // miss: evict LRU
    int evict = 0;
    uint64_t oldest = UINT64_MAX;
    for (int w = 0; w < WAYS; ++w) {
        if (!cache[idx][w].valid) { evict = w; break; }
        if (cache[idx][w].last_access < oldest) { oldest = cache[idx][w].last_access; evict = w; }
    }
    cache[idx][evict].valid = 1;
    cache[idx][evict].tag = tag;
    cache[idx][evict].last_access = cycle;
}

Assembly-accelerated helper (GCC inline asm) for bit-popcount or warp operations used in analysis:

static inline int fast_popcount64(uint64_t x) {
    int r;
    __asm__("popcnt %1, %0" : "=r"(r) : "r"(x));
    return r;
}

System Impact & Results