Monday, January 08, 2007

Blackfin CPLB - 1

Data Structure:
======================

mach/bf537.h
-----------------
#define MAX_CPLBS (16 * 2)

/*
* Number of required data CPLB switchtable entries
* MEMSIZE / 4 (we mostly install 4M page size CPLBs
* approx 16 for smaller 1MB page size CPLBs for allignment purposes
* 1 for L1 Data Memory
* 1 for CONFIG_DEBUG_HUNT_FOR_ZERO
* 1 for ASYNC Memory
*/

#define MAX_SWITCH_D_CPLBS (((CONFIG_MEM_SIZE / 4) + 16 + 1 + 1 + 1) *
2)

/*
* Number of required instruction CPLB switchtable entries
* MEMSIZE / 4 (we mostly install 4M page size CPLBs
* approx 12 for smaller 1MB page size CPLBs for allignment purposes
* 1 for L1 Instruction Memory
* 1 for CONFIG_DEBUG_HUNT_FOR_ZERO
*/

#define MAX_SWITCH_I_CPLBS (((CONFIG_MEM_SIZE / 4) + 12 + 1 + 1) * 2)

include/asm/cplbinit.h
----------------------

enum
{ZERO_P, L1I_MEM, L1D_MEM, SDRAM_KERN , SDRAM_RAM_MTD, SDRAM_DMAZ,
RES_MEM, ASYNC_MEM, L2_MEM};

struct cplb_desc {
u32 start; /* start address */
u32 end; /* end address */
u32 psize; /* prefered size if any otherwise 1MB or 4MB*/
u16 attr;/* attributes */
u16 i_conf;/* I-CPLB DATA */
u16 d_conf;/* D-CPLB DATA */
u16 valid;/* valid */
const s8 name[30];/* name */
};

struct cplb_tab {
u_long *tab;
u16 pos;
u16 size;
};

u_long icplb_table[MAX_CPLBS+1];
u_long dcplb_table[MAX_CPLBS+1];

/* Till here we are discussing about the static memory management
model.
* However, the operating envoronments commonly define more CPLB
* descriptors to cover the entire addressable memory than will fit into
* the available on-chip 16 CPLB MMRs. When this happens, the below
table
* will be used which will hold all the potentially required CPLB
descriptors
*
* This is how Page descriptor Table is implemented in uClinux/Blackfin.
*/

u_long ipdt_table[MAX_SWITCH_I_CPLBS+1];
u_long dpdt_table[MAX_SWITCH_D_CPLBS+1];

#ifdef CONFIG_CPLB_INFO
u_long ipdt_swapcount_table[MAX_SWITCH_I_CPLBS];
u_long dpdt_swapcount_table[MAX_SWITCH_D_CPLBS];
#==endif

struct s_cplb {
struct cplb_tab init_i;
struct cplb_tab init_d;
struct cplb_tab switch_i;
struct cplb_tab switch_d;
};

static struct cplb_desc cplb_data[] = {
{
.start = 0,
.end = SIZE_4K,
.psize = SIZE_4K,
.attr = INITIAL_T | SWITCH_T | I_CPLB | D_CPLB,
.i_conf = SDRAM_OOPS,
.d_conf = SDRAM_OOPS,
#if defined(CONFIG_DEBUG_HUNT_FOR_ZERO)
.valid = 1,
#else
.valid = 0,
#==endif
.name = "ZERO Pointer Saveguard",
},
{
.start = L1_CODE_START,
.end = L1_CODE_START + L1_CODE_LENGTH,
.psize = SIZE_4M,
.attr = INITIAL_T | SWITCH_T | I_CPLB,
.i_conf = L1_IMEMORY,
.d_conf = 0,
.valid = 1,
.name = "L1 I-Memory",
},
{
.start = L1_DATA_A_START,
.end = L1_DATA_B_START + L1_DATA_B_LENGTH,
.psize = SIZE_4M,
.attr = INITIAL_T | SWITCH_T | D_CPLB,
.i_conf = 0,
.d_conf = L1_DMEMORY,
.valid = 1,
.name = "L1 D-Memory",
},
{
.start = 0,
.end = 0, /* dynamic */
.psize = 0,
.attr = INITIAL_T | SWITCH_T | I_CPLB | D_CPLB,
.i_conf = SDRAM_IGENERIC,
.d_conf = SDRAM_DGENERIC,
.valid = 1,
.name = "SDRAM Kernel",
},
{
.start = 0, /* dynamic */
.end = 0, /* dynamic */
.psize = 0,
.attr = INITIAL_T | SWITCH_T | D_CPLB,
.i_conf = SDRAM_IGENERIC,
.d_conf = SDRAM_DNON_CHBL,
.valid = 1,
.name = "SDRAM RAM MTD",
},
{
.start = 0, /* dynamic */
.end = 0, /* dynamic */
.psize = SIZE_1M,
.attr = INITIAL_T | SWITCH_T | D_CPLB,
.d_conf = SDRAM_DNON_CHBL,
.valid = 1,//(DMA_UNCACHED_REGION > 0),
.name = "SDRAM Uncached DMA ZONE",
},
{
.start = 0, /* dynamic */
.end = 0, /* dynamic */
.psize = 0,
.attr = SWITCH_T | D_CPLB,
.i_conf = 0, /* dynamic */
.d_conf = 0, /* dynamic */
.valid = 1,
.name = "SDRAM Reserved Memory",
},
{
.start = ASYNC_BANK0_BASE,
.end = ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE,
.psize = 0,
.attr = SWITCH_T | D_CPLB,
.d_conf = SDRAM_EBIU,
.valid = 1,
.name = "ASYNC Memory",
},
{
#if defined(CONFIG_BF561)
.start = L2_SRAM,
.end = L2_SRAM_END,
.psize = SIZE_1M,
.attr = SWITCH_T | D_CPLB,
.i_conf = L2_MEMORY,
.d_conf = L2_MEMORY,
.valid = 1,
#else
.valid = 0,
#==endif
.name = "L2 Memory",
}
};

