#include "config.h" #include "tlsf.h" #include "support.h" #include "cache.h" //#include "ps_protocol.h" #include "M68k.h" #include union CacheLine { uint128_t cl_128; uint64_t cl_64[2]; uint32_t cl_32[4]; uint16_t cl_16[8]; uint8_t cl_8[16]; }; #define D(x) /* x */ #define F_VALID 0x80 #define F_DIRTY0 0x01 #define F_DIRTY1 0x02 #define F_DIRTY2 0x04 #define F_DIRTY3 0x08 #if CACHE_WAY_COUNT > 32 #error CACHE_WAY_COUNT shall be less or equal 32 #endif #define GET_SET(x) (((x) >> 4) & (CACHE_SET_COUNT - 1)) #define GET_TAG(x) ((x) & ~(CACHE_SET_COUNT * 16 - 1)) struct Cache { union CacheLine c_Lines[CACHE_SET_COUNT][CACHE_WAY_COUNT]; uint32_t c_Tags[CACHE_SET_COUNT][CACHE_WAY_COUNT]; uint8_t c_Flags[CACHE_SET_COUNT][CACHE_WAY_COUNT]; uint32_t c_WaySelect[CACHE_SET_COUNT]; }; struct Cache *IC; struct Cache *DC; void cache_mark_hit(struct Cache *cache, int set, int way) { /* Mark the way as accessed */ cache->c_WaySelect[set] |= 1 << way; /* If all ways are marked as accessed, clear them all and set the current one again */ if (cache->c_WaySelect[set] == (0xffffffff >> (32 - CACHE_WAY_COUNT))) { cache->c_WaySelect[set] = 1 << way; } } int cache_get_way(struct Cache *cache, int set) { return __builtin_ffs(~cache->c_WaySelect[set]) - 1; } void cache_setup() { (kprintf("[CACHE] Cache setup. Cache sizeof=%lu\n", sizeof(struct Cache))); (kprintf("[CACHE] Way count: %d, Set count: %d\n", CACHE_WAY_COUNT, CACHE_SET_COUNT)); IC = (struct Cache *)tlsf_malloc(tlsf, sizeof(struct Cache)); DC = (struct Cache *)tlsf_malloc(tlsf, sizeof(struct Cache)); for(int i=0; i < CACHE_SET_COUNT; i++) { IC->c_WaySelect[i] = 0; DC->c_WaySelect[i] = 0; for (int j=0; j < CACHE_WAY_COUNT; j++) { IC->c_Flags[i][j] = 0; IC->c_Tags[i][j] = 0; } } (kprintf("[CACHE] ICache @ %p, DCache @ %p\n", IC, DC)); } void cache_invalidate_all(enum CacheType type) { struct Cache *cache = (type == ICACHE) ? IC : DC; D(kprintf("[CACHE] %cCache invalidate all\n", type == ICACHE ? 'I':'D')); for (int i=0; i < CACHE_SET_COUNT; i++) { cache->c_WaySelect[i] = 0; for (int j=0; j < CACHE_WAY_COUNT; j++) { cache->c_Flags[i][j] = 0; } } } void cache_flush_all(enum CacheType type) { if (type == ICACHE) cache_invalidate_all(type); struct Cache *cache = (type == ICACHE) ? IC : DC; D(kprintf("[CACHE] %cCache flush all\n", type == ICACHE ? 'I':'D')); for (int set=0; set < CACHE_SET_COUNT; set++) { cache->c_WaySelect[set] = 0; for (int way=0; way < CACHE_WAY_COUNT; way++) { if ((cache->c_Flags[set][way] & F_VALID) && (cache->c_Flags[set][way] & (F_DIRTY0 | F_DIRTY1 | F_DIRTY2 | F_DIRTY3)) != 0) { uint32_t line_address = cache->c_Tags[set][way] + (set << 4); D(kprintf("[CACHE] cache line was previously used, tag=%08x, address=%08x, flushing\n", cache->c_Tags[set][way], line_address)); /* Write cache back if the lines are dirty */ if (cache->c_Flags[set][way] & F_DIRTY0) *(uint32_t *)(uintptr_t)(line_address) = cache->c_Lines[set][way].cl_32[0]; if (cache->c_Flags[set][way] & F_DIRTY1) *(uint32_t *)(uintptr_t)(line_address + 4) = cache->c_Lines[set][way].cl_32[1]; if (cache->c_Flags[set][way] & F_DIRTY2) *(uint32_t *)(uintptr_t)(line_address + 8) = cache->c_Lines[set][way].cl_32[2]; if (cache->c_Flags[set][way] & F_DIRTY3) *(uint32_t *)(uintptr_t)(line_address + 12) = cache->c_Lines[set][way].cl_32[3]; } cache->c_Flags[set][way] = 0; } } } void cache_invalidate_line(enum CacheType type, uint32_t address) { struct Cache *cache = (type == ICACHE) ? IC : DC; D(kprintf("[CACHE] %cCache invalidate line (%08lx)\n", type == ICACHE ? 'I':'D', address)); const uint32_t tag = GET_TAG(address); const uint32_t set = GET_SET(address); for (unsigned i=0; i < CACHE_WAY_COUNT; i++) { if ((cache->c_Tags[set][i] == tag) && (cache->c_Flags[set][i] & F_VALID)) { cache->c_Flags[set][i] = 0; cache->c_WaySelect[set] &= ~(1 << i); } } } void cache_invalidate_range(enum CacheType type, uint32_t address, uint32_t len) { const uint64_t start = address & 0xfffffff0; const uint64_t end = (address + len - 1) & 0xfffffff0; for (uint64_t addr = start; addr <= end; addr += 16) { cache_invalidate_line(type, addr); } } void cache_flush_line(enum CacheType type, uint32_t address) { if (type == ICACHE) cache_invalidate_line(type, address); struct Cache *cache = (type == ICACHE) ? IC : DC; D(kprintf("[CACHE] %cCache invalidate line (%08lx)\n", type == ICACHE ? 'I':'D', address)); const uint32_t tag = GET_TAG(address); const uint32_t set = GET_SET(address); for (unsigned i=0; i < CACHE_WAY_COUNT; i++) { if ((cache->c_Tags[set][i] == tag) && (cache->c_Flags[set][i] & F_VALID)) { uint32_t line_address = cache->c_Tags[set][i] + (set << 4); /* Write cache back if the lines are dirty */ if (cache->c_Flags[set][i] & F_DIRTY0) *(uint32_t *)(uintptr_t)(line_address) = cache->c_Lines[set][i].cl_32[0]; if (cache->c_Flags[set][i] & F_DIRTY1) *(uint32_t *)(uintptr_t)(line_address + 4) = cache->c_Lines[set][i].cl_32[1]; if (cache->c_Flags[set][i] & F_DIRTY2) *(uint32_t *)(uintptr_t)(line_address + 8) = cache->c_Lines[set][i].cl_32[2]; if (cache->c_Flags[set][i] & F_DIRTY3) *(uint32_t *)(uintptr_t)(line_address + 12) = cache->c_Lines[set][i].cl_32[3]; cache->c_Flags[set][i] = 0; cache->c_WaySelect[set] &= ~(1 << i); } } } uint128_t cache_read_128(enum CacheType type, uint32_t address) { struct Cache *cache = (type == ICACHE) ? IC : DC; const uint32_t tag = GET_TAG(address); const uint32_t set = GET_SET(address); int way = -1; if (address >= 0x01000000) return *(uint128_t *)(uintptr_t)address; #if 0 if (type == DCACHE) return 0; /* Exit if given cache is disabled in CACR */ uint32_t cacr; asm volatile("mov %w0, v31.s[0]":"=r"(cacr)); if (type == ICACHE && (cacr & CACR_IE) == 0) return 0; else if (type == DCACHE && (cacr & CACR_DE) == 0) return 0; /* Only ROM is cacheable! */ if (!((address >= 0x00e00000 && address < 0x00e80000) || (address >= 0x00f00000 && address < 0x01000000))) return 0; #endif D(kprintf("[CACHE] %cCache read_128(%08lx)\n", type == ICACHE ? 'I':'D', address)); D(kprintf("[CACHE] set = %u, ", set)); D(kprintf("tag = %08x, ", tag)); if ((address & 15) != 0) { uint128_t data; data.hi = cache_read_64(type, address); data.lo = cache_read_64(type, address + 8); return data; } /* Check the given set and find the cache way with mathing tag and falid flag set */ for (unsigned i=0; i < CACHE_WAY_COUNT; i++) { if ((cache->c_Tags[set][i] == tag) && (cache->c_Flags[set][i] & F_VALID)) { way = i; break; } } D(kprintf("way = %d\n", way)); /* There was no valid cache line matching the tag, get one */ if (way == -1) { way = cache_get_way(cache, set); D(kprintf("[CACHE] allocated way = %d\n", way)); if (cache->c_Flags[set][way] & F_VALID) { uint32_t line_address = cache->c_Tags[set][way] + (set << 4); D(kprintf("[CACHE] cache line was previously used, tag=%08x, address=%08x, flushing\n", cache->c_Tags[set][way], line_address)); /* Write cache back if the lines are dirty */ if (cache->c_Flags[set][way] & F_DIRTY0) *(uint32_t*)(uintptr_t)line_address = cache->c_Lines[set][way].cl_32[0]; if (cache->c_Flags[set][way] & F_DIRTY1) *(uint32_t*)(uintptr_t)(line_address + 4) = cache->c_Lines[set][way].cl_32[1]; if (cache->c_Flags[set][way] & F_DIRTY2) *(uint32_t*)(uintptr_t)(line_address + 8) = cache->c_Lines[set][way].cl_32[2]; if (cache->c_Flags[set][way] & F_DIRTY3) *(uint32_t*)(uintptr_t)(line_address + 12) = cache->c_Lines[set][way].cl_32[3]; } /* Load the cache line */ D(kprintf("[CACHE] loading line from address %08x\n", address & 0xfffffff0)); cache->c_Lines[set][way].cl_128 = *(uint128_t *)(uintptr_t)(address & 0xfffffff0); /* Update tag, mark page as valid */ cache->c_Flags[set][way] = F_VALID; cache->c_Tags[set][way] = tag; } /* Mark LRUm */ cache_mark_hit(cache, set, way); uint128_t data = cache->c_Lines[set][way].cl_128; D(kprintf("[CACHE] => %016lx%016lx\n", data.hi, data.lo)); return data; } uint64_t cache_read_64(enum CacheType type, uint32_t address) { struct Cache *cache = (type == ICACHE) ? IC : DC; const uint32_t tag = GET_TAG(address); const uint32_t set = GET_SET(address); int way = -1; if (address >= 0x01000000) return *(uint64_t *)(uintptr_t)address; #if 0 if (type == DCACHE) return 0; /* Exit if given cache is disabled in CACR */ uint32_t cacr; asm volatile("mov %w0, v31.s[0]":"=r"(cacr)); if (type == ICACHE && (cacr & CACR_IE) == 0) return 0; else if (type == DCACHE && (cacr & CACR_DE) == 0) return 0; /* Only ROM is cacheable! */ if (!((address >= 0x00e00000 && address < 0x00e80000) || (address >= 0x00f00000 && address < 0x01000000))) return 0; #endif D(kprintf("[CACHE] %cCache read_64(%08lx)\n", type == ICACHE ? 'I':'D', address)); D(kprintf("[CACHE] set = %u, ", set)); D(kprintf("tag = %08x, ", tag)); if ((address & 15) > 8) { uint64_t data = 0; switch (address & 15) { case 9: data = cache_read_64(type, address - 1) << 8; data |= cache_read_8(type, address + 7); break; case 10: data = cache_read_64(type, address - 2) << 16; data |= cache_read_16(type, address + 6); break; case 11: data = cache_read_64(type, address - 3) << 24; data |= cache_read_32(type, address + 5) >> 8; break; case 12: data = (uint64_t)cache_read_32(type, address) << 32; data |= cache_read_32(type, address + 4); break; case 13: data = (uint64_t)cache_read_16(type, address) << 40; data |= cache_read_64(type, address + 3) >> 24; break; case 14: data = (uint64_t)cache_read_16(type, address) << 48; data |= cache_read_64(type, address + 2) >> 16; break; case 15: data = (uint64_t)cache_read_8(type, address) << 56; data |= cache_read_64(type, address + 1) >> 8; break; default: break; } return data; } /* Check the given set and find the cache way with mathing tag and falid flag set */ for (unsigned i=0; i < CACHE_WAY_COUNT; i++) { if ((cache->c_Tags[set][i] == tag) && (cache->c_Flags[set][i] & F_VALID)) { way = i; break; } } D(kprintf("way = %d\n", way)); /* There was no valid cache line matching the tag, get one */ if (way == -1) { way = cache_get_way(cache, set); D(kprintf("[CACHE] allocated way = %d\n", way)); if (cache->c_Flags[set][way] & F_VALID) { uint32_t line_address = cache->c_Tags[set][way] + (set << 4); D(kprintf("[CACHE] cache line was previously used, tag=%08x, address=%08x, flushing\n", cache->c_Tags[set * CACHE_WAY_COUNT + way], line_address)); /* Write cache back if the lines are dirty */ if (cache->c_Flags[set][way] & F_DIRTY0) *(uint32_t*)(uintptr_t)line_address = cache->c_Lines[set][way].cl_32[0]; if (cache->c_Flags[set][way] & F_DIRTY1) *(uint32_t*)(uintptr_t)(line_address + 4) = cache->c_Lines[set][way].cl_32[1]; if (cache->c_Flags[set][way] & F_DIRTY2) *(uint32_t*)(uintptr_t)(line_address + 8) = cache->c_Lines[set][way].cl_32[2]; if (cache->c_Flags[set][way] & F_DIRTY3) *(uint32_t*)(uintptr_t)(line_address + 12) = cache->c_Lines[set][way].cl_32[3]; } /* Load the cache line */ D(kprintf("[CACHE] loading line from address %08x\n", address & 0xfffffff0)); cache->c_Lines[set][way].cl_128 = *(uint128_t *)(uintptr_t)(address & 0xfffffff0); /* Update tag, mark page as valid */ cache->c_Flags[set][way] = F_VALID; cache->c_Tags[set][way] = tag; } /* Mark LRUm */ cache_mark_hit(cache, set, way); uint64_t data = *(uint64_t *)&cache->c_Lines[set][way].cl_8[address & 15]; D(kprintf("[CACHE] => %016lx\n", data)); return data; } uint32_t cache_read_32(enum CacheType type, uint32_t address) { struct Cache *cache = (type == ICACHE) ? IC : DC; const uint32_t tag = GET_TAG(address); const uint32_t set = GET_SET(address); int way = -1; if (address >= 0x01000000) return *(uint32_t *)(uintptr_t)address; #if 0 if (type == DCACHE) return 0; /* Exit if given cache is disabled in CACR */ uint32_t cacr; asm volatile("mov %w0, v31.s[0]":"=r"(cacr)); if (type == ICACHE && (cacr & CACR_IE) == 0) return 0; else if (type == DCACHE && (cacr & CACR_DE) == 0) return 0; /* Only ROM is cacheable! */ if (!((address >= 0x00e00000 && address < 0x00e80000) || (address >= 0x00f00000 && address < 0x01000000))) return 0; #endif D(kprintf("[CACHE] %cCache read_32(%08lx)\n", type == ICACHE ? 'I':'D', address)); D(kprintf("[CACHE] set = %u, ", set)); D(kprintf("tag = %08x, ", tag)); if ((address & 15) > 12) { uint32_t data; switch (address & 15) { case 13: data = cache_read_32(type, address - 1) << 8; data |= cache_read_8(type, address + 3); break; case 14: data = cache_read_16(type, address) << 16; data |= cache_read_16(type, address + 2); break; case 15: data = cache_read_8(type, address) << 24; data |= cache_read_32(type, address + 1) >> 8; break; default: break; } return data; } /* Check the given set and find the cache way with mathing tag and falid flag set */ for (unsigned i=0; i < CACHE_WAY_COUNT; i++) { if ((cache->c_Tags[set][i] == tag) && (cache->c_Flags[set][i] & F_VALID)) { way = i; break; } } D(kprintf("way = %d\n", way)); /* There was no valid cache line matching the tag, get one */ if (way == -1) { way = cache_get_way(cache, set); D(kprintf("[CACHE] allocated way = %d\n", way)); if (cache->c_Flags[set][way] & F_VALID) { uint32_t line_address = cache->c_Tags[set][way] + (set << 4); D(kprintf("[CACHE] cache line was previously used, tag=%08x, address=%08x, flushing\n", cache->c_Tags[set][way], line_address)); /* Write cache back if the lines are dirty */ if (cache->c_Flags[set][way] & F_DIRTY0) *(uint32_t*)(uintptr_t)line_address = cache->c_Lines[set][way].cl_32[0]; if (cache->c_Flags[set][way] & F_DIRTY1) *(uint32_t*)(uintptr_t)(line_address + 4) = cache->c_Lines[set][way].cl_32[1]; if (cache->c_Flags[set][way] & F_DIRTY2) *(uint32_t*)(uintptr_t)(line_address + 8) = cache->c_Lines[set][way].cl_32[2]; if (cache->c_Flags[set][way] & F_DIRTY3) *(uint32_t*)(uintptr_t)(line_address + 12) = cache->c_Lines[set][way].cl_32[3]; } /* Load the cache line */ D(kprintf("[CACHE] loading line from address %08x\n", address & 0xfffffff0)); cache->c_Lines[set][way].cl_128 = *(uint128_t *)(uintptr_t)(address & 0xfffffff0); /* Update tag, mark page as valid */ cache->c_Flags[set][way] = F_VALID; cache->c_Tags[set][way] = tag; } /* Mark LRUm */ cache_mark_hit(cache, set, way); uint32_t data = *(uint32_t *)&cache->c_Lines[set][way].cl_8[address & 15]; D(kprintf("[CACHE] => %08x\n", data)); return data; } uint16_t cache_read_16(enum CacheType type, uint32_t address) { struct Cache *cache = (type == ICACHE) ? IC : DC; const uint32_t tag = GET_TAG(address); const uint32_t set = GET_SET(address); int way = -1; if (address >= 0x01000000) return *(uint16_t *)(uintptr_t)address; #if 0 if (type == DCACHE) return 0; /* Exit if given cache is disabled in CACR */ uint32_t cacr; asm volatile("mov %w0, v31.s[0]":"=r"(cacr)); if (type == ICACHE && (cacr & CACR_IE) == 0) return 0; else if (type == DCACHE && (cacr & CACR_DE) == 0) return 0; /* Only ROM is cacheable! */ if (!((address >= 0x00e00000 && address < 0x00e80000) || (address >= 0x00f00000 && address < 0x01000000))) return 0; #endif D(kprintf("[CACHE] %cCache read_16(%08lx)\n", type == ICACHE ? 'I':'D', address)); D(kprintf("[CACHE] set = %u, ", set)); D(kprintf("tag = %08x, ", tag)); if ((address & 15) > 14) { uint16_t data = cache_read_8(type, address) << 8; data |= cache_read_8(type, address + 1); return data; } /* Check the given set and find the cache way with mathing tag and falid flag set */ for (unsigned i=0; i < CACHE_WAY_COUNT; i++) { if ((cache->c_Tags[set][i] == tag) && (cache->c_Flags[set][i] & F_VALID)) { way = i; break; } } D(kprintf("way = %d\n", way)); /* There was no valid cache line matching the tag, get one */ if (way == -1) { way = cache_get_way(cache, set); D(kprintf("[CACHE] allocated way = %d\n", way)); if (cache->c_Flags[set][way] & F_VALID) { uint32_t line_address = cache->c_Tags[set][way] + (set << 4); D(kprintf("[CACHE] cache line was previously used, tag=%08x, address=%08x, flushing\n", cache->c_Tags[set][way], line_address)); /* Write cache back if the lines are dirty */ if (cache->c_Flags[set][way] & F_DIRTY0) *(uint32_t*)(uintptr_t)line_address = cache->c_Lines[set][way].cl_32[0]; if (cache->c_Flags[set][way] & F_DIRTY1) *(uint32_t*)(uintptr_t)(line_address + 4) = cache->c_Lines[set][way].cl_32[1]; if (cache->c_Flags[set][way] & F_DIRTY2) *(uint32_t*)(uintptr_t)(line_address + 8) = cache->c_Lines[set][way].cl_32[2]; if (cache->c_Flags[set][way] & F_DIRTY3) *(uint32_t*)(uintptr_t)(line_address + 12) = cache->c_Lines[set][way].cl_32[3]; } /* Load the cache line */ //kprintf("[CACHE] loading line from address %08x\n", address & 0xfffffff0); cache->c_Lines[set][way].cl_128 = *(uint128_t *)(uintptr_t)(address & 0xfffffff0); /* Update tag, mark page as valid */ cache->c_Flags[set][way] = F_VALID; cache->c_Tags[set][way] = tag; } /* Mark LRUm */ cache_mark_hit(cache, set, way); uint16_t data = *(uint16_t *)(void *)&cache->c_Lines[set][way].cl_8[address & 15]; D(kprintf("[CACHE] => %04x\n", data)); return data; } uint8_t cache_read_8(enum CacheType type, uint32_t address) { struct Cache *cache = (type == ICACHE) ? IC : DC; const uint32_t tag = GET_TAG(address); const uint32_t set = GET_SET(address); int way = -1; if (address >= 0x01000000) return *(uint8_t *)(uintptr_t)address; #if 0 if (type == DCACHE) return 0; /* Exit if given cache is disabled in CACR */ uint32_t cacr; asm volatile("mov %w0, v31.s[0]":"=r"(cacr)); if (type == ICACHE && (cacr & CACR_IE) == 0) return 0; else if (type == DCACHE && (cacr & CACR_DE) == 0) return 0; /* Only ROM is cacheable! */ if (!((address >= 0x00e00000 && address < 0x00e80000) || (address >= 0x00f00000 && address < 0x01000000))) return 0; #endif D(kprintf("[CACHE] %cCache read_8(%08lx)\n", type == ICACHE ? 'I':'D', address)); D(kprintf("[CACHE] set = %u, ", set)); D(kprintf("tag = %08x, ", tag)); /* Check the given set and find the cache way with mathing tag and falid flag set */ for (unsigned i=0; i < CACHE_WAY_COUNT; i++) { if ((cache->c_Tags[set][i] == tag) && (cache->c_Flags[set][i] & F_VALID)) { way = i; break; } } D(kprintf("way = %d\n", way)); /* There was no valid cache line matching the tag, get one */ if (way == -1) { way = cache_get_way(cache, set); D(kprintf("[CACHE] allocated way = %d\n", way)); if (cache->c_Flags[set][way] & F_VALID) { uint32_t line_address = cache->c_Tags[set][way] + (set << 4); D(kprintf("[CACHE] cache line was previously used, tag=%08x, address=%08x, flushing\n", cache->c_Tags[set][way], line_address)); /* Write cache back if the lines are dirty */ if (cache->c_Flags[set][way] & F_DIRTY0) *(uint32_t*)(uintptr_t)line_address = cache->c_Lines[set][way].cl_32[0]; if (cache->c_Flags[set][way] & F_DIRTY1) *(uint32_t*)(uintptr_t)(line_address + 4) = cache->c_Lines[set][way].cl_32[1]; if (cache->c_Flags[set][way] & F_DIRTY2) *(uint32_t*)(uintptr_t)(line_address + 8) = cache->c_Lines[set][way].cl_32[2]; if (cache->c_Flags[set][way] & F_DIRTY3) *(uint32_t*)(uintptr_t)(line_address + 12) = cache->c_Lines[set][way].cl_32[3]; } /* Load the cache line */ D(kprintf("[CACHE] loading line from address %08x\n", address & 0xfffffff0)); cache->c_Lines[set][way].cl_128 = *(uint128_t *)(uintptr_t)(address & 0xfffffff0); /* Update tag, mark page as valid */ cache->c_Flags[set][way] = F_VALID; cache->c_Tags[set][way] = tag; } /* Mark LRUm */ cache_mark_hit(cache, set, way); uint8_t data = cache->c_Lines[set][way].cl_8[address & 15]; D(kprintf("[CACHE] => %02x\n", data)); return data; } int cache_write_128(enum CacheType type, uint32_t address, uint128_t data, uint8_t write_back) { struct Cache *cache = (type == ICACHE) ? IC : DC; const uint32_t tag = GET_TAG(address); const uint32_t set = GET_SET(address); int way = -1; #if 0 return 0; /* Exit if given cache is disabled in CACR */ uint32_t cacr; asm volatile("mov %w0, v31.s[0]":"=r"(cacr)); if (type == ICACHE) return 0; else if (type == DCACHE && (cacr & CACR_DE) == 0) return 0; /* Only ROM is cacheable! */ if (!((address >= 0x00e00000 && address < 0x00e80000) || (address >= 0x00f00000 && address < 0x01000000))) return 0; #endif D(kprintf("[CACHE] %cCache write_128(%08lx, %016lx%016lx, %x)\n", type == ICACHE ? 'I':'D', address, data.hi, data.lo, write_back)); D(kprintf("[CACHE] set = %u, ", set)); D(kprintf("tag = %08x, ", tag)); if ((address & 15) != 0) { D(kprintf("[CACHE] Accessed data spans over two cache lines, aborting\n")); return 0; } /* Check the given set and find the cache way with mathing tag and falid flag set */ for (unsigned i=0; i < CACHE_WAY_COUNT; i++) { if ((cache->c_Tags[set][i] == tag) && (cache->c_Flags[set][i] & F_VALID)) { way = i; break; } } D(kprintf("way = %d\n", way)); /* There was no valid cache line matching the tag */ if (way == -1) { /* Write-through cache does not load cache line, performs direct write instead */ if (write_back == 0) return 0; /* Write-back cache loads whole cache line and updates the value */ way = cache_get_way(cache, set); D(kprintf("[CACHE] allocated way = %d\n", way)); if (cache->c_Flags[set][way] & F_VALID) { uint32_t line_address = cache->c_Tags[set][way] + (set << 4); D(kprintf("[CACHE] cache line was previously used, tag=%08x, address=%08x, flushing\n", cache->c_Tags[set][way], line_address)); /* Write cache back if the lines are dirty */ if (cache->c_Flags[set][way] & F_DIRTY0) *(uint32_t*)(uintptr_t)line_address = cache->c_Lines[set][way].cl_32[0]; if (cache->c_Flags[set][way] & F_DIRTY1) *(uint32_t*)(uintptr_t)(line_address + 4) = cache->c_Lines[set][way].cl_32[1]; if (cache->c_Flags[set][way] & F_DIRTY2) *(uint32_t*)(uintptr_t)(line_address + 8) = cache->c_Lines[set][way].cl_32[2]; if (cache->c_Flags[set][way] & F_DIRTY3) *(uint32_t*)(uintptr_t)(line_address + 12) = cache->c_Lines[set][way].cl_32[3]; } /* No need to load cache line here, it gets overwritten and marked dirty immediately */ /* Update tag, mark page as valid */ cache->c_Flags[set][way] = F_VALID; cache->c_Tags[set][way] = tag; } /* Mark LRUm */ cache_mark_hit(cache, set, way); cache->c_Lines[set][way].cl_128 = data; if (write_back) { /* Write-back cache marks the portion of cache line as dirty */ cache->c_Flags[set][way] |= F_DIRTY0 | F_DIRTY1 | F_DIRTY2 | F_DIRTY3; } else { /* Write-through cache performs direct write to memory, cache line remains not-dirty */ *(uint128_t *)(uintptr_t)address = data; } return 1; } int cache_write_64(enum CacheType type, uint32_t address, uint64_t data, uint8_t write_back) { struct Cache *cache = (type == ICACHE) ? IC : DC; const uint32_t tag = GET_TAG(address); const uint32_t set = GET_SET(address); int way = -1; #if 0 return 0; /* Exit if given cache is disabled in CACR */ uint32_t cacr; asm volatile("mov %w0, v31.s[0]":"=r"(cacr)); if (type == ICACHE) return 0; else if (type == DCACHE && (cacr & CACR_DE) == 0) return 0; /* Only ROM is cacheable! */ if (!((address >= 0x00e00000 && address < 0x00e80000) || (address >= 0x00f00000 && address < 0x01000000))) return 0; #endif D(kprintf("[CACHE] %cCache write_64(%08lx, %016lx, %x)\n", type == ICACHE ? 'I':'D', address, data, write_back)); D(kprintf("[CACHE] set = %u, ", set)); D(kprintf("tag = %08x, ", tag)); if ((address & 15) > 8) { D(kprintf("[CACHE] Accessed data spans over two cache lines, aborting\n")); return 0; } /* Check the given set and find the cache way with mathing tag and falid flag set */ for (unsigned i=0; i < CACHE_WAY_COUNT; i++) { if ((cache->c_Tags[set][i] == tag) && (cache->c_Flags[set][i] & F_VALID)) { way = i; break; } } D(kprintf("way = %d\n", way)); /* There was no valid cache line matching the tag */ if (way == -1) { /* Write-through cache does not load cache line, performs direct write instead */ if (write_back == 0) return 0; /* Write-back cache loads whole cache line and updates the value */ way = cache_get_way(cache, set); D(kprintf("[CACHE] allocated way = %d\n", way)); if (cache->c_Flags[set][way] & F_VALID) { uint32_t line_address = cache->c_Tags[set][way] + (set << 4); D(kprintf("[CACHE] cache line was previously used, tag=%08x, address=%08x, flushing\n", cache->c_Tags[set][way], line_address)); /* Write cache back if the lines are dirty */ if (cache->c_Flags[set][way] & F_DIRTY0) *(uint32_t*)(uintptr_t)line_address = cache->c_Lines[set][way].cl_32[0]; if (cache->c_Flags[set][way] & F_DIRTY1) *(uint32_t*)(uintptr_t)(line_address + 4) = cache->c_Lines[set][way].cl_32[1]; if (cache->c_Flags[set][way] & F_DIRTY2) *(uint32_t*)(uintptr_t)(line_address + 8) = cache->c_Lines[set][way].cl_32[2]; if (cache->c_Flags[set][way] & F_DIRTY3) *(uint32_t*)(uintptr_t)(line_address + 12) = cache->c_Lines[set][way].cl_32[3]; } /* Load the cache line */ D(kprintf("[CACHE] loading line from address %08x\n", address & 0xfffffff0)); cache->c_Lines[set][way].cl_128 = *(uint128_t *)(uintptr_t)(address & 0xfffffff0); /* Update tag, mark page as valid */ cache->c_Flags[set][way] = F_VALID; cache->c_Tags[set][way] = tag; } /* Mark LRUm */ cache_mark_hit(cache, set, way); (*(uint64_t *)(void*)&cache->c_Lines[set][way].cl_8[address & 15]) = data; if (write_back) { /* Write-back cache marks the portion of cache line as dirty */ switch((address >> 2) & 3) { case 0: cache->c_Flags[set][way] |= F_DIRTY0 | F_DIRTY1; if ((((address + 7) >> 2) & 3) == 2) cache->c_Flags[set][way] |= F_DIRTY2; break; case 1: cache->c_Flags[set][way] |= F_DIRTY1 | F_DIRTY2; if ((((address + 7) >> 2) & 3) == 3) cache->c_Flags[set][way] |= F_DIRTY3; break; case 2: cache->c_Flags[set][way] |= F_DIRTY2 | F_DIRTY3; break; } } else { /* Write-through cache performs direct write to memory, cache line remains not-dirty */ *(uint64_t *)(uintptr_t)address = data; } return 1; } int cache_write_32(enum CacheType type, uint32_t address, uint32_t data, uint8_t write_back) { struct Cache *cache = (type == ICACHE) ? IC : DC; const uint32_t tag = GET_TAG(address); const uint32_t set = GET_SET(address); int way = -1; #if 0 return 0; /* Exit if given cache is disabled in CACR */ uint32_t cacr; asm volatile("mov %w0, v31.s[0]":"=r"(cacr)); if (type == ICACHE) return 0; else if (type == DCACHE && (cacr & CACR_DE) == 0) return 0; /* Only ROM is cacheable! */ if (!((address >= 0x00e00000 && address < 0x00e80000) || (address >= 0x00f00000 && address < 0x01000000))) return 0; #endif D(kprintf("[CACHE] %cCache write_32(%08lx, %08x, %x)\n", type == ICACHE ? 'I':'D', address, data, write_back)); D(kprintf("[CACHE] set = %u, ", set)); D(kprintf("tag = %08x, ", tag)); if ((address & 15) > 12) { D(kprintf("[CACHE] Accessed data spans over two cache lines, aborting\n")); return 0; } /* Check the given set and find the cache way with mathing tag and falid flag set */ for (unsigned i=0; i < CACHE_WAY_COUNT; i++) { if ((cache->c_Tags[set][i] == tag) && (cache->c_Flags[set][i] & F_VALID)) { way = i; break; } } D(kprintf("way = %d\n", way)); /* There was no valid cache line matching the tag */ if (way == -1) { /* Write-through cache does not load cache line, performs direct write instead */ if (write_back == 0) return 0; /* Write-back cache loads whole cache line and updates the value */ way = cache_get_way(cache, set); D(kprintf("[CACHE] allocated way = %d\n", way)); if (cache->c_Flags[set][way] & F_VALID) { uint32_t line_address = cache->c_Tags[set][way] + (set << 4); D(kprintf("[CACHE] cache line was previously used, tag=%08x, address=%08x, flushing\n", cache->c_Tags[set][way], line_address)); /* Write cache back if the lines are dirty */ if (cache->c_Flags[set][way] & F_DIRTY0) *(uint32_t*)(uintptr_t)line_address = cache->c_Lines[set][way].cl_32[0]; if (cache->c_Flags[set][way] & F_DIRTY1) *(uint32_t*)(uintptr_t)(line_address + 4) = cache->c_Lines[set][way].cl_32[1]; if (cache->c_Flags[set][way] & F_DIRTY2) *(uint32_t*)(uintptr_t)(line_address + 8) = cache->c_Lines[set][way].cl_32[2]; if (cache->c_Flags[set][way] & F_DIRTY3) *(uint32_t*)(uintptr_t)(line_address + 12) = cache->c_Lines[set][way].cl_32[3]; } /* Load the cache line */ D(kprintf("[CACHE] loading line from address %08x\n", address & 0xfffffff0)); cache->c_Lines[set][way].cl_128 = *(uint128_t *)(uintptr_t)(address & 0xfffffff0); /* Update tag, mark page as valid */ cache->c_Flags[set][way] = F_VALID; cache->c_Tags[set][way] = tag; } /* Mark LRUm */ cache_mark_hit(cache, set, way); (*(uint32_t *)(void*)&cache->c_Lines[set][way].cl_8[address & 15]) = data; if (write_back) { /* Write-back cache marks the portion of cache line as dirty */ switch((address >> 2) & 3) { case 0: cache->c_Flags[set][way] |= F_DIRTY0; if ((((address + 3) >> 2) & 3) == 1) cache->c_Flags[set][way] |= F_DIRTY1; break; case 1: cache->c_Flags[set][way] |= F_DIRTY1; if ((((address + 3) >> 2) & 3) == 2) cache->c_Flags[set][way] |= F_DIRTY2; break; case 2: cache->c_Flags[set][way] |= F_DIRTY2; if ((((address + 3) >> 2) & 3) == 3) cache->c_Flags[set][way] |= F_DIRTY3; break; case 3: cache->c_Flags[set][way] |= F_DIRTY3; break; } } else { /* Write-through cache performs direct write to memory, cache line remains not-dirty */ *(uint32_t *)(uintptr_t)address = data; } return 1; } int cache_write_16(enum CacheType type, uint32_t address, uint16_t data, uint8_t write_back) { struct Cache *cache = (type == ICACHE) ? IC : DC; const uint32_t tag = GET_TAG(address); const uint32_t set = GET_SET(address); int way = -1; #if 0 return 0; /* Exit if given cache is disabled in CACR */ uint32_t cacr; asm volatile("mov %w0, v31.s[0]":"=r"(cacr)); if (type == ICACHE) return 0; else if (type == DCACHE && (cacr & CACR_DE) == 0) return 0; /* Only ROM is cacheable! */ if (!((address >= 0x00e00000 && address < 0x00e80000) || (address >= 0x00f00000 && address < 0x01000000))) return 0; #endif D(kprintf("[CACHE] %cCache write_16(%08lx, %04x, %x)\n", type == ICACHE ? 'I':'D', address, data, write_back)); D(kprintf("[CACHE] set = %u, ", set)); D(kprintf("tag = %08x, ", tag)); if ((address & 15) > 14) { D(kprintf("[CACHE] Accessed data spans over two cache lines, aborting\n")); return 0; } /* Check the given set and find the cache way with mathing tag and falid flag set */ for (unsigned i=0; i < CACHE_WAY_COUNT; i++) { if ((cache->c_Tags[set][i] == tag) && (cache->c_Flags[set][i] & F_VALID)) { way = i; break; } } D(kprintf("way = %d\n", way)); /* There was no valid cache line matching the tag */ if (way == -1) { /* Write-through cache does not load cache line, performs direct write instead */ if (write_back == 0) return 0; /* Write-back cache loads whole cache line and updates the value */ way = cache_get_way(cache, set); D(kprintf("[CACHE] allocated way = %d\n", way)); if (cache->c_Flags[set][way] & F_VALID) { uint32_t line_address = cache->c_Tags[set][way] + (set << 4); D(kprintf("[CACHE] cache line was previously used, tag=%08x, address=%08x, flushing\n", cache->c_Tags[set][way], line_address)); /* Write cache back if the lines are dirty */ if (cache->c_Flags[set][way] & F_DIRTY0) *(uint32_t*)(uintptr_t)line_address = cache->c_Lines[set][way].cl_32[0]; if (cache->c_Flags[set][way] & F_DIRTY1) *(uint32_t*)(uintptr_t)(line_address + 4) = cache->c_Lines[set][way].cl_32[1]; if (cache->c_Flags[set][way] & F_DIRTY2) *(uint32_t*)(uintptr_t)(line_address + 8) = cache->c_Lines[set][way].cl_32[2]; if (cache->c_Flags[set][way] & F_DIRTY3) *(uint32_t*)(uintptr_t)(line_address + 12) = cache->c_Lines[set][way].cl_32[3]; } /* Load the cache line */ D(kprintf("[CACHE] loading line from address %08x\n", address & 0xfffffff0)); cache->c_Lines[set][way].cl_128 = *(uint128_t *)(uintptr_t)(address & 0xfffffff0); /* Update tag, mark page as valid */ cache->c_Flags[set][way] = F_VALID; cache->c_Tags[set][way] = tag; } /* Mark LRUm */ cache_mark_hit(cache, set, way); (*(uint16_t *)(void*)&cache->c_Lines[set][way].cl_8[address & 15]) = data; if (write_back) { /* Write-back cache marks the portion of cache line as dirty */ switch((address >> 2) & 3) { case 0: cache->c_Flags[set][way] |= F_DIRTY0; if ((((address + 1) >> 2) & 3) == 1) cache->c_Flags[set][way] |= F_DIRTY1; break; case 1: cache->c_Flags[set][way] |= F_DIRTY1; if ((((address + 1) >> 2) & 3) == 2) cache->c_Flags[set][way] |= F_DIRTY2; break; case 2: cache->c_Flags[set][way] |= F_DIRTY2; if ((((address + 1) >> 2) & 3) == 3) cache->c_Flags[set][way] |= F_DIRTY3; break; case 3: cache->c_Flags[set][way] |= F_DIRTY3; break; } } else { /* Write-through cache performs direct write to memory, cache line remains not-dirty */ *(uint16_t*)(uintptr_t)address = data; } return 1; } int cache_write_8(enum CacheType type, uint32_t address, uint8_t data, uint8_t write_back) { struct Cache *cache = (type == ICACHE) ? IC : DC; const uint32_t tag = GET_TAG(address); const uint32_t set = GET_SET(address); int way = -1; #if 0 return 0; /* Exit if given cache is disabled in CACR */ uint32_t cacr; asm volatile("mov %w0, v31.s[0]":"=r"(cacr)); if (type == ICACHE) return 0; else if (type == DCACHE && (cacr & CACR_DE) == 0) return 0; /* Only ROM is cacheable! */ if (!((address >= 0x00e00000 && address < 0x00e80000) || (address >= 0x00f00000 && address < 0x01000000))) return 0; #endif D(kprintf("[CACHE] %cCache write_8(%08lx, %02x, %x)\n", type == ICACHE ? 'I':'D', address, data, write_back)); D(kprintf("[CACHE] set = %u, ", set)); D(kprintf("tag = %08x, ", tag)); /* Check the given set and find the cache way with mathing tag and falid flag set */ for (unsigned i=0; i < CACHE_WAY_COUNT; i++) { if ((cache->c_Tags[set][i] == tag) && (cache->c_Flags[set][i] & F_VALID)) { way = i; break; } } D(kprintf("way = %d\n", way)); /* There was no valid cache line matching the tag */ if (way == -1) { /* Write-through cache does not load cache line, performs direct write instead */ if (write_back == 0) return 0; /* Write-back cache loads whole cache line and updates the value */ way = cache_get_way(cache, set); D(kprintf("[CACHE] allocated way = %d\n", way)); if (cache->c_Flags[set][way] & F_VALID) { uint32_t line_address = cache->c_Tags[set][way] + (set << 4); D(kprintf("[CACHE] cache line was previously used, tag=%08x, address=%08x, flushing\n", cache->c_Tags[set][way], line_address)); /* Write cache back if the lines are dirty */ if (cache->c_Flags[set][way] & F_DIRTY0) *(uint32_t*)(uintptr_t)line_address = cache->c_Lines[set][way].cl_32[0]; if (cache->c_Flags[set][way] & F_DIRTY1) *(uint32_t*)(uintptr_t)(line_address + 4) = cache->c_Lines[set][way].cl_32[1]; if (cache->c_Flags[set][way] & F_DIRTY2) *(uint32_t*)(uintptr_t)(line_address + 8) = cache->c_Lines[set][way].cl_32[2]; if (cache->c_Flags[set][way] & F_DIRTY3) *(uint32_t*)(uintptr_t)(line_address + 12) = cache->c_Lines[set][way].cl_32[3]; } /* Load the cache line */ D(kprintf("[CACHE] loading line from address %08x\n", address & 0xfffffff0)); cache->c_Lines[set][way].cl_128 = *(uint128_t *)(uintptr_t)(address & 0xfffffff0); /* Update tag, mark page as valid */ cache->c_Flags[set][way] = F_VALID; cache->c_Tags[set][way] = tag; } /* Mark LRUm */ cache_mark_hit(cache, set, way); cache->c_Lines[set][way].cl_8[address & 15] = data; if (write_back) { /* Write-back cache marks the portion of cache line as dirty */ switch((address >> 2) & 3) { case 0: cache->c_Flags[set][way] |= F_DIRTY0; break; case 1: cache->c_Flags[set][way] |= F_DIRTY1; break; case 2: cache->c_Flags[set][way] |= F_DIRTY2; break; case 3: cache->c_Flags[set][way] |= F_DIRTY3; break; } } else { /* Write-through cache performs direct write to memory, cache line remains not-dirty */ *(uint8_t *)(uintptr_t)address = data; } return 1; }