本節內容:
首先我們會給出LED燈閃爍的彙編代碼,然後把上節遺留下來的內容(即SD卡啓動時對映像文件的要求)進行補充,然後給出映像文件生成的源代碼。最後我們會介紹Makefile的基本書寫格式以及介紹gcc、objcopy、objdump的基本用法,對於彙編指令我不做詳細介紹,如果想學一點彙編的同學們還是買一本arm的書認真學一下彙編指令,會彙編指令也就那麼幾頁的內容(其實挺多的QAQ)。
start.S:
在start.S代碼中主要實現了4個LED燈的閃爍,這只是一個示例的彙編代碼,其實用匯編實現一個複雜的功能是一個費力不討好的事,因爲彙編指令不值得過於糾結,什麼都用匯編寫那還需要C語言幹嘛?實在想糾結匯編的可以通過C語言寫出對應的代碼,然後一個gcc -S選項即可生成彙編代碼。我的硬件情況是這樣的,我們的4個LED分別接的是GPJ2[3:0],只要給0即可點亮LED,因此我們這裏要做的就是把GPJ2CON配置爲輸出模式,然後設置對應位爲0點亮LED,設置對應爲爲0則關閉LED,我們現在看看start.S的具體代碼:
.global _start
_start:
<span style="white-space:pre"> </span>//GPJ2CON[3:0] set output mode
<span style="white-space:pre"> </span>ldr r1, =0xE0200280
<span style="white-space:pre"> </span>ldr r0, =0x00001111
<span style="white-space:pre"> </span>str r0, [r1]
<span style="white-space:pre"> </span>
led_blink:
<span style="white-space:pre"> </span>//set led on => GPJ2DAT[3:0] is low
<span style="white-space:pre"> </span>ldr r1, =0xE0200284
<span style="white-space:pre"> </span>mov r0, #0
<span style="white-space:pre"> </span>str r0, [r1]
<span style="white-space:pre"> </span>bl delay
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>//set led off => GPJ2DAT[3:0] is off
<span style="white-space:pre"> </span>ldr r1, =0xE0200284
<span style="white-space:pre"> </span>mov r0, #0xF
<span style="white-space:pre"> </span>str r0, [r1]
<span style="white-space:pre"> </span>bl delay
<span style="white-space:pre"> </span>b led_blink
halt:
<span style="white-space:pre"> </span>b halt
delay:
<span style="white-space:pre"> </span>mov r0, #0xD00000
delay_loop:
<span style="white-space:pre"> </span>cmp r0, #0
<span style="white-space:pre"> </span>sub r0, r0, #1
<span style="white-space:pre"> </span>bne delay_loop
<span style="white-space:pre"> </span>mov pc, lr
.end
上面的代碼其實就是做了配置GPIO的工作模式(輸出),設置全部燈關閉(GPJ2DAT=0xFF),延時,設置全部燈打開(GPJ2DAT=0x00)。
SD卡啓動要求:
前面講過,S5pv210上電時候根據OM[5:0]引腳識別是1st boot是哪個設備,然後把設備的前16K代碼拷貝到片內iram中,如果代碼有效則執行片內代碼。那麼問題來了,片內代碼怎麼樣纔算有效?芯片規定,程序的前16字節用於一些特別的信息(整個代碼的字節大小,代碼的校驗信息),因此我們生成的映像文件要求是:16字節的頭部 + 實際代碼,然後把映像文件通過dd命令拷貝到SD卡中,S5pv210的BL0通過校驗信息知道我們的程序是否是有誤(最怕的就是SD卡沒程序,卻選擇SD卡啓動,這時通過校驗信息知道SD卡內部數據無效就不會執行SD卡內的“代碼”了)。首先我給出程序頭的格式:
映像文件的第一個字(4字節)保存的是整個映像文件的大小(我們設置成16K即可),第3個字是用於設置校驗信息,校驗信息是有生成規則的,S5pv210規定代碼中按照4個字節對齊,設置uint32_t cheksum=0,每次把4個字節取出來(假設保存在tmp中),執行cheksum+=tmp,直到所有字節都取出來計算過爲止,這時checksum中的值就是我們程序的校驗信息,把它保存在映像文件的第三個字的地方即可,最後把start.S編譯生成的代碼拷貝到整個映像文件的後面即完成鏡像文件的製作,這些工作都是由源文件mkv210_image.c完成的期待嗎如下所示:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUFSIZE (16*1024)
#define IMG_SIZE (16*1024)
#define SPL_HEADER_SIZE 16
#define SPL_HEADER "S5PC110 HEADER "
int main (int argc, char *argv[])
{
<span style="white-space:pre"> </span>FILE<span style="white-space:pre"> </span>*fp;
<span style="white-space:pre"> </span>char<span style="white-space:pre"> </span>*Buf, *a;
<span style="white-space:pre"> </span>int<span style="white-space:pre"> </span>BufLen;
<span style="white-space:pre"> </span>int<span style="white-space:pre"> </span>nbytes, fileLen;
<span style="white-space:pre"> </span>unsigned int<span style="white-space:pre"> </span>checksum, count;
<span style="white-space:pre"> </span>int<span style="white-space:pre"> </span>i;
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>if (argc != 3)//使用格式:mkv210_image 編譯後的文件名 生成的映像文件的名字
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>printf("Usage: mkbl1 <source file> <destination file>\n");
<span style="white-space:pre"> </span>return -1;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>BufLen = BUFSIZE;
<span style="white-space:pre"> </span>Buf = (char *)malloc(BufLen);//分配16K的內存,前16字節保存校驗信息
<span style="white-space:pre"> </span>if (!Buf)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>printf("Alloc buffer failed!\n");
<span style="white-space:pre"> </span>return -1;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>memset(Buf, 0x00, BufLen);//全部字節清零
<span style="white-space:pre"> </span>fp = fopen(argv[1], "rb");
<span style="white-space:pre"> </span>if( fp == NULL)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>printf("source file open error\n");
<span style="white-space:pre"> </span>free(Buf);
<span style="white-space:pre"> </span>return -1;
<span style="white-space:pre"> </span>}
//filelen就是16K,爲了代碼的通用性寫代碼的人裝逼了→_→
<span style="white-space:pre"> </span>fseek(fp, 0L, SEEK_END);
<span style="white-space:pre"> </span>fileLen = ftell(fp);
<span style="white-space:pre"> </span>fseek(fp, 0L, SEEK_SET);
<span style="white-space:pre"> </span>count = (fileLen < (IMG_SIZE - SPL_HEADER_SIZE))
<span style="white-space:pre"> </span>? fileLen : (IMG_SIZE - SPL_HEADER_SIZE);
//設置頭部信息
<span style="white-space:pre"> </span>memcpy(&Buf[0], SPL_HEADER, SPL_HEADER_SIZE);
//把程序拷貝到進去
<span style="white-space:pre"> </span>nbytes = fread(Buf + SPL_HEADER_SIZE, 1, count, fp);
<span style="white-space:pre"> </span>if ( nbytes != count )
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>printf("source file read error\n");
<span style="white-space:pre"> </span>free(Buf);
<span style="white-space:pre"> </span>fclose(fp);
<span style="white-space:pre"> </span>return -1;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>fclose(fp);
<span style="white-space:pre"> </span>a = Buf + SPL_HEADER_SIZE;
<span style="white-space:pre"> </span>for(i = 0, checksum = 0; i < IMG_SIZE - SPL_HEADER_SIZE; i++)
<span style="white-space:pre"> </span>checksum += (0x000000FF) & *a++;
<span style="white-space:pre"> </span>a = Buf + 8;
<span style="white-space:pre"> </span>*( (unsigned int *)a ) = checksum;
<span style="white-space:pre"> </span>fp = fopen(argv[2], "wb");
<span style="white-space:pre"> </span>if (fp == NULL)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>printf("destination file open error\n");
<span style="white-space:pre"> </span>free(Buf);
<span style="white-space:pre"> </span>return -1;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>a = Buf;
<span style="white-space:pre"> </span>nbytes<span style="white-space:pre"> </span>= fwrite( a, 1, BufLen, fp);
<span style="white-space:pre"> </span>if ( nbytes != BufLen )
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>printf("destination file write error\n");
<span style="white-space:pre"> </span>free(Buf);
<span style="white-space:pre"> </span>fclose(fp);
<span style="white-space:pre"> </span>return -1;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>free(Buf);
<span style="white-space:pre"> </span>fclose(fp);
<span style="white-space:pre"> </span>return 0;
}
Makefile文件:
gcc 用於編譯文件,ld工具用於將多個目標文件連接成一個可執行文件(elf格式),objcoy將elf文件中的可執行代碼拿出來保存在一個文件裏頭(elf格式的文件含有很多調試信息,符號表等,我們要把它幹掉),objdump用於將elf文件生成對應的調試文件(其實就是以彙編的形式列出整個文件的代碼),makefile文件如下:
led.bin: start.o
arm-linux-ld -Ttext 0x0 -o led.elf $^
arm-linux-objcopy -O binary led.elf led.bin
arm-linux-objdump -D led.elf > led_elf.dis
gcc mkv210_image.c -o mkmini210
./mkmini210 led.bin 210.bin
%.o : %.S
arm-linux-gcc -o $@ $< -c
%.o : %.c
arm-linux-gcc -o $@ $< -c
clean:
rm *.o *.elf *.bin *.dis mkmini210 -f
對於ld工具,-T指定連接的信息,例如 -Tlink.lds表示指定鏈接文件link.lds,-Ttext 0x0就是說可執行文件的鏈接地址是0地址處。objcopy的-O表示指定輸出文件格式,我們舍值爲binary(二進制輸出)其中led.elf爲輸入文件,生成的文件名爲led.bin。objdump的-D說的是debug,通過led.elf生成調試文件led_ellf.dis(重定向輸出)。