Operation:

Initialize:
===============

arch/blackfin/kernel/setup.c:
------------------------------
/* Initilize bellow tables using definition in cplb_data[]

u_long icplb_table[MAX_CPLBS+1];
u_long dcplb_table[MAX_CPLBS+1];

u_long ipdt_table[MAX_SWITCH_I_CPLBS+1];
u_long dpdt_table[MAX_SWITCH_D_CPLBS+1];

*/

static unsigned short __init
fill_cplbtab(struct cplb_tab *table,
unsigned long start, unsigned long end,
unsigned long block_size, unsigned long cplb_data)
{
}

static unsigned short __init
close_cplbtab(struct cplb_tab *table)
{
}

static void __init generate_cpl_tables(void)
{

}

mach-common/cacheinit.S:
-------------------------

/* Copy icplb_table[], dcplb_table[] to CPLB MMRs */

Note that when the Cache is disabled, the CPLB MMRs will not be filled.

#if defined(CONFIG_BLKFIN_CACHE)
ENTRY(_bfin_icache_init)

/* Initialize Instruction CPLBS */

I0.L = (ICPLB_ADDR0 & 0xFFFF);
I0.H = (ICPLB_ADDR0 >> 16);

I1.L = (ICPLB_DATA0 & 0xFFFF);
I1.H = (ICPLB_DATA0 >> 16);

I2.L = _icplb_table;
I2.H = _icplb_table;

r1 = -1; /* end point comparison */
r3 = 15; /* max counter */

/* read entries from table */

.Lread_iaddr:
R0 = [I2++];
CC = R0 == R1;
IF CC JUMP .Lidone;
[I0++] = R0;

.Lread_idata:
R2 = [I2++];
[I1++] = R2;
R3 = R3 + R1;
CC = R3 == R1;
IF !CC JUMP .Lread_iaddr;

.Lidone:
/* Enable Instruction Cache */
P0.l = (IMEM_CONTROL & 0xFFFF);
P0.h = (IMEM_CONTROL >> 16);
R1 = [P0];
R0 = (IMC | ENICPLB);
R0 = R0 | R1;

/* Anomaly 05000125 */
CLI R2;
SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
.align 8;
[P0] = R0;
SSYNC;
STI R2;
RTS;
#==endif

#if defined(CONFIG_BLKFIN_DCACHE)
ENTRY(_bfin_dcache_init)

/* Initialize Data CPLBS */

I0.L = (DCPLB_ADDR0 & 0xFFFF);
I0.H = (DCPLB_ADDR0 >> 16);

I1.L = (DCPLB_DATA0 & 0xFFFF);
I1.H = (DCPLB_DATA0 >> 16);

I2.L = _dcplb_table;
I2.H = _dcplb_table;

R1 = -1; /* end point comparison */
R3 = 15; /* max counter */

/* read entries from table */
.Lread_daddr:
R0 = [I2++];
cc = R0 == R1;
IF CC JUMP .Lddone;
[I0++] = R0;

.Lread_ddata:
R2 = [I2++];
[I1++] = R2;
R3 = R3 + R1;
CC = R3 == R1;
IF !CC JUMP .Lread_daddr;
.Lddone:
P0.L = (DMEM_CONTROL & 0xFFFF);
P0.H = (DMEM_CONTROL >> 16);
R1 = [P0];

R0 = DMEM_CNTR;

R0 = R0 | R1;
/* Anomaly 05000125 */
CLI R2;
SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */
.align 8;
[P0] = R0;
SSYNC;
STI R2;
RTS;
#==endif

Replacement:
================

Exception -> entry(trap)->extable:

mach-common/entry.S
--------------------

ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor
mode)*/
/* Since the kernel stack can be anywhere, it's not guaranteed
to be
* covered by a CPLB. Switch to an exception stack; use RETN as
a
* scratch register (for want of a better option).
*/
retn = sp;
sp.l = _exception_stack_top;
sp.h = _exception_stack_top;
/* Try to deal with syscalls quickly. */
[--sp] = ASTAT;
[--sp] = (R7:6, P5:4);
DEBUG_STOP_HWTRACE
r7 = SEQSTAT; /* reason code is in bit 5:0 */
r6.l = lo(SEQSTAT_EXCAUSE);
r6.h = hi(SEQSTAT_EXCAUSE);
r7 = r7 & r6;
p5.h = _extable;
p5.l = _extable;
p4 = r7;
p5 = p5 + (p4 << 2);
p4 = [p5];
jump (p4);

