Search
lxdream.org :: lxdream/src/sh4/cache.c
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
nkeynes@953
     1
/**
nkeynes@953
     2
 * $Id$
nkeynes@953
     3
 * Implements the on-chip operand cache, instruction cache, and store queue.
nkeynes@953
     4
 *
nkeynes@953
     5
 * Copyright (c) 2008 Nathan Keynes.
nkeynes@953
     6
 *
nkeynes@953
     7
 * This program is free software; you can redistribute it and/or modify
nkeynes@953
     8
 * it under the terms of the GNU General Public License as published by
nkeynes@953
     9
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@953
    10
 * (at your option) any later version.
nkeynes@953
    11
 *
nkeynes@953
    12
 * This program is distributed in the hope that it will be useful,
nkeynes@953
    13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@953
    14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@953
    15
 * GNU General Public License for more details.
nkeynes@953
    16
 */
nkeynes@953
    17
nkeynes@953
    18
#define MODULE sh4_module
nkeynes@953
    19
nkeynes@953
    20
#include <string.h>
nkeynes@953
    21
#include "dream.h"
nkeynes@953
    22
#include "mem.h"
nkeynes@953
    23
#include "mmio.h"
nkeynes@953
    24
#include "sh4/sh4core.h"
nkeynes@953
    25
#include "sh4/sh4mmio.h"
nkeynes@953
    26
#include "sh4/xltcache.h"
nkeynes@953
    27
#include "sh4/mmu.h"
nkeynes@953
    28
nkeynes@953
    29
#define OCRAM_START (0x7C000000>>LXDREAM_PAGE_BITS)
nkeynes@953
    30
#define OCRAM_MID   (0x7E000000>>LXDREAM_PAGE_BITS)
nkeynes@953
    31
#define OCRAM_END   (0x80000000>>LXDREAM_PAGE_BITS)
nkeynes@953
    32
nkeynes@953
    33
#define CACHE_VALID 1
nkeynes@953
    34
#define CACHE_DIRTY 2
nkeynes@953
    35
nkeynes@953
    36
#define ICACHE_ENTRY_COUNT 256
nkeynes@953
    37
#define OCACHE_ENTRY_COUNT 512
nkeynes@953
    38
nkeynes@953
    39
struct cache_line {
nkeynes@953
    40
    uint32_t key;  // Fast address match - bits 5..28 for valid entry, -1 for invalid entry
nkeynes@953
    41
    uint32_t tag;  // tag + flags value from the address field
nkeynes@953
    42
};    
nkeynes@953
    43
nkeynes@953
    44
nkeynes@953
    45
static struct cache_line ccn_icache[ICACHE_ENTRY_COUNT];
nkeynes@953
    46
static struct cache_line ccn_ocache[OCACHE_ENTRY_COUNT];
nkeynes@953
    47
static unsigned char ccn_icache_data[ICACHE_ENTRY_COUNT*32];
nkeynes@953
    48
static unsigned char ccn_ocache_data[OCACHE_ENTRY_COUNT*32];
nkeynes@953
    49
nkeynes@953
    50
nkeynes@953
    51
/*********************** General module requirements ********************/
nkeynes@953
    52
nkeynes@953
    53
void CCN_save_state( FILE *f )
nkeynes@953
    54
{
nkeynes@953
    55
    fwrite( &ccn_icache, sizeof(ccn_icache), 1, f );
nkeynes@953
    56
    fwrite( &ccn_icache_data, sizeof(ccn_icache_data), 1, f );
nkeynes@953
    57
    fwrite( &ccn_ocache, sizeof(ccn_ocache), 1, f);
nkeynes@953
    58
    fwrite( &ccn_ocache_data, sizeof(ccn_ocache_data), 1, f);
nkeynes@953
    59
}
nkeynes@953
    60
nkeynes@953
    61
