快樂蝦http://blog.csdn.net/lights_joy/lights@hb165.com 本文適用於ADI bf561 DSPuclinux-2008r1-rc8 (移植到vdsp5)Visual DSP++ 5.0 歡迎轉載,但請保留作者資訊
1.1 icache支援
1.1.1 寄存器配置初始化在核心中,如果要支援icache,必須首先定義一個宏:#define CONFIG_BFIN_ICACHE 1對icache的初始化配置在arch/blackfin/kernel/cplb-nompu/cacheinit.c的bfin_icache_init函數完成:#if defined(CONFIG_BFIN_ICACHE)void bfin_icache_init(void){ unsigned long *table = icplb_table; unsigned long ctrl; int i; for (i = 0; i < MAX_CPLBS; i++) { unsigned long addr = *table++; unsigned long data = *table++; if (addr == (unsigned long)-1) break; bfin_write32(ICPLB_ADDR0 + i * 4, addr); bfin_write32(ICPLB_DATA0 + i * 4, data); } ctrl = bfin_read_IMEM_CONTROL(); ctrl |= IMC | ENICPLB; bfin_write_IMEM_CONTROL(ctrl); SSYNC();}#endif在這裡,MAX_CPLBS的定義為:#define MAX_CPLBS (16 * 2)之所以定義為16*2是因為它把ICPLB_ADDR和ICPLB_DATA交叉存放在一個數組中,因此需要*2。icplb_table則是一個全域變數,其定義為:u_long icplb_table[MAX_CPLBS + 1];最後一個CPLB元素的值為-1(不一定是數組的最後一個元素),用作上述函數中for迴圈的允出準則。那麼icplb_table的值從哪來呢?答案在cplbinit.c。
1.1.2 icplb_table的產生icplb_table的產生是由generate_cpl_tables實現的。以下代碼刪除了與icplb初始化化無關的部分:void __init generate_cpl_tables(void){ u16 i, j, process; u32 a_start, a_end, as, ae, as_1m; struct cplb_tab *t_i = NULL; struct s_cplb cplb; cplb.init_i.size = MAX_CPLBS; cplb.init_i.pos = 0; cplb.init_i.tab = icplb_table; for (i = ZERO_P; i < ARRAY_SIZE(cplb_data); ++i) { if (!cplb_data[i].valid) continue; as = cplb_data[i].start % SIZE_4M; ae = cplb_data[i].end % SIZE_4M; if (as) a_start = cplb_data[i].start + (SIZE_4M - (as)); else a_start = cplb_data[i].start; a_end = cplb_data[i].end - ae; for (j = INITIAL_T; j <= SWITCH_T; j++) { switch (j) { case INITIAL_T: if (cplb_data[i].attr & INITIAL_T) { t_i = &cplb.init_i; t_d = &cplb.init_d; process = 1; } else process = 0; break; default: process = 0; break; } if (!process) continue; if (cplb_data[i].attr & I_CPLB) __fill_code_cplbtab(t_i, i, a_start, a_end); } } /* close tables */ close_cplbtab(&cplb.init_i); cplb.init_i.tab[cplb.init_i.pos] = -1;}在這裡cplb_data是在檔案頭中定義的一個10個元素的數組,它詳細說明了每塊記憶體的屬性,如大小,類型等等。當程式運行到__fill_code_cplbtab時,以下幾塊將被填入ICPLB的數組中: { .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 = 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 = "Kernel Memory", },其中,Kernel Memory這個塊的.end已經設定為SDRAM的結束位置。
1.1.3 __fill_code_cplbtab這個函數的實現為:/* helper function */static void __fill_code_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end){ if (cplb_data[i].psize) { fill_cplbtab(t, cplb_data[i].start, cplb_data[i].end, cplb_data[i].psize, cplb_data[i].i_conf); #if defined(CONFIG_BFIN_ICACHE) if (ANOMALY_05000263 && i == SDRAM_KERN) { fill_cplbtab(t, cplb_data[i].start, cplb_data[i].end, SIZE_4M, cplb_data[i].i_conf); } else#endif} else { fill_cplbtab(t, cplb_data[i].start, a_start, SIZE_1M, cplb_data[i].i_conf); fill_cplbtab(t, a_start, a_end, SIZE_4M, cplb_data[i].i_conf); fill_cplbtab(t, a_end, cplb_data[i].end, SIZE_1M, cplb_data[i].i_conf); }}static unsigned short __initfill_cplbtab(struct cplb_tab *table, unsigned long start, unsigned long end, unsigned long block_size, unsigned long cplb_data){ int i; switch (block_size) { case SIZE_4M: i = 3; break; case SIZE_1M: i = 2; break; case SIZE_4K: i = 1; break; case SIZE_1K: default: i = 0; break; } cplb_data = (cplb_data & ~(3 << 16)) | (i << 16); while ((start < end) && (table->pos < table->size)) { table->tab[table->pos++] = start; if (lock_kernel_check(start, start + block_size) == IN_KERNEL) table->tab[table->pos++] = cplb_data | CPLB_LOCK | CPLB_DIRTY; else table->tab[table->pos++] = cplb_data; start += block_size; } return 0;}
1.1.3.1 L1 I-Memory當 { .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", },這個塊傳遞進來時,它將直接調用 fill_cplbtab(t, cplb_data[i].start, cplb_data[i].end, cplb_data[i].psize, cplb_data[i].i_conf);從fill_cplbtab這個函數可以看出此時ICPLB_ADDR的值將為L1_CODE_START,也就是0xffa0 0000,而ICPLB_DATA的值將為0x0003 0007,這個值的意義是:16位和17位的值為11,即頁面大小為4M。第0位,也即CPLB_VALID,為1,表示Valid CPLB Entry。第1位,也即CPLB_LOCK,為1,表示CPLB Entry Should not be replaced。第2位,也即CPLB_USER_RD,為1,表示User mode read access permitted。
1.1.3.2 Kernel 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 = "Kernel Memory", },這個塊傳進來的時候,它將調用 fill_cplbtab(t, cplb_data[i].start, cplb_data[i].end, SIZE_4M, cplb_data[i].i_conf);此時將產生15個SDRAM的塊,每個都是4M,且從0開始遞增。此時它們的CPLB_DATA值將為0x0003 1205,這個值的意義是:16位和17位的值為11,即頁面大小為4M。第0位,也即CPLB_VALID,為1,表示Valid CPLB Entry。第1位,也即CPLB_LOCK,為0,表示CPLB Entry can be replaced。第2位,也即CPLB_USER_RD,為1,表示User mode read access permitted。第9位,也即CPLB_MEM_LEV,為1,表示Determins Line Buffer, Low Priority。第12位,也即CPLB_L1_CHBL,為1,表示Cacherable in l1。
1.1.4 換頁問題很明顯,在初始化的時候,當SDRAM>32M時,icache是無法覆蓋整個儲存空間的,此時,uclinux核心必然會發生I-fetch CPLB miss異常。uclinux核心對這種異常情況的處理由_cplb_mgr完成,這個函數在arch/blackfin/kernel/cplb-nompu/cplbmgr.S中,它將在整個儲存地區的配置列表中尋找一個合適的塊,替換掉ICACHE_DATA15和ICACHE_ADDR15,即它總是替換最新的一個頁。在這裡,整個儲存地區配置的列表儲存在一個叫ipdt_table的數組中,看看他的定義:u_long ipdt_table[MAX_SWITCH_I_CPLBS + 1] PDT_ATTR;/** 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* possibly 1 for L2 Instruction Memory* 1 for CONFIG_DEBUG_HUNT_FOR_ZERO*/ #define MAX_SWITCH_I_CPLBS (((CONFIG_MEM_SIZE / 4) + 12 + 1 + 1 + 1) * 2)這個數組同樣是交叉儲存了ICPLB_ADDR和ICPLB_DATA的值,它的初始化同樣由generate_cpl_tables函數完成,與icplb_table的初始化類似。