zoukankan      html  css  js  c++  java
  • 拷贝代码和连接脚本的改进

    上一篇随笔说了,需要清除bss段,我们现在定义main函数如下:

    注意这个全局变量是没有初始值的,即存放在bss段中,如果我们的启动文件没有清除bss段,串口的输出将是你想不到情况。比如,现在程序运行执行了++操作20次,你下次快速断电再上电的时候,g_Char2的值是接着之前的值增加的,而增加了清除bss段之后,每次重新上电,都是从0开始的。到这里我是觉得纳闷的,我们程序是重定位到了sdram的,既然是ram中,掉电不就应该是马上丢失数据了吗?然后查询了一下资料,发现:

    然后我就多等了一些时间,果然,可以看到sdram数据在慢慢丢失,又涨知识了~

    现在我们在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

    连接脚本也需要更改:

    SECTIONS
    { 
        .text 0: {*(.text)}
        .rodata : {*(.rodata)}
        .data 0x30000000 :AT(0x800)
        {
            data_load_add = LOADADDR(.data);    
            data_start = . ;
            *(.data)
            data_end = . ;
        }
     
       .bss  : 
       {
        bss_start = .;
        
       *(.bss) *(COMMON) ;
       
        bss_end = .;
       }
     
    }

    这样(上面链接脚本中bss段中.bss  *(COMMEN)后那个分号仅仅为了美观,会被连接器忽略的,这个在ld连接器那篇文章中有讲到)就可以看到串口输出了,是从ascii码0开始打印的。

     回到本文的重点,拷贝代码和连接脚本的改进

    我们的sdram是32位的,nor falsh是16位的。

    我们的copy和clean采用的b(字节)操作,这样是很费时的,因为sdram每次操作的最小单位是32位的,即四字节,如果采取一个字节的方式,sdram需要发送3个数据屏蔽信号DQM,这样才能只写一个字节,可见效率之低。现在采取4字节方式,改进如下:

        /* 重定位data段 */
        ldr r1, =data_load_addr  /* data段在bin文件中的地址, 加载地址 */
        ldr r2, =data_start      /* data段在重定位地址, 运行时的地址 */
        ldr r3, =data_end          /* data段结束地址 */
    
    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, =bss_end
        mov r3, #0
    clean:
        str r3, [r1]
        add r1, r1, #4
        cmp r1, r2
        ble clean

    这里说明一下 ble汇编指令:

        cmp r1, r2
        ble clean
    比较 r1和r2,如果r1小于等于r2,就跳转到clean。

    为什么之前都是用的 bne,而现在要用 ble 了呢?之前是一个字节一个字节的移动,每次都是加一,所以一定会有 r1和r2相等的时候,不相等就复制,相等了就不再执行,可是现在转换成4字节移动的时候,比如我们data段数据只有2个字节,只用一次赋值4个字节,就复制完毕了,但是现在起始地址需要加4,这样就会出现起始地址加4之后大于结束地址,那么就应该不再复制了,这也就是为什么使用ble的原因。可是,使用ble虽然很好的解决了上面所说的问题,也依旧存在一个潜在的浪费,比如我的data段刚好是4个字节,那样执行一次复制之后,其实地址加4刚好就等于结束地址,可是ble是小于等于都要执行,这样的话,就会再次执行拷贝,而其实拷贝在第一次的时候就已经完成了,这样就会多做一次加载地址到运行地址的拷贝,综上,选择使用 BLO或BCC【查看反汇编,使用BLO此时编译器也是转换成BCC执行的】(地址是无符号的,blt是有符号运算)更合适,这也是韦老大的没有注意的地方吧。

    下面是可获得的条件代码的列表:

    EQ: Equal   等于,(Z = 1)
    NE: Not Equal   不等于 (Z = 0)
    CS: Carry Set   有进位 (C = 1)
    HS: (unsigned Higher Or Same) 同CS (C = 1)
    CC: (Carry Clear) 没有进位 (C = 0)
    LO: (unsigned Lower) 同CC  (C = 0)
    MI: (Minus) 结果小于0  (N = 1)
    PL: (Plus) 结果大于等于0 (N = 0)
    VS: (oVerflow Set) 溢出 (V = 1)
    VC: (oVerflow Clear) 无溢出 (V = 0)
    HI : (unsigned Higher) 无符号比较,大于 (C = 1 & Z = 0)
    LS: (unsigned Lower or Same) 无符号比较,小于等于 (C = 0 & Z = 1)
    GE: (signed Greater than or Equal) 有符号比较,大于等于 (N = V)
    LT: (signed Less Than) 有符号比较,小于  (N != V)
    GT: (signed Greater Than) 有符号比较,大于 (Z = 0 & N = V)
    LE: (signed Less Than or Equal) 有符号比较,小于等于 (Z = 1 | N != V)
    AL: (Always) 无条件,默认值
    NV: (Never) 从不执行

     不过很遗憾,我们上面的代码并不能正常工作!

    首先说明,我定义了两个全局变量,类型为char,一个字节,共计两个字节,我们的sdram地址是0x30000000,两个字节后,应该从0x30000002开始存储,由于我们清零操作使用str,四字节操作,需要对4的整数倍取地址,所以,这里清零操作会从0x30000000开始,就会使清零bss段的代码也把全局变量给清除了。你或许会问,为什么不是从0x30000004开始,都是4的整数倍,为什么是向下呢(编译器以最节约内存的方式,指定了对齐方式就不同了)?因为我们没有指定对齐的字节数目,向4取整,不足4的倍数,往下计算,而当我们指定对齐4字节时,虽然此时是0x30000002地址,由于规定了对齐字节,会变成0x30000004地址处,为什么这里是向上了?这个C语言中结构体内存对齐的原理一致,前面0x30000002有了存储内容,后面新开始存储的,就需要往后延伸至4的倍数。

    所以,改进代码如下:

    #include "s3c2440_soc.h"
    #include "uart.h"
    #include "init.h"
    
    char g_Char = 'A';
    char g_Char2 ;
    int g_A = 0;
    int g_B;
    
    void delay(volatile int d)
    {
        while (d--);
    }
    
    int main(void)
    {
        uart0_init();
    
        while (1)
        {
            putchar(g_Char2);
            g_Char2++;        
            delay(1000000);
            putchar(g_Char);
            g_Char++;        
            delay(1000000);
        }
    
        
        return 0;
    }

    这样之后,正常运行的同时也没有做多余的操作,经过分析反汇编之后,发现和我理解的一致,韦老大那个代码做了多余的copy操作,反汇编可以清楚的看到data段的起始地址和结束地址,当起始地址累加到等于结束地址的时候,就不该再执行复制操作了,所以不应该使用ble这个有符号的还是条件小于等于的指令。

  • 相关阅读:
    grpc学习
    01
    样本1
    杀死长时间占用CPU的进程
    SWFTools pdf2swf 参数详解
    C#自动下载并保存文件示例
    Flex初始化时加载外部XML
    通过XPDF抽取PDF中的中文文本
    Flex操作Json数据示例
    C#下载文件和将文件转换为数据流下载的示例
  • 原文地址:https://www.cnblogs.com/yangguang-it/p/7912630.html
Copyright © 2011-2022 走看看