Search
lxdream.org :: lxdream/src/sh4/cache.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/cache.c
changeset 946:d41ee7994db7
prev939:6f2302afeb89
next1067:d3c00ffccfcd
author nkeynes
date Tue Jan 06 01:58:08 2009 +0000 (15 years ago)
branchlxdream-mem
permissions -rw-r--r--
last change Fully integrate SQ with the new address space code - added additional 'prefetch'
memory accessor. TLB is utterly untested, but non-TLB at least still works.
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@946
    27
#include "sh4/mmu.h"
nkeynes@931
    28
nkeynes@931
    29
#define OCRAM_START (0x7C000000>>LXDREAM_PAGE_BITS)
nkeynes@931
    30
#define OCRAM_MID   (0x7E000000>>LXDREAM_PAGE_BITS)
nkeynes@931
    31
#define OCRAM_END   (0x80000000>>LXDREAM_PAGE_BITS)
nkeynes@931
    32
nkeynes@931
    33
#define CACHE_VALID 1
nkeynes@931
    34
#define CACHE_DIRTY 2
nkeynes@931
    35
nkeynes@931
    36
#define ICACHE_ENTRY_COUNT 256
nkeynes@931
    37
#define OCACHE_ENTRY_COUNT 512
nkeynes@931
    38
nkeynes@931
    39
struct cache_line {
nkeynes@931
    40
    uint32_t key;  // Fast address match - bits 5..28 for valid entry, -1 for invalid entry
nkeynes@931
    41
    uint32_t tag;  // tag + flags value from the address field
nkeynes@931
    42
};    
nkeynes@931
    43
nkeynes@931
    44
nkeynes@931
    45
static struct cache_line ccn_icache[ICACHE_ENTRY_COUNT];
nkeynes@931
    46
static struct cache_line ccn_ocache[OCACHE_ENTRY_COUNT];
nkeynes@931
    47
static unsigned char ccn_icache_data[ICACHE_ENTRY_COUNT*32];
nkeynes@931
    48
static unsigned char ccn_ocache_data[OCACHE_ENTRY_COUNT*32];
nkeynes@931
    49
nkeynes@931
    50
nkeynes@931
    51
/*********************** General module requirements ********************/
nkeynes@931
    52
nkeynes@931
    53
void CCN_save_state( FILE *f )
nkeynes@931
    54
{
nkeynes@931
    55
    fwrite( &ccn_icache, sizeof(ccn_icache), 1, f );
nkeynes@931
    56
    fwrite( &ccn_icache_data, sizeof(ccn_icache_data), 1, f );
nkeynes@931
    57
    fwrite( &ccn_ocache, sizeof(ccn_ocache), 1, f);
nkeynes@931
    58
    fwrite( &ccn_ocache_data, sizeof(ccn_ocache_data), 1, f);
nkeynes@931
    59
}
nkeynes@931
    60
nkeynes@931
    61
int CCN_load_state( FILE *f )
nkeynes@931
    62
{
nkeynes@931
    63
    /* Setup the cache mode according to the saved register value
nkeynes@931
    64
     * (mem_load runs before this point to load all MMIO data)
nkeynes@931
    65
     */
nkeynes@931
    66
    mmio_region_MMU_write( CCR, MMIO_READ(MMU, CCR) );
nkeynes@931
    67
nkeynes@931
    68
    if( fread( &ccn_icache, sizeof(ccn_icache), 1, f ) != 1 ) {
nkeynes@931
    69
        return 1;
nkeynes@931
    70
    }
nkeynes@931
    71
    if( fread( &ccn_icache_data, sizeof(ccn_icache_data), 1, f ) != 1 ) {
nkeynes@931
    72
        return 1;
nkeynes@931
    73
    }
nkeynes@931
    74
    if( fread( &ccn_ocache, sizeof(ccn_ocache), 1, f ) != 1 ) {
nkeynes@931
    75
        return 1;
nkeynes@931
    76
    }
nkeynes@931
    77
    if( fread( &ccn_ocache_data, sizeof(ccn_ocache_data), 1, f ) != 1 ) {
nkeynes@931
    78
        return 1;
nkeynes@931
    79
    }
nkeynes@931
    80
    return 0;
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@946
   125
        ocram_page0_read_burst, ocram_page0_write_burst,
nkeynes@946
   126
        unmapped_prefetch };