mach_common/entry.S:
--------------------
extable: (64 entry - corresponds to SEQSTAT[EXCAUSE] (0-5))

/*
* Put these in the kernel data section - that should always be covered
by
* a CPLB. This is needed to ensure we don't get double fault conditions
*/

.data
ALIGN
_extable:
/* entry for each EXCAUSE[5:0]
* This table bmust be in sync with the table
in ./kernel/traps.c
* EXCPT instruction can provide 4 bits of EXCAUSE, allowing 16
to be user defined
*/
/-------------------
The Force Exception instruction forces an exception with code uimm4.
When the EXCPT instruction is issued, the sequencer vectors to the
exception handler that the user provides.
Application-level code uses the Force Exception instruction for
operating system calls. The instruction does not set the EVSW bit (bit
3) of the ILAT register.
--------------------/

.long _ex_syscall; /* 0x00 - User Defined - Linux Syscall
*/
.long _ex_soft_bp /* 0x01 - User Defined - Software
breakpoint */
.long _ex_trap_c /* 0x02 - User Defined */
.long _ex_trap_c /* 0x03 - User Defined - Atomic test
and set service */
.long _ex_spinlock /* 0x04 - User Defined */
.long _ex_trap_c /* 0x05 - User Defined */
.long _ex_trap_c /* 0x06 - User Defined */
.long _ex_trap_c /* 0x07 - User Defined */
.long _ex_trap_c /* 0x08 - User Defined */
.long _ex_trap_c /* 0x09 - User Defined */
.long _ex_trap_c /* 0x0A - User Defined */
.long _ex_trap_c /* 0x0B - User Defined */
.long _ex_trap_c /* 0x0C - User Defined */
.long _ex_trap_c /* 0x0D - User Defined */
.long _ex_trap_c /* 0x0E - User Defined */
.long _ex_trap_c /* 0x0F - User Defined */
.long _ex_single_step /* 0x10 - HW Single step */
.long _ex_trap_c /* 0x11 - Trace Buffer Full */
.long _ex_trap_c /* 0x12 - Reserved */
.long _ex_trap_c /* 0x20 - Reserved */
<snip>
.long _ex_trap_c /* 0x21 - Undefined Instruction */
.long _ex_trap_c /* 0x22 - Illegal Instruction
Combination */
.long _ex_dcplb /* 0x23 - Data CPLB Protection Violation
*/
.long _ex_trap_c /* 0x24 - Data access misaligned */
.long _ex_trap_c /* 0x25 - Unrecoverable Event */
.long _ex_dcplb /* 0x26 - Data CPLB Miss */
.long _ex_trap_c /* 0x27 - Data CPLB Multiple Hits -
Linux Trap Zero */
.long _ex_trap_c /* 0x28 - Emulation Watchpoint */
.long _ex_trap_c /* 0x29 - Instruction fetch access error
(535 only) */
.long _ex_trap_c /* 0x2A - Instruction fetch misaligned
*/
.long _ex_icplb /* 0x2B - Instruction CPLB protection
Violation */
.long _ex_icplb /* 0x2C - Instruction CPLB miss */
.long _ex_trap_c /* 0x2D - Instruction CPLB Multiple Hits
*/
.long _ex_trap_c /* 0x2E - Illegal use of Supervisor
Resource */
.long _ex_trap_c /* 0x2E - Illegal use of Supervisor
Resource */
<snip>

/* Slightly simplified and streamlined entry point for CPLB misses.
* This one does not lower the level to IRQ5, and thus can be used to
* patch up CPLB misses on the kernel stack.
*/
ENTRY(_ex_dcplb)
/*
* Work around an anomaly: if we see a new DCPLB fault, return
* without doing anything. Then, if we get the same fault
again,
* handle it.
*/
p5.l = _last_cplb_fault_retx;
p5.h = _last_cplb_fault_retx;
r7 = [p5];
r6 = retx;
[p5] = r6;
cc = r6 == r7;
if !cc jump _return_from_exception;
/* fall through */
/-------------------------
/* Used by the assembly entry point to work around an anomaly. */
_last_cplb_fault_retx:
.long 0;
--------------------------/

ENTRY(_ex_icplb)
(R7:6,P5:4) = [sp++];
ASTAT = [sp++];
SAVE_ALL_SYS
call __cplb_hdr;
DEBUG_START_HWTRACE
RESTORE_ALL_SYS
SP = RETN;
rtx;

ENTRY(_handle_bad_cplb)
/* To get here, we just tried and failed to change a CPLB
* so, handle things in trap_c (C code), by lowering to
* IRQ5, just like we normally do. Since this is not a
* "normal" return path, we have a do alot of stuff to
* the stack to get ready so, we can fall through - we
* need to make a CPLB exception look like a normal exception
*/

