2010年6月29日 星期二

memtest86關於smp實作

As we know memtest86+ is to originate from memtest86.但是memtest86 3.5裡頭關於SMP的源碼,在memtest86+ 4.0及4.1獲得延續。4.0及4.1雖然從代碼仍可看出SMP足跡,但實際上已經沒有支援SMP了。因此我們若要查看SMP的完整實作,必需Download memtest86 3.5 source code。
在main.c/test_start/initialise_cpus();首先要支援SMP就必需先調用這個routine,此處的test_start相當於memtest86+版的do_test。
void initialise_cpus(void)
{
int cpu_num, j;
smp_init_bsp();
num_cpus = smp_num_cpus();
/* let the BSP initialise the APs. */
for(cpu_num = 1; cpu_num < color="#3333ff">smp_boot_ap(cpu_num);
if(!smp_mode) {
/* some error in booting the AP,
* halt the already started APs */
for (j = 1; j <=cpu_num; j++) { jmp_address[j] = (void *)ap_halt; } break; } } } smp_init_bsp要看懂這個routine要先查詢一些資訊:小木偶裡頭有關cpuid的指令說明;Intel64 and IA-32 Architectures Software Developer's Manual-3A.pdf第8章。主要是找出CPU的核心數及尋找mp_config_table表。
smp_boot_ap這個routine才是重點。
以下資料取自於http://blog.chinaunix.net/u1/41699/showart_537820.html
多核初始化和啟動過程
多核初始化協議定義了兩類處理器:bootstrap processor(BSP) 和 application processors (APs). 上電或者MP重定後,系統硬體動態選擇一個作為BSP,其餘的作為APs。BSP的IA32_APIC_BASE中的BSP flag將被置位,其他的處理器中該位將被清空.上電或者復位後,AP等待BSP的SIPI(startup signal)信號,接收到SIPI信號後,AP執行BIOS中的AP配置代碼,然後進入halt狀態。對於支援Hyper_Threading的處理器,每個邏輯處理器將被作為一個單獨的處理器對待,都會有一個唯一的APIC ID。
志強(xeon)處理器MP初始化協定演算法:
1)基於系統拓撲結構,每個邏輯處理器將分配一個8位的APIC ID.這個ID將被寫入到每個處理器的local APIC ID寄存器中.
2)BSP被確定後,BSP創建一個ACPI的table和一個MP的table並將自己的初始APIC ID加到這些表中
3)在boot-strap流程的最後,BSP設置處理器數目為1,並開始廣播SIPI. 這裏,SIPI包含BIOS AP初始化代碼的向量
4)AP收到SIPI後,第一個得到信號量的AP開始執行初始化代碼,將自己的APIC ID加到ACPI和MP的表裏面,並將處理器數目加1.完成初始化後,AP執行一個CLI指令並halt自己
5)所有的AP都初始化完後,BSP得到一個系統處理器的個數,完成boot-strap代碼,進入OS.
6)此時AP保持halt狀態,等待INITs, NMIs和SMIs.
就我所了解以上的部分應該是BIOS所完成的,參考:http://www.biosren.com/thread-1395-1-1.html

BSP初始化過程
1. 初始化memory
2. Load microcode到處理器
3. 初始化記憶體範圍寄存器(MTRRs)
4. enable cache
5. 確定BSP是否是"GenuineIntel"
6. 執行CPUID,保存CPU資訊為將來使用
7. load AP的啟動代碼到低1M的一個4K的頁裏
8. 切換到保護模式
9. 轉換4K的頁基址為一個8位的向量. 例如 0x000BD000H --> 0xBDH
10.設置APIC的SVR的bit8來enable local APIC
11.建立錯誤處理handler
12.初始化鎖信號量
13.探測系統中的AP, 方法如下:
- 設置處理器COUNT為1
- 啟動一個timer,BSP開始等待
- 此時AP開始初始化,並將COUNT加1
- timer到期,BSP檢查COUNT,如果沒有增加,就表示系統中沒有AP.
14. 等timer中斷,檢查COUNT並建立處理器數目
AP初始化
1. 獲取信號量,開始初始化
2. load microcode到處理器
3. 初始化記憶體範圍寄存器(MTRRs)
4. enable cache
5. 檢查AP是否是"GenuineIntel"
6. 保存CPUID資訊,為將來使用
7. 切換到保護模式
8. 配置AP的共存記憶體介面執行環境
9. 將處理器個數加1
10.釋放信號量
11. 執行CLI並且進入halt狀態
12.等待INIT IPI

