Search
lxdream.org :: lxdream/src/sh4/cache.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/cache.c
changeset 953:f4a156508ad1
next968:6fb1481859a4
author nkeynes
date Tue Jan 13 11:56:28 2009 +0000 (13 years ago)
permissions -rw-r--r--
last change Merge lxdream-mem branch back to trunk
file annotate diff log raw
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/sh4/cache.c Tue Jan 13 11:56:28 2009 +0000
1.3 @@ -0,0 +1,360 @@
1.4 +/**
1.5 + * $Id$
1.6 + * Implements the on-chip operand cache, instruction cache, and store queue.
1.7 + *
1.8 + * Copyright (c) 2008 Nathan Keynes.
1.9 + *
1.10 + * This program is free software; you can redistribute it and/or modify
1.11 + * it under the terms of the GNU General Public License as published by
1.12 + * the Free Software Foundation; either version 2 of the License, or
1.13 + * (at your option) any later version.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful,
1.16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.18 + * GNU General Public License for more details.
1.19 + */
1.20 +
1.21 +#define MODULE sh4_module
1.22 +
1.23 +#include <string.h>
1.24 +#include "dream.h"
1.25 +#include "mem.h"
1.26 +#include "mmio.h"
1.27 +#include "sh4/sh4core.h"
1.28 +#include "sh4/sh4mmio.h"
1.29 +#include "sh4/xltcache.h"
1.30 +#include "sh4/mmu.h"
1.31 +
1.32 +#define OCRAM_START (0x7C000000>>LXDREAM_PAGE_BITS)
1.33 +#define OCRAM_MID (0x7E000000>>LXDREAM_PAGE_BITS)
1.34 +#define OCRAM_END (0x80000000>>LXDREAM_PAGE_BITS)
1.35 +
1.36 +#define CACHE_VALID 1
1.37 +#define CACHE_DIRTY 2
1.38 +
1.39 +#define ICACHE_ENTRY_COUNT 256
1.40 +#define OCACHE_ENTRY_COUNT 512
1.41 +
1.42 +struct cache_line {
1.43 + uint32_t key; // Fast address match - bits 5..28 for valid entry, -1 for invalid entry
1.44 + uint32_t tag; // tag + flags value from the address field
1.45 +};
1.46 +
1.47 +
1.48 +static struct cache_line ccn_icache[ICACHE_ENTRY_COUNT];
1.49 +static struct cache_line ccn_ocache[OCACHE_ENTRY_COUNT];
1.50 +static unsigned char ccn_icache_data[ICACHE_ENTRY_COUNT*32];
1.51 +static unsigned char ccn_ocache_data[OCACHE_ENTRY_COUNT*32];
1.52 +
1.53 +
1.54 +/*********************** General module requirements ********************/
1.55 +
1.56 +void CCN_save_state( FILE *f )
1.57 +{
1.58 + fwrite( &ccn_icache, sizeof(ccn_icache), 1, f );
1.59 + fwrite( &ccn_icache_data, sizeof(ccn_icache_data), 1, f );
1.60 + fwrite( &ccn_ocache, sizeof(ccn_ocache), 1, f);
1.61 + fwrite( &ccn_ocache_data, sizeof(ccn_ocache_data), 1, f);
1.62 +}
1.63 +
1.64 +int CCN_load_state( FILE *f )
1.65 +{
1.66 + /* Setup the cache mode according to the saved register value
1.67 + * (mem_load runs before this point to load all MMIO data)
1.68 + */
1.69 + mmio_region_MMU_write( CCR, MMIO_READ(MMU, CCR) );
1.70 +
1.71 + if( fread( &ccn_icache, sizeof(ccn_icache), 1, f ) != 1 ) {
1.72 + return 1;
1.73 + }
1.74 + if( fread( &ccn_icache_data, sizeof(ccn_icache_data), 1, f ) != 1 ) {
1.75 + return 1;
1.76 + }
1.77 + if( fread( &ccn_ocache, sizeof(ccn_ocache), 1, f ) != 1 ) {
1.78 + return 1;
1.79 + }
1.80 + if( fread( &ccn_ocache_data, sizeof(ccn_ocache_data), 1, f ) != 1 ) {
1.81 + return 1;
1.82 + }
1.83 + return 0;
1.84 +}
1.85 +
1.86 +/************************* OCRAM memory address space ************************/
1.87 +
1.88 +#define OCRAMPAGE0 (&ccn_ocache_data[4096]) /* Lines 128-255 */
1.89 +#define OCRAMPAGE1 (&ccn_ocache_data[12288]) /* Lines 384-511 */
1.90 +
1.91 +static int32_t FASTCALL ocram_page0_read_long( sh4addr_t addr )
1.92 +{
1.93 + return *((int32_t *)(OCRAMPAGE0 + (addr&0x00000FFF)));
1.94 +}
1.95 +static int32_t FASTCALL ocram_page0_read_word( sh4addr_t addr )
1.96 +{
1.97 + return SIGNEXT16(*((int16_t *)(OCRAMPAGE0 + (addr&0x00000FFF))));
1.98 +}
1.99 +static int32_t FASTCALL ocram_page0_read_byte( sh4addr_t addr )
1.100 +{
1.101 + return SIGNEXT8(*((int16_t *)(OCRAMPAGE0 + (addr&0x00000FFF))));
1.102 +}
1.103 +static void FASTCALL ocram_page0_write_long( sh4addr_t addr, uint32_t val )
1.104 +{
1.105 + *(uint32_t *)(OCRAMPAGE0 + (addr&0x00000FFF)) = val;
1.106 +}
1.107 +static void FASTCALL ocram_page0_write_word( sh4addr_t addr, uint32_t val )
1.108 +{
1.109 + *(uint16_t *)(OCRAMPAGE0 + (addr&0x00000FFF)) = (uint16_t)val;
1.110 +}
1.111 +static void FASTCALL ocram_page0_write_byte( sh4addr_t addr, uint32_t val )
1.112 +{
1.113 + *(uint8_t *)(OCRAMPAGE0 + (addr&0x00000FFF)) = (uint8_t)val;
1.114 +}
1.115 +static void FASTCALL ocram_page0_read_burst( unsigned char *dest, sh4addr_t addr )
1.116 +{
1.117 + memcpy( dest, OCRAMPAGE0+(addr&0x00000FFF), 32 );
1.118 +}
1.119 +static void FASTCALL ocram_page0_write_burst( sh4addr_t addr, unsigned char *src )
1.120 +{
1.121 + memcpy( OCRAMPAGE0+(addr&0x00000FFF), src, 32 );
1.122 +}
1.123 +
1.124 +struct mem_region_fn mem_region_ocram_page0 = {
1.125 + ocram_page0_read_long, ocram_page0_write_long,
1.126 + ocram_page0_read_word, ocram_page0_write_word,
1.127 + ocram_page0_read_byte, ocram_page0_write_byte,
1.128 + ocram_page0_read_burst, ocram_page0_write_burst,
1.129 + unmapped_prefetch };
1.130 +
1.131 +static int32_t FASTCALL ocram_page1_read_long( sh4addr_t addr )
1.132 +{
1.133 + return *((int32_t *)(OCRAMPAGE1 + (addr&0x00000FFF)));
1.134 +}
1.135 +static int32_t FASTCALL ocram_page1_read_word( sh4addr_t addr )
1.136 +{
1.137 + return SIGNEXT16(*((int16_t *)(OCRAMPAGE1 + (addr&0x00000FFF))));
1.138 +}
1.139 +static int32_t FASTCALL ocram_page1_read_byte( sh4addr_t addr )
1.140 +{
1.141 + return SIGNEXT8(*((int16_t *)(OCRAMPAGE1 + (addr&0x00000FFF))));
1.142 +}
1.143 +static void FASTCALL ocram_page1_write_long( sh4addr_t addr, uint32_t val )
1.144 +{
1.145 + *(uint32_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = val;
1.146 +}
1.147 +static void FASTCALL ocram_page1_write_word( sh4addr_t addr, uint32_t val )
1.148 +{
1.149 + *(uint16_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = (uint16_t)val;
1.150 +}
1.151 +static void FASTCALL ocram_page1_write_byte( sh4addr_t addr, uint32_t val )
1.152 +{
1.153 + *(uint8_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = (uint8_t)val;
1.154 +}
1.155 +static void FASTCALL ocram_page1_read_burst( unsigned char *dest, sh4addr_t addr )
1.156 +{
1.157 + memcpy( dest, OCRAMPAGE1+(addr&0x00000FFF), 32 );
1.158 +}
1.159 +static void FASTCALL ocram_page1_write_burst( sh4addr_t addr, unsigned char *src )
1.160 +{
1.161 + memcpy( OCRAMPAGE1+(addr&0x00000FFF), src, 32 );
1.162 +}
1.163 +
1.164 +struct mem_region_fn mem_region_ocram_page1 = {
1.165 + ocram_page1_read_long, ocram_page1_write_long,
1.166 + ocram_page1_read_word, ocram_page1_write_word,
1.167 + ocram_page1_read_byte, ocram_page1_write_byte,
1.168 + ocram_page1_read_burst, ocram_page1_write_burst,
1.169 + unmapped_prefetch };
1.170 +
1.171 +/************************** Cache direct access ******************************/
1.172 +
1.173 +static int32_t ccn_icache_addr_read( sh4addr_t addr )
1.174 +{
1.175 + int entry = (addr & 0x00001FE0);
1.176 + return ccn_icache[entry>>5].tag;
1.177 +}
1.178 +
1.179 +static void ccn_icache_addr_write( sh4addr_t addr, uint32_t val )
1.180 +{
1.181 + int entry = (addr & 0x00003FE0);
1.182 + struct cache_line *line = &ccn_ocache[entry>>5];
1.183 + if( addr & 0x08 ) { // Associative
1.184 + /* FIXME: implement this - requires ITLB lookups, with exception in case of multi-hit */
1.185 + } else {
1.186 + line->tag = val & 0x1FFFFC01;
1.187 + line->key = (val & 0x1FFFFC00)|(entry & 0x000003E0);
1.188 + }
1.189 +}
1.190 +
1.191 +struct mem_region_fn p4_region_icache_addr = {
1.192 + ccn_icache_addr_read, ccn_icache_addr_write,
1.193 + unmapped_read_long, unmapped_write_long,
1.194 + unmapped_read_long, unmapped_write_long,
1.195 + unmapped_read_burst, unmapped_write_burst,
1.196 + unmapped_prefetch };
1.197 +
1.198 +
1.199 +static int32_t ccn_icache_data_read( sh4addr_t addr )
1.200 +{
1.201 + int entry = (addr & 0x00001FFC);
1.202 + return *(uint32_t *)&ccn_icache_data[entry];
1.203 +}
1.204 +
1.205 +static void ccn_icache_data_write( sh4addr_t addr, uint32_t val )
1.206 +{
1.207 + int entry = (addr & 0x00001FFC);
1.208 + *(uint32_t *)&ccn_icache_data[entry] = val;
1.209 +}
1.210 +
1.211 +struct mem_region_fn p4_region_icache_data = {
1.212 + ccn_icache_data_read, ccn_icache_data_write,
1.213 + unmapped_read_long, unmapped_write_long,
1.214 + unmapped_read_long, unmapped_write_long,
1.215 + unmapped_read_burst, unmapped_write_burst,
1.216 + unmapped_prefetch };
1.217 +
1.218 +
1.219 +static int32_t ccn_ocache_addr_read( sh4addr_t addr )
1.220 +{
1.221 + int entry = (addr & 0x00003FE0);
1.222 + return ccn_ocache[entry>>5].tag;
1.223 +}
1.224 +
1.225 +static void ccn_ocache_addr_write( sh4addr_t addr, uint32_t val )
1.226 +{
1.227 + int entry = (addr & 0x00003FE0);
1.228 + struct cache_line *line = &ccn_ocache[entry>>5];
1.229 + if( addr & 0x08 ) { // Associative
1.230 + } else {
1.231 + if( (line->tag & (CACHE_VALID|CACHE_DIRTY)) == (CACHE_VALID|CACHE_DIRTY) ) {
1.232 + char *cache_data = &ccn_ocache_data[entry&0x00003FE0];
1.233 + // Cache line is dirty - writeback.
1.234 + ext_address_space[line->tag>>12]->write_burst(line->key, cache_data);
1.235 + }
1.236 + line->tag = val & 0x1FFFFC03;
1.237 + line->key = (val & 0x1FFFFC00)|(entry & 0x000003E0);
1.238 + }
1.239 +}
1.240 +
1.241 +struct mem_region_fn p4_region_ocache_addr = {
1.242 + ccn_ocache_addr_read, ccn_ocache_addr_write,
1.243 + unmapped_read_long, unmapped_write_long,
1.244 + unmapped_read_long, unmapped_write_long,
1.245 + unmapped_read_burst, unmapped_write_burst,
1.246 + unmapped_prefetch };
1.247 +
1.248 +
1.249 +static int32_t ccn_ocache_data_read( sh4addr_t addr )
1.250 +{
1.251 + int entry = (addr & 0x00003FFC);
1.252 + return *(uint32_t *)&ccn_ocache_data[entry];
1.253 +}
1.254 +
1.255 +static void ccn_ocache_data_write( sh4addr_t addr, uint32_t val )
1.256 +{
1.257 + int entry = (addr & 0x00003FFC);
1.258 + *(uint32_t *)&ccn_ocache_data[entry] = val;
1.259 +}
1.260 +
1.261 +struct mem_region_fn p4_region_ocache_data = {
1.262 + ccn_ocache_data_read, ccn_ocache_data_write,
1.263 + unmapped_read_long, unmapped_write_long,
1.264 + unmapped_read_long, unmapped_write_long,
1.265 + unmapped_read_burst, unmapped_write_burst,
1.266 + unmapped_prefetch };
1.267 +
1.268 +
1.269 +/****************** Cache control *********************/
1.270 +
1.271 +void CCN_set_cache_control( int reg )
1.272 +{
1.273 + uint32_t i;
1.274 +
1.275 + if( reg & CCR_ICI ) { /* icache invalidate */
1.276 + for( i=0; i<ICACHE_ENTRY_COUNT; i++ ) {
1.277 + ccn_icache[i].tag &= ~CACHE_VALID;
1.278 + }
1.279 + }
1.280 +
1.281 + if( reg & CCR_OCI ) { /* ocache invalidate */
1.282 + for( i=0; i<OCACHE_ENTRY_COUNT; i++ ) {
1.283 + ccn_ocache[i].tag &= ~(CACHE_VALID|CACHE_DIRTY);
1.284 + }
1.285 + }
1.286 +
1.287 + switch( reg & (CCR_OIX|CCR_ORA|CCR_OCE) ) {
1.288 + case MEM_OC_INDEX0: /* OIX=0 */
1.289 + for( i=OCRAM_START; i<OCRAM_END; i+=4 ) {
1.290 + sh4_address_space[i] = &mem_region_ocram_page0;
1.291 + sh4_address_space[i+1] = &mem_region_ocram_page0;
1.292 + sh4_address_space[i+2] = &mem_region_ocram_page1;
1.293 + sh4_address_space[i+3] = &mem_region_ocram_page1;
1.294 + }
1.295 + break;
1.296 + case MEM_OC_INDEX1: /* OIX=1 */
1.297 + for( i=OCRAM_START; i<OCRAM_MID; i++ )
1.298 + sh4_address_space[i] = &mem_region_ocram_page0;
1.299 + for( i=OCRAM_MID; i<OCRAM_END; i++ )
1.300 + sh4_address_space[i] = &mem_region_ocram_page1;
1.301 + break;
1.302 + default: /* disabled */
1.303 + for( i=OCRAM_START; i<OCRAM_END; i++ )
1.304 + sh4_address_space[i] = &mem_region_unmapped;
1.305 + break;
1.306 + }
1.307 +}
1.308 +
1.309 +/**
1.310 + * Prefetch for non-storequeue regions
1.311 + */
1.312 +void FASTCALL ccn_prefetch( sh4addr_t addr )
1.313 +{
1.314 +
1.315 +}
1.316 +
1.317 +/**
1.318 + * Prefetch for non-cached regions. Oddly enough, this does nothing whatsoever.
1.319 + */
1.320 +void FASTCALL ccn_uncached_prefetch( sh4addr_t addr )
1.321 +{
1.322 +
1.323 +}
1.324 +/********************************* Store-queue *******************************/
1.325 +/*
1.326 + * The storequeue is strictly speaking part of the cache, but most of
1.327 + * the complexity is actually around its addressing (ie in the MMU). The
1.328 + * methods here can assume we've already passed SQMD protection and the TLB
1.329 + * lookups (where appropriate).
1.330 + */
1.331 +void FASTCALL ccn_storequeue_write_long( sh4addr_t addr, uint32_t val )
1.332 +{
1.333 + sh4r.store_queue[(addr>>2)&0xF] = val;
1.334 +}
1.335 +int32_t FASTCALL ccn_storequeue_read_long( sh4addr_t addr )
1.336 +{
1.337 + return sh4r.store_queue[(addr>>2)&0xF];
1.338 +}
1.339 +
1.340 +/**
1.341 + * Variant used when tlb is disabled - address will be the original prefetch
1.342 + * address (ie 0xE0001234). Due to the way the SQ addressing is done, it can't
1.343 + * be hardcoded on 4K page boundaries, so we manually decode it here.
1.344 + */
1.345 +void FASTCALL ccn_storequeue_prefetch( sh4addr_t addr )
1.346 +{
1.347 + int queue = (addr&0x20)>>2;
1.348 + sh4ptr_t src = (sh4ptr_t)&sh4r.store_queue[queue];
1.349 + uint32_t hi = MMIO_READ( MMU, QACR0 + (queue>>1)) << 24;
1.350 + sh4addr_t target = (addr&0x03FFFFE0) | hi;
1.351 + ext_address_space[target>>12]->write_burst( target, src );
1.352 +}
1.353 +
1.354 +/**
1.355 + * Variant used when tlb is enabled - address in this case is already
1.356 + * mapped to the external target address.
1.357 + */
1.358 +void FASTCALL ccn_storequeue_prefetch_tlb( sh4addr_t addr )
1.359 +{
1.360 + int queue = (addr&0x20)>>2;
1.361 + sh4ptr_t src = (sh4ptr_t)&sh4r.store_queue[queue];
1.362 + ext_address_space[addr>>12]->write_burst( (addr & 0x1FFFFFE0), src );
1.363 +}
.