DEBUG_START_HWTRACE
RESTORE_ALL_SYS
[--sp] = ASTAT;
[--sp] = (R7:6, P5:4);

ENTRY(_ex_trap_c)
/* Call C code (trap_c) to handle the exception, which most
* likely involves sending a signal to the current process.
* To avoid double faults, lower our priority to IRQ5 first.
*/
P5.h = _exception_to_level5;
P5.l = _exception_to_level5;
p4.l = lo(EVT5);
p4.h = hi(EVT5);
[p4] = p5;
csync;

/* Disable all interrupts, but make sure level 5 is enabled so
* we can switch to that level. Save the old mask. */
cli r6;
p4.l = _excpt_saved_imask;
p4.h = _excpt_saved_imask;
[p4] = r6;
r6 = 0x3f;
sti r6;

/* Save the excause into a circular buffer, in case the
instruction
* which caused this excecptions causes others.
*/
P5.l = _in_ptr_excause;
P5.h = _in_ptr_excause;
R7 = [P5];
R7 += 4;
R6 = 0xF;
R7 = R7 & R6;
[P5] = R7;
R6.l = _excause_circ_buf;
R6.h = _excause_circ_buf;
R7 = R7 + R6;
p5 = R7;
R6 = SEQSTAT;
[P5] = R6;

DEBUG_START_HWTRACE
(R7:6,P5:4) = [sp++];
ASTAT = [sp++];
SP = RETN;
raise 5;
rtx;

mach-common/cplbhdlr.S
---------------------------

.text

.type _cplb_mgr, STT_FUNC;
.type _panic_cplb_error, STT_FUNC;

.align 2

.global __cplb_hdr;
.type __cplb_hdr, STT_FUNC;
ENTRY(__cplb_hdr)
R2 = SEQSTAT;

/* Mask the contents of SEQSTAT and leave only EXCAUSE in R2 */
R2 <<= 26;
R2 >>= 26;

R1 = 0x23; /* Data access CPLB protection violation */
CC = R2 == R1;
IF !CC JUMP .Lnot_data_write;
R0 = 2; /* is a write to data space*/
JUMP .Lis_icplb_miss;

.Lnot_data_write:
R1 = 0x2C; /* CPLB miss on an instruction fetch */
CC = R2 == R1;
R0 = 0; /* is_data_miss == False*/
IF CC JUMP .Lis_icplb_miss;

R1 = 0x26;
CC = R2 == R1;
IF !CC JUMP .Lunknown;

R0 = 1; /* is_data_miss == True*/

.Lis_icplb_miss:

#if defined(CONFIG_BLKFIN_CACHE) || defined(CONFIG_BLKFIN_DCACHE)
# if defined(CONFIG_BLKFIN_CACHE) && !defined(CONFIG_BLKFIN_DCACHE)
R1 = CPLB_ENABLE_ICACHE;
#==endif
# if !defined(CONFIG_BLKFIN_CACHE) && defined(CONFIG_BLKFIN_DCACHE)
R1 = CPLB_ENABLE_DCACHE;
#==endif
# if defined(CONFIG_BLKFIN_CACHE) && defined(CONFIG_BLKFIN_DCACHE)
R1 = CPLB_ENABLE_DCACHE | CPLB_ENABLE_ICACHE;
#==endif
#else
R1 = 0;
#==endif

[--SP] = RETS;
CALL _cplb_mgr;
RETS = [SP++];
CC = R0 == 0;
IF !CC JUMP .Lnot_replaced;
RTS;

/*
* Diagnostic exception handlers
*/
.Lunknown:
R0 = CPLB_UNKNOWN_ERR;
JUMP .Lcplb_error;

.Lnot_replaced:
CC = R0 == CPLB_NO_UNLOCKED;
IF !CC JUMP .Lnext_check;
R0 = CPLB_NO_UNLOCKED;
JUMP .Lcplb_error;

.Lnext_check:
CC = R0 == CPLB_NO_ADDR_MATCH;
IF !CC JUMP .Lnext_check2;
R0 = CPLB_NO_ADDR_MATCH;
JUMP .Lcplb_error;

.Lnext_check2:
CC = R0 == CPLB_PROT_VIOL;
IF !CC JUMP .Lstrange_return_from_cplb_mgr;
R0 = CPLB_PROT_VIOL;
JUMP .Lcplb_error;

.Lstrange_return_from_cplb_mgr:
IDLE;
CSYNC;
JUMP .Lstrange_return_from_cplb_mgr;

.Lcplb_error:
R1 = sp;
SP += -12;
call _panic_cplb_error;
SP += 12;
JUMP _handle_bad_cplb;

mach-common/cplbmgr.S
-----------------------------

/* Usage: int _cplb_mgr(is_data_miss,int enable_cache)
* is_data_miss==2 => Mark as Dirty, write to the clean data page
* is_data_miss==1 => Replace a data CPLB.
* is_data_miss==0 => Replace an instruction CPLB.
*
* Returns:
* CPLB_RELOADED => Successfully updated CPLB table.
* CPLB_NO_UNLOCKED => All CPLBs are locked, so cannot be evicted.
* This indicates that the CPLBs in the configuration
* tablei are badly configured, as this should never
* occur.
* CPLB_NO_ADDR_MATCH => The address being accessed, that triggered the
* exception, is not covered by any of the CPLBs in
* the configuration table. The application is
* presumably misbehaving.
* CPLB_PROT_VIOL => The address being accessed, that triggered the
* exception, was not a first-write to a clean Write
* Back Data page, and so presumably is a genuine
* violation of the page's protection attributes.
* The application is misbehaving.
*/