了解以上步驟變可從代碼來查看如何實現. update中....
--> 閱讀更多...

2010年6月16日 星期三

從start_armboot routine來了解u-boot--PART2

接續上回談到env_init,列出相關代碼:注意,這裡的代碼分散在不同的檔案
typedef struct environment_s {
uint32_t crc; /* CRC32 over data bytes */
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
unsigned char flags; /* active/obsolete flags */
#endif
unsigned char data[ENV_SIZE]; /* Environment data */
} env_t;


#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
# define ENV_HEADER_SIZE (sizeof(uint32_t) + 1)
#else
# define ENV_HEADER_SIZE (sizeof(uint32_t))
#endif


#ifdef ENV_IS_EMBEDDED //這個marco未定義
extern uchar environment[];
env_t *env_ptr = (env_t *)(&environment[0]); //因此以上兩行未執行
#else /* ! ENV_IS_EMBEDDED */
env_t *env_ptr = 0;
#endif /* ENV_IS_EMBEDDED */


#define local static
#define ZEXPORT


#ifdef DYNAMIC_CRC_TABLE //在u-boot的code裡並未定義這個MARCO;所以並不會動態產生CRC_TABLE
local int crc_table_empty = 1;
local uint32_t crc_table[256];
local void make_crc_table OF((void));


local void make_crc_table()
{
uint32_t c;
int n, k;
uLong poly; /* polynomial exclusive-or pattern */
/* terms of polynomial defining this crc (except x^32): */
static const Byte p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};

/* make exclusive-or pattern from polynomial (0xedb88320L) */
poly = 0L;
for (n = 0; n < n =" 0;" c =" (uLong)n;" k =" 0;" c =" c">> 1) : c >> 1;
crc_table[n] = c;
}
crc_table_empty = 0;
}
#else
/* ========================================================================
* Table of CRC-32's of all single-byte values (made by make_crc_table)
*/
local const uint32_t crc_table[256] = { //這個crc_table會被配置
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
0x2d02ef8dL
};
#endif /*DYNAMIC_CRC_TABLE */


#ifndef OF /* function prototypes */
# ifdef STDC
# define OF(args) args
# else
# define OF(args) ()
# endif
#endif


#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
#define DO2(buf) DO1(buf); DO1(buf);
#define DO4(buf) DO2(buf); DO2(buf);
#define DO8(buf) DO4(buf); DO4(buf);

//以下這個routine在lib_generic/crc32.c

uint32_t ZEXPORT crc32 (uint32_t crc, const Bytef *buf, uInt len)
{
#ifdef DYNAMIC_CRC_TABLE
if (crc_table_empty) make_crc_table(); //這行不會被執行
#endif
crc = crc ^ 0xffffffffL;
while (len >= 8)
{
DO8(buf);
len -= 8;
}
if (len) do {
DO1(buf);
} while (--len);
return crc ^ 0xffffffffL;
}


