天天看點

DM6446 uboot分析 添加達芬奇NOR flash讀寫程式 添加新的指令

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-&gt;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-&gt;flash_id

info-&gt;flash_id += flash_s29gl064a;

info-&gt;flash_id += flash_s29gl064a;   //id

info-&gt;sector_count = 135; //135 sector 

info-&gt;size = 0x00800000;   //8m

//前面8k*8=64k

for(i =0; i&lt; 8; i++)  

info-&gt;start[i] = (ulong)addr + i*0x2000;

}

//後面64k*(135-8)=64k*128=8m大小,每個sector是 64k

for (i = 8; i &lt; info-&gt;sector_count; i++)

info-&gt;start[i] = (ulong)addr + 0x10000 * (i-7);

break;

d) 獲得上面的資訊後,flash複位,設定為讀模式即可

static void flash_reset(flash_info_t *info)

fpwv *base = (fpwv *)(info-&gt;start[0]);

/* put flash back in read mode */

if ((info-&gt;flash_id &amp; flash_vendmask) == flash_man_intel)

*base = (fpw)0x00ff00ff; /* intel read mode */

else if ((info-&gt;flash_id &amp; 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-&gt;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 &amp; bit7)  == (uldata &amp; 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(&lt;=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等資訊