.align 2;
ENTRY(_cplb_mgr)

[--SP]=( R7:4,P5:3 );

CC = R0 == 2;
IF CC JUMP .Ldcplb_write;

CC = R0 == 0;
IF !CC JUMP .Ldcplb_miss_compare;

/* ICPLB Miss Exception. We need to choose one of the
* currently-installed CPLBs, and replace it with one
* from the configuration table.
*/

P4.L = (ICPLB_FAULT_ADDR & 0xFFFF);
P4.H = (ICPLB_FAULT_ADDR >> 16);

P1 = 16;
P5.L = _page_size_table;
P5.H = _page_size_table;

P0.L = (ICPLB_DATA0 & 0xFFFF);
P0.H = (ICPLB_DATA0 >> 16);
R4 = [P4]; /* Get faulting address*/
R6 = 64; /* Advance past the fault address, which*/
R6 = R6 + R4; /* we'll use if we find a match*/
R3 = ((16 << 8) | 2); /* Extract mask, bits 16 and 17.*/

R5 = 0;
.Lisearch:

R1 = [P0-0x100]; /* Address for this CPLB */

R0 = [P0++]; /* Info for this CPLB*/
CC = BITTST(R0,0); /* Is the CPLB valid?*/
IF !CC JUMP .Lnomatch; /* Skip it, if not.*/
CC = R4 < R1(IU); /* If fault address less than page start*/
IF CC JUMP .Lnomatch; /* then skip this one.*/
R2 = EXTRACT(R0,R3.L) (Z); /* Get page size*/
P1 = R2;
P1 = P5 + (P1<<2); /* index into page-size table*/
R2 = [P1]; /* Get the page size*/
R1 = R1 + R2; /* and add to page start, to get page end*/
CC = R4 < R1(IU); /* and see whether fault addr is in page.*/
IF !CC R4 = R6; /* If so, advance the address and finish loop.*/
IF !CC JUMP .Lisearch_done;
.Lnomatch:
/* Go around again*/
R5 += 1;
CC = BITTST(R5, 4); /* i.e CC = R5 >= 16*/
IF !CC JUMP .Lisearch;

.Lisearch_done:
I0 = R4; /* Fault address we'll search for*/

/* set up pointers */
P0.L = (ICPLB_DATA0 & 0xFFFF);
P0.H = (ICPLB_DATA0 >> 16);

/* The replacement procedure for ICPLBs */

P4.L = (IMEM_CONTROL & 0xFFFF);
P4.H = (IMEM_CONTROL >> 16);

/* disable cplbs */
R5 = [P4]; /* Control Register*/
BITCLR(R5,ENICPLB_P);
CLI R1;
SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
.align 8;
[P4] = R5;
SSYNC;
STI R1;

R1 = -1; /* end point comparison */
R3 = 16; /* counter */

/* Search through CPLBs for first non-locked entry */
/* Overwrite it by moving everyone else up by 1 */
.Licheck_lock:
R0 = [P0++];
R3 = R3 + R1;
CC = R3 == R1;
IF CC JUMP .Lall_locked;
CC = BITTST(R0, 0); /* an invalid entry is good */
IF !CC JUMP .Lifound_victim;
CC = BITTST(R0,1); /* but a locked entry isn't */
IF CC JUMP .Licheck_lock;

.Lifound_victim:
#ifdef CONFIG_CPLB_INFO
R7 = [P0 - 0x104];
P2.L = _ipdt_table;
P2.H = _ipdt_table;
P3.L = _ipdt_swapcount_table;
P3.H = _ipdt_swapcount_table;
P3 += -4;
.Licount:
R2 = [P2]; /* address from config table */
P2 += 8;
P3 += 8;
CC = R2==-1;
IF CC JUMP .Licount_done;
CC = R7==R2;
IF !CC JUMP .Licount;
R7 = [P3];
R7 += 1;
[P3] = R7;
CSYNC;
.Licount_done:
#==endif
LC0=R3;
LSETUP(.Lis_move,.Lie_move) LC0;
.Lis_move:
R0 = [P0];
[P0 - 4] = R0;
R0 = [P0 - 0x100];
[P0-0x104] = R0;
.Lie_move:P0+=4;

/* We've made space in the ICPLB table, so that ICPLB15
* is now free to be overwritten. Next, we have to determine
* which CPLB we need to install, from the configuration
* table. This is a matter of getting the start-of-page
* addresses and page-lengths from the config table, and
* determining whether the fault address falls within that
* range.
*/

P2.L = _ipdt_table;
P2.H = _ipdt_table;
#ifdef CONFIG_CPLB_INFO
P3.L = _ipdt_swapcount_table;
P3.H = _ipdt_swapcount_table;
P3 += -8;
#==endif
P0.L = _page_size_table;
P0.H = _page_size_table;

