韋東山嵌入式Linux學習----010 代碼重定位與優化

代碼重定位與優化

/*
*硬件平臺:韋東山嵌入式Linxu開發板(S3C2440.v3)
*軟件平臺:運行於VMware Workstation 12 Player下的UbuntuLTS16.04_x64 系統
*參考資料:Using LD, the GNU linker:http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.htm,
EM63A165TS(SDRAM)datasheet,S3C2440datasheet,開發版原理圖
*/


一、基礎知識點

  1. 程序由以下五個段組成
    {
    .text(代碼段),
    .data(數據段),
    .rodata(只讀數據段),
    .bss(.bss,未初始化或初始化爲0的全局變量),
    .COMMON(註釋段),
    }

  2. 彙編指令解析
    2.1 ldr:把數據從內存加載到寄存器
    {
     LDR指令的格式爲:
     LDR{條件} 目的寄存器,<存儲器地址>
     LDR指令用於從存儲器中將一個32位的字數據傳送到目的寄存器中。
    }
    2.2 str:把數據從寄存器保存到內存
    {
     STR指令的格式爲:
     STR{條件} 源寄存器,<存儲器地址>
     STR指令用於從源寄存器中將一個32位的字數據傳送到存儲器中。
    }
    2.3 bne:是“不相等或不爲0跳轉指令”

      cmp同bne搭配理解
      如:cmp r0,r1
      bne clean_bss//如果r0!=r1,就執行bne,跳轉到clean_bss函數處執行,否則向下執行。

  3. C函數怎麼使用lds文件中的變量abc?

    3.1 在C函數中聲明改變量爲extern類型, 比如:
      extern int abc;
    3.2. 使用時, 要加取址符&, 比如:
       int *p = &abc; // p的值即爲lds文件中abc的值

二、什麼是重定位?

  重定位就是把代碼段搬移到自身想要的地址。本來程序是運行在運行地址處的,你可以通過重定位搬移到鏈接地址處。

三、爲什麼要重定位?

  1、被動原因:Flash本身的內存大小不足,沒有辦法保證代碼運行時的完整性。CPU發出的地址可以直接到達SDRAM,SRAM,NOR但是無法直接到達NAND
因此我們的程序可以直接放在NOR,SDRAM直接運行,假設我們把程序燒錄到NAND中,CPU無法直接從NAND取地址運行。
  2、主動原因:自身代碼設計要求。

四、怎麼重定位?

!在東山嵌入式Linxu開發板(S3C2440.v3)進行學習!
  第一步
  判斷bin文件所要燒寫到NOR FLASH還是NAND FLASH
  第二步
  實驗一:燒寫到NOR FLASH
  目的:需要把data段重定位到SDRAM
  原因:NOR FLASH中的數據只可讀不可寫,修改其中的數據是無效的
*************************************************************
  實驗二:燒寫到NAND FLASH
  目的:需要把data段重定位到SDRAM
  原因:NAND FLASH中的內存大小爲4k,若程序的bin文件>4K時,前4K的代碼需要把整個程序讀出,放到SDRAM
  第三步
  進行代碼的編寫工作

五、怎麼在代碼中實現數據段重定位?

Makefile文件:
添加腳本文件

arm-linux-ld -T sdram.lds start.o led.o uart.o sdram_init.o  main.o -o sdram.elf

sdram.lds腳本文件:

根據《Using LD, the GNU linker》中的
SECTIONS {
	...
	secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
		{ contents } >region :phdr =fill
	...
	}
編譯出:
SECTIONS
{
	. = 0x30000000;

	. = ALIGN(4);
	.text :{*(.text)}

	. = ALIGN(4);
	.rodata : { *(.rodata) }

	. = ALIGN(4);
	.data : { *(.data) }

	. = ALIGN(4);
	__bss_start = .;
	.bss : { *(.bss) *(.COMMON) }
	_end = .;
}

start.S彙編文件數據段重定位和清除bss,COMMON段功能實現:

bl sdram_init
	
	/* 重定位text,rodata,data段整個程序 */
	mov r1, #0
	ldr r2, =_start   
	ldr r3, =__bss_start      
       	
cpy:
	ldr r4, [r1]      //從r1存儲器中傳送數據到r4寄存器中,r4 = [r1]
	str r4, [r2]      //把r4寄存器中的數據寫入到r2存儲器中,r4 ->[r2]
	add r1, r1,#4     
	add r2, r2,#4     
	cmp r2, r3        //比較r2-r3 =0?,!=則繼續執行cpy程序
	ble cpy

	/* 清除BSS段 */
	ldr r1, =__bss_start
	ldr r2, =_end
	mov r3, #0
clean:
	str r3, [r1]
	add r1, r1, #4
	cmp r1, r2
	ble clean
	
	//bl main    /* 使用BL命令相對跳轉, 程序仍然在NOR/sram執行 */
	ldr pc, =main     /* 絕對跳轉, 跳到SDRAM */

六、怎麼在代碼中實現優化?

1、start.S彙編文件的優化
  分析:在上述的彙編文件中,我們直接用了彙編代碼對sdram.lds中的變量進行操作,那麼是否可以在C文件中對sdram.lds文件的變量進行操作,後在start.S文件中調用相關函數,增加代碼的可讀性和可移植性。

改寫代碼:

bl sdram_init

bl copy_to_sdram

bl clean_bss

//bl main    /* 使用BL命令相對跳轉, 程序仍然在NOR/sram執行 */
ldr pc, =main     /* 絕對跳轉, 跳到SDRAM */

2、sdram_init.c文件的改寫

void sdram_init(void)
{

	BWSCON = 0x22000000;     //初始化BWSCON,選用BANK6,7

	BANKCON6 = 0x00018001;
	BANKCON7 = 0x00018001;

	REFRESH = 0x8404f5;
	
	BANKSIZE = 0x000000b1;

	MRSRB6 = 0x00000020;     //設置CL爲2clock,具體查手冊
	MRSRB7 = 0x00000020;     //設置CL爲2clock,具體查手冊
		
}

/*功能:複製整個text,rodata,data段到SDRAM中*/
void copy_to_sdram(void)
{
	extern int start,__bss_start;    //建立外部變量,方便獲取lds文件中的量
	
	volatile unsigned int *text =(volatile unsigned int *) &start;      //text指向程序開頭地址
	volatile unsigned int *end = (volatile unsigned int *)&__bss_start;    //end指向bss段開頭的地址
	volatile unsigned int *src = (volatile unsigned int *)0;       //src指向0地址.即FLASH的開頭地址

	while(text < end)
	{
		*text++ = *src++; 	//運行邏輯:1、*text = *src 2、*src++ 3、*text++	
	}
}

/*功能:清楚全部bss段*/
void clean_bss(void)
{

	extern int __bss_start,_end;     //建立外部變量,方便獲取lds文件中的量
	volatile unsigned int *_start = (volatile unsigned int *)&__bss_start;
	volatile unsigned int *end = (volatile unsigned int *)&_end;

	while(_start <= end)
	{
		*(_start)++ =0; 
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章