int CCN_load_state( FILE *f )
nkeynes@953
    62
{
nkeynes@953
    63
    /* Setup the cache mode according to the saved register value
nkeynes@953
    64
     * (mem_load runs before this point to load all MMIO data)
nkeynes@953
    65
     */
nkeynes@953
    66
    mmio_region_MMU_write( CCR, MMIO_READ(MMU, CCR) );
nkeynes@953
    67
nkeynes@953
    68
    if( fread( &ccn_icache, sizeof(ccn_icache), 1, f ) != 1 ) {
nkeynes@953
    69
        return 1;
nkeynes@953
    70
    }
nkeynes@953
    71
    if( fread( &ccn_icache_data, sizeof(ccn_icache_data), 1, f ) != 1 ) {
nkeynes@953
    72
        return 1;
nkeynes@953
    73
    }
nkeynes@953
    74
    if( fread( &ccn_ocache, sizeof(ccn_ocache), 1, f ) != 1 ) {
nkeynes@953
    75
        return 1;
nkeynes@953
    76
    }
nkeynes@953
    77
    if( fread( &ccn_ocache_data, sizeof(ccn_ocache_data), 1, f ) != 1 ) {
nkeynes@953
    78
        return 1;
nkeynes@953
    79
    }
nkeynes@953
    80
    return 0;
nkeynes@953
    81
}
nkeynes@953
    82
nkeynes@953
    83
/************************* OCRAM memory address space ************************/
nkeynes@953
    84
nkeynes@953
    85
#define OCRAMPAGE0 (&ccn_ocache_data[4096])  /* Lines 128-255 */
nkeynes@953
    86
#define OCRAMPAGE1 (&ccn_ocache_data[12288]) /* Lines 384-511 */
nkeynes@953
    87
nkeynes@953
    88
static int32_t FASTCALL ocram_page0_read_long( sh4addr_t addr )
nkeynes@953
    89
{
nkeynes@953
    90
    return *((int32_t *)(OCRAMPAGE0 + (addr&0x00000FFF)));
nkeynes@953
    91
}
nkeynes@953
    92
static int32_t FASTCALL ocram_page0_read_word( sh4addr_t addr )
nkeynes@953
    93
{
nkeynes@953
    94
    return SIGNEXT16(*((int16_t *)(OCRAMPAGE0 + (addr&0x00000FFF))));
nkeynes@953
    95
}
nkeynes@953
    96
static int32_t FASTCALL ocram_page0_read_byte( sh4addr_t addr )
nkeynes@953
    97
{
nkeynes@953
    98
    return SIGNEXT8(*((int16_t *)(OCRAMPAGE0 + (addr&0x00000FFF))));
nkeynes@953
    99
}
nkeynes@953
   100
static void FASTCALL ocram_page0_write_long( sh4addr_t addr, uint32_t val )
nkeynes@953
   101
{
nkeynes@953
   102
    *(uint32_t *)(OCRAMPAGE0 + (addr&0x00000FFF)) = val;
nkeynes@953
   103
}
nkeynes@953
   104
static void FASTCALL ocram_page0_write_word( sh4addr_t addr, uint32_t val )
nkeynes@953
   105
{
nkeynes@953
   106
    *(uint16_t *)(OCRAMPAGE0 + (addr&0x00000FFF)) = (uint16_t)val;
nkeynes@953
   107
}
nkeynes@953
   108
static void FASTCALL ocram_page0_write_byte( sh4addr_t addr, uint32_t val )
nkeynes@953
   109
{
nkeynes@953
   110
    *(uint8_t *)(OCRAMPAGE0 + (addr&0x00000FFF)) = (uint8_t)val;
nkeynes@953
   111
}
nkeynes@953
   112
static void FASTCALL ocram_page0_read_burst( unsigned char *dest, sh4addr_t addr )
nkeynes@953
   113
{
nkeynes@953
   114
    memcpy( dest, OCRAMPAGE0+(addr&0x00000FFF), 32 );
nkeynes@953
   115
}
nkeynes@953
   116
static void FASTCALL ocram_page0_write_burst( sh4addr_t addr, unsigned char *src )
nkeynes@953
   117
{
nkeynes@953
   118
    memcpy( OCRAMPAGE0+(addr&0x00000FFF), src, 32 );
nkeynes@953
   119
}
nkeynes@953
   120