/* Retrieve our fault address (which may have been advanced
* because the faulting instruction crossed a page boundary).
*/

R0 = I0;

/* An extraction pattern, to get the page-size bits from
* the CPLB data entry. Bits 16-17, so two bits at posn 16.
*/

R1 = ((16<<8)|2);
.Linext: R4 = [P2++]; /* address from config table */
R2 = [P2++]; /* data from config table */
#ifdef CONFIG_CPLB_INFO
P3 += 8;
#==endif

CC = R4 == -1; /* End of config table*/
IF CC JUMP .Lno_page_in_table;

/* See if failed address > start address */
CC = R4 <= R0(IU);
IF !CC JUMP .Linext;

/* extract page size (17:16)*/
R3 = EXTRACT(R2, R1.L) (Z);

/* add page size to addr to get range */

P5 = R3;
P5 = P0 + (P5 << 2); /* scaled, for int access*/
R3 = [P5];
R3 = R3 + R4;

/* See if failed address < (start address + page size) */
CC = R0 < R3(IU);
IF !CC JUMP .Linext;

/* We've found a CPLB in the config table that covers
* the faulting address, so install this CPLB into the
* last entry of the table.
*/

P1.L = (ICPLB_DATA15 & 0xFFFF); /* ICPLB_DATA15 */
P1.H = (ICPLB_DATA15 >> 16);
[P1] = R2;
[P1-0x100] = R4;
#ifdef CONFIG_CPLB_INFO
R3 = [P3];
R3 += 1;
[P3] = R3;
#==endif

/* P4 points to IMEM_CONTROL, and R5 contains its old
* value, after we disabled ICPLBS. Re-enable them.
*/

BITSET(R5,ENICPLB_P);
CLI R2;
SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
.align 8;
[P4] = R5;
SSYNC;
STI R2;

( R7:4,P5:3 ) = [SP++];
R0 = CPLB_RELOADED;
RTS;

/* FAILED CASES*/
.Lno_page_in_table:
( R7:4,P5:3 ) = [SP++];
R0 = CPLB_NO_ADDR_MATCH;
RTS;
.Lall_locked:
( R7:4,P5:3 ) = [SP++];
R0 = CPLB_NO_UNLOCKED;
RTS;
.Lprot_violation:
( R7:4,P5:3 ) = [SP++];
R0 = CPLB_PROT_VIOL;
RTS;

.Ldcplb_write:

/* if a DCPLB is marked as write-back (CPLB_WT==0), and
* it is clean (CPLB_DIRTY==0), then a write to the
* CPLB's page triggers a protection violation. We have to
* mark the CPLB as dirty, to indicate that there are
* pending writes associated with the CPLB.
*/

P4.L = (DCPLB_STATUS & 0xFFFF);
P4.H = (DCPLB_STATUS >> 16);
P3.L = (DCPLB_DATA0 & 0xFFFF);
P3.H = (DCPLB_DATA0 >> 16);
R5 = [P4];

/* A protection violation can be caused by more than just writes
* to a clean WB page, so we have to ensure that:
* - It's a write
* - to a clean WB page
* - and is allowed in the mode the access occurred.
*/

CC = BITTST(R5, 16); /* ensure it was a write*/
IF !CC JUMP .Lprot_violation;

/* to check the rest, we have to retrieve the DCPLB.*/

/* The low half of DCPLB_STATUS is a bit mask*/

R2 = R5.L (Z); /* indicating which CPLB triggered the event.*/
R3 = 30; /* so we can use this to determine the offset*/
R2.L = SIGNBITS R2;
R2 = R2.L (Z); /* into the DCPLB table.*/
R3 = R3 - R2;
P4 = R3;
P3 = P3 + (P4<<2);
R3 = [P3]; /* Retrieve the CPLB*/

/* Now we can check whether it's a clean WB page*/

CC = BITTST(R3, 14); /* 0==WB, 1==WT*/
IF CC JUMP .Lprot_violation;
CC = BITTST(R3, 7); /* 0 == clean, 1 == dirty*/
IF CC JUMP .Lprot_violation;

/* Check whether the write is allowed in the mode that was active.*/

R2 = 1<<3; /* checking write in user mode*/
CC = BITTST(R5, 17); /* 0==was user, 1==was super*/
R5 = CC;
R2 <<= R5; /* if was super, check write in super mode*/
R2 = R3 & R2;
CC = R2 == 0;
IF CC JUMP .Lprot_violation;

/* It's a genuine write-to-clean-page.*/

BITSET(R3, 7); /* mark as dirty*/
[P3] = R3; /* and write back.*/
NOP;
CSYNC;
( R7:4,P5:3 ) = [SP++];
R0 = CPLB_RELOADED;
RTS;

.Ldcplb_miss_compare:

/* Data CPLB Miss event. We need to choose a CPLB to
* evict, and then locate a new CPLB to install from the
* config table, that covers the faulting address.
*/

P1.L = (DCPLB_DATA15 & 0xFFFF);
P1.H = (DCPLB_DATA15 >> 16);

