1. 頂層目錄下的makefile
按照配置順序:
davinci_config : unconfig
@./mkconfig $(@:_config=) arm arm926ejs davinci
執行配置指令:
make davinci_config
通過./mkconfig腳本會生成include/config.mk的配置頭檔案。
内容如下:
arch = arm
cpu = arm926ejs
board = davinci
是以,我們可以得知,該u-boot工程的目錄路徑。
board/davinci/下存放的是達芬奇的電路相關内容
cpu/arm926ejs存放cpu相關
lib_arm/
include/arm-asm/
include/configs/davinci.h ------包含了基本所有的闆子相關宏定義,預設的參數清單也在
在make davinci_config之後,再次檢視最上層的makefile,我們發現
include include/config.mk
export arch cpu board ...
上述的内容就是将闆子的配置内容導出設定為環境變量
makefile的編譯選項 和編譯規則 都放在頂層目錄的config.mk檔案中定義。各種體系結構通用的規則直接在這個檔案中定義
在makefile中,包含了一些字首
ifeq ($(arch),arm)
cross_compile = arm_v5t_le-
endif
export cross_compile
接着是處理器相關的目标檔案
根據上述的cpu=arm926ejs得知包含的路徑為
objs = cpu/$(cpu)/start.o
。。。
libs=lib_generic/libgeneric.a 定義libs依賴目錄,将目标檔案連結成*.a檔案(靜态庫)
u-boot生成鏡像的makefile生成目标
#########################################################################
all = u-boot.srec u-boot.bin system.map
all: $(all)
u-boot.hex: u-boot
$(objcopy) ${objcflags} -o ihex $< $@
u-boot.srec: u-boot
$(objcopy) ${objcflags} -o srec $< $@
u-boot.bin: u-boot
$(objcopy) ${objcflags} -o binary $< $@
u-boot.img: u-boot.bin
./tools/mkimage -a $(arch) -t firmware -c none \
-a $(text_base) -e 0 \
-n $(shell sed -n -e 's/.*u_boot_version//p' include/version.h | \
sed -e 's/"[ ]*$$/ for $(board) board"/') \
-d $< $@
u-boot.dis: u-boot
$(objdump) -d $< > $@
u-boot: depend $(subdirs) $(objs) $(libs) $(ldscript)
undef_sym=`$(objdump) -x $(libs) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
$(ld) $(ldflags) $$undef_sym $(objs) \
--start-group $(libs) --end-group $(platform_libs) \
-map u-boot.map -o u-boot
上述中makefile預設的編譯目标為all, 包括u-boot.srec、u-boot.bin 、system.map。 u-boot就是通過ld指令按照u-boot.map位址表将目标組成u-boot
2. 開發闆相關配置
除了編譯過程makefile以外, 還要在程式中為開發闆定義配置選項或者參數。這個頭檔案就是include/configs/davinci.h.
如下:配置cpu
#define config_arm926ejs /* this is an arm926ejs cpu core */
#define config_sys_clk_freq 297000000 /* 時鐘arm clock frequency */
…..
3. 編譯結果
通過上面的了解,先執行 make davinci_config
然後執行make即可
(清除執行make clean或者make distclean)
4. 添加u-boot指令
5. u-boot啟動過程分析
運作記憶體位址 定義:
board/davinci/config.mk
board/davinci/config.mk:26:text_base = 0x81080000
.text 0x81080000 0x13504
cpu/arm926ejs/start.o(.text)
.text 0x81080000 0x3c0 cpu/arm926ejs/start.o
0x81080000 _start
上面是記憶體的運作起始位址
0x81098424 __bss_start = .
後面是代碼段和堆棧段
.bss 0x810a0d1c 0x4 lib_arm/libarm.a(armlinux.o)
0x810a0d20 _end = .
由board/davinci/u-boot.lds看到
output_format("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
output_arch(arm)
entry(_start)
sections
{
. = 0x00000000;
. = align(4);
.text :
{
cpu/arm926ejs/start.o (.text)
*(.text)
}
。。。。。。。。。。。。。
該入口由start.o開始執行
cpu/arm926ejs/start.s
_start_armboot: .word start_armboot
….
該函數在lib_arm/board.c中實作
1. start_armboot(void)函數
__asm__ __volatile__("": : :"memory"); 記憶體屏蔽:
__asm__ 用于在此處插入彙編語句
__volatile__用于告訴編譯器,嚴禁将此處的彙編語句與其它的語句重組合優化。即,原原本本的按照原來的樣子處理這裡的彙編。
memory強制gcc編譯器假設ram所有記憶體單元均被彙編指令修改,這樣cpu中的registers和cache中已緩存的記憶體單元中的資料将廢棄。cpu将不得不在需要的時候重新讀取記憶體中的資料。這就阻止了cpu又将registers,cache中的資料用于去優化指令,而避免去通路記憶體。
"":::表示這是個空指令
注意:我們很多的配置都根據make davinci_config後,檢視include/configs/davinci.h
如:配置#define cfg_malloc_len (0x10000 + 128*1024) /* malloc () len */
該說明,我們u-boot占用flash的大小是128k+64k=192k
需要将u-boot寫在從0開始到0x30000為止。
start_armboot入口進入後主要進行就是一些初始化的工作。
認識一些全局變量:
a.記憶體占用初始化大小
mem_malloc_start = dest_addr;
mem_malloc_end = dest_addr + cfg_malloc_len; // 0x10000+128*1024=192k
資訊:
mem_malloc_start=81050000, mem_malloc_end=81080000, monitor_flash_len= 18484
b. 初始化函數功能指針
init_sequence----(cpu_init, board_init, interrupt_init, env_init, init_baudrate,……..)
具體的檢視達芬奇手冊的96頁的表: psc寄存器映射表
電源和睡眠模式控制
#define psc_addr
0x01c41000
後面還包含了電源管理指令和狀态寄存器
cpu_init() -----------是否開啟arm的快速中斷模式,如果開始,那麼在起始記憶體前面的128byte加上中斷向量表。
board_init()-----------主要是電源管理和睡眠模式管理,上電開啟所有子產品的電源。
目錄
<a href="http://blog.csdn.net/ninver2007/article/details/8048774#_toc267658653">添加達芬奇nor flash讀寫程式 5</a>
<a href="http://blog.csdn.net/ninver2007/article/details/8048774#_toc267658654">(flash init初始化第一步)發送指令讀取廠商裝置id 7</a>
<a href="http://blog.csdn.net/ninver2007/article/details/8048774#_toc267658655">(flash初始化第二步)擦除flash的某個塊 10</a>
<a href="http://blog.csdn.net/ninver2007/article/details/8048774#_toc267658656">(flash初始化第三步) 寫入資料flash某個塊 11</a>
<a href="http://blog.csdn.net/ninver2007/article/details/8048774#_toc267658657">(flash初始化第四步)讀取flash某個塊的資料 12</a>
<a href="http://blog.csdn.net/ninver2007/article/details/8048774#_toc267658658">添加新的指令 14</a>
關鍵代碼檔案
common/flash.c
board/davinci/flash.c
include /configs/davinci.h
先檢視一下指令
nor flash(s29gxxxn_00_a6_e)的硬體電路接法是位址和資料分開的,類似于ddr的接法,但是和ddr不同的是,這個也是需要指令的形式來進行傳輸,
如下圖:
nor flash的讀寫比nand flash的要簡單許多。
我們這裡達芬奇接的nor flash emif接口的基位址是0x0200 0000,并且是16bit的資料位寬度,讀寫如下:
設定flash_info_t資訊中的id, sector數量,以及flash size大小
1. nor flash讀取生廠商id和裝置id的步驟
a) 執行相應的指令序列:(參考 nor flash的資料手冊,見71page)
=è向基位址發送指令
*(0x02000000 + 0x0555)=0x00aa;
*(0x02000000 + 0x02aa)=0x0055;
*(0x02000000 + 0x0555)=0x0090;
也可以定義一個基位址:volatile unsigned short *addr;
将addr變量指派為0x02000000
上面相當于addr[0x0555]=0x00aa;
b) 當在基位址寫了上面的3個指令後,就可以開始讀取資料
上面的指令是讀取生産商的id: manufacturer id
(u16)mnfid=*(0x02000000+0x0);
這裡我們讀到的值為1
讀到該值後,我們指派一個初始值;
flash_info_t info;
info->flash_id = 0x0000000; // (flash_man_amd) amd的flash産商
c) 讀取device id:
(u16)devid=*(0x0c000000+0x1);
(u16)devid3=*(0x0c000000+0x0e);
(u16)devid4=*(0x0c000000+0x0f);
這裡我們利用這個devid(switch ((fpw)addr[flash_id2]))來判斷資訊
printf("deviceid=%8x\n", (fpw)addr[flash_id2]);
//這裡我讀到得是227e
好,看看下面的這些定義:
看來這個id是一個家族,需要多個id來識别
#define amd_id_mirror 0x227e227e /* 1st id word for mirrorbit family */
#define amd_id_dl640g_2 0x22022202 /* 2nd id word for am29dl640g at 0x38 */
#define amd_id_dl640g_3 0x22012201 /* 3rd id word for am29dl640g at 0x3c */
#define amd_id_lv640u_2 0x220c220c /* 2nd id word for am29lv640m at 0x38 */
#define amd_id_lv640u_3 0x22012201 /* 3rd id word for am29lv640m at 0x3c */
#define amd_id_lv640mt_2 0x22102210 /* 2nd id word for am29lv640mt at 0x38 */
#define amd_id_lv640mt_3 0x22012201 /* 3rd id word for am29lv640mt at 0x3c */
#define amd_id_lv640mb_2 0x22102210 /* 2nd id word for am29lv640mb at 0x38 */
#define amd_id_lv640mb_3 0x22002200 /* 3rd id word for am29lv640mb at 0x3c */
#define amd_id_lv128u_2 0x22122212 /* 2nd id word for am29lv128m at 0x38 */
#define amd_id_lv128u_3 0x22002200 /* 3rd id word for am29lv128m at 0x3c */
#define amd_id_lv256u_2 0x22122212 /* 2nd id word for am29lv256m at 0x38 */
#define amd_id_lv256u_3 0x22012201 /* 3rd id word for am29lv256m at 0x3c */
#define amd_id_gl064m_2 0x22132213 /* 2nd id word for s29gl064m-r6 */
#define amd_id_gl064m_3 0x22012201 /* 3rd id word for s29gl064m-r6 */
那麼我們添加一個id3 ,id4
printf("deviceid2=%4x , id3=%4x,id4=%4x\n", (fpw)addr[flash_id2],(fpw)addr[flash_id3],(fpw)addr[flash_id4]);
//這裡我讀到得是227e ,後面的是 2210 ,2200
通過條件判斷
info->flash_id
info->flash_id += flash_s29gl064a;
info->flash_id += flash_s29gl064a; //id
info->sector_count = 135; //135 sector
info->size = 0x00800000; //8m
//前面8k*8=64k
for(i =0; i< 8; i++)
info->start[i] = (ulong)addr + i*0x2000;
}
//後面64k*(135-8)=64k*128=8m大小,每個sector是 64k
for (i = 8; i < info->sector_count; i++)
info->start[i] = (ulong)addr + 0x10000 * (i-7);
break;
d) 獲得上面的資訊後,flash複位,設定為讀模式即可
static void flash_reset(flash_info_t *info)
fpwv *base = (fpwv *)(info->start[0]);
/* put flash back in read mode */
if ((info->flash_id & flash_vendmask) == flash_man_intel)
*base = (fpw)0x00ff00ff; /* intel read mode */
else if ((info->flash_id & flash_vendmask) == flash_man_amd)
*base = (fpw)0x00f000f0; /* amd read mode */
可以說,flash的資訊是我們自己根據flash硬體連接配接的手冊自行指派設定的。
在上述中,讀模式也就是nor flash的複位模式,向某個塊位址寫入0x00ff或者0x00f0就可以回到read array模式。
手冊中經常用read array mode來表示讀模式,要進入該模式隻要
*base=(fpw)0x00f000f0;即可(intel 寫入 0x00ff即可)
2. 以塊為機關擦除nor flash的步驟
操作原理:nor flash也是按塊sector來進行擦除的,該塊大小是64k,先寫指令到0x0555和0x02aa,然後檢查該塊的資料是否為0xffff
環境: 假設我們用的是8m大小的nor flash, 那麼根據64k大小來分,一共有135個sector,是以我們要擦除2620000~ 263ffff的flash, 即 2個sector,105和106兩個塊(要注意到,最前面的64k,分為8個sector,每個sector大小是8k, 後面的從第9個sector開始才是64k大小的,并且用flinfo檢視屬性,可以看到前面的8k的8個sector是隻讀的,不能改的。8m=128*64k , 但是這裡,8k*8+ 127*64k=8m,是以一共135個sector.)
擦除的指令就是向該塊位址寫指令:(檢視手冊也可以知道)
*(0x02000000+0x0555)= (unsigned short)0x00aa00aa;
*(0x02000000+0x02aa)= (unsigned short)0x00550055
*(0x02000000+0x02aa)= (unsigned short)0x00800080 --擦除模式
*(0x02000000+0x02aa)= (unsigned short)0x00aa00aa
*baseaddr(要擦除的sector位址)= 0x00300x0030
while((*(volatile unsigned short int )baseaddr) != 0xffff);
printf(“擦除結束!\n”);
達芬奇代碼:
fpwv *base; /* first address in bank */
base = (fpwv *)(info->start[0]);
base[flash_cycle1] = (fpw)0x00aa00aa; /* unlock */
base[flash_cycle2] = (fpw)0x00550055; /* unlock */
base[flash_cycle1] = (fpw)0x00800080; /* erase mode */
*addr = (fpw)0x00300030; /* erase sector */
while (*((vhwdptr)addr) != amd_erase_done);
printf("done.\n");
*((vhwdptr)address_cs + amd_cmd0_addr) = amd_prog_cmd0;
*((vhwdptr)address_cs + amd_cmd1_addr) = amd_prog_cmd1;
*((vhwdptr)address_cs + amd_cmd2_addr) = amd_prog_cmd2;
即
*(0x02000000+0x02aa)= (unsigned short)0x00a000a0 --程式設計模式
将要寫入的位址指派資料
*psaddress = uldata;
這樣其實就已經寫入了,将uldata資料,寫入了psaddress中
但是我們需要進行等待,判斷是否已經寫好了,寫好後進行下個資料的寫
while(1){
tmp = *psaddress; 用tmp變量來判斷
if( (tmp & bit7) == (uldata & bit7))
上面的是最簡單的判斷,就是判斷高位位元組是否相等
在軟體複位後,直接讀即可
這樣複位後,就可以讀了。
看,添加列印資訊:我們可以檢視2020000開始放env環境變量參數開始的nor flash内容
添加代碼:
addr=(fpw *)0x2020000;
printf("0x2020000=%4lx\n",*(addr));
printf("0x2020001=%4lx\n",*(addr+1));
---前面的這4個byte是無效的
printf("0x2020002=%4lx\n",*(addr+2));
printf("0x2020003=%4lx\n",*(addr+3));
printf("0x2020004=%4lx\n",*((addr+4)));
//printf("0x2020005=%4lx\n",*((addr+5)));
printf("0x2020006=%4lx\n",*((addr+6)));
printf("0x2020008=%4lx\n",*((addr+8)));
printf("0x202000a=%4lx\n",*((addr+0x0a)));
列印結果:
0x2020000=5e92
0x2020001=d070
0x2020002=6f62
0x2020003=746f
0x2020004=6564
0x2020005=616c
0x2020006=3d79
0x2020008=6162
0x202000a=6172
排放的順序:
62 6f 6f 74 64 65 6c 61 79 3d
b o o t d e l a y =
有這幾個我們可以看出參數開始排放了
自己寫自己的指令
tftp 8000000 u-boot.bin
看到大小為1865c
protect off all
或者一段段的将protect off 2020000 2030000
cp.b 80800000 2020000 1865c(<=20000 ,128k的16進制)
等待done結束
erase 2020000 202ffff 一個段擦除
參考:nor flash讀寫
http://hi.baidu.com/liang888%ba%c3/blog/item/81e330191d35b54942a9add1.html
關于指令的幾個檔案:
include /command.h
common/command.c
include/cmd_confdefs.h
common/cmd_*.c
先看一下添加指令的宏:
u_boot_cmd
定義在include/command.h中
#define u_boot_cmd(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name struct_section = {#name, maxargs, rep, cmd, usage, help}
可以看到每一個指令定義一個cmd_tbl_t結構體,該結構體也定義在同個頭檔案中。
cfg_cmd_* 等的指令的配置定義在:
include/cmd_confdefs.h中
隻有定義了相關的配置,在common/cmd_*.c指令函數才會有效
我們現在不建立一個新的cmd_*.c,而是在原來的基礎上添加一個指令
例如:添加flash的強制寫指令
添加在common/cmd_flash.c檔案中
int do_writeuboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
int rcode = 0;
printf("add by jk for test the writeboot\n");
return 0;
這個宏中的字元串資訊是幫助資訊:當使用help的時候就會列印出來
u_boot_cmd(
writeboot, cfg_maxargs, 1, do_writeuboot,
"writeuboot - update the davinci u-boot.bin in nor flash\n"
" argv[1] --start the nor flash address'\n"
" argv[2] --start the memory address'\n"
" argv[3] --the u-boot.bin hex size'\n" ,
);
在do_writeboot函數中,僅僅就列印一條資訊
是以在指令中,隻要輸入了writeboot,那麼就列印這個資訊
可以在程式中補充完整的是writeboot –help等資訊