filename | src/sh4/cache.c |
changeset | 939:6f2302afeb89 |
prev | 933:880c37bb1909 |
next | 946:d41ee7994db7 |
author | nkeynes |
date | Sat Jan 03 03:30:26 2009 +0000 (15 years ago) |
branch | lxdream-mem |
permissions | -rw-r--r-- |
last change | MMU work-in-progress * Move SDRAM out into separate sdram.c * Move all page-table management into mmu.c * Convert UTLB management to use the new page-tables * Rip out all calls to mmu_vma_to_phys_* and replace with direct access |
file | annotate | diff | log | raw |
nkeynes@931 | 1 | /** |
nkeynes@931 | 2 | * $Id$ |
nkeynes@939 | 3 | * Implements the on-chip operand cache, instruction cache, and store queue. |
nkeynes@931 | 4 | * |
nkeynes@931 | 5 | * Copyright (c) 2008 Nathan Keynes. |
nkeynes@931 | 6 | * |
nkeynes@931 | 7 | * This program is free software; you can redistribute it and/or modify |
nkeynes@931 | 8 | * it under the terms of the GNU General Public License as published by |
nkeynes@931 | 9 | * the Free Software Foundation; either version 2 of the License, or |
nkeynes@931 | 10 | * (at your option) any later version. |
nkeynes@931 | 11 | * |
nkeynes@931 | 12 | * This program is distributed in the hope that it will be useful, |
nkeynes@931 | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
nkeynes@931 | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
nkeynes@931 | 15 | * GNU General Public License for more details. |
nkeynes@931 | 16 | */ |
nkeynes@931 | 17 | |
nkeynes@931 | 18 | #define MODULE sh4_module |
nkeynes@931 | 19 | |
nkeynes@931 | 20 | #include <string.h> |
nkeynes@931 | 21 | #include "dream.h" |
nkeynes@931 | 22 | #include "mem.h" |
nkeynes@931 | 23 | #include "mmio.h" |
nkeynes@931 | 24 | #include "sh4/sh4core.h" |
nkeynes@931 | 25 | #include "sh4/sh4mmio.h" |
nkeynes@931 | 26 | #include "sh4/xltcache.h" |
nkeynes@931 | 27 | |
nkeynes@931 | 28 | #define OCRAM_START (0x7C000000>>LXDREAM_PAGE_BITS) |
nkeynes@931 | 29 | #define OCRAM_MID (0x7E000000>>LXDREAM_PAGE_BITS) |
nkeynes@931 | 30 | #define OCRAM_END (0x80000000>>LXDREAM_PAGE_BITS) |
nkeynes@931 | 31 | |
nkeynes@931 | 32 | #define CACHE_VALID 1 |
nkeynes@931 | 33 | #define CACHE_DIRTY 2 |
nkeynes@931 | 34 | |
nkeynes@931 | 35 | #define ICACHE_ENTRY_COUNT 256 |
nkeynes@931 | 36 | #define OCACHE_ENTRY_COUNT 512 |
nkeynes@931 | 37 | |
nkeynes@931 | 38 | struct cache_line { |
nkeynes@931 | 39 | uint32_t key; // Fast address match - bits 5..28 for valid entry, -1 for invalid entry |
nkeynes@931 | 40 | uint32_t tag; // tag + flags value from the address field |
nkeynes@931 | 41 | }; |
nkeynes@931 | 42 | |
nkeynes@931 | 43 | |
nkeynes@931 | 44 | static struct cache_line ccn_icache[ICACHE_ENTRY_COUNT]; |
nkeynes@931 | 45 | static struct cache_line ccn_ocache[OCACHE_ENTRY_COUNT]; |
nkeynes@931 | 46 | static unsigned char ccn_icache_data[ICACHE_ENTRY_COUNT*32]; |
nkeynes@931 | 47 | static unsigned char ccn_ocache_data[OCACHE_ENTRY_COUNT*32]; |
nkeynes@931 | 48 | |
nkeynes@931 | 49 | |
nkeynes@931 | 50 | /*********************** General module requirements ********************/ |
nkeynes@931 | 51 | |
nkeynes@931 | 52 | void CCN_save_state( FILE *f ) |
nkeynes@931 | 53 | { |
nkeynes@931 | 54 | fwrite( &ccn_icache, sizeof(ccn_icache), 1, f ); |
nkeynes@931 | 55 | fwrite( &ccn_icache_data, sizeof(ccn_icache_data), 1, f ); |
nkeynes@931 | 56 | fwrite( &ccn_ocache, sizeof(ccn_ocache), 1, f); |
nkeynes@931 | 57 | fwrite( &ccn_ocache_data, sizeof(ccn_ocache_data), 1, f); |
nkeynes@931 | 58 | } |
nkeynes@931 | 59 | |
nkeynes@931 | 60 | int CCN_load_state( FILE *f ) |
nkeynes@931 | 61 | { |
nkeynes@931 | 62 | /* Setup the cache mode according to the saved register value |
nkeynes@931 | 63 | * (mem_load runs before this point to load all MMIO data) |
nkeynes@931 | 64 | */ |
nkeynes@931 | 65 | mmio_region_MMU_write( CCR, MMIO_READ(MMU, CCR) ); |
nkeynes@931 | 66 | |
nkeynes@931 | 67 | if( fread( &ccn_icache, sizeof(ccn_icache), 1, f ) != 1 ) { |
nkeynes@931 | 68 | return 1; |
nkeynes@931 | 69 | } |
nkeynes@931 | 70 | if( fread( &ccn_icache_data, sizeof(ccn_icache_data), 1, f ) != 1 ) { |
nkeynes@931 | 71 | return 1; |
nkeynes@931 | 72 | } |
nkeynes@931 | 73 | if( fread( &ccn_ocache, sizeof(ccn_ocache), 1, f ) != 1 ) { |
nkeynes@931 | 74 | return 1; |
nkeynes@931 | 75 | } |
nkeynes@931 | 76 | if( fread( &ccn_ocache_data, sizeof(ccn_ocache_data), 1, f ) != 1 ) { |
nkeynes@931 | 77 | return 1; |
nkeynes@931 | 78 | } |
nkeynes@931 | 79 | return 0; |
nkeynes@931 | 80 | } |
nkeynes@931 | 81 | |
nkeynes@931 | 82 | |
nkeynes@931 | 83 | /************************* OCRAM memory address space ************************/ |
nkeynes@931 | 84 | |
nkeynes@931 | 85 | #define OCRAMPAGE0 (&ccn_ocache_data[4096]) /* Lines 128-255 */ |
nkeynes@931 | 86 | #define OCRAMPAGE1 (&ccn_ocache_data[12288]) /* Lines 384-511 */ |
nkeynes@931 | 87 | |
nkeynes@931 | 88 | static int32_t FASTCALL ocram_page0_read_long( sh4addr_t addr ) |
nkeynes@931 | 89 | { |
nkeynes@931 | 90 | return *((int32_t *)(OCRAMPAGE0 + (addr&0x00000FFF))); |
nkeynes@931 | 91 | } |
nkeynes@931 | 92 | static int32_t FASTCALL ocram_page0_read_word( sh4addr_t addr ) |
nkeynes@931 | 93 | { |
nkeynes@931 | 94 | return SIGNEXT16(*((int16_t *)(OCRAMPAGE0 + (addr&0x00000FFF)))); |
nkeynes@931 | 95 | } |
nkeynes@931 | 96 | static int32_t FASTCALL ocram_page0_read_byte( sh4addr_t addr ) |
nkeynes@931 | 97 | { |
nkeynes@931 | 98 | return SIGNEXT8(*((int16_t *)(OCRAMPAGE0 + (addr&0x00000FFF)))); |
nkeynes@931 | 99 | } |
nkeynes@931 | 100 | static void FASTCALL ocram_page0_write_long( sh4addr_t addr, uint32_t val ) |
nkeynes@931 | 101 | { |
nkeynes@931 | 102 | *(uint32_t *)(OCRAMPAGE0 + (addr&0x00000FFF)) = val; |
nkeynes@931 | 103 | } |
nkeynes@931 | 104 | static void FASTCALL ocram_page0_write_word( sh4addr_t addr, uint32_t val ) |
nkeynes@931 | 105 | { |
nkeynes@931 | 106 | *(uint16_t *)(OCRAMPAGE0 + (addr&0x00000FFF)) = (uint16_t)val; |
nkeynes@931 | 107 | } |
nkeynes@931 | 108 | static void FASTCALL ocram_page0_write_byte( sh4addr_t addr, uint32_t val ) |
nkeynes@931 | 109 | { |
nkeynes@931 | 110 | *(uint8_t *)(OCRAMPAGE0 + (addr&0x00000FFF)) = (uint8_t)val; |
nkeynes@931 | 111 | } |
nkeynes@931 | 112 | static void FASTCALL ocram_page0_read_burst( unsigned char *dest, sh4addr_t addr ) |
nkeynes@931 | 113 | { |
nkeynes@931 | 114 | memcpy( dest, OCRAMPAGE0+(addr&0x00000FFF), 32 ); |
nkeynes@931 | 115 | } |
nkeynes@931 | 116 | static void FASTCALL ocram_page0_write_burst( sh4addr_t addr, unsigned char *src ) |
nkeynes@931 | 117 | { |
nkeynes@931 | 118 | memcpy( OCRAMPAGE0+(addr&0x00000FFF), src, 32 ); |
nkeynes@931 | 119 | } |
nkeynes@931 | 120 | |
nkeynes@931 | 121 | struct mem_region_fn mem_region_ocram_page0 = { |
nkeynes@931 | 122 | ocram_page0_read_long, ocram_page0_write_long, |
nkeynes@931 | 123 | ocram_page0_read_word, ocram_page0_write_word, |
nkeynes@931 | 124 | ocram_page0_read_byte, ocram_page0_write_byte, |
nkeynes@931 | 125 | ocram_page0_read_burst, ocram_page0_write_burst }; |
nkeynes@931 | 126 | |
nkeynes@931 | 127 | static int32_t FASTCALL ocram_page1_read_long( sh4addr_t addr ) |
nkeynes@931 | 128 | { |
nkeynes@931 | 129 | return *((int32_t *)(OCRAMPAGE1 + (addr&0x00000FFF))); |
nkeynes@931 | 130 | } |
nkeynes@931 | 131 | static int32_t FASTCALL ocram_page1_read_word( sh4addr_t addr ) |
nkeynes@931 | 132 | { |
nkeynes@931 | 133 | return SIGNEXT16(*((int16_t *)(OCRAMPAGE1 + (addr&0x00000FFF)))); |
nkeynes@931 | 134 | } |
nkeynes@931 | 135 | static int32_t FASTCALL ocram_page1_read_byte( sh4addr_t addr ) |
nkeynes@931 | 136 | { |
nkeynes@931 | 137 | return SIGNEXT8(*((int16_t *)(OCRAMPAGE1 + (addr&0x00000FFF)))); |
nkeynes@931 | 138 | } |
nkeynes@931 | 139 | static void FASTCALL ocram_page1_write_long( sh4addr_t addr, uint32_t val ) |
nkeynes@931 | 140 | { |
nkeynes@931 | 141 | *(uint32_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = val; |
nkeynes@931 | 142 | } |
nkeynes@931 | 143 | static void FASTCALL ocram_page1_write_word( sh4addr_t addr, uint32_t val ) |
nkeynes@931 | 144 | { |
nkeynes@931 | 145 | *(uint16_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = (uint16_t)val; |
nkeynes@931 | 146 | } |
nkeynes@931 | 147 | static void FASTCALL ocram_page1_write_byte( sh4addr_t addr, uint32_t val ) |
nkeynes@931 | 148 | { |
nkeynes@931 | 149 | *(uint8_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = (uint8_t)val; |
nkeynes@931 | 150 | } |
nkeynes@931 | 151 | static void FASTCALL ocram_page1_read_burst( unsigned char *dest, sh4addr_t addr ) |
nkeynes@931 | 152 | { |
nkeynes@931 | 153 | memcpy( dest, OCRAMPAGE1+(addr&0x00000FFF), 32 ); |
nkeynes@931 | 154 | } |
nkeynes@931 | 155 | static void FASTCALL ocram_page1_write_burst( sh4addr_t addr, unsigned char *src ) |
nkeynes@931 | 156 | { |
nkeynes@931 | 157 | memcpy( OCRAMPAGE1+(addr&0x00000FFF), src, 32 ); |
nkeynes@931 | 158 | } |
nkeynes@931 | 159 | |
nkeynes@931 | 160 | struct mem_region_fn mem_region_ocram_page1 = { |
nkeynes@931 | 161 | ocram_page1_read_long, ocram_page1_write_long, |
nkeynes@931 | 162 | ocram_page1_read_word, ocram_page1_write_word, |
nkeynes@931 | 163 | ocram_page1_read_byte, ocram_page1_write_byte, |
nkeynes@931 | 164 | ocram_page1_read_burst, ocram_page1_write_burst }; |
nkeynes@931 | 165 | |
nkeynes@933 | 166 | /************************** Cache direct access ******************************/ |
nkeynes@933 | 167 | |
nkeynes@933 | 168 | static int32_t ccn_icache_addr_read( sh4addr_t addr ) |
nkeynes@933 | 169 | { |
nkeynes@933 | 170 | int entry = (addr & 0x00001FE0); |
nkeynes@933 | 171 | return ccn_icache[entry>>5].tag; |
nkeynes@933 | 172 | } |
nkeynes@933 | 173 | |
nkeynes@933 | 174 | static void ccn_icache_addr_write( sh4addr_t addr, uint32_t val ) |
nkeynes@933 | 175 | { |
nkeynes@933 | 176 | int entry = (addr & 0x00003FE0); |
nkeynes@933 | 177 | struct cache_line *line = &ccn_ocache[entry>>5]; |
nkeynes@933 | 178 | if( addr & 0x08 ) { // Associative |
nkeynes@933 | 179 | /* FIXME: implement this - requires ITLB lookups, with exception in case of multi-hit */ |
nkeynes@933 | 180 | } else { |
nkeynes@933 | 181 | line->tag = val & 0x1FFFFC01; |
nkeynes@933 | 182 | line->key = (val & 0x1FFFFC00)|(entry & 0x000003E0); |
nkeynes@933 | 183 | } |
nkeynes@933 | 184 | } |
nkeynes@933 | 185 | |
nkeynes@933 | 186 | struct mem_region_fn p4_region_icache_addr = { |
nkeynes@933 | 187 | ccn_icache_addr_read, ccn_icache_addr_write, |
nkeynes@933 | 188 | unmapped_read_long, unmapped_write_long, |
nkeynes@933 | 189 | unmapped_read_long, unmapped_write_long, |
nkeynes@933 | 190 | unmapped_read_burst, unmapped_write_burst }; |
nkeynes@933 | 191 | |
nkeynes@933 | 192 | |
nkeynes@933 | 193 | static int32_t ccn_icache_data_read( sh4addr_t addr ) |
nkeynes@933 | 194 | { |
nkeynes@933 | 195 | int entry = (addr & 0x00001FFC); |
nkeynes@933 | 196 | return *(uint32_t *)&ccn_icache_data[entry]; |
nkeynes@933 | 197 | } |
nkeynes@933 | 198 | |
nkeynes@933 | 199 | static void ccn_icache_data_write( sh4addr_t addr, uint32_t val ) |
nkeynes@933 | 200 | { |
nkeynes@933 | 201 | int entry = (addr & 0x00001FFC); |
nkeynes@933 | 202 | *(uint32_t *)&ccn_icache_data[entry] = val; |
nkeynes@933 | 203 | } |
nkeynes@933 | 204 | |
nkeynes@933 | 205 | struct mem_region_fn p4_region_icache_data = { |
nkeynes@933 | 206 | ccn_icache_data_read, ccn_icache_data_write, |
nkeynes@933 | 207 | unmapped_read_long, unmapped_write_long, |
nkeynes@933 | 208 | unmapped_read_long, unmapped_write_long, |
nkeynes@933 | 209 | unmapped_read_burst, unmapped_write_burst }; |
nkeynes@933 | 210 | |
nkeynes@933 | 211 | |
nkeynes@933 | 212 | static int32_t ccn_ocache_addr_read( sh4addr_t addr ) |
nkeynes@933 | 213 | { |
nkeynes@933 | 214 | int entry = (addr & 0x00003FE0); |
nkeynes@933 | 215 | return ccn_ocache[entry>>5].tag; |
nkeynes@933 | 216 | } |
nkeynes@933 | 217 | |
nkeynes@933 | 218 | static void ccn_ocache_addr_write( sh4addr_t addr, uint32_t val ) |
nkeynes@933 | 219 | { |
nkeynes@933 | 220 | int entry = (addr & 0x00003FE0); |
nkeynes@933 | 221 | struct cache_line *line = &ccn_ocache[entry>>5]; |
nkeynes@933 | 222 | if( addr & 0x08 ) { // Associative |
nkeynes@933 | 223 | } else { |
nkeynes@933 | 224 | if( (line->tag & (CACHE_VALID|CACHE_DIRTY)) == (CACHE_VALID|CACHE_DIRTY) ) { |
nkeynes@933 | 225 | char *cache_data = &ccn_ocache_data[entry&0x00003FE0]; |
nkeynes@933 | 226 | // Cache line is dirty - writeback. |
nkeynes@933 | 227 | ext_address_space[line->tag>>12]->write_burst(line->key, cache_data); |
nkeynes@933 | 228 | } |
nkeynes@933 | 229 | line->tag = val & 0x1FFFFC03; |
nkeynes@933 | 230 | line->key = (val & 0x1FFFFC00)|(entry & 0x000003E0); |
nkeynes@933 | 231 | } |
nkeynes@933 | 232 | } |
nkeynes@933 | 233 | |
nkeynes@933 | 234 | struct mem_region_fn p4_region_ocache_addr = { |
nkeynes@933 | 235 | ccn_ocache_addr_read, ccn_ocache_addr_write, |
nkeynes@933 | 236 | unmapped_read_long, unmapped_write_long, |
nkeynes@933 | 237 | unmapped_read_long, unmapped_write_long, |
nkeynes@933 | 238 | unmapped_read_burst, unmapped_write_burst }; |
nkeynes@933 | 239 | |
nkeynes@933 | 240 | |
nkeynes@933 | 241 | static int32_t ccn_ocache_data_read( sh4addr_t addr ) |
nkeynes@933 | 242 | { |
nkeynes@933 | 243 | int entry = (addr & 0x00003FFC); |
nkeynes@933 | 244 | return *(uint32_t *)&ccn_ocache_data[entry]; |
nkeynes@933 | 245 | } |
nkeynes@933 | 246 | |
nkeynes@933 | 247 | static void ccn_ocache_data_write( sh4addr_t addr, uint32_t val ) |
nkeynes@933 | 248 | { |
nkeynes@933 | 249 | int entry = (addr & 0x00003FFC); |
nkeynes@933 | 250 | *(uint32_t *)&ccn_ocache_data[entry] = val; |
nkeynes@933 | 251 | } |
nkeynes@933 | 252 | |
nkeynes@933 | 253 | struct mem_region_fn p4_region_ocache_data = { |
nkeynes@933 | 254 | ccn_ocache_data_read, ccn_ocache_data_write, |
nkeynes@933 | 255 | unmapped_read_long, unmapped_write_long, |
nkeynes@933 | 256 | unmapped_read_long, unmapped_write_long, |
nkeynes@933 | 257 | unmapped_read_burst, unmapped_write_burst }; |
nkeynes@933 | 258 | |
nkeynes@933 | 259 | |
nkeynes@931 | 260 | /****************** Cache control *********************/ |
nkeynes@931 | 261 | |
nkeynes@931 | 262 | void CCN_set_cache_control( int reg ) |
nkeynes@931 | 263 | { |
nkeynes@931 | 264 | uint32_t i; |
nkeynes@933 | 265 | |
nkeynes@933 | 266 | if( reg & CCR_ICI ) { /* icache invalidate */ |
nkeynes@933 | 267 | for( i=0; i<ICACHE_ENTRY_COUNT; i++ ) { |
nkeynes@933 | 268 | ccn_icache[i].tag &= ~CACHE_VALID; |
nkeynes@933 | 269 | } |
nkeynes@933 | 270 | } |
nkeynes@933 | 271 | |
nkeynes@933 | 272 | if( reg & CCR_OCI ) { /* ocache invalidate */ |
nkeynes@933 | 273 | for( i=0; i<OCACHE_ENTRY_COUNT; i++ ) { |
nkeynes@933 | 274 | ccn_ocache[i].tag &= ~(CACHE_VALID|CACHE_DIRTY); |
nkeynes@933 | 275 | } |
nkeynes@933 | 276 | } |
nkeynes@933 | 277 | |
nkeynes@931 | 278 | switch( reg & (CCR_OIX|CCR_ORA|CCR_OCE) ) { |
nkeynes@931 | 279 | case MEM_OC_INDEX0: /* OIX=0 */ |
nkeynes@931 | 280 | for( i=OCRAM_START; i<OCRAM_END; i+=4 ) { |
nkeynes@931 | 281 | sh4_address_space[i] = &mem_region_ocram_page0; |
nkeynes@931 | 282 | sh4_address_space[i+1] = &mem_region_ocram_page0; |
nkeynes@931 | 283 | sh4_address_space[i+2] = &mem_region_ocram_page1; |
nkeynes@931 | 284 | sh4_address_space[i+3] = &mem_region_ocram_page1; |
nkeynes@931 | 285 | } |
nkeynes@931 | 286 | break; |
nkeynes@931 | 287 | case MEM_OC_INDEX1: /* OIX=1 */ |
nkeynes@931 | 288 | for( i=OCRAM_START; i<OCRAM_MID; i++ ) |
nkeynes@931 | 289 | sh4_address_space[i] = &mem_region_ocram_page0; |
nkeynes@931 | 290 | for( i=OCRAM_MID; i<OCRAM_END; i++ ) |
nkeynes@931 | 291 | sh4_address_space[i] = &mem_region_ocram_page1; |
nkeynes@931 | 292 | break; |
nkeynes@931 | 293 | default: /* disabled */ |
nkeynes@931 | 294 | for( i=OCRAM_START; i<OCRAM_END; i++ ) |
nkeynes@931 | 295 | sh4_address_space[i] = &mem_region_unmapped; |
nkeynes@931 | 296 | break; |
nkeynes@931 | 297 | } |
nkeynes@939 | 298 | } |
nkeynes@939 | 299 | |
nkeynes@939 | 300 | |
nkeynes@939 | 301 | /***** Store-queue (considered part of the cache by the SH7750 manual) ******/ |
nkeynes@939 | 302 | static void FASTCALL p4_storequeue_write_long( sh4addr_t addr, uint32_t val ) |
nkeynes@939 | 303 | { |
nkeynes@939 | 304 | sh4r.store_queue[(addr>>2)&0xF] = val; |
nkeynes@939 | 305 | } |
nkeynes@939 | 306 | static int32_t FASTCALL p4_storequeue_read_long( sh4addr_t addr ) |
nkeynes@939 | 307 | { |
nkeynes@939 | 308 | return sh4r.store_queue[(addr>>2)&0xF]; |
nkeynes@939 | 309 | } |
nkeynes@939 | 310 | |
nkeynes@939 | 311 | struct mem_region_fn p4_region_storequeue = { |
nkeynes@939 | 312 | p4_storequeue_read_long, p4_storequeue_write_long, |
nkeynes@939 | 313 | p4_storequeue_read_long, p4_storequeue_write_long, |
nkeynes@939 | 314 | p4_storequeue_read_long, p4_storequeue_write_long, |
nkeynes@939 | 315 | unmapped_read_burst, unmapped_write_burst }; // No burst access. |
.