P4.L = (DCPLB_FAULT_ADDR & 0xFFFF);
P4.H = (DCPLB_FAULT_ADDR >> 16);
R4 = [P4];
I0 = R4;

/* The replacement procedure for DCPLBs*/

R6 = R1; /* Save for later*/

/* Turn off CPLBs while we work.*/
P4.L = (DMEM_CONTROL & 0xFFFF);
P4.H = (DMEM_CONTROL >> 16);
R5 = [P4];
BITCLR(R5,ENDCPLB_P);
CLI R0;
SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */
.align 8;
[P4] = R5;
SSYNC;
STI R0;

/* Start looking for a CPLB to evict. Our order of preference
* is: invalid CPLBs, clean CPLBs, dirty CPLBs. Locked CPLBs
* are no good.
*/

I1.L = (DCPLB_DATA0 & 0xFFFF);
I1.H = (DCPLB_DATA0 >> 16);
P1 = 2;
P2 = 16;
I2.L = _dcplb_preference;
I2.H = _dcplb_preference;
LSETUP(.Lsdsearch1, .Ledsearch1) LC0 = P1;
.Lsdsearch1:
R0 = [I2++]; /* Get the bits we're interested in*/
P0 = I1; /* Go back to start of table*/
LSETUP (.Lsdsearch2, .Ledsearch2) LC1 = P2;
.Lsdsearch2:
R1 = [P0++]; /* Fetch each installed CPLB in turn*/
R2 = R1 & R0; /* and test for interesting bits.*/
CC = R2 == 0; /* If none are set, it'll do.*/
IF !CC JUMP .Lskip_stack_check;

R2 = [P0 - 0x104]; /* R2 - PageStart */
P3.L = _page_size_table; /* retrieve end address */
P3.H = _page_size_table; /* retrieve end address */
R3 = 0x1002; /* 16th - position, 2 bits -length */
nop; /* Anomaly 05000209 */
R7 = EXTRACT(R1,R3.l);
R7 = R7 << 2; /* Page size index offset */
P5 = R7;
P3 = P3 + P5;
R7 = [P3]; /* page size in bytes */

R7 = R2 + R7; /* R7 - PageEnd */
R4 = SP; /* Test SP is in range */

CC = R7 < R4; /* if PageEnd < SP */
IF CC JUMP .Ldfound_victim;
R3 = 0x284; /* stack length from start of trap till
* the point.
* 20 stack locations for future modifications
*/
R4 = R4 + R3;
CC = R4 < R2; /* if SP + stacklen < PageStart */
IF CC JUMP .Ldfound_victim;
.Lskip_stack_check:

.Ledsearch2: NOP;
.Ledsearch1: NOP;

/* If we got here, we didn't find a DCPLB we considered
* replacable, which means all of them were locked.
*/

JUMP .Lall_locked;
.Ldfound_victim:

#ifdef CONFIG_CPLB_INFO
R7 = [P0 - 0x104];
P2.L = _dpdt_table;
P2.H = _dpdt_table;
P3.L = _dpdt_swapcount_table;
P3.H = _dpdt_swapcount_table;
P3 += -4;
.Ldicount:
R2 = [P2];
P2 += 8;
P3 += 8;
CC = R2==-1;
IF CC JUMP .Ldicount_done;
CC = R7==R2;
IF !CC JUMP .Ldicount;
R7 = [P3];
R7 += 1;
[P3] = R7;
.Ldicount_done:
#==endif

/* Clean down the hardware loops*/
R2 = 0;
LC1 = R2;
LC0 = R2;

/* There's a suitable victim in [P0-4] (because we've
* advanced already).
*/

.LDdoverwrite:

/* [P0-4] is a suitable victim CPLB, so we want to
* overwrite it by moving all the following CPLBs
* one space closer to the start.
*/

R1.L = (DCPLB_DATA16 & 0xFFFF); /* DCPLB_DATA15 + 4 */
R1.H = (DCPLB_DATA16 >> 16);
R0 = P0;

/* If the victim happens to be in DCPLB15,
* we don't need to move anything.
*/

CC = R1 == R0;
IF CC JUMP .Lde_moved;
R1 = R1 - R0;
R1 >>= 2;
P1 = R1;
LSETUP(.Lds_move, .Lde_move) LC0=P1;
.Lds_move:
R0 = [P0++]; /* move data */
[P0 - 8] = R0;
R0 = [P0-0x104] /* move address */
.Lde_move: [P0-0x108] = R0;

/* We've now made space in DCPLB15 for the new CPLB to be
* installed. The next stage is to locate a CPLB in the
* config table that covers the faulting address.
*/

.Lde_moved:NOP;
R0 = I0; /* Our faulting address */

P2.L = _dpdt_table;
P2.H = _dpdt_table;
#ifdef CONFIG_CPLB_INFO
P3.L = _dpdt_swapcount_table;
P3.H = _dpdt_swapcount_table;
P3 += -8;
#==endif

P1.L = _page_size_table;
P1.H = _page_size_table;

/* An extraction pattern, to retrieve bits 17:16.*/

