bf561中提供了16K的指令Cache和32K的数据Cache,在uClinux中可以配置为使用Cache也可以不使用。
1 CPLB初始化
cplb的初始化代码在arch/blackfin/kenel/setup.c中,在uClinux初始化的时候将调用setup_arch,在此函数中又调用了
bf53x_cache_init();
进行初始化,下面就是这个函数的内容:
void __init bf53x_cache_init(void)
{
#if defined(CONFIG_BLKFIN_DCACHE) || defined(CONFIG_BLKFIN_CACHE)
generate_cpl_tables();
#endif
#ifdef CONFIG_BLKFIN_CACHE
bfin_icache_init();
printk(KERN_INFO "Instruction Cache Enabled/n");
#endif
#ifdef CONFIG_BLKFIN_DCACHE
bfin_dcache_init();
printk(KERN_INFO "Data Cache Enabled"
# if defined CONFIG_BLKFIN_WB
" (write-back)"
# elif defined CONFIG_BLKFIN_WT
" (write-through)"
# endif
"/n");
#endif
}
从上述代码中可以很容易发现与CPLB相关的几个宏定义。正是通过这些宏,可以很容易配置uClinux中的CPLB应用。
下面再看看generate_cpl_table中生成的缓存区域,以下数据定义放在include/asm-blackfin/cplbinit.h中。
在uClinux内核中,使用了如下的结构来表示每一块缓存区域:
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 */
};
然后定义了一个数组对这些区域进行描述:
static struct cplb_desc cplb_data[] = {
{
…
};
总共分为9块区域,可以通过
enum
{ZERO_P, L1I_MEM, L1D_MEM, SDRAM_KERN , SDRAM_RAM_MTD, SDRAM_DMAZ, RES_MEM, ASYNC_MEM, L2_MEM};
进行访问。
这个数组是不能直接挂到BF561上的,因此还必须将之转换为bf561可以接受的数据结构(icplb_table和dcplb_table)。generate_cpl_table这个函数的作用就是填充cplb_data这个数组的内容并将之转换为BF561可以接受的数据结构。由于Cache的页面大小的限制,对每一块内存区域可能需要用多个页面描述结构来表示。
执行完generate_cpl_table后cplb_data的典型值为:
Start
|
End
|
psize
|
Name
|
0x0000 0000
|
0x0000 0400
|
0x0000 0400
|
ZERO Pointer Saveguard
|
0xfa00 0000
|
0xffa0 4000
|
0x0040 0000
|
L1 I-Memory
|
0xff80 0000
|
0xff90 4000
|
0x0040 0000
|
L1 D-Memory
|
0x0000 0000
|
0x0350 0000
|
0x0000 0000
|
SDRAM Kernel
|
0x0350 0000
|
0x03f0 0000
|
0x0000 0000
|
SDRAM RAM MTD
|
0x03f0 0000
|
0x0400 0000
|
0x0010 0000
|
SDRAM Uncached DMA ZONE
|
0x0400 0000
|
0x0400 0000
|
0x0000 0000
|
SDRAM Reserved Memory
|
0x2000 0000
|
0x3000 0000
|
0x0000 0000
|
ASYNC Memory
|
0xfeb0 0000
|
0xfeb2 0000
|
0x0010 0000
|
L2 Memory
|
icplb_table和dcplb_table的值为:
ICPLB_ADDRx
|
ICPLB_DATAx
|
DCPLB_ADDRx
|
DCPLB_DATAx
|
0xffa0 0000
|
0x0003 0007
|
0xff80 0000
|
0x0003 029f
|
0x0000 0000
|
0x0003 1287
|
0x0000 0000
|
0x0003 d29f
|
0x0040 0000
|
0x0003 1205
|
0x0040 0000
|
0x0003 d29d
|
0x0080 0000
|
0x0003 1205
|
0x0080 0000
|
0x0003 d29d
|
0x00c0 0000
|
0x0003 1205
|
0x00c0 0000
|
0x0003 d29d
|
0x0100 0000
|
0x0003 1205
|
0x0100 0000
|
0x0003 d29d
|
0x0140 0000
|
0x0003 1205
|
0x0140 0000
|
0x0003 d29d
|
0x0180 0000
|
0x0003 1205
|
0x0180 0000
|
0x0003 d29d
|
0x01c0 0000
|
0x0003 1205
|
0x01c0 0000
|
0x0003 d29d
|
0x0200 0000
|
0x0003 1205
|
0x0200 0000
|
0x0003 d29d
|
0x0240 0000
|
0x0003 1205
|
0x0240 0000
|
0x0003 d29d
|
0x0280 0000
|
0x0003 1205
|
0x0280 0000
|
0x0003 d29d
|
0x02c0 0000
|
0x0003 1205
|
0x02c0 0000
|
0x0003 d29d
|
0x0300 0000
|
0x0003 1205
|
0x0300 0000
|
0x0003 d29d
|
0x0340 0000
|
0x0003 1205
|
0x0340 0000
|
0x0003 d29d
|
0x0000 0000
|
0x0000 0000
|
0x0350 0000
|
0x0002 029d
|
2 异常处理
uClinux的异常入口为arch/blackfin/mach-common/entry.s中的trap函数,当发生异常的原因为0x23,0x26时表示DCPLB发生异常,此时转入ex_dcplb进行处理。当异常原因是0x2b,0x2c时表示发生了ICPLB异常,此时程序将转入ex_icplb进行处理。这两个入口都调用了_cplb_hdr(arch/blackfin/mach-common/cplbhdlr.s)进行处理。
2.1 0x23:Data access CPLB protection violation
以下代码来自arch/blackfin/mach-common/cplbhdr.s中的__cplb_hdr入口:
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;
…
.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;
…
.Lnot_replaced:
…
.Lcplb_error:
R1 = sp;
SP += -12;
call _panic_cplb_error;
SP += 12;
JUMP _handle_bad_cplb;
程序调用cplb_mgr(arch/blackfin/mach-common/cplbmgr.S)进行错误处理,如果返回CPLB_RELOADED,也就是0,那么程序将返回发生异常的位置继续执行。否则将调用错误处理函数输出错误信息并中止运行。其中的panic_cplb_error函数位于arch/blackfin/kernel/traps.c,而handle_bad_cplb则位于arch/blackfin/mach-common/entry.S,但是此函数仅当错误类型为CPLB_PROT_VIOL时才会执行。
/* 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.
*/
从函数调用规则可以看出,此时的调用参数为:cplb_mgr(2, CPLB_ENABLE_DCACHE | CPLB_ENABLE_ICACHE)
下面看看cplbmgr的代码:
CC = R0 == 2;
IF CC JUMP .Ldcplb_write;
…
.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;
nop;nop;nop;nop;
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;
…
.Lprot_violation:
( R7:4,P5:3 ) = [SP++];
R0 = CPLB_PROT_VIOL;
RTS;
2.2 0x26:Data access CPLB miss
以下代码来自cplbhdr:
R1 = 0x26;
CC = R2 == R1;
IF !CC JUMP .Lunknown;
R0 = 1; /* is_data_miss == True*/
.Lis_icplb_miss:
R1 = CPLB_ENABLE_DCACHE | CPLB_ENABLE_ICACHE;
[--SP] = RETS;
CALL _cplb_mgr;
RETS = [SP++];
CC = R0 == 0;
IF !CC JUMP .Lnot_replaced;
RTS;
与0x23的处理一样,程序同样调用cplb_mgr(arch/blackfin/mach-common/cplbmgr.S)进行错误处理。所不同的是,此时的调用参数为:cplb_mgr(1, CPLB_ENABLE_DCACHE | CPLB_ENABLE_ICACHE)。如果函数返回CPLB_RELOADED,也就是0,那么程序将返回发生异常的位置继续执行。否则将调用错误处理函数输出错误信息并中止运行。其中的panic_cplb_error函数位于arch/blackfin/kernel/traps.c,而handle_bad_cplb则位于arch/blackfin/mach-common/entry.S,但是此函数仅当错误类型为CPLB_PROT_VIOL时才会执行。
下面看看cplb_mgr对此种情况的处理:
…
CC = R0 == 0;
IF !CC JUMP .Ldcplb_miss_compare;
…
.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.
*/
…
/* Start looking for a CPLB to evict. Our order of preference
* is: invalid CPLBs, clean CPLBs, dirty CPLBs. Locked CPLBs
* are no good.
*/
…
( R7:4,P5:3 ) = [SP++];
R0 = CPLB_RELOADED;
RTS;
实际上就是进行了换页处理。
2.3 0x2b:I-fetch protection violation
先看看cplbhdr的处理:
…
R1 = 0x26;
CC = R2 == R1;
IF !CC JUMP .Lunknown;
…
/*
* Diagnostic exception handlers
*/
.Lunknown:
R0 = CPLB_UNKNOWN_ERR;
JUMP .Lcplb_error;
…
.Lcplb_error:
R1 = sp;
SP += -12;
call _panic_cplb_error;
SP += 12;
JUMP _handle_bad_cplb;
此时,输出错误信息后终止运行。
2.4 0x2c:I-fetch CPLB miss
先看看cplbhdr的处理:
R1 = 0x2C; /* CPLB miss on an instruction fetch */
CC = R2 == R1;
R0 = 0; /* is_data_miss == False*/
IF CC JUMP .Lis_icplb_miss;
…
.Lis_icplb_miss:
R1 = CPLB_ENABLE_DCACHE | CPLB_ENABLE_ICACHE;
[--SP] = RETS;
CALL _cplb_mgr;
RETS = [SP++];
CC = R0 == 0;
IF !CC JUMP .Lnot_replaced;
RTS;
与0x23的处理一样,程序同样调用cplb_mgr(arch/blackfin/mach-common/cplbmgr.S)进行错误处理。所不同的是,此时的调用参数为:cplb_mgr(0, CPLB_ENABLE_DCACHE | CPLB_ENABLE_ICACHE)。如果函数返回CPLB_RELOADED,也就是0,那么程序将返回发生异常的位置继续执行。否则将调用错误处理函数输出错误信息并中止运行。其中的panic_cplb_error函数位于arch/blackfin/kernel/traps.c,而handle_bad_cplb则位于arch/blackfin/mach-common/entry.S,但是此函数仅当错误类型为CPLB_PROT_VIOL时才会执行。
下面看看cplb_mgr对此种情况的处理:
…
/* ICPLB Miss Exception. We need to choose one of the
* currently-installed CPLBs, and replace it with one
* from the configuration table.
*/
…
( R7:4,P5:3 ) = [SP++];
R0 = CPLB_RELOADED;
RTS;
同样进行换页处理后程序继续执行。