第001節(jié)段的概念重定位的引入
S3C2440的CPU可以直接給SDRAM發(fā)送命令、給Nor Flash發(fā)送命令、給4K的片上SDRAM發(fā)送命令,但是不能直接給Nand Flsh發(fā)送命令
假如把程序燒寫到Nand Flsh上,即向Nand Flsh燒入* bin* 文件,CPU是無法從Nand Flsh中取代碼執(zhí)行的。
為什還可以使用NAND啟動?
上電后,Nand啟動硬件會自動把Nand Flsh前4K復(fù)制到SRAM;
CPU從0地址運行SRAM;
如果我的程序大于4K怎么辦?
前4K的代碼需要把整個程序讀出來放到SDRAM(即代碼重定位)。
如果從Nor Flash啟動,會出現(xiàn)什么問題?
將撥動開關(guān)撥到Nor Flash啟動時,此時CPU認為的 0地址 在Nor Flash上面,片內(nèi)內(nèi)存SRAM的基地址就變成了0x40000000(Nand啟動時片內(nèi)內(nèi)存SRAM的基地址基地址是0),
由于Nor Flash特性:可以像內(nèi)存一樣讀,但不能像內(nèi)存直接寫,因此需要把全局變量和靜態(tài)變量重定位 放到SDRAM里。
例如執(zhí)行如下幾條匯編指令
MOV R0, #0
LDR R1, [R0] @讀有效
STR R1, [R0] @寫無效
當程序中含有需要寫的全局變量或靜態(tài)變量時,假如是在Nand Flash可以正常操作,如果是在Nor Flash,修改無效。因此我們需要把全局變量和靜態(tài)變量重定位 放到SDRAM
#include "s3c2440_soc.h"
#include "uart.h"
#include "init.h"
char g_Char = 'A'; //定義一個全局變量
const char g_Char2 = 'B'; //定義固定的全局變量
int g_A = 0;
int g_B;
int main(void)
{
uart0_init();
while (1)
{
putchar(g_Char); /*讓g_Char輸出*/
g_Char++; /* nor啟動時, 此代碼無效 */
delay(1000000);
}
return 0;
}
編譯運行查看是否有效果
查看sdram.dis文件 發(fā)現(xiàn)data數(shù)據(jù)段放在了0x00008474這個地址導(dǎo)致 程序太大
在makefile中加入這么一句話
arm-linux-ld -Ttext 0 ** -Tdata 0x700 ** start.o led.o uart.o init.o main.o -o sdram.elf
16進制的700就是十進制的2048
這時我們的bin文件就變?yōu)?049
燒寫程序:
燒寫在NORFlash 和 燒寫在NANDFlash觀察這兩種的效果。
設(shè)置成NANDFlash啟動沒有問題 顯示ABCDE…
設(shè)置成NORFlash啟動顯示AAA…
對于NOR啟動時g_Char++; /* nor啟動時, 此代碼無效 */
Disassembly of section .data:
00000700 <__data_start>:
700: Address 0x700 is out of bounds. //數(shù)據(jù)段
Disassembly of section .rodata:
//放在只讀數(shù)據(jù)段內(nèi)
00000474
474: Address 0x474 is out of bounds.
Disassembly of section .bss: //bss段
00000804
804: 00000000 andeq r0, r0, r0
00000808
808: 00000000 andeq r0, r0, r0
Disassembly of section .comment:
一個程序里面有
.text 代碼段
.data 數(shù)據(jù)段
rodata 只讀數(shù)據(jù)段(const全局變量)
bss段 (初始值為0,無初始值的全局變量)
commen 注釋
其中bss段和commen 注釋不保存在bin文件中。
第002節(jié)_鏈接腳本的引入與簡單測試
前面程序運行,發(fā)現(xiàn)從Nand Flash啟動和從Nor Flash啟動表現(xiàn)是不一樣的。
設(shè)置成Nand Flash啟動沒有問題 顯示ABCDE…
設(shè)置成NOor Flash啟動則顯示AAA…
這是什么原因呢?
假如現(xiàn)在是Nor啟動:
Nor Flash就被認為是0地址,g_Char被放在0x700后面。CPU上電后從0地址開始執(zhí)行,它能讀取Nor Flash上的代碼,打印出A,當進行g(shù)_Char++的時候,寫操作操作無效,下次讀取的數(shù)據(jù)仍然是A。
假如現(xiàn)在是Nor啟動:
上電后,Nand Flash前4K代碼就被自動的復(fù)制到SRAM里面,SRAM是CPU認為的0地址。CPU上電后從0地址開始執(zhí)行,它讀取SRAM上的代碼,并g_Char++修改變量,下次讀取的數(shù)據(jù)就依次增加了。
為了解決Nor Flash里面的變量不能寫的問題,我們把變量所在的數(shù)據(jù)段放在SDRAM里面,看行不行。
修改Makefile 指定數(shù)據(jù)段為0x30000000 -Tdata 0x30000000:
arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf
這樣的話編譯出來的bin文件 從0地址 到 0x30000000地址 文件大小有700多MB,代碼段和數(shù)據(jù)段直接有間隔,稱之為黑洞
解決黑洞有兩個辦法:
第一個方法
把數(shù)據(jù)段的g_Char和代碼段靠在一起;
燒寫在Nor Flash上面;
運行時把g_char(全局變量)復(fù)制到SDRAM,即0x3000000位置(重定位);
第二個方法
讓文件直接從0x30000000開始,全局變量在0x3……;
燒寫Nor Flash上 0地址處;
運行會把整個代碼段數(shù)據(jù)段(整個程序)從0地址復(fù)制到SDRAM的0x30000000(重定位);
這兩個方法的區(qū)別是前者只重定位了數(shù)據(jù)段,后者重定位了數(shù)據(jù)段和代碼段。
參考文檔
[http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html Using LD, the GNU linker]
第一種辦法如何實現(xiàn)
修改Makefile的代碼段地址,使用鏈接腳本sdram.lds指定。
#arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf
arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf
鏈接腳本的語法:
SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
{ contents } >region :phdr =fill
...
}
我們需要依次排列 代碼段、只讀數(shù)據(jù)段、數(shù)據(jù)段、.bss段、.common。
其中數(shù)據(jù)段放在0x700,但運行時在0x3000000:
SECTIONS {
.text 0 : { *(.text) }//所有文件的.text
.rodata : { *(.rodata) } //只讀數(shù)據(jù)段
.data 0x30000000 : AT(0x700) { *(.data) } //放在0x700,但運行時在0x3000000
.bss : { *(.bss) *(.COMMON) }//所有文件的bss段,所有文件的.COMMON段
}
重新編譯后燒寫bin文件,發(fā)現(xiàn)啟動后顯示亂碼。原因是我們從0x30000000處獲取g_Char,但在這之前,并沒有在0x30000000處準備好數(shù)據(jù)。因此需要重定位數(shù)據(jù)段,將0x700的數(shù)據(jù)移動到0x30000000處,在start.S加入:
bl sdram_init
/* 重定位data段 */
mov r1, #0x700
ldr r0, [r1]
mov r1, #0x30000000
str r0, [r1]
bl main
上面的這種方法,只能復(fù)制0x700處的一位數(shù)據(jù),不太通用,下面寫一個更加通用的復(fù)制方法:
鏈接腳本修改如下:
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0x700)
{
data_load_addr = LOADADDR(.data);
data_start = . ;//等于當前位置
*(.data) //等于數(shù)據(jù)段的大小
data_end = . ;//等于當前位置
}
.bss : { *(.bss) *(.COMMON) }
}
修改start.S
bl sdram_init
/* 重定位data段 */
ldr r1, =data_load_addr /* data段在bin文件中的地址, 加載地址 */
ldr r2, =data_start /* data段在重定位地址, 運行時的地址 */
ldr r3, =data_end /* data段結(jié)束地址 */
cpy:
ldrb r4, [r1] //從r1讀到r4
strb r4, [r2] //r4存放到r2
add r1, r1, #1 //r1+1
add r2, r2, #1 //r2+1
cmp r2, r3 //r2 r3比較
bne cpy //如果不等則繼續(xù)拷貝
bl main
第003節(jié)_鏈接腳本的解析
鏈接腳本的語法
SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
{ contents } >region :phdr =fill
...
}
解釋:
secname :段名
start :起始地址:運行時的地址(runtime addr);重定位地址(relocate addr)
AT ( ldadr ) :可有可無(load addr:加載地址) 不寫時LoadAddr = runtime addr
{ contents } 的內(nèi)容:
start.o //內(nèi)容為start.o文件
*(.text)所有的代碼段文件
start.o *(.text)文件
elf文件格式
1 鏈接得到elf文件,含有地址信息(load addr)
2 使用加載器
:: 2.1 對于裸板是JTAG調(diào)試工具
:: 2.2 對于APP,加載器也是APP 把elf文件解析讀入內(nèi)存的加載地址
3 運行程序
4 如果loadaddr != runtimeaddr程序本身要重定位
核心程序運行時應(yīng)該位于 runtimeaddr(reloate addr)或者鏈接地址
bin文件
1 elf生成bin文件
2 硬件機制啟動
3 如果bin文件所在位置 不等于runtimeaddr ,程序本身實現(xiàn)重定位
bin文件/elf文件都不保存bss段 這些都是初始值為0 或者沒有初始化的全局變量
程序運行時把bss段對應(yīng)的空間清零
做個實驗,把全局變量g_A以16進制打印出來
/* 0xABCDEF12 */
void printHex(unsigned int val)
{
int i;
unsigned char arr[8];
/* 先取出每一位的值 */
for (i = 0; i < 8; i++)
{
arr[i] = val & 0xf;
val >>= 4; /* arr[0] = 2, arr[1] = 1, arr[2] = 0xF */
}
/* 打印 */
puts("0x");
for (i = 7; i >=0; i--)
{
if (arr[i] >= 0 && arr[i] <= 9)
putchar(arr[i] + '0');
else if(arr[i] >= 0xA && arr[i] <= 0xF)
putchar(arr[i] - 0xA + 'A');
}
}
//打印初始值為0的變量
int g_A = 0;
int g_B;
int main(void)
{
uart0_init();
puts("\n\rg_A = ");
printHex(g_A);
puts("\n\r");
上述代碼,沒有清理bss段 g_A等于莫名奇妙的值 并不等于0 所以需要清理bss段
修改lds鏈接文件
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0x700)
{
data_load_addr = LOADADDR(.data);
data_start = . ;
*(.data)
data_end = . ;
}
bss_start = .; //bss開始地址是當前位置
.bss : { *(.bss) *(.COMMON) }
bss_end = .; //bss結(jié)束地址也是當前位置
}
修改start.s,清除bss段
/* 清除BSS段 */
ldr r1, =bss_start
ldr r2, =bss_end
mov r3, #0
clean:
strb r3, [r1]
add r1, r1, #1
cmp r1, r2
bne clean
bl main
halt:
現(xiàn)在的代碼全局變量就是為0,通過幾行代碼,就可以少幾十個甚至上千個全局變量的存儲空間。
第004節(jié)_拷貝代碼和鏈接腳本的改進
本節(jié)進行拷貝代碼的改進和鏈接腳本的改進。
前面重定位時,需要ldrb命令從的Nor Flash讀取1字節(jié)數(shù)據(jù),再用strb命令將1字節(jié)數(shù)據(jù)寫到SDRAM里面。
cpy:
ldrb r4, [r1] /*首先從flash讀出一個字節(jié)*/
strb r4, [r2] /*讓后把數(shù)據(jù)寫到SDRAM*/
add r1, r1, #1
add r2, r2, #1
cmp r2, r3
bne cpy
JZ2440上的Nor Flash是16位,SDRAM是32位。
假設(shè)現(xiàn)在需要復(fù)制16byte數(shù)據(jù),
采用ldrb命令每次只能加載1byte,因此CPU需要發(fā)出16次命令,內(nèi)存控制器每次收到命令后,訪問硬件Nor Flash,因此需要訪問硬件16次;
同理,訪問SDRAM時,CPU需要執(zhí)行strb 16次,內(nèi)存控制器每次收到命令后,訪問硬件SDRAM,也要16次,這樣總共訪問32次。
現(xiàn)在對其進行改進,使用ldr從Nor Flash中讀,ldr命令每次加載4字節(jié)數(shù)據(jù),因此CPU只需執(zhí)行4次,但由于Nor Flash是16位的,內(nèi)存控制器每次收到CPU命令后,需要拆分成兩次訪問,因此需要訪問硬件8次;
使用str寫SDRAM,CPU只需執(zhí)行4次,內(nèi)存控制器每次收到命令后,直接硬件訪問32位的SDRAM,因此這里只需要4次,這樣總共訪問只需要12次。
在整個操作中,花費時間最長的就是硬件訪問,改進后代碼,減少了硬件訪問的次數(shù),極大的提高了效率。
根據(jù)上面原理修改代碼,修改start.S:
cpy:
ldr r4, [r1]
str r4, [r2]
add r1, r1, #4 //r1加4
add r2, r2, #4 //r2加4
cmp r2, r3 //如果r2 =< r3繼續(xù)拷貝
ble cpy
/* 清除BSS段 */
ldr r1, =bss_start
ldr r2, =bss_end
mov r3, #0
clean:
str r3, [r1]
add r1, r1, #4
cmp r1, r2 //如果r1 =< r2則繼續(xù)拷貝
ble clean
bl main
然后編譯燒寫,發(fā)現(xiàn)啟動后沒有輸出字符。修改主程序,嘗試以整數(shù)格式輸出字符,發(fā)現(xiàn)輸出的數(shù)從0開始,應(yīng)該是
全局變量被破壞了。
屏蔽掉start.S里面的清理命令,測試是否是清除bss段是清除了全局變量。
clean:
//str r3, [r1] //注釋掉此句話,str不僅把bss段清除,把全局變量這些也清除了
add r1, r1, #4
cmp r1, r2
ble clean
bl main
屏蔽后,正常輸出,鎖定了問題大致位置。查看反匯編文件,原來是沒有向4取整。
修改鏈接腳本讓bss段,使用ALIGN(4)向4取整。
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0x700)
{
data_load_addr = LOADADDR(.data);
. = ALIGN(4);
data_start = . ;
*(.data)
data_end = . ;
}
. = ALIGN(4);//讓當前地址向4對齊
bss_start = .;
.bss : { *(.bss) *(.COMMON) }
bss_end = .;
}
現(xiàn)在重新編譯燒寫,測試結(jié)果正常。
再次查看反匯編文件,發(fā)現(xiàn)現(xiàn)在bss段以4字節(jié)對齊,清理bss段也是正常的。
Disassembly of section .bss:
30000004
30000004: 00000000 andeq r0, r0, r0
30000008
30000008: 00000000 andeq r0, r0, r0
Disassembly of section .comment:
同樣的問題也會出在代碼重定位這里,如何保證data段起始地址也是向4對齊呢?
也是使用ALIGN(4)向4取整。
SECTIONS
{
. = 0x30000000;
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
_end = .;
}
Uboot是裸機的集大成者,可以參考uboot鏈接腳本也是類似的。
第005節(jié)_代碼重定位與位置無關(guān)碼
一個程序,由代碼段、只讀數(shù)據(jù)段、數(shù)據(jù)段、bss段等組成。
程序一開始可以燒在Nor Flash上面,運行時代碼段仍可以在Nor Flash運行,但對于數(shù)據(jù)段,就必須把數(shù)據(jù)段移到SDRAM中,因為只要在SDRAM里面,數(shù)據(jù)段的變量才能被寫操作,把程序從一個位置移動到另一個位置,把這個過程就稱為重定位。
前面的例子,我們只是重定位了數(shù)據(jù)段,這里我們再嘗試重定位整個代碼。
先梳理下把整個程序復(fù)制到SDRAM需要哪些技術(shù)細節(jié):
1. 把程序從Flash復(fù)制到運行地址,鏈接腳本中就要指定運行地址為SDRAM地址;
2. 編譯鏈接生成的bin文件,需要在SDRAM地址上運行,但上電后卻必須先在0地址運行,這就要求重定位之前的代碼與位置無關(guān)(是位置無關(guān)碼);
參考Uboot修改鏈接腳本:
SECTIONS
{
. = 0x30000000;
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
_end = .;
}
現(xiàn)在我們寫的這個鏈接腳本,稱為一體式鏈接腳本,對比前面的分體式鏈接腳本區(qū)別在于代碼段和數(shù)據(jù)段的存放位置是否是分開的。
例如現(xiàn)在的一體式鏈接腳本的代碼段后面依次就是只讀數(shù)據(jù)段、數(shù)據(jù)段、bss段,都是連續(xù)在一起的。
分體式鏈接腳本則是代碼段、只讀數(shù)據(jù)段,中間相關(guān)很遠之后才是數(shù)據(jù)段、bss段。
我們以后的代碼更多的采用一體式鏈接腳本,原因如下:
1. 分體式鏈接腳本適合單片機,單片機自帶有flash,不需要再將代碼復(fù)制到內(nèi)存占用空間。而我們的嵌入式系統(tǒng)內(nèi)存非常大,沒必要節(jié)省這點空間,并且有些嵌入式系統(tǒng)沒有Nor Flash等可以直接運行代碼的Flash,就需要從Nand Flash或者SD卡復(fù)制整個代碼到內(nèi)存;
2. JTAG等調(diào)試器一般只支持一體式鏈接腳本;
修改start.S段
/* 重定位text, rodata, data段整個程序 */
mov r1, #0
ldr r2, =_start /* 第1條指令運行時的地址 */
ldr r3, =__bss_start /* bss段的起始地址 */
cpy:
ldr r4, [r1]
str r4, [r2]
add r1, r1, #4
add r2, r2, #4
cmp r2, r3
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
halt:
b halt
將修改后的代碼重新編譯燒寫在Nor Flash上,上電運行。
對本代碼的啟動情況進行分析:
在生成的bin文件里,代碼保存的位置是0x30000000。隨后燒寫到NOR Flash的0地址,但代碼的結(jié)構(gòu)沒有變化。之后再重定位到SDRAM。
查看反匯編:
3000005c: eb000106 bl 3000047830000060: e3a01000 mov r1, #0 ; 0x0 30000064: e59f204c ldr r2, [pc, #76] ; 300000b8 <.text+0xb8> 30000068: e59f304c ldr r3, [pc, #76] ; 300000bc <.text+0xbc>123456
這里的bl 30000478不是跳轉(zhuǎn)到30000478,這個時候sdram并未初始化;
為了驗證,我們做另一個實驗,修改連接腳本sdram.lds, 鏈接地址改為0x32000478,編譯,查看反匯編:
3000005c: eb000106 bl 3000047830000060: e3a01000 mov r1, #0 ; 0x0 30000064: e59f204c ldr r2, [pc, #76] ; 300000b8 <.text+0xb8> 30000068: e59f304c ldr r3, [pc, #76] ; 300000bc <.text+0xbc>123456
可以看到現(xiàn)在變成了bl 30000478,但兩個的機器碼eb000106都是一樣的,機器碼一樣,執(zhí)行的內(nèi)容肯定都是一樣的。
因此這里并不是跳轉(zhuǎn)到顯示的地址,而是跳轉(zhuǎn)到: pc + offset,這個由鏈接器決定。
假設(shè)程序從0x30000000執(zhí)行,當前指令地址:0x3000005c ,那么就是跳到0x30000478;如果程序從0運行,當前指令地址:0x5c 調(diào)到:0x00000478
跳轉(zhuǎn)到某個地址并不是由bl指令所決定,而是由當前pc值決定。反匯編顯示這個值只是為了方便讀代碼。
重點:
反匯編文件里, B或BL 某個值,只是起到方便查看的作用,并不是真的跳轉(zhuǎn)。
怎么寫位置無關(guān)碼?
使用相對跳轉(zhuǎn)命令 b或bl;
重定位之前,不可使用絕對地址,不可訪問全局變量/靜態(tài)變量,也不可訪問有初始值的數(shù)組(因為初始值放在rodata里,使用絕對地址來訪問);
重定位之后,使用ldr pc = xxx,跳轉(zhuǎn)到/runtime地址;
寫位置無關(guān)碼,其實就是不使用絕對地址,判斷有沒有使用絕對地址,除了前面的幾個規(guī)則,最根本的辦法看反匯編。
因此,前面的例子程序使用bl命令相對跳轉(zhuǎn),程序仍在NOR/sram執(zhí)行,要想讓main函數(shù)在SDRAM執(zhí)行,需要修改代碼:
//bl main /*bl相對跳轉(zhuǎn),程序仍在NOR/sram執(zhí)行*/ ldr pc, =main/*絕對跳轉(zhuǎn),跳到SDRAM*/123
第006節(jié)重定位清除BSS段的C函數(shù)實現(xiàn)
在前面,我們使用匯編程序來實現(xiàn)了重定位和清bss段,本節(jié)我們將使用C語言,實現(xiàn)重定位和清除bss段。
1.打開start.S把原來的匯編代碼刪除改為調(diào)用C函數(shù)
/* 重定位text, rodata, data段整個程序 */ mov r1, #0 ldr r2, =_start /* 第1條指令運行時的地址 */ ldr r3, =__bss_start /* bss段的起始地址 */cpy: ldr r4, [r1] str r4, [r2] add r1, r1, #4 add r2, r2, #4 cmp r2, r3 ble cpy /* 清除BSS段 */ ldr r1, =__bss_start ldr r2, =_end mov r3, #0clean: str r3, [r1] add r1, r1, #4 cmp r1, r2 ble clean1234567891011121314151617181920212223
改為
/* 重定位text, rodata, data段整個程序 */ mov r0, #0 ldr r1, =_start /* 第1條指令運行時的地址 */ ldr r2, =__bss_start /* bss段的起始地址 */ sub r2, r2, r1 /*長度*/ bl copy2sdram /* src, dest, len */ /* 清除BSS段 */ ldr r0, =__bss_start ldr r1, =_end bl clean_bss /* start, end */123456789101112131415
在init.c 實現(xiàn)如上兩個C函數(shù)
void copy2sdram(volatile unsigned int *src, volatile unsigned int *dest, unsigned int len) /* src, dest, len */{ unsigned int i = 0; while (i < len) { *dest++ = *src++; i += 4; } }void clean_bss(volatile unsigned int *start, volatile unsigned int *end) /* start, end */{ while (start <= end) { *start++ = 0; } }12345678910111213141516171819202122
匯編中,為C語言傳入的參數(shù),依次就是R1、R2、R3。
編譯,燒寫運行沒有問題。
我們假設(shè)不想?yún)R編傳入?yún)?shù),而是C語言直接取參數(shù)。
修改start.S 跳轉(zhuǎn)到C函數(shù)不需要任何參數(shù)
bl sdram_init //bl sdram_init2 /* 用到有初始值的數(shù)組, 不是位置無關(guān)碼 */ /* 重定位text, rodata, data段整個程序 */ bl copy2sdram /* 清除BSS段 */ bl clean_bss123456789
修改鏈接腳本,讓__code_start 等于當前地址,也就是這里的0x30000000
SECTIONS { . = 0x30000000; __code_start = .; //定義__code_start地址位當前地址 . = ALIGN(4); .text : { *(.text) } . = ALIGN(4); .rodata : { *(.rodata) } . = ALIGN(4); .data : { *(.data) } . = ALIGN(4); __bss_start = .; .bss : { *(.bss) *(.COMMON) } _end = .; }1234567891011121314151617181920212223
3.修改init.c 用函數(shù)來獲取參數(shù)
void copy2sdram(void) { /* 要從lds文件中獲得 __code_start, __bss_start * 然后從0地址把數(shù)據(jù)復(fù)制到__code_start */ extern int __code_start, __bss_start;//聲明外部變量 volatile unsigned int *dest = (volatile unsigned int *)&__code_start; volatile unsigned int *end = (volatile unsigned int *)&__bss_start; volatile unsigned int *src = (volatile unsigned int *)0; while (dest < end) { *dest++ = *src++; } }void clean_bss(void) { /* 要從lds文件中獲得 __bss_start, _end */ extern int _end, __bss_start; volatile unsigned int *start = (volatile unsigned int *)&__bss_start; volatile unsigned int *end = (volatile unsigned int *)&_end; while (start <= end) { *start++ = 0; } }1234567891011121314151617181920212223242526272829303132333435
編譯燒寫運行 ,沒有問題。
總結(jié):
C函數(shù)怎么使用lds文件總的變量abc?
在C函數(shù)中聲明改變量為extern外部變量類型,比如:extern int abc;
使用時,要取址,比如:int *p = &abc;//p的只即為lds文件中abc的值
匯編文件中可以直接使用外部鏈接腳本中的變量,但C函數(shù)中要加上取址符號。
解釋一下原因:
C函數(shù)中,定義一個全局變量int g_i;,程序中必然有4字節(jié)的空間留出來給這個變量g_i。
假如我們的lds文件中有很多變量
lds{ a1 = ; a2 = ; a3 = ; ... }
如果我們C程序只用到幾個變量,完全沒必要全部存儲lds里面的所有變量,C程序是不保存lds中的變量的。
對于萬一要用到的變量,編譯程序時,有一個symbol table符號表:
如何使用symbol table符號表?
對于常規(guī)變量g_i,得到里面的值,使用&g_i得到addr;
為了保持代碼的一致,對于lds中的a1,使用&a1得到里面的值;
這只是一個編譯器的小技巧,不用深究。
結(jié)論:
C程序中不保存lds文件中的變量,lds再大也不影響;
借助symbol table保存lds的變量,使用時加上”&”得到它的值,鏈接腳本的變量要在C程序中聲明為外部變量,任何類型都可以;
上一篇:s3c2440中斷程序(燒錄到NORFlash,運行在SDRAM中)
下一篇:存儲控制器與外設(shè)之間的關(guān)系
推薦閱讀
史海拾趣
自1989年成立以來,CUI Inc.一直站在電源設(shè)計的前沿。公司不斷投資于研發(fā),致力于開發(fā)出更高效、更環(huán)保的電源產(chǎn)品。通過引入先進的電源管理技術(shù)和創(chuàng)新的設(shè)計方法,CUI成功地幫助客戶提高了應(yīng)用的能效,減少了能源消耗。這種對電源技術(shù)的專注和創(chuàng)新,使CUI在競爭激烈的電子行業(yè)中脫穎而出,贏得了眾多客戶的信賴和好評。
背景:進入21世紀后,F(xiàn)iradec公司意識到全球化市場的重要性,開始實施全球化戰(zhàn)略布局。
發(fā)展:公司首先在歐洲和亞洲設(shè)立了研發(fā)中心和銷售網(wǎng)絡(luò),以便更好地貼近當?shù)厥袌鲂枨蟆kS后,F(xiàn)iradec通過并購和合作等方式,進一步擴大了在全球范圍內(nèi)的市場份額和影響力。
影響:全球化戰(zhàn)略的成功實施,使Firadec公司能夠迅速響應(yīng)全球市場的變化,抓住新的發(fā)展機遇。同時,公司也通過與國際知名企業(yè)的合作,不斷提升自身的技術(shù)實力和市場競爭力。
在技術(shù)創(chuàng)新的基礎(chǔ)上,Component General Inc公司開始積極拓展市場。公司不僅在國內(nèi)市場取得了良好的銷售業(yè)績,還積極開拓國際市場,與多家海外企業(yè)建立了合作關(guān)系。同時,公司也注重品牌建設(shè),通過參加各種行業(yè)展會、舉辦技術(shù)研討會等方式,提升公司的知名度和影響力。
Connect-Tech Products Corp公司始終將產(chǎn)品質(zhì)量視為企業(yè)發(fā)展的生命線。公司建立了嚴格的質(zhì)量管理體系,從原材料采購、生產(chǎn)加工到產(chǎn)品出廠,每一個環(huán)節(jié)都嚴格把控。此外,公司還注重持續(xù)改進,不斷優(yōu)化生產(chǎn)流程和技術(shù)創(chuàng)新,確保產(chǎn)品質(zhì)量的穩(wěn)定性和可靠性。這些措施使得Connect-Tech Products Corp公司的產(chǎn)品在市場上贏得了良好的口碑和信譽。
比如說我要在00H到10H單元依次寫入一段數(shù)據(jù)然后再讀取出來,這個在匯編容易實現(xiàn),但是C語言怎樣實現(xiàn)呢?… 查看全部問答∨ |
|
用鐵通網(wǎng)和大局域網(wǎng)做遠程監(jiān)控的親身經(jīng)歷 轉(zhuǎn)自:中安網(wǎng) 論壇 深圳小姚 以上兩個圖的IP地址(121.34.95.55)分別從路由器上和IP138.com得到,如一至說明你本地的IP地址是公網(wǎng)IP地址,是可以做端口應(yīng)射和域名綁定的,大家可以試一下,很簡單。 關(guān)于用鐵通網(wǎng)和大局域網(wǎng)做遠程監(jiān)控的經(jīng) ...… 查看全部問答∨ |
新接觸PXA303 CPU,在使用中發(fā)現(xiàn)GPIO1中斷檢測不到, 1. 通過配置GPIO1輸出方波,示波器可以檢測到,說明硬件連接應(yīng)該沒有問題,但是配置為輸入則檢測不到高低電平 2. 配置別的GPIO,可以檢測到中斷,會運行相應(yīng)的中斷處理函數(shù),只有配置GPIO1時檢測不 ...… 查看全部問答∨ |
現(xiàn)在急需這種模擬器,版本越高越好。哪位大俠幫個忙,提供個網(wǎng)址或發(fā)個到郵箱,不勝感激!yccwt@qq.com… 查看全部問答∨ |
|
求基于TMS320F2812的圖形點陣液晶屏及鍵盤控制的原理圖 pcb 軟件流程及代碼 求基于TMS320F2812的圖形點陣液晶屏及鍵盤控制的原理圖 pcb 軟件流程及代碼 用DSP控制液晶屏和鍵盤,模擬簡單功能示波器界面。能顯示波形、電壓值、頻率;能通過鍵盤調(diào)節(jié)顯示的波形,電壓幅值以及頻率。原理圖 pcb 軟件流程及代碼 郵箱 ...… 查看全部問答∨ |
我現(xiàn)在用的仿真器是JLINK V8(山寨),編譯器KEIL4.12,KEIL4.22,KEIL4.54,IAR6.3.0.1,IAR6.4(都試過),老是出現(xiàn)各種各樣的問題,最主要的問題是燒寫能燒進去,就是不能正常仿真。PS:需要使用片內(nèi)FLASH進行編程。 用KEIL的話, ...… 查看全部問答∨ |
設(shè)計資源 培訓(xùn) 開發(fā)板 精華推薦
- 英飛凌推出XENSIV? 3D磁傳感器,為汽車、工業(yè)和消費類應(yīng)用帶來高精度位置檢測功能
- RKDC2025 丨米爾亮相第九屆瑞芯微開發(fā)者大會,共繪工業(yè)數(shù)智新圖景
- 「芯生態(tài)」杰發(fā)科技AC7870攜手IAR開發(fā)工具鏈,助推汽車電子全棧全域智能化落地
- 緊湊型PTS647輕觸開關(guān)系列增加了降噪和防塵功能
- 電動汽車換電的好處
- 高壓BMS如何增強安全性并延長電池的使用壽命
- 消息稱英偉達-聯(lián)發(fā)科 AI PC 芯片延遲除設(shè)計修改亦與微軟系統(tǒng)進度緩慢等有關(guān)
- 消息稱長江存儲 2026 年底 NAND 產(chǎn)能市占有望達 15%
- 用于電動汽車供電網(wǎng)絡(luò)(PDN)的高壓母線轉(zhuǎn)換器48V電源模塊
- Wolfspeed與恩智浦攜手推出經(jīng)過全面測試的800V牽引逆變器參考設(shè)計
- ARM GPIO接口置位方法
- ARM處理器學(xué)習(xí)之--GPIO操作篇
- 蘋果Siri不溫不火 產(chǎn)品最后一位創(chuàng)始人也離職了
- 2018《財富》世界500強公布:中國500強企業(yè)僅比美國少一家
- 華帝回應(yīng)中消協(xié)喊話,稱會履行承諾,全力配合退款
- 紫光股份預(yù)計2017年凈利潤16.20億 深康佳去年業(yè)績預(yù)增超51倍
- 隆基股份預(yù)計2017年凈利潤達36.00億元,同比增長133%
- 芯片問題對云計算應(yīng)用的影響低于事先預(yù)期
- 聯(lián)想、OPPO、vivo和小米分別與高通簽署諒解備忘錄
- 基于DSP和CMOS圖像傳感器的實時圖像采集系統(tǒng)的實現(xiàn)方案