R1 = (16<<8)|2;
.Ldnext: R4 = [P2++]; /* address */
R2 = [P2++]; /* data */
#ifdef CONFIG_CPLB_INFO
P3 += 8;
#==endif

CC = R4 == -1;
IF CC JUMP .Lno_page_in_table;

/* See if failed address > start address */
CC = R4 <= R0(IU);
IF !CC JUMP .Ldnext;

/* extract page size (17:16)*/
R3 = EXTRACT(R2, R1.L) (Z);

/* add page size to addr to get range */

P5 = R3;
P5 = P1 + (P5 << 2);
R3 = [P5];
R3 = R3 + R4;

/* See if failed address < (start address + page size) */
CC = R0 < R3(IU);
IF !CC JUMP .Ldnext;

/* We've found the CPLB that should be installed, so
* write it into CPLB15, masking off any caching bits
* if necessary.
*/

P1.L = (DCPLB_DATA15 & 0xFFFF);
P1.H = (DCPLB_DATA15 >> 16);

/* If the DCPLB has cache bits set, but caching hasn't
* been enabled, then we want to mask off the cache-in-L1
* bit before installing. Moreover, if caching is off, we
* also want to ensure that the DCPLB has WT mode set, rather
* than WB, since WB pages still trigger first-write exceptions
* even when not caching is off, and the page isn't marked as
* cachable. Finally, we could mark the page as clean, not dirty,
* but we choose to leave that decision to the user; if the user
* chooses to have a CPLB pre-defined as dirty, then they always
* pay the cost of flushing during eviction, but don't pay the
* cost of first-write exceptions to mark the page as dirty.
*/

#ifdef CONFIG_BLKFIN_WT
BITSET(R6, 14); /* Set WT*/
#=e=ndif

[P1] = R2;
[P1-0x100] = R4;
#ifdef CONFIG_CPLB_INFO
R3 = [P3];
R3 += 1;
[P3] = R3;
#==endif

/* We've installed the CPLB, so re-enable CPLBs. P4
* points to DMEM_CONTROL, and R5 is the value we
* last wrote to it, when we were disabling CPLBs.
*/

BITSET(R5,ENDCPLB_P);
CLI R2;
.align 8;
[P4] = R5;
SSYNC;
STI R2;

( R7:4,P5:3 ) = [SP++];
R0 = CPLB_RELOADED;
RTS;

/proc/cplbinfo
================
root:/mnt> cat /proc/cplbinfo
------------------ CPLB Information ------------------

Instrction CPLB entry:
Address Data Size Valid Locked Swapin iCount oCount
0x00000000 0x10283 4K Y Y 0 0 0
0xffa00000 0x30007 4M Y Y 1 0 0
0x00000000 0x31287 4M Y Y 2 0 0
0x00400000 0x31205 4M Y N 3 0 0
0x00800000 0x31205 4M Y N 4 0 0
0x00c00000 0x31205 4M Y N 5 0 0
0x01000000 0x31205 4M Y N 6 0 0
0x01400000 0x31205 4M Y N 7 0 0
0x01800000 0x31205 4M Y N 8 0 0
0x01c00000 0x31205 4M Y N 9 0 0
0x02000000 0x31205 4M Y N 10 0 0
0x02400000 0x31205 4M Y N 11 0 0
0x02800000 0x31205 4M Y N 12 0 0
0x02c00000 0x31205 4M Y N 13 0 0
0x03000000 0x31205 4M Y N 14 0 0
0x03400000 0x31205 4M Y N 15 0 0

Data CPLB entry:
Address Data Size Valid Locked Swapin iCount oCount
0x00000000 0x10283 4K Y Y 0 0 0
0xff800000 0x3029d 4M Y N 8 19 19
0x00000000 0x3129f 4M Y Y 1 0 0
0x00400000 0x3129d 4M Y N 7 20 20
0x00800000 0x3129d 4M Y N 9 20 20
0x00c00000 0x3129d 4M Y N 5 19 19
0x01000000 0x3129d 4M Y N 14 18 18
0x01400000 0x3129d 4M Y N 3 14 14
0x01800000 0x3129d 4M Y N 2 14 14
0x01c00000 0x3129d 4M Y N -1 0 1
0x02000000 0x3129d 4M Y N -1 18 19
0x02400000 0x3129d 4M Y N -1 2 3
0x02800000 0x3129d 4M Y N 10 19 19
0x02c00000 0x3129d 4M Y N -1 18 19
0x03000000 0x3129d 4M Y N 11 20 20
0x03400000 0x2129d 1M Y N 12 20 20
0x03500000 0x2129d 1M Y N 6 8 7
0x03600000 0x2129d 1M Y N 13 21 20
0x03700000 0x2029d 1M Y N -1 9 9
0x03800000 0x3029d 4M Y N -1 1 1
0x03c00000 0x2029d 1M Y N -1 1 1
0x03d00000 0x2029d 1M Y N -1 1 1
0x03e00000 0x2029d 1M Y N -1 1 1
0x03f00000 0x2029d 1M Y N 4 20 19
0x20000000 0x3029d 4M Y N 15 20 19

Blog Archive