s3c2440代码重定位和段的引入——学以致用,综合Makefile的锻炼

发布时间:2023-09-25  

对于2440而言,nand启动,nand的前4k内容由硬件复制到sram。


nor flash,可以像内存一样读,但是不能像内存一样写,执行写操作需要特殊的操作。


程序中包含有需要写的全局或者静态变量,它们在bin文件中,写在nor flash上,直接修改这样的变量是无效的。


到底什么意思呢?还是看例子比较有说服力。


在学习C语言的过程中,我们或多或少知道一些东西,c/c++可执行文件需要预处理,编译,汇编,连接。


程序有text段,data段,bss段,rodata段等等,今天,就和它们来个亲密接触吧。


还是先说上面的问题吧,看例子:


在之前的程序代码基础上,启动代码增加自动识别是nand还是nor启动:


    /* 设置内存: sp 栈 */

    /* 分辨是nor/nand启动

     * 写0到0地址, 再读出来

     * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动

     * 否则就是nor启动

     */

    mov r1, #0

    ldr r0, [r1] /* 读出原来的值备份 */

    str r1, [r1] /* 0->[0] */ 

    ldr r2, [r1] /* r2=[0] */

    cmp r1, r2   /* r1==r2? 如果相等表示是NAND启动 */

    ldr sp, =0x40000000+4096 /* 先假设是nor启动 */

    ldreq sp, =4096  /* nand启动 */

    streq r0, [r1]   /* 恢复原来的值 */


nor flash启动,写入和读出不会相等,即执行ldr sp, =0x40000000+4096 /* 先假设是nor启动 */


而nand启动,由于写入读出都相等,会执行


ldreq sp, =4096 /* nand启动 */

streq r0, [r1] /* 恢复原来的值 */


上面也说了,由于nor flash 的特性,导致像内存一样读可以,可是写操作需要特殊处理,后面的例子你将会看到按内存方式直接写nor flash是无效的。


start.S:


.text

.global  _start


_start:

    /* 关闭看门狗 */

    ldr r0, =0x53000000

    ldr r1, =0

    str r1, [r0]


    /* 设置内存: sp 栈 */

    /* 分辨是nor/nand启动

     * 写0到0地址, 再读出来

     * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动

     * 否则就是nor启动

     */

    mov r1, #0

    ldr r0, [r1] /* 读出原来的值备份 */

    str r1, [r1] /* 0->[0] */ 

    ldr r2, [r1] /* r2=[0] */

    cmp r1, r2   /* r1==r2? 如果相等表示是NAND启动 */

    ldr sp, =0x40000000+4096 /* 先假设是nor启动 */

    ldreq sp, =4096  /* nand启动 */

    streq r0, [r1]   /* 恢复原来的值 */


    bl SystemInit

    //调用main函数

    bl main


halt:

    b halt


main.c:


#include "s3c2440_gpio.h"

#include "s3c2440_soc.h"

#include "uart.h"

#include "init.h"

unsigned char glob_a='a';

unsigned char glob_b='b';


const char p=1;

char *q="char *q";


static int golb_c;

static int golb_d;


int glob_e=1;

int glob_f;

void SystemInit(void)

{

    //配置LOCKTIME(0x4C000000) = 0xFFFFFFFF 

    *(volatile unsigned int *)0x4C000000=0xFFFFFFFF;

    //CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8

    *(volatile unsigned int *)0x4C000014=0x5;

    //协处理指仿

    __asm__(

    "mrc    p15, 0, r1, c1, c0, 0n"        /* 读出控制寄存噿*/ 

    "orr    r1, r1, #0xc0000000n"          /* 设置为“asynchronous bus mode‿*/

    "mcr    p15, 0, r1, c1, c0, 0n"        /* 写入控制寄存噿*/

    );

    /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) 

     *  m = MDIV+8 = 92+8=100

     *  p = PDIV+2 = 1+2 = 3

     *  s = SDIV = 1

     *  FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M

     */

    *(volatile unsigned int *)0x4C000004=(92<<12)|(1<<4)|(1<<0);

}

void Delay(uint32_t count)

{

    while(count--);

}

int main(void)

{

    

    uart_init();

    puts("Hello, world!nr");

    //sdram_init();


    while (1)

    {

        putchar(glob_a);

        glob_a++;

        Delay(100000);

    }

    return 0;

}


注意,上面的红色部分,此前的代码我们都没有使用全局变量,这样加上之后,我们编译生成bin文件,其他文件夹都是前面例子的,其实这里只是使用了配置时钟,区分是nand还是nor启动以及前面的串口程序。把这个程序烧写在nand flash上运行,和我们想象中的一致,但是,烧写在nor flash 上,代码却和我们预想的不一致了。


在nand 上 运行,串口会打印abcde。。。。


在nor上运行,串口一直打印a!


这是为什么呢?


我们程序不是做了++处理吗?


查看反汇编:


Disassembly of section .data:


00000800 <__data_start>:

 800:    00006261     andeq    r6, r0, r1, ror #4


00000801 :

 801:    d0000062     andle    r0, r0, r2, rrx


00000804 :

 804:    000005d0     ldreqd    r0, [r0], -r0


00000808 :

 808:    00000001     andeq    r0, r0, r1

Disassembly of section .rodata:


000005cc