先說明關於CRC,以CRC-16為例來說明其生成過程:
CRC-16碼由兩個位元組構成,在開始時CRC寄存器的每一位都預置為1,然後把CRC寄存器與8-bit的資料進行異或(異或:二進位運算相同為0,不同為1;0^0=0;0^1=1;1^0=1;1^1=0),之後對CRC寄存器從高到低進行移位,在最高位(MSB)的位置補零,而最低位(LSB,移位後已經被移出CRC寄存器)如果為1,則把寄存器與預定義的多項式碼進行異或,否則如果LSB為零,則無需進行異或。重複上述的由高至低的移位8次,第一個8-bit資料處理完畢,用此時CRC寄存器的值與下一個8-bit資料異或並進行如前一個資料似的8次移位元。所有的字元處理完成後CRC寄存器內的值即為最終的CRC值。
下面為CRC的計算過程:
来源:(http://blog.sina.com.cn/s/blog_639260ff0100g1ms.html) - 循环冗余校验CRC_Striker_新 1.設置CRC寄存器,並給其賦值FFFF(hex)。
2.將資料的第一個8-bit字元與16位元CRC寄存器的低8位進行異或,並把結果存入CRC寄存器。
3.CRC寄存器向右移一位,MSB補零,移出並檢查LSB。
4.如果LSB為0,重複第三步;若LSB為1,CRC寄存器與多項式碼相異或。
5.重複第3與第4步直到8次移位元全部完成。此時一個8-bit資料處理完畢。
6.重複第2至第5步直到所有資料全部處理完成。
7.最終CRC寄存器的內容即為CRC值。
常用的CRC迴圈冗餘校驗標準多項式如下:
CRC(16位) = X16+X15+X2+1
CRC(CCITT) = X16+X12 +X5+1
CRC(32位) = X32+X26+X23+X16+X12+X11+X10+ X8+X7+X5+X4+X2+X+1

--> 閱讀更多...

2010年6月14日 星期一

從start_armboot routine來了解u-boot--PART1

有關這方面ㄉ相關資訊參考:http://blog.chinaunix.net/u2/70445/showart_1852111.html
http://6xudonghai.blog.163.com/blog/static/33640629200911364735696/
http://www.63da.com/?All1-uboot%D4%B4%C2%EB%B7%D6%CE%F6/
(1):typedef int (init_fnc_t) (void);
(2):init_fnc_t *init_sequence[] ={
cpu_init, /* basic cpu dependent setup */ (1)
board_init, /* basic board dependent setup */(2)
interrupt_init, /* set up exceptions */ (3)
env_init, /* initialize environment */ (4)
init_baudrate, /* initialze baudrate settings */(5)
serial_init, /* serial communications setup */ (6)
console_init_f, /* stage 1 init of console */ (7)
display_banner, /* say that we are here */ (8)
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */(9)
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */ (10)
#endif
#if defined(CONFIG_HARD_I2C) defined(CONFIG_SOFT_I2C)
init_func_i2c, (11)
#endif
dram_init, /* configure available RAM banks */ (12)
display_dram_config, (13)
NULL,
};

(3):init_fnc_t **init_fnc_ptr;

(4)for (init_fnc_ptr = init_sequence ; *init_fnc_ptr ; ++init_fnc_ptr){
if ((*init_fnc_ptr)() != 0)
{hang (); }
}
以上四段code分散在board.c中,但是它們息息相關,而最下面的for迴圈更是高竿的程式寫法:以下我們就先針對這四段code了解其如何對硬體層面作完整的初始化。
我想大家對c語言有了解的應該都知道int main(int argc,char**argv){ };其實也可以這樣寫int main(int argc,char*argv[]){ };當然以下這樣也行int main(int argc,char argv[][]){ }; 所以上面的code不言自明,說穿了也是函式指標的應用。
剛才講到for迴圈,它主要的工作是硬體週邊初始化;和硬體週邊相關的代碼必需查datasheet,才能了解其實作,前三個:cpu_init、board_init、interrupt_init我認為較簡單,而且代碼上都有註解。其他部份我想未來有談到再補充。
在此我認為較難懂的是env_init,因為實際上在u-boot中這個routine就有9個之多,所以我在上面貼了一個參考連結,專門講述這個routine。雖然裡頭並未說明---實際呼叫的是env_nand.c或是env_dataflash.c或是env_flash.c...中的env_init();實際查看在common/makefile如下:
# environment
COBJS-y += env_common.o
COBJS-$(CONFIG_ENV_IS_IN_DATAFLASH) += env_dataflash.o
COBJS-$(CONFIG_ENV_IS_IN_EEPROM) += env_eeprom.o
COBJS-y += env_embedded.o
COBJS-$(CONFIG_ENV_IS_IN_FLASH) += env_flash.o
COBJS-$(CONFIG_ENV_IS_IN_NAND) += env_nand.o
COBJS-$(CONFIG_ENV_IS_IN_NVRAM) += env_nvram.o
COBJS-$(CONFIG_ENV_IS_IN_ONENAND) += env_onenand.o
COBJS-$(CONFIG_ENV_IS_IN_SPI_FLASH) += env_sf.o
COBJS-$(CONFIG_ENV_IS_NOWHERE) += env_nowhere.o
然後再查看include\configs\smdk2410.h如下:
#define CONFIG_ENV_IS_IN_FLASH 1
#define CONFIG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
●所以在此會被編譯的只有env_flash.c
然而在tekkaman所改版的部分它將smdk2410.h重新命名為mini2440.h,並修改其中內容如下:
//#define CONFIG_ENV_IS_IN_EEPROM 1/* use EEPROM for environment vars */
//#define CONFIG_ENV_OFFSET 0x000 /* environment starts at offset 0 */
//#define CONFIG_ENV_SIZE 0x400 /* 1KB */
#define CONFIG_ENV_IS_IN_NAND 1
//#define CONFIG_ENV_IS_IN_FLASH 1 //注意此行被mark掉了
#define CONFIG_ENV_OFFSET 0X60000
#define CONFIG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
所以在此會被編譯的是env_nand.c

另外題外話:最近使用arm-linux-objdump -D u-boot >u-boot.asm這個指令將u-boot反組譯後有助於trace參考之方便性。
--> 閱讀更多...

2010年6月10日 星期四

S3C2440與NAND FLASHK9F1208的接線分析

圖一
圖二
圖三
圖四
圖五
本文來自: 高校自動化網(http://www.zdh1909.com/) 詳細出處參考(轉載請保留本鏈結):http://www.zdh1909.com/html/MCS51/8883.html
NAND FLASH的接線方式和NOR FLASH,SDRAM都不一樣。以TQ2440開發板用的K9F1208為例,分析NAND FLASH的接線方式。K9F1208結構如圖一所示:
K9F1208位寬是8bit。
一頁: 512byte + 16byte 最後16byte是用於存儲校驗碼和其他資訊用的,不能存放實際的資料。
一個塊有32 page:(16k+512)byte
K9F1208有4096個塊:(64M+2M)byte,總共有64Mbyte可操作的晶片容量
NAND FLASH以頁為單位讀寫資料,以塊為單位擦除數據
S3C24440和K9F1208的接線如圖二:
圖三是S3C2440的NAND FLASH引腳配置:
當選定一個NAND FLASH的型號後,要根據選定的NAND FLASH來確定S3C2440的NCON,GPG13,GPG14,GPG15的狀態。
圖四是S3C2440中4個腳位元狀態的定義:
K9F1208的一頁是512byte所以NCON接低電平,GPG13接高電平
K9F1208需要4個定址命令,所以GPG14接高電平K9F1208的位寬是8,所以GPG15接低電平。
NAND FLASH定址對K9F1208來說,地址和命令只能在I/O[7:0]上傳遞,資料寬度是8位元。
地址傳遞分為4步,如圖五
第1步發送列位址,既選中一頁512BYTE中的一個位元組。512byte需要9bit來選擇,這裏只用了A0-A7,原因是把一頁分成了2部分,每部分256位元組。通過發送的讀命令字來確定是讀的前256位元組還是後256位元組。
當要讀取的起始地址(Column Address)在0~255內時我們用00h命令,當讀取的起始位址是在256~511時,則使用01h命令。
一個塊有32頁,用A9-A13共5位來選擇一個塊中的某個頁。
總共有4096個塊,用A14-A25共12位來選擇一個塊。
K9F1208總共有64Mbyte,需要A0-A25共26個位址位。
例如要讀NAND FLASH的第5000位元組開始的內容。把5000分解成列地址和行地址。
Column_address = 5000%512 = 392
Page_address = (5000>>9) = 9
因為column_address>255,所以用01h命令讀
發送命令和參數的順序是:
NFCMMD = 0x01;從後256位元組開始讀
NFADDR = column_address & 0xff;取column_address的低8位元送到數據線
NFADDR = page_address & 0xff;發送A9-A16
NFADDR = (page_address >>8) & 0xff; 發送A17-A24
NFADDR = (page_address >> 16) & 0xff;發送A25
上面的NFCMMD,NFADDR.是S3C2440的NAND FLASH控制寄存器。讀取的數據會放在NFDATA中。
--> 閱讀更多...