DSP

uClinux2.6(bf561)中的CPLB

2019-07-13 15:58发布

  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; 同样进行换页处理后程序继续执行。