日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

自己写一个最简单的bootloader_jz2440

發布時間:2025/10/17 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 自己写一个最简单的bootloader_jz2440 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

寫在前面:

我的博客已遷移至自建服務器:博客傳送門,CSDN博客暫時停止,如有機器學習方面的興趣,歡迎來看一看。

此外目前我在gitHub上準備一些李航的《統計學習方法》的實現算法,目標將書內算法全部手打實現,歡迎參觀并打星。GitHib傳送門

正文

boot是為了啟動內核,本質上也就是一個裸板程序,就是為了引導內核的啟動。所以打算自己寫一個boot,功能只有引導內核啟動。

首先是匯編的代碼段,是為了關閉看門狗,設置時鐘以及代碼的重定位,這些都是在main函數之前執行的。之前學習單片機的時候,我們只看到main函數,實際上是main之前的執行步驟都被包起來了。

整個匯編文件的開頭要寫上

.text @這是為了表示這是一個代碼段 .global _start _start:

第一步:關閉看門狗,2440是默認關門狗打開的,如果不關閉看門狗,三秒鐘后板子會自動重啟

/* 關看門狗 */ldr r0, = 0x53000000 /* 看門狗的寄存器地址,通過芯片手冊可以查看 */mov r1, #0 /* 把0放入r1*/str r1, [r0] /* 把r1中的0賦給看門狗,即關閉看門狗 */

第二步:設置時鐘,2440板子的晶振是12M(也可以使用16M或其他的),如果不設置時鐘去倍頻,板子是以12M的速度跑的,這里是設置分頻系數以及設置板子的頻率為400M
//經過實測,boot在200M和400M的情況下,都需要6秒才能啟動內核,速度有點慢,所以使用了ICACHE提高速度,使用ICACHE以后,啟動內核只需要2秒,可以接受

#define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01)) /* 設置時鐘 */ldr r0, = 0x4c000014mov r1, #0x05 //FCLK:HCLK:PCLK=1:4:8str r1, [r0]ldr r0, =0x4c000004ldr r1, =S3C2440_MPLL_400MHZstr r1, [r0]/******以下為啟動ICACHE*******/ /* 啟動ICACHE */mrc p15, 0, r0, c1, c0, 0orr r0, r0, #(1<<12)mcr p15, 0, r0, c1, c0, 0 /******* 以上為啟動ICACHE *****/

啟動Icahe的原理是這樣:每次CPU去RAM讀代碼,很費力,ICACHE就是把這段代碼copy到片子里的一個區域,CPU直接去讀這塊區域就行了,而且這塊區域的讀速度比RAM快多了。就好像每天都有一份快遞,自己每天去取費時費力,直接讓快遞員送門口,就省很多事。

第三步:初始化SDRAM,代碼需要在SDRAM中執行,所以需要初始化

/* 初始化SDRAM */ldr r0, =MEM_CTL_BASE //設置SDRAM寄存器的首地址adr r1, sdram_config /*SDRAM每個寄存器的配置值 */add r3, r0, #(13 * 4) //SDRAM寄存器的尾地址1:ldr r2, [r1], #4 //把寄存器的配置值寫入r2,然后r1地址加4字節,定位到sdram_config的下一個配置值str r2, [r0], #4 //r2里的值寫入到r0地址,也就是SDRAM的第一個寄存器,然后寄存器地址加四字節,指 //向下一個寄存器cmp r0, r3 //當前寄存器地址和寄存器尾地址比較,如果不一致說明還沒配置完,跳轉到1繼續循環bne 1b/*放在文件末尾,這是關于SDRAM的每個寄存器的配置值*/ sdram_config:.long 0x22011110 //BWSCON.long 0x00000700 //BANKCON0.long 0x00000700 //BANKCON1.long 0x00000700 //BANKCON2.long 0x00000700 //BANKCON3 .long 0x00000700 //BANKCON4.long 0x00000700 //BANKCON5.long 0x00018005 //BANKCON6.long 0x00018005 //BANKCON7.long 0x008C04F4 //REFRESH.long 0x000000B1 //BANKSIZE.long 0x00000030 //MRSRB6.long 0x00000030 //MRSRB7

第四步:代碼重定位,因為代碼是存在NOR FLASH 或者 NAND FLASH里面的,CPU要將代碼移入SDRAM中才可以使用