nkeynes@953
   121
struct mem_region_fn mem_region_ocram_page0 = {
nkeynes@953
   122
        ocram_page0_read_long, ocram_page0_write_long,
nkeynes@953
   123
        ocram_page0_read_word, ocram_page0_write_word,
nkeynes@953
   124
        ocram_page0_read_byte, ocram_page0_write_byte,
nkeynes@953
   125
        ocram_page0_read_burst, ocram_page0_write_burst,
nkeynes@953
   126
        unmapped_prefetch };
nkeynes@953
   127
nkeynes@953
   128
static int32_t FASTCALL ocram_page1_read_long( sh4addr_t addr )
nkeynes@953
   129
{
nkeynes@953
   130
    return *((int32_t *)(OCRAMPAGE1 + (addr&0x00000FFF)));
nkeynes@953
   131
}
nkeynes@953
   132
static int32_t FASTCALL ocram_page1_read_word( sh4addr_t addr )
nkeynes@953
   133
{
nkeynes@953
   134
    return SIGNEXT16(*((int16_t *)(OCRAMPAGE1 + (addr&0x00000FFF))));
nkeynes@953
   135
}
nkeynes@953
   136
static int32_t FASTCALL ocram_page1_read_byte( sh4addr_t addr )
nkeynes@953
   137
{
nkeynes@953
   138
    return SIGNEXT8(*((int16_t *)(OCRAMPAGE1 + (addr&0x00000FFF))));
nkeynes@953
   139
}
nkeynes@953
   140