nkeynes@931
   127
nkeynes@931
   128
static int32_t FASTCALL ocram_page1_read_long( sh4addr_t addr )
nkeynes@931
   129
{
nkeynes@931
   130
    return *((int32_t *)(OCRAMPAGE1 + (addr&0x00000FFF)));
nkeynes@931
   131
}
nkeynes@931
   132
static int32_t FASTCALL ocram_page1_read_word( sh4addr_t addr )
nkeynes@931
   133
{
nkeynes@931
   134
    return SIGNEXT16(*((int16_t *)(OCRAMPAGE1 + (addr&0x00000FFF))));
nkeynes@931
   135
}
nkeynes@931
   136
static int32_t FASTCALL ocram_page1_read_byte( sh4addr_t addr )
nkeynes@931
   137
{
nkeynes@931
   138
    return SIGNEXT8(*((int16_t *)(OCRAMPAGE1 + (addr&0x00000FFF))));
nkeynes@931
   139
}
nkeynes@931
   140
static void FASTCALL ocram_page1_write_long( sh4addr_t addr, uint32_t val )
nkeynes@931
   141
{
nkeynes@931
   142
    *(uint32_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = val;
nkeynes@931
   143
}
nkeynes@931
   144
static void FASTCALL ocram_page1_write_word( sh4addr_t addr, uint32_t val )
nkeynes@931
   145
{
nkeynes@931
   146
    *(uint16_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = (uint16_t)val;
nkeynes@931
   147
}
nkeynes@931
   148
static void FASTCALL ocram_page1_write_byte( sh4addr_t addr, uint32_t val )
nkeynes@931
   149
{
nkeynes@931
   150
    *(uint8_t *)(OCRAMPAGE1 + (addr&0x00000FFF)) = (uint8_t)val;
nkeynes@931
   151
}
nkeynes@931
   152
static void FASTCALL ocram_page1_read_burst( unsigned char *dest, sh4addr_t addr )
nkeynes@931
   153
{
nkeynes@931
   154
    memcpy( dest, OCRAMPAGE1+(addr&0x00000FFF), 32 );
nkeynes@931
   155
}
nkeynes@931
   156
static void FASTCALL ocram_page1_write_burst( sh4addr_t addr, unsigned char *src )
nkeynes@931
   157
{
nkeynes@931
   158
    memcpy( OCRAMPAGE1+(addr&0x00000FFF), src, 32 );
nkeynes@931
   159
}
nkeynes@931
   160
nkeynes@931
   161
struct mem_region_fn mem_region_ocram_page1 = {
nkeynes@931
   162
        ocram_page1_read_long, ocram_page1_write_long,
nkeynes@931
   163
        ocram_page1_read_word, ocram_page1_write_word,
nkeynes@931
   164
        ocram_page1_read_byte, ocram_page1_write_byte,
nkeynes@946
   165
        ocram_page1_read_burst, ocram_page1_write_burst,
nkeynes@946
   166
        unmapped_prefetch };
nkeynes@931
   167
nkeynes@933
   168
/************************** Cache direct access ******************************/
nkeynes@933
   169
nkeynes@933
   170
static int32_t ccn_icache_addr_read( sh4addr_t addr )
nkeynes@933
   171
{
nkeynes@933
   172
    int entry = (addr & 0x00001FE0);
nkeynes@933
   173
    return ccn_icache[entry>>5].tag;
nkeynes@933
   174
}
nkeynes@933
   175
nkeynes@933
   176
static void ccn_icache_addr_write( sh4addr_t addr, uint32_t val )
nkeynes@933
   177
{
nkeynes@933
   178
    int entry = (addr & 0x00003FE0);
nkeynes@933
   179
    struct cache_line *line = &ccn_ocache[entry>>5];
nkeynes@933
   180
    if( addr & 0x08 ) { // Associative
nkeynes@933
   181
        /* FIXME: implement this - requires ITLB lookups, with exception in case of multi-hit */
nkeynes@933
   182
    } else {
nkeynes@933
   183
        line->tag = val & 0x1FFFFC01;
nkeynes@933
   184
        line->key = (val & 0x1FFFFC00)|(entry & 0x000003E0);
nkeynes@933
   185
    }
nkeynes@933
   186
}
nkeynes@933
   187
nkeynes@933
   188
struct mem_region_fn p4_region_icache_addr = {
nkeynes@933
   189
        ccn_icache_addr_read, ccn_icache_addr_write,
nkeynes@933
   190
        unmapped_read_long, unmapped_write_long,
nkeynes@933
   191
        unmapped_read_long, unmapped_write_long,
nkeynes@946
   192
        unmapped_read_burst, unmapped_write_burst,
nkeynes@946
   193
        unmapped_prefetch };
nkeynes@933
   194
nkeynes@933
   195
nkeynes@933
   196
static int32_t ccn_icache_data_read( sh4addr_t addr )
nkeynes@933
   197
{
nkeynes@933
   198
    int entry = (addr & 0x00001FFC);
nkeynes@933
   199
    return *(uint32_t *)&ccn_icache_data[entry];
nkeynes@933
   200
}
nkeynes@933
   201
nkeynes@933
   202
static void ccn_icache_data_write( sh4addr_t addr, uint32_t val )
nkeynes@933
   203
{
nkeynes@933
   204
    int entry = (addr & 0x00001FFC);
nkeynes@933
   205
    *(uint32_t *)&ccn_icache_data[entry] = val;    
nkeynes@933
   206
}
nkeynes@933
   207
nkeynes@933
   208
struct mem_region_fn p4_region_icache_data = {
nkeynes@933
   209
        ccn_icache_data_read, ccn_icache_data_write,
nkeynes@933
   210
        unmapped_read_long, unmapped_write_long,
nkeynes@933
   211
        unmapped_read_long, unmapped_write_long,
nkeynes@946
   212
        unmapped_read_burst, unmapped_write_burst,
nkeynes@946
   213
        unmapped_prefetch };
nkeynes@933
   214
nkeynes@933
   215
nkeynes@933
   216
static int32_t ccn_ocache_addr_read( sh4addr_t addr )
nkeynes@933
   217
{
nkeynes@933
   218
    int entry = (addr & 0x00003FE0);
nkeynes@933
   219
    return ccn_ocache[entry>>5].tag;
nkeynes@933
   220
}
nkeynes@933
   221
nkeynes@933
   222
static void ccn_ocache_addr_write( sh4addr_t addr, uint32_t val )
nkeynes@933
   223
{
nkeynes@933
   224
    int entry = (addr & 0x00003FE0);
nkeynes@933
   225
    struct cache_line *line = &ccn_ocache[entry>>5];
nkeynes@933
   226
    if( addr & 0x08 ) { // Associative
nkeynes@933
   227
    } else {
nkeynes@933
   228
        if( (line->tag & (CACHE_VALID|CACHE_DIRTY)) == (CACHE_VALID|CACHE_DIRTY) ) {
nkeynes@933
   229
            char *cache_data = &ccn_ocache_data[entry&0x00003FE0];
nkeynes@933
   230
            // Cache line is dirty - writeback. 
nkeynes@933
   231
            ext_address_space[line->tag>>12]->write_burst(line->key, cache_data);
nkeynes@933
   232
        }
nkeynes@933
   233
        line->tag = val & 0x1FFFFC03;
nkeynes@933
   234
        line->key = (val & 0x1FFFFC00)|(entry & 0x000003E0);
nkeynes@933
   235
    }
nkeynes@933
   236
}
nkeynes@933
   237
nkeynes@933
   238
struct mem_region_fn p4_region_ocache_addr = {
nkeynes@933
   239
        ccn_ocache_addr_read, ccn_ocache_addr_write,
nkeynes@933
   240
        unmapped_read_long, unmapped_write_long,
nkeynes@933
   241
        unmapped_read_long, unmapped_write_long,
nkeynes@946
   242
        unmapped_read_burst, unmapped_write_burst,
nkeynes@946
   243
        unmapped_prefetch };
nkeynes@933
   244
nkeynes@933
   245
nkeynes@933
   246
static int32_t ccn_ocache_data_read( sh4addr_t addr )
nkeynes@933
   247
{
nkeynes@933
   248
    int entry = (addr & 0x00003FFC);
nkeynes@933
   249
    return *(uint32_t *)&ccn_ocache_data[entry];
nkeynes@933
   250
}
nkeynes@933
   251
nkeynes@933
   252
static void ccn_ocache_data_write( sh4addr_t addr, uint32_t val )
nkeynes@933
   253
{
nkeynes@933
   254
    int entry = (addr & 0x00003FFC);
nkeynes@933
   255
    *(uint32_t *)&ccn_ocache_data[entry] = val;
nkeynes@933
   256
}
nkeynes@933
   257
nkeynes@933
   258
struct mem_region_fn p4_region_ocache_data = {
nkeynes@933
   259
        ccn_ocache_data_read, ccn_ocache_data_write,
nkeynes@933
   260
        unmapped_read_long, unmapped_write_long,
nkeynes@933
   261
        unmapped_read_long, unmapped_write_long,
nkeynes@946
   262
        unmapped_read_burst, unmapped_write_burst,
nkeynes@946
   263
        unmapped_prefetch };
nkeynes@933
   264
nkeynes@933
   265
nkeynes@931
   266
/****************** Cache control *********************/
nkeynes@931
   267
nkeynes@931
   268
void CCN_set_cache_control( int reg )
nkeynes@931
   269
{
nkeynes@931
   270
    uint32_t i;
nkeynes@933
   271
    
nkeynes@933
   272
    if( reg & CCR_ICI ) { /* icache invalidate */
nkeynes@933
   273
        for( i=0; i<ICACHE_ENTRY_COUNT; i++ ) {
nkeynes@933
   274
            ccn_icache[i].tag &= ~CACHE_VALID;
nkeynes@933
   275
        }
nkeynes@933
   276
    }
nkeynes@933
   277
    
nkeynes@933
   278
    if( reg & CCR_OCI ) { /* ocache invalidate */
nkeynes@933
   279
        for( i=0; i<OCACHE_ENTRY_COUNT; i++ ) {
nkeynes@933
   280
            ccn_ocache[i].tag &= ~(CACHE_VALID|CACHE_DIRTY);
nkeynes@933
   281
        }
nkeynes@933
   282
    }
nkeynes@933
   283
    
nkeynes@931
   284
    switch( reg & (CCR_OIX|CCR_ORA|CCR_OCE) ) {
nkeynes@931
   285
    case MEM_OC_INDEX0: /* OIX=0 */
nkeynes@931
   286
        for( i=OCRAM_START; i<OCRAM_END; i+=4 ) {
nkeynes@931
   287
            sh4_address_space[i] = &mem_region_ocram_page0;
nkeynes@931
   288
            sh4_address_space[i+1] = &mem_region_ocram_page0;
nkeynes@931
   289
            sh4_address_space[i+2] = &mem_region_ocram_page1;
nkeynes@931
   290
            sh4_address_space[i+3] = &mem_region_ocram_page1;
nkeynes@931
   291
        }
nkeynes@931
   292
        break;
nkeynes@931
   293
    case MEM_OC_INDEX1: /* OIX=1 */
nkeynes@931
   294
        for( i=OCRAM_START; i<OCRAM_MID; i++ )
nkeynes@931
   295
            sh4_address_space[i] = &mem_region_ocram_page0;
nkeynes@931
   296
        for( i=OCRAM_MID; i<OCRAM_END; i++ )
nkeynes@931
   297
            sh4_address_space[i] = &mem_region_ocram_page1;
nkeynes@931
   298
        break;
nkeynes@931
   299
    default: /* disabled */
nkeynes@931
   300
        for( i=OCRAM_START; i<OCRAM_END; i++ )
nkeynes@931
   301
            sh4_address_space[i] = &mem_region_unmapped;
nkeynes@931
   302
        break;
nkeynes@931
   303
    }
nkeynes@939
   304
}
nkeynes@939
   305
nkeynes@946
   306
/**
nkeynes@946
   307
 * Prefetch for non-storequeue regions
nkeynes@946
   308
 */
nkeynes@946
   309
void FASTCALL ccn_prefetch( sh4addr_t addr )
nkeynes@946
   310
{
nkeynes@946
   311
    
nkeynes@946
   312
}
nkeynes@939
   313
nkeynes@946
   314
/**
nkeynes@946
   315
 * Prefetch for non-cached regions. Oddly enough, this does nothing whatsoever.
nkeynes@946
   316
 */
nkeynes@946
   317
void FASTCALL ccn_uncached_prefetch( sh4addr_t addr )
nkeynes@946
   318
{
nkeynes@946
   319
    
nkeynes@946
   320
}
nkeynes@946
   321
/********************************* Store-queue *******************************/
nkeynes@946
   322
/*
nkeynes@946
   323
 * The storequeue is strictly speaking part of the cache, but most of 
nkeynes@946
   324
 * the complexity is actually around its addressing (ie in the MMU). The
nkeynes@946
   325
 * methods here can assume we've already passed SQMD protection and the TLB
nkeynes@946
   326
 * lookups (where appropriate).
nkeynes@946
   327
 */  
nkeynes@946
   328
void FASTCALL ccn_storequeue_write_long( sh4addr_t addr, uint32_t val )
nkeynes@939
   329
{
nkeynes@939
   330
    sh4r.store_queue[(addr>>2)&0xF] = val;
nkeynes@939
   331
}
nkeynes@946
   332
int32_t FASTCALL ccn_storequeue_read_long( sh4addr_t addr )
nkeynes@939
   333
{
nkeynes@939
   334
    return sh4r.store_queue[(addr>>2)&0xF];
nkeynes@939
   335
}
nkeynes@939
   336
nkeynes@946
   337
/**
nkeynes@946
   338
 * Variant used when tlb is disabled - address will be the original prefetch
nkeynes@946
   339
 * address (ie 0xE0001234). Due to the way the SQ addressing is done, it can't
nkeynes@946
   340
 * be hardcoded on 4K page boundaries, so we manually decode it here.
nkeynes@946
   341
 */
nkeynes@946
   342
void FASTCALL ccn_storequeue_prefetch( sh4addr_t addr ) 
nkeynes@946
   343
{
nkeynes@946
   344
    int queue = (addr&0x20)>>2;
nkeynes@946
   345
    sh4ptr_t src = (sh4ptr_t)&sh4r.store_queue[queue];
nkeynes@946
   346
    uint32_t hi = MMIO_READ( MMU, QACR0 + (queue>>1)) << 24;
nkeynes@946
   347
    sh4addr_t target = (addr&0x03FFFFE0) | hi;
nkeynes@946
   348
    ext_address_space[target>>12]->write_burst( target, src );
nkeynes@946
   349
}
nkeynes@946
   350
nkeynes@946
   351
/**
nkeynes@946
   352
 * Variant used when tlb is enabled - address in this case is already
nkeynes@946
   353
 * mapped to the external target address.
nkeynes@946
   354
 */
nkeynes@946
   355
void FASTCALL ccn_storequeue_prefetch_tlb( sh4addr_t addr )
nkeynes@946
   356
{
nkeynes@946
   357
    int queue = (addr&0x20)>>2;
nkeynes@946
   358
    sh4ptr_t src = (sh4ptr_t)&sh4r.store_queue[queue];
nkeynes@946
   359
    ext_address_space[addr>>12]->write_burst( (addr & 0x1FFFFFE0), src );
nkeynes@946
   360
}
.