/* 重定位 */ldr sp, =0x34000000 //因為nand初始化比較復雜,使用C語言實現,如果要使用C語言,需要設置堆棧指針bl nand_init //初始化nand,其實如果使用的是nor,完全可以不初始化nand,這里是考慮到不知道使用的是nor還是nand,所以把nand初始化了,之后會判斷使用的是什么flash。之所以不用初始化nor,是因為CPU可以直接讀normov r0, #0 //設置copy_code_to_sdram的參數,r0是該函數第一個參數,r1是第二個,r2是第三個,很好理解ldr r1, =_startldr r2, = __bss_startsub r2, r2, r1bl copy_code_to_sdram //調用C函數,實現將代碼copy至SDRAMbl clear_bss //清除BSS段,未初始化或初始化為0的變量都存在這里,所以需要清零

bss段挺好玩的,這樣的,如果你程序里面設置了很多變量,初始值都為0,那么程序一開始一個一個去賦0效率太低了,所以所有初始值為0的變量,都保存在bss代碼段里,程序啟動前將整個代碼段清零就行了,就很方便

第五步:執行main函數

/* 執行main */ldr lr, =halt //設置main函數的返回地址,其實boot啟動內核以后直接就死掉了,根本不會返回,這里還是寫一下返回地址,main返回以后去執行下面的halt段,不斷地死循環,防止出問題ldr pc, =main //指針定位到main函數halt:b halt

/* 重定位過程中使用的C函數 挑了幾個稍微重要點的,別的都不寫了,沒什么意思*/
/* nand初始化,沒什么意思,就不多寫了 */

void nand_init (void) { #define TACLS 0 #define TWRPH0 1 #define TWRPH1 0/* 設置時序 */ NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); /* 使能NAND Flash控制器, 初始化ECC, 禁止片選 */ NFCONT = (1<<4)|(1<<1)|(1<<0); }

/* 復制代碼段到SDRAM*/
//這里就是判斷是NOR還是NAND,因為它們的特性不同,導致nand可讀可寫,NOR只能讀,所以測試的時候就隨便找個地址,往里寫一個數,再往外讀,如果值被修改了,說明是nand,反之是nor

void copy_code_to_sdram (unsigned char *src, unsigned char *dest, unsigned int len) {int i = 0;/* 如果是NOR啟動 */if (isBootFromNorFlash()){while(i < len){dest[i] = src[i];i++;}}else{nand_read((unsigned int)src, dest, len);} } /* main函數 */ int main (void) {void (*theKernel)(int zero, int arch, unsigned int params); //申明內核的指針/*0: 設置串口,內核啟動的開始部分會從串口打印一些信息,但是內核一開始并沒有初始化串口,提前初始化好,免得內核沒有串口用 */uart0_init();/* 1.從NAND FLASH 里把內核讀入內存 */puts("Copy kernel from nand\n\r");nand_read(0x60000 + 64, (unsigned char *)0x30008000, 0x200000); /*讀出地址是根據板子看的,mtd命令可以看板子的分區,找到kernel分區就行了*//* 存入地址在內核啟動的時候會顯示, 大小也是,板子用的內核是1.8M ,這里給了2M 空間 *//* 2. 設置參數 */ //這里設置各種傳給內核的參數,因為啟動內核以后boot就死了,沒法和內核面對面交流,boot將一些命令放在一塊區域里,內核啟動以后去這個地址讀就行了puts("Set boot params\n\r");setup_start_tag();setup_memory_tag();setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");setup_end_tag();/* 3. 跳轉執行 */puts("Boot kernel\n\r");theKernel = ((void (*)(int, int, uint))0x30008000);theKernel(0, 362, 0x30000100); //第一個參數寫的就是0,我就直接寫0了,第二個參數是ID,2440的ID是這個,第三個參數是參數存放的地址,內核直接去這個地址讀就可以了/* 如果一切正常,不會執行到這里,也就是內核啟動以后不會再回到boot */puts("Error!\n\r");return -1; }/* 給內核傳入的各種命令 */ void setup_start_tag (void) {params = (struct tag *)0x30000100;params->hdr.tag = ATAG_CORE;params->hdr.size = tag_size (tag_core);params->u.core.flags = 0;params->u.core.pagesize = 0;params->u.core.rootdev = 0;params = tag_next (params); }void setup_memory_tag (void) {params->hdr.tag = ATAG_MEM;params->hdr.size = tag_size (tag_mem32);params->u.mem.start = 0x30000000;params->u.mem.size = 64*1024*1024;params = tag_next (params); } void setup_commandline_tag (char *cmdline) {int len = strlen(cmdline) + 1;params->hdr.tag = ATAG_CMDLINE;params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2;strcpy (params->u.cmdline.cmdline, cmdline);params = tag_next (params); }void setup_end_tag (void) {params->hdr.tag = ATAG_NONE;params->hdr.size = 0; }

總結

以上是生活随笔為你收集整理的自己写一个最简单的bootloader_jz2440的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。