:

 5cc:    00000001     andeq    r0, r0, r1

 5d0:    72616863     rsbvc    r6, r1, #6488064    ; 0x630000

 5d4:    00712a20     rsbeqs    r2, r1, r0, lsr #20

 5d8:    6c6c6548     cfstr64vs    mvdx6, [ip], #-288

 5dc:    77202c6f     strvc    r2, [r0, -pc, ror #24]!

 5e0:    646c726f     strvsbt    r7, [ip], #-623

 5e4:    000d0a21     andeq    r0, sp, r1, lsr #20

Disassembly of section .bss:


0000080c :

 80c:    00000000     andeq    r0, r0, r0


00000810 :

 810:    00000000     andeq    r0, r0, r0


00000814 :

 814:    00000000     andeq    r0, r0, r0

Disassembly of section .comment:


00000000 <.comment>:

   0:    43434700     cmpmi    r3, #0    ; 0x0

   4:    4728203a     undefined

   8:    2029554e     eorcs    r5, r9, lr, asr #10

   c:    2e342e33     mrccs    14, 1, r2, cr4, cr3, {1}

  10:    47000035     smladxmi    r0, r5, r0, r0

  14:    203a4343     eorcss    r4, sl, r3, asr #6

  18:    554e4728     strplb    r4, [lr, #-1832]

  1c:    2e332029     cdpcs    0, 3, cr2, cr3, cr9, {1}

  20:    00352e34     eoreqs    r2, r5, r4, lsr lr

  24:    43434700     cmpmi    r3, #0    ; 0x0

  28:    4728203a     undefined

  2c:    2029554e     eorcs    r5, r9, lr, asr #10

  30:    2e342e33     mrccs    14, 1, r2, cr4, cr3, {1}

  34:    47000035     smladxmi    r0, r5, r0, r0

  38:    203a4343     eorcss    r4, sl, r3, asr #6

  3c:    554e4728     strplb    r4, [lr, #-1832]

  40:    2e332029     cdpcs    0, 3, cr2, cr3, cr9, {1}

  44:    00352e34     eoreqs    r2, r5, r4, lsr lr


这个我们知道了一点(上面例子的第一个static给个非零初始值就更好了,但我不想开虚拟机了,没动手的你可要试试哦),全局或者静态变量初始化为0或者不初始化的都放在.bss段,初始化了的全局或者静态变量放在.data段,const修饰的变量放在.rodata段,.comment段是注释段,比如上面的注释前面几个机器码,我们可以看出它注释的是编译器是gnu gcc。(如果你感兴趣可以全部打出来看看注释信息,采用的UE查看的)

小端模式,所以是反着输入的。


然后就应该是Makefile了,上面的代码需要连接一个data段,否则生成的bin文件会非常大。


.PHONY:clean

objcts :=start.o main.o init.o uart.o s3c2440_gpio.o

sdram.bin:$(objcts)

    arm-linux-ld -Ttext 0 -Tdata 0x800 $^ -o sdram.elf

    arm-linux-objcopy -O binary -S sdram.elf $@

    arm-linux-objdump -S -D sdram.elf > sdram.dis

%.o:%.c

    arm-linux-gcc -o $@ -c -g $<

%.o:%.S

    arm-linux-gcc -o $@ -c -g $<

clean:

    -rm *.bin *.o *.elf *.dis


是不是发现好像makefile变了,是的,是时候加进一步了,之前为了不附加难度,都是使用的最基本的方式书写Makefile,现在熟悉了2440大部分裸机代码了,就该写出点其他的make了。这个Makefile还不是最终版,有潜藏bug,后面再说怎么进一步优化。其中增加了一个连接地址


-Tdata 0x800

这是把数据段从0x800开始存储。为什么要从0x800开始连接数据段呢?

先看看我们没有指定这个选项的时候:

3cb8411350034544e968746793d24771_1100576-20171125135917703-910223116.png

可以看到这个bin文件的大小居然有30多k,而我们的代码是非常小的,这显然是不正常的,我们加上连接时指定数据段地址之后:

094032ca0ad512a18c5eaaa41d77c782_1100576-20171125140102328-1023083401.png

这样之后,我们发现文件的大小就比较好接受了。这个数据段地址,需要根据实际情况调整,这里只是演示作用。

好的,现在回到Makefile:


Makefile命令中的带有-(减号)时,表示忽略错误,继续执行make。

@:使命令在被执行前不被回显。


这里主要说明-(减号):


.PHONY:clean


app:main.o


        gcc -o app main.o


main.o:main.c


        gcc -c main.c


clean:


        rm *.o


        rm app


main函数只有一个printf函数,此时执行make

895e6cb78e53e1850906c670927f85a7_1100576-20171125140655484-485021967.png


.PHONY:clean 

app:main.o 

    gcc -o app main.o 

main.o:main.c 

    gcc -c main.c  

clean: 

    -rm *.o  

    rm app

a80edf42a37e4a66256b8cd6bb3b42c5_1100576-20171125140746671-1909744822.png

关于2440上面的那个Makefile,在我之前的随笔中是有说明的,


我们的2440依赖了一个头文件(s3c2440_soc.h),而这个头文件是没有对应源文件的,这样更改这个头文件之后,必须make clean之后,再make才能确保是最新的,如果更改了这个头文件,还是直接make,那么make是不会响应你最新改动的,所以需要我们使用sed指令来进行Makefile的书写,也就是上面的Makefile7所说明的东西。


以前学过了的东西,一定要试着拿在实验或者项目上应用,否则,学那么不使用又有什么意义呢?


 Question:


main函数中的


char *q="char *q";


q变量倒是存在.data段中,那么“char *q”这个字符串存放在哪里呢?后面的随笔会给出答案,其实这也是C语言中接触过的知识点。


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>