static void FASTCALL ocram_page1_write_long( sh4addr_t addr, uint32_t val )
nkeynes@953
   141
{
nkeynes@953
   142
    *(uint32_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = val;
nkeynes@953
   143
}
nkeynes@953
   144
static void FASTCALL ocram_page1_write_word( sh4addr_t addr, uint32_t val )
nkeynes@953
   145
{
nkeynes@953
   146
    *(uint16_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = (uint16_t)val;
nkeynes@953
   147
}
nkeynes@953
   148
static void FASTCALL ocram_page1_write_byte( sh4addr_t addr, uint32_t val )
nkeynes@953
   149
{
nkeynes@953
   150
    *(uint8_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = (uint8_t)val;
nkeynes@953
   151
}
nkeynes@953
   152
static void FASTCALL ocram_page1_read_burst( unsigned char *dest, sh4addr_t addr )
nkeynes@953
   153
{
nkeynes@953
   154
    memcpy( dest, OCRAMPAGE1+(addr&0x00000FFF), 32 );
nkeynes@953
   155
}
nkeynes@953
   156
static void FASTCALL ocram_page1_write_burst( sh4addr_t addr, unsigned char *src )
nkeynes@953
   157
{
nkeynes@953
   158
    memcpy( OCRAMPAGE1+(addr&0x00000FFF), src, 32 );
nkeynes@953
   159
}
nkeynes@953
   160
nkeynes@953
   161
struct mem_region_fn mem_region_ocram_page1 = {
nkeynes@953
   162
        ocram_page1_read_long, ocram_page1_write_long,
nkeynes@953
   163
        ocram_page1_read_word, ocram_page1_write_word,
nkeynes@953
   164
        ocram_page1_read_byte, ocram_page1_write_byte,
nkeynes@953
   165
        ocram_page1_read_burst, ocram_page1_write_burst,
nkeynes@953
   166
        unmapped_prefetch };
nkeynes@953
   167
nkeynes@953
   168
/************************** Cache direct access ******************************/
nkeynes@953
   169
nkeynes@953
   170
static int32_t ccn_icache_addr_read( sh4addr_t addr )
nkeynes@953
   171
{
nkeynes@953
   172
    int entry = (addr & 0x00001FE0);
nkeynes@953
   173
    return ccn_icache[entry>>5].tag;
nkeynes@953
   174
}
nkeynes@953
   175
nkeynes@953
   176
static void ccn_icache_addr_write( sh4addr_t addr, uint32_t val )
nkeynes@953
   177
{
nkeynes@953
   178
    int entry = (addr & 0x00003FE0);
nkeynes@953
   179
    struct cache_line *line = &ccn_ocache[entry>>5];
nkeynes@953
   180
    if( addr & 0x08 ) { // Associative
nkeynes@953
   181
        /* FIXME: implement this - requires ITLB lookups, with exception in case of multi-hit */
nkeynes@953
   182
    } else {
nkeynes@953
   183
        line->tag = val & 0x1FFFFC01;
nkeynes@953
   184
        line->key = (val & 0x1FFFFC00)|(entry & 0x000003E0);
nkeynes@953
   185
    }
nkeynes@953
   186
}
nkeynes@953
   187
nkeynes@953
   188
struct mem_region_fn p4_region_icache_addr = {
nkeynes@953
   189
        ccn_icache_addr_read, ccn_icache_addr_write,
nkeynes@953
   190
        unmapped_read_long, unmapped_write_long,
nkeynes@953
   191
        unmapped_read_long, unmapped_write_long,
nkeynes@953
   192
        unmapped_read_burst, unmapped_write_burst,
nkeynes@953
   193
        unmapped_prefetch };
nkeynes@953
   194
nkeynes@953
   195
nkeynes@953
   196
static int32_t ccn_icache_data_read( sh4addr_t addr )
nkeynes@953
   197
{
nkeynes@953
   198
    int entry = (addr & 0x00001FFC);
nkeynes@953
   199
    return *(uint32_t *)&ccn_icache_data[entry];
nkeynes@953
   200
}
nkeynes@953
   201
nkeynes@953
   202
static void ccn_icache_data_write( sh4addr_t addr, uint32_t val )
nkeynes@953
   203
{
nkeynes@953
   204
    int entry = (addr & 0x00001FFC);
nkeynes@953
   205
    *(uint32_t *)&ccn_icache_data[entry] = val;    
nkeynes@953
   206
}
nkeynes@953
   207
nkeynes@953
   208
struct mem_region_fn p4_region_icache_data = {
nkeynes@953
   209
        ccn_icache_data_read, ccn_icache_data_write,
nkeynes@953
   210
        unmapped_read_long, unmapped_write_long,
nkeynes@953
   211
        unmapped_read_long, unmapped_write_long,
nkeynes@953
   212
        unmapped_read_burst, unmapped_write_burst,
nkeynes@953
   213
        unmapped_prefetch };
nkeynes@953
   214
nkeynes@953
   215
nkeynes@953
   216
static int32_t ccn_ocache_addr_read( sh4addr_t addr )
nkeynes@953
   217
{
nkeynes@953
   218
    int entry = (addr & 0x00003FE0);
nkeynes@953
   219
    return ccn_ocache[entry>>5].tag;
nkeynes@953
   220
}
nkeynes@953
   221
nkeynes@953
   222
static void ccn_ocache_addr_write( sh4addr_t addr, uint32_t val )
nkeynes@953
   223
{
nkeynes@953
   224
    int entry = (addr & 0x00003FE0);
nkeynes@953
   225
    struct cache_line *line = &ccn_ocache[entry>>5];
nkeynes@953
   226
    if( addr & 0x08 ) { // Associative
nkeynes@953
   227
    } else {
nkeynes@953
   228
        if( (line->tag & (CACHE_VALID|CACHE_DIRTY)) == (CACHE_VALID|CACHE_DIRTY) ) {
nkeynes@953
   229
            char *cache_data = &ccn_ocache_data[entry&0x00003FE0];
nkeynes@953
   230
            // Cache line is dirty - writeback. 
nkeynes@953
   231
            ext_address_space[line->tag>>12]->write_burst(line->key, cache_data);
nkeynes@953
   232
        }
nkeynes@953
   233
        line->tag = val & 0x1FFFFC03;
nkeynes@953
   234
        line->key = (val & 0x1FFFFC00)|(entry & 0x000003E0);
nkeynes@953
   235
    }
nkeynes@953
   236
}
nkeynes@953
   237
nkeynes@953
   238
struct mem_region_fn p4_region_ocache_addr = {
nkeynes@953
   239
        ccn_ocache_addr_read, ccn_ocache_addr_write,
nkeynes@953
   240
        unmapped_read_long, unmapped_write_long,
nkeynes@953
   241
        unmapped_read_long, unmapped_write_long,
nkeynes@953
   242
        unmapped_read_burst, unmapped_write_burst,
nkeynes@953
   243
        unmapped_prefetch };
nkeynes@953
   244
nkeynes@953
   245
nkeynes@953
   246
static int32_t ccn_ocache_data_read( sh4addr_t addr )
nkeynes@953
   247
{
nkeynes@953
   248
    int entry = (addr & 0x00003FFC);
nkeynes@953
   249
    return *(uint32_t *)&ccn_ocache_data[entry];
nkeynes@953
   250
}
nkeynes@953
   251
nkeynes@953
   252
static void ccn_ocache_data_write( sh4addr_t addr, uint32_t val )
nkeynes@953
   253
{
nkeynes@953
   254
    int entry = (addr & 0x00003FFC);
nkeynes@953
   255
    *(uint32_t *)&ccn_ocache_data[entry] = val;
nkeynes@953
   256
}
nkeynes@953
   257
nkeynes@953
   258
struct mem_region_fn p4_region_ocache_data = {
nkeynes@953
   259
        ccn_ocache_data_read, ccn_ocache_data_write,
nkeynes@953
   260
        unmapped_read_long, unmapped_write_long,
nkeynes@953
   261
        unmapped_read_long, unmapped_write_long,
nkeynes@953
   262
        unmapped_read_burst, unmapped_write_burst,
nkeynes@953
   263
        unmapped_prefetch };
nkeynes@953
   264
nkeynes@953
   265
nkeynes@953
   266
/****************** Cache control *********************/
nkeynes@953
   267
nkeynes@953
   268
void CCN_set_cache_control( int reg )
nkeynes@953
   269
{
nkeynes@953
   270
    uint32_t i;
nkeynes@953
   271
    
nkeynes@953
   272
    if( reg & CCR_ICI ) { /* icache invalidate */
nkeynes@953
   273
        for( i=0; i<ICACHE_ENTRY_COUNT; i++ ) {
nkeynes@953
   274
            ccn_icache[i].tag &= ~CACHE_VALID;
nkeynes@953
   275
        }
nkeynes@953
   276
    }
nkeynes@953
   277
    
nkeynes@953
   278
    if( reg & CCR_OCI ) { /* ocache invalidate */
nkeynes@953
   279
        for( i=0; i<OCACHE_ENTRY_COUNT; i++ ) {
nkeynes@953
   280
            ccn_ocache[i].tag &= ~(CACHE_VALID|CACHE_DIRTY);
nkeynes@953
   281
        }
nkeynes@953
   282
    }
nkeynes@953
   283
    
nkeynes@953
   284
    switch( reg & (CCR_OIX|CCR_ORA|CCR_OCE) ) {
nkeynes@953
   285
    case MEM_OC_INDEX0: /* OIX=0 */
nkeynes@953
   286
        for( i=OCRAM_START; i<OCRAM_END; i+=4 ) {
nkeynes@953
   287
            sh4_address_space[i] = &mem_region_ocram_page0;
nkeynes@953
   288
            sh4_address_space[i+1] = &mem_region_ocram_page0;
nkeynes@953
   289
            sh4_address_space[i+2] = &mem_region_ocram_page1;
nkeynes@953
   290
            sh4_address_space[i+3] = &mem_region_ocram_page1;
nkeynes@953
   291
        }
nkeynes@953
   292
        break;
nkeynes@953
   293
    case MEM_OC_INDEX1: /* OIX=1 */
nkeynes@953
   294
        for( i=OCRAM_START; i<OCRAM_MID; i++ )
nkeynes@953
   295
            sh4_address_space[i] = &mem_region_ocram_page0;
nkeynes@953
   296
        for( i=OCRAM_MID; i<OCRAM_END; i++ )
nkeynes@953
   297
            sh4_address_space[i] = &mem_region_ocram_page1;
nkeynes@953
   298
        break;
nkeynes@953
   299
    default: /* disabled */
nkeynes@953
   300
        for( i=OCRAM_START; i<OCRAM_END; i++ )
nkeynes@953
   301
            sh4_address_space[i] = &mem_region_unmapped;
nkeynes@953
   302
        break;
nkeynes@953
   303
    }
nkeynes@953
   304
}
nkeynes@953
   305
nkeynes@953
   306
/**
nkeynes@953
   307
 * Prefetch for non-storequeue regions
nkeynes@953
   308
 */
nkeynes@953
   309
void FASTCALL ccn_prefetch( sh4addr_t addr )
nkeynes@953
   310
{
nkeynes@953
   311
    
nkeynes@953
   312
}
nkeynes@953
   313
nkeynes@953
   314
/**
nkeynes@953
   315
 * Prefetch for non-cached regions. Oddly enough, this does nothing whatsoever.
nkeynes@953
   316
 */
nkeynes@953
   317
void FASTCALL ccn_uncached_prefetch( sh4addr_t addr )
nkeynes@953
   318
{
nkeynes@953
   319
    
nkeynes@953
   320
}
nkeynes@953
   321
/********************************* Store-queue *******************************/
nkeynes@953
   322
/*
nkeynes@953
   323
 * The storequeue is strictly speaking part of the cache, but most of 
nkeynes@953
   324
 * the complexity is actually around its addressing (ie in the MMU). The
nkeynes@953
   325
 * methods here can assume we've already passed SQMD protection and the TLB
nkeynes@953
   326
 * lookups (where appropriate).
nkeynes@953
   327
 */  
nkeynes@953
   328
void FASTCALL ccn_storequeue_write_long( sh4addr_t addr, uint32_t val )
nkeynes@953
   329
{
nkeynes@953
   330
    sh4r.store_queue[(addr>>2)&0xF] = val;
nkeynes@953
   331
}
nkeynes@953
   332
int32_t FASTCALL ccn_storequeue_read_long( sh4addr_t addr )
nkeynes@953
   333
{
nkeynes@953
   334
    return sh4r.store_queue[(addr>>2)&0xF];
nkeynes@953
   335
}
nkeynes@953
   336
nkeynes@953
   337
/**
nkeynes@953
   338
 * Variant used when tlb is disabled - address will be the original prefetch
nkeynes@953
   339
 * address (ie 0xE0001234). Due to the way the SQ addressing is done, it can't
nkeynes@953
   340
 * be hardcoded on 4K page boundaries, so we manually decode it here.
nkeynes@953
   341
 */
nkeynes@953
   342
void FASTCALL ccn_storequeue_prefetch( sh4addr_t addr ) 
nkeynes@953
   343
{
nkeynes@953
   344
    int queue = (addr&0x20)>>2;
nkeynes@953
   345
    sh4ptr_t src = (sh4ptr_t)&sh4r.store_queue[queue];
nkeynes@953
   346
    uint32_t hi = MMIO_READ( MMU, QACR0 + (queue>>1)) << 24;
nkeynes@953
   347
    sh4addr_t target = (addr&0x03FFFFE0) | hi;
nkeynes@953
   348
    ext_address_space[target>>12]->write_burst( target, src );
nkeynes@953
   349
}
nkeynes@953
   350
nkeynes@953
   351
/**
nkeynes@953
   352
 * Variant used when tlb is enabled - address in this case is already
nkeynes@953
   353
 * mapped to the external target address.
nkeynes@953
   354
 */
nkeynes@953
   355
void FASTCALL ccn_storequeue_prefetch_tlb( sh4addr_t addr )
nkeynes@953
   356
{
nkeynes@953
   357
    int queue = (addr&0x20)>>2;
nkeynes@953
   358
    sh4ptr_t src = (sh4ptr_t)&sh4r.store_queue[queue];
nkeynes@953
   359
    ext_address_space[addr>>12]->write_burst( (addr & 0x1FFFFFE0), src );
nkeynes@953
   360
}
.