1
0
Files
2022-09-29 17:59:04 +03:00

659 lines
16 KiB
C

/*
* cpu/icache.c
*
*
* Copyright 1991, Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
#ident "$Revision: 1.5 $"
#include <sys/param.h>
#include <sys/sbd.h>
#include <sys/cpu.h>
#include <fault.h>
#include <setjmp.h>
#include <uif.h>
#include <libsc.h>
#include <libsk.h>
#include "cache.h"
static jmp_buf fault_buf;
/*
* defines for use with tag_inf.state
*/
#define PI_INVALID 0
#define PI_CLEAN_EXCL 2
int icachedata(void);
int icacheaddr(void);
int icachetag(void);
int icachefetch(void);
void ifetch_loop(void);
int icache_fill_writeback(uint *, uint);
/*
* icache First level instruction cache test
*/
u_int
icache()
{
int error = 0;
int r5000 = ((get_prid()&C0_IMPMASK)>>C0_IMPSHIFT) == C0_IMP_R5000;
invalidate_caches();
msg_printf(VRB, "Instruction cache misc tests\n");
if(!r5000) {
msg_printf(VRB, "skipping for r10000/r12000\n");
okydoky();
return error;
}
run_uncached();
busy (1);
invalidate_caches();
error = icachedata() || icachetag() || icachefetch();
run_cached();
invalidate_caches();
if (error)
sum_error("instruction cache misc");
else
okydoky();
return error;
}
/*
* icachedata - block mode read/write test
*
* all memory locations in the cache are set to the same values, so it
* will not detect addressing errors well, if at all, but should have
* very good coverage of single-bit and stuck-bit errors
*/
int icachedata(void)
{
register uint *fillptr, i, j, pattern;
uint fail;
char * mesg;
mesg = "Data";
msg_printf(VRB, "icachedata\n");
/*
* fill icache with 0's, fill phys address with 0xFFFFFFFF, and
* try writeback
*/
pattern = 0;
invalidate_caches();
fillptr = (uint *) PHYS_TO_K1(PHYS_CHECK_LO);
i = PI_SIZE / sizeof(uint);
while (i--)
*fillptr++ = pattern;
fail = icache_fill_writeback ((uint *)PHYS_CHECK_LO, ~pattern);
i = PI_SIZE / sizeof(uint);
fillptr = (uint *) PHYS_TO_K1(PHYS_CHECK_LO);
while (i--)
{
/*
* log error if writeback value is not same as pattern
*/
if (*fillptr != pattern)
{
msg_printf(ERR,
"Cache err: (1) Address:0x%x, Actual:0x%x, Expect:0x%x\n",
K1_TO_K0((uint)fillptr), *fillptr, pattern);
fail = 1;
}
fillptr++;
}
/*
* fill icache with 0xFFFFFFFF's, fill phys address with 0, and
* try writeback
*/
pattern = ~0;
fillptr = (uint *) PHYS_TO_K1(PHYS_CHECK_LO);
i = PI_SIZE / sizeof(uint);
while (i--)
*fillptr++ = pattern;
fail = icache_fill_writeback ((uint *)PHYS_CHECK_LO, ~pattern);
i = PI_SIZE / sizeof(uint);
fillptr = (uint *) PHYS_TO_K0(PHYS_CHECK_LO);
while (i--)
{
/*
* log error if writeback value is not same as pattern
*/
if (*fillptr != pattern)
{
msg_printf(ERR,
"Cache err: (2) Address:0x%x, Actual:0x%x, Expect:0x%x\n",
K1_TO_K0((uint)fillptr), *fillptr, pattern);
fail = 1;
}
fillptr++;
}
/*
* single-bit marching ones pattern
*/
pattern = 1;
j = 8 * sizeof(uint);
while (j--)
{
fillptr = (uint *) PHYS_TO_K1(PHYS_CHECK_LO);
i = PI_SIZE / sizeof(uint);
while (i--)
*fillptr++ = pattern;
fail = icache_fill_writeback ((uint *)PHYS_CHECK_LO, ~pattern);
i = PI_SIZE / sizeof(uint);
fillptr = (uint *) PHYS_TO_K1(PHYS_CHECK_LO);
while (i--)
{
/*
* log error if writeback value is not same as pattern
*/
if (*fillptr != pattern)
{
msg_printf(ERR,
"Cache err: (3) Address:0x%x, Actual:0x%x, Expect:0x%x\n",
K1_TO_K0((uint)fillptr), *fillptr, pattern);
fail = 1;
}
fillptr++;
}
pattern <<= 1;
}
/*
* march from 1 to all 1's
*/
pattern = 1;
j = 8 * sizeof(uint);
while (j--)
{
fillptr = (uint *) PHYS_TO_K1(PHYS_CHECK_LO);
i = PI_SIZE / sizeof(uint);
while (i--)
*fillptr++ = pattern;
fail = icache_fill_writeback ((uint *)PHYS_CHECK_LO, ~pattern);
i = PI_SIZE / sizeof(uint);
fillptr = (uint *) PHYS_TO_K1(PHYS_CHECK_LO);
while (i--)
{
/*
* log error if writeback value is not same as pattern
*/
if (*fillptr != pattern)
{
msg_printf(ERR,
"Cache err: (4) Address:0x%x, Actual:0x%x, Expect:0x%x\n",
K1_TO_K0((uint)fillptr), *fillptr, pattern);
fail = 1;
}
fillptr++;
}
pattern <<= 1;
pattern |= 1;
}
/*
* march from all 1's to single one
*/
pattern = ~0;
j = 8 * sizeof(uint);
while (j--)
{
busy (1);
fillptr = (uint *) PHYS_TO_K1(PHYS_CHECK_LO);
i = PI_SIZE / sizeof(uint);
while (i--)
*fillptr++ = pattern;
fail = icache_fill_writeback ((uint *)PHYS_CHECK_LO, ~pattern);
i = PI_SIZE / sizeof(uint);
fillptr = (uint *) PHYS_TO_K1(PHYS_CHECK_LO);
while (i--)
{
/*
* log error if writeback value is not same as pattern
*/
if (*fillptr != pattern)
{
msg_printf(ERR,
"Cache err: (5) Address:0x%x, Actual:0x%x, Expect:0x%x\n",
K1_TO_K0((uint)fillptr), *fillptr, pattern);
fail = 1;
}
fillptr++;
}
pattern <<= 1;
}
busy (0);
return fail;
}
/*
* icacheaddr - address-in-address checks
*
* does not provide the bit error coverage of icachedata, but by storing
* address-in-address and ~address-in-address, should find if each
* cache location is individually addressable
*/
int icacheaddr(void)
{
register uint *fillptr, *mapptr, i;
uint fail;
char * mesg;
mesg = "Address";
msg_printf(VRB, "icacheaddr\n");
/*
* fill icache with address-in-address data, set phys memory to 0xFFFFFFFF,
* and try writeback
*/
fillptr = (uint *) PHYS_TO_K1(PHYS_CHECK_LO);
mapptr = (uint *) PHYS_TO_K0(PHYS_CHECK_LO);
i = PI_SIZE / sizeof(uint);
while (i--)
*fillptr++ = (uint) mapptr++;
fail = icache_fill_writeback ((uint *)PHYS_CHECK_LO, ~0);
i = PI_SIZE / sizeof(uint);
fillptr = (uint *) PHYS_TO_K1(PHYS_CHECK_LO);
mapptr = (uint *) PHYS_TO_K0(PHYS_CHECK_LO);
while (i--)
{
/*
* log error if writeback value is not same as pattern
*/
if (*fillptr != (uint) mapptr)
{
msg_printf(ERR,
"Cache err: (6) Address:0x%x, Actual:0x%x, Expect:0x%x\n",
K1_TO_K0((uint)fillptr), *fillptr, (uint)mapptr);
fail = 1;
}
fillptr++;
mapptr++;
}
/*
* fill icache with ~address-in-address data, set phys memory to
* 0xFFFFFFFF, and try writeback
*/
fillptr = (uint *) PHYS_TO_K1(PHYS_CHECK_LO);
mapptr = (uint *) PHYS_TO_K0(PHYS_CHECK_LO);
i = PI_SIZE / sizeof(uint);
while (i--)
*fillptr++ = ~((uint) mapptr++);
fail = icache_fill_writeback ((uint *)PHYS_CHECK_LO, ~0);
i = PI_SIZE / sizeof(uint);
fillptr = (uint *) PHYS_TO_K1(PHYS_CHECK_LO);
mapptr = (uint *) PHYS_TO_K0(PHYS_CHECK_LO);
while (i--)
{
/*
* log error if writeback value is not same as pattern
*/
if (*fillptr != ~((uint) mapptr))
{
msg_printf(ERR,
"Cache err: (7) Address:0x%x, Actual:0x%x, Expect:0x%x\n",
K1_TO_K0((uint)fillptr), *fillptr, (uint)mapptr);
fail = 1;
}
fillptr++;
mapptr++;
}
return fail;
}
/*
* icachetag- icache tag read/write test
*
* EXTREMELY simple-minded - essentially, invalidates all tags in icache,
* reads them back to check that they are invalid, force-loads all cache
* lines, and reads the tags again to check that they are now valid.
*/
int icachetag(void)
{
register uint *ptr, i, j;
tag_info_t tag_info; /* and store the state and addr here */
uint fail;
fail = 0;
j = PIL_SIZE / sizeof(int);
msg_printf(VRB, "icachetag\n");
/*
* invalidate the caches
*/
invalidate_caches();
/*
* check the primary icache tags for invalid
*/
ptr = (uint *) PHYS_CHECK_LO;
i = PI_SIZE / PIL_SIZE;
while (i--)
{
get_tag_info(PRIMARYI, K0_TO_PHYS(ptr), &tag_info);
/*
* error if cache line not invalid
*/
if (tag_info.state != PI_INVALID)
{
msg_printf(ERR, "Cache tag err: icache not invalid at 0x%x\n",
K0_TO_PHYS(ptr));
msg_printf(ERR, "State: 0x%x, Phys Address: 0x%x\n",
tag_info.state, tag_info.physaddr);
fail = 1;
}
ptr += j;
}
/*
* invalidate the caches again - just in case
*/
invalidate_caches();
/*
* fill the icache from the specified block - normal cache error
* are enabled, so any fill errors should be seen
*/
ptr = (uint *) PHYS_TO_K0(PHYS_CHECK_LO);
i = PI_SIZE / PIL_SIZE;
if (setjmp(fault_buf))
{
msg_printf(ERR, "exception on cache fill at 0x%x\n", (uint) ptr);
if (_exc_save != EXCEPT_ECC)
{
msg_printf(ERR, "no cache error seen - dumping status\n");
show_fault();
}
DoErrorEret();
fail = 1;
}
else
{
nofault = fault_buf; /* enable error handler */
/*
* enable cache interrupts, but not for parity/ecc
*/
SetSR(SR_FORCE_OFF | SR_DE);
while(i--)
{
fill_ipline(ptr); /* force cache load */
ptr += j;
}
/*
* clear error handler - delay is necessary do work around
* pipeline delays in interrupt/exception generation
*/
DELAY(2);
nofault = 0;
}
/*
* check the primary icache tags for invalid
*/
ptr = (uint *) PHYS_CHECK_LO;
i = PI_SIZE / PIL_SIZE;
while (i--)
{
get_tag_info(PRIMARYI, K0_TO_PHYS(ptr), &tag_info);
/*
* error if valid bit is not set
*/
if (tag_info.state != PI_CLEAN_EXCL)
{
msg_printf(ERR, "Cache tag err: icache not valid at 0x%x\n",
K0_TO_PHYS(ptr));
msg_printf(ERR, "State: 0x%x, Phys Address: 0x%x\n",
tag_info.state, tag_info.physaddr);
fail = 1;
}
ptr += j;
}
return fail;
}
/*
* icachefetch - icache instruction fetch test
*
* rather simple - runs the same routine in both cached and uncached modes;
* if the elapsed clock cycles are the same, the icache is not functioning
* properly. One nice feature is the the same primitives can be used to
* calculate the percent speed improvement of cached vs uncached routines
*/
int icachefetch(void)
{
register uint *ptr, i, j;
uint tcached, tuncached, fail;
fail = 0;
msg_printf(VRB, "icachefetch\n");
/*
* make sure caches are invalid to force icache fetch
*/
invalidate_caches();
/*
* time ifetch_loop() in both cached and uncached modes
*/
tcached = time_function(PHYS_TO_K0(KDM_TO_PHYS((uint)ifetch_loop)));
tuncached = time_function(PHYS_TO_K1(KDM_TO_PHYS((uint)ifetch_loop)));
/*
* very simpleminded test - if it did not run faster in cached mode,
* the cache is assumed to be bad
*/
if (tcached >= tuncached)
{
msg_printf(ERR, "Cache err: no icache speedup in icache fetch test\n");
msg_printf(ERR,
"Cached Time: %x, Uncached Time: %x\n", tcached, tuncached);
fail = 1;
}
return fail;
}
/*
* dummy routine to use for simple icache functionality test
*/
void ifetch_loop(void)
{
register i,j;
i = 0;
j = 10000;
while (j--)
{
i++;
}
}
/*
* fills icache with data whose PHYSICAL address is given, sets the
* memory at the physical address to fillpat, then attempts to write
* back the correct information from the icache onto the physical address
*
* returns 0 for success, 1 if cache errors occured
*/
int icache_fill_writeback(uint *physaddr, uint fillpat)
{
register uint *fillptr, i, j;
volatile uint dummy;
uint fail;
/*
* words/ipline will be used a lot in this diag, so set it once
*/
j = PIL_SIZE / sizeof(uint);
/*
* failure flag - will set if parse_cacherr detects problems
*/
fail = 0;
/*
* clear out the current cache contents
*/
invalidate_caches();
/*
* fill secondary cache and set primary dcache to fill pattern
*/
fillptr = (uint *) PHYS_TO_K0(physaddr);
i = PI_SIZE / sizeof(uint);
while(i--)
*fillptr++ = fillpat;
/*
* fill the icache from the specified block (in secondary)
*/
fillptr = (uint *) PHYS_TO_K0(physaddr);
i = PI_SIZE / PIL_SIZE;
while(i--)
{
u_int old_cache_err = get_cache_err();
tag_regs_t tag_regs;
int bit, parity;
int orion = ((get_prid()&0xFF00)>>8) == C0_IMP_R4600;
int r4700 = ((get_prid()&0xFF00)>>8) == C0_IMP_R4700;
int r5000 = ((get_prid()&0xFF00)>>8) == C0_IMP_R5000;
/*
* fill current icache line from the specified block
*/
fill_ipline(fillptr);
if (old_cache_err != (old_cache_err = get_cache_err()))
fail = 1;
/*
* if we are on an R4600/R4700/R5000 we need to read the tag and then
* force it into the correct member of the set by using an
* index write which chooses the slot based on bit 13 of the
* vaddr. This is a ugly hack, but it does ensure that
* addr + 0x2000 goes into the other slot.
*/
if (orion || r4700 || r5000) {
read_tag(PRIMARYI, K0_TO_PHYS(fillptr), &tag_regs);
pi_HINV(fillptr);
write_tag(PRIMARYI, K0_TO_PHYS(fillptr), &tag_regs);
fill_ipline(fillptr);
}
if (old_cache_err != get_cache_err())
fail = 1;
fillptr += j;
}
/*
* invalidate data in primary dcache and flush fill pattern to
* secondary cache - if primary data cache is still valid, the
* icache writeback will fail
*/
fillptr = (uint *) PHYS_TO_K0(physaddr);
i = PI_SIZE / PDL_SIZE;
while(i--)
{
pd_HWBINV((uint *)fillptr);
fillptr += j;
}
/*
* flush data in secondary cache to main memory and confirm via
* uncached readback
*/
fillptr = (uint *) PHYS_TO_K0(physaddr);
/*
* XXX: a quick hack to get it to work. What really needs to happen is that
* SIDL_SIZE needs to be set to the same value as PIL_SIZE for the 4600; this
* loop only works if SIDL_SIZE is the same as or greater than PIL_SIZE,
* which is true for the R4000, but not for the R4600.
*/
i = PI_SIZE / (_sidcache_size ? SIDL_SIZE : PIL_SIZE);
while(i--)
{
/* check for a secondary cache, note the above code that
* mentions secondary cache is ok if one doesn't exist. The Pdata
* will just go straight through to memory.
*/
if( _sidcache_size) {
sd_HWB((uint)fillptr);
}
if ((dummy = *(uint *)K0_TO_K1((uint)fillptr)) != fillpat)
{
msg_printf(ERR,
"Uncached readback at 0x%x - expected 0x%x, got 0x%x\n",
(uint) K0_TO_K1((uint)fillptr), fillpat, dummy);
fail = 1;
}
fillptr += j;
}
/*
* write back the data in the primary icache to the secondary
* or to memory if secondary doesn't exist.
*/
fillptr = (uint *) PHYS_TO_K0(physaddr);
i = PI_SIZE / PIL_SIZE;
while (i--)
{
u_int old_cache_err = get_cache_err();
write_ipline (fillptr);
if (old_cache_err != get_cache_err())
fail = 1;
fillptr += j;
}
/* R.Frias added check for SC */
if( _sidcache_size) {
/*
* force writeback of data in secondary cache to phys memory
*/
fillptr = (uint *) PHYS_TO_K0(physaddr);
j = SIDL_SIZE / sizeof(uint);
i = PI_SIZE / SIDL_SIZE;
while (i--)
{
flush2ndline(K0_TO_PHYS((uint)fillptr));
fillptr += j;
}
}
return (fail);
}