查看: 1100|回复: 7

[原创] 【LPC845-BRK板卡试用申请】(二)shell 移植适配

[复制链接]
  • TA的每日心情
    无聊
    6 小时前
  • 签到天数: 598 天

    [LV.9]以坛为家II

    51

    主题

    2232

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    7113
    最后登录
    2024-5-6
    发表于 2023-4-3 23:00:32 | 显示全部楼层 |阅读模式
    本帖最后由 andeyqi 于 2023-4-5 14:59 编辑



    Shell 在开发过程中经常会被使用到,本次移植适配的Shell 是基于Uart 方式实现,Shell 程序的移植适配需要依赖底层的接口位串口的手法函数,上一篇的(【LPC845-BRK板卡试用申请】(一)开发板体验之上电体验) 工程中已经可以通过串口输出和输入串口数据,shell 的移植依赖也就不需要额外适配了,串口的移植适已经完成S08平台完成过,移植说明参考此贴(【S08P闯关赛经验分享】+添加shell控制开发板)只要将源文件加入编译即可完成,因之前是基于IAR工具链本次使用的gcc的工具链实现上会有些差距,本篇主要介绍移植过程中遇到的问题进行整理。

    1.MCUXpresso IDE(v11.7.1_9221)  如何将文件夹源文件加入编译

    MCUXpresso 同IAR/MDK 常用的开发工具加入文件的方法稍微有些不同,IAR/MDK 将文件夹加入编译在workspace 下创建一个gpoup 将源文件加入编译列表即可参与编译,在MCUXpresso  工具下将文件夹放到工程目录下MCUXpresso   会自动刷新目录结构,如下将shell 文件夹放入工程目录下后workspace 下会多出shell 对应的文件夹,这时进行build 会发现shell 文件夹并未参与编译,仔细查看也会发现参与编译的未参与编译的显示上也会有差别,图中方框的部分是参与编译的文件夹,椭圆形的是未参与编译的目录。
    build_path.png


    通过工程 Properties->C/C++ General->Paths and Symbols->Source location 路径下将文件夹加入编译路径。

    addsource.png

    将源码文件夹加入编译路径后在对比下文件夹的显示情况会发现和之前已经不同了,此时进行build 该文件夹已经参与编译了。

    update.png



    2.MCUXpresso IDE(v11.7.1_9221)链接脚本自定义的段不进行覆盖

    shell 的实现是在flash 中定义一个FSymTab 的section,代码中在 FSymTab  起始地址和结束地址范围内进行检索查找输入的命令是否吻合,如果一致的话就执行绑定的函数,在工程的lpc845breakout_hello_world_Debug.ld 文件中加入如下 FSymTab  section 区间。
         /* MAIN TEXT SECTION */
        .text : ALIGN(4)
        {
          . = ALIGN(4);
               /* section information for shell */
            __fsymtab_start = .;
            KEEP(*(FSymTab))
            __fsymtab_end = .;

        } > PROGRAM_FLASH
    加入上述的section  后,重新build 发现本地修改的链接脚本文件被覆盖了恢复成改动之前的样子了,从MCUXpresso 的帮助文档中可知默认是开启自动生成link file 的属性的



    1. Disabling Managed Linker Scripts

    2. It is possible to disable the managed linker script mechanism if required and provide your own linker scripts, but this is not recommended for most users. In most circumstance, the facilities provided by the managed linker script mechanism, and its underlying FreeMarker template mechanism should allow you to avoid the need for writing your own linker scripts. But if you do wish to do this, then untick the appropriate option at:

    3. Properties -> C/C++ Build -> Settings -> MCU Linker -> Managed Linker Script
    复制代码


    对于我们的使用场景需要关闭此特性,加入自定义的section,通过如下path 即可设置关闭。
    Properties -> C/C++ Build -> Settings -> MCU Linker -> Managed Linker Script
    linkoverwrite.png


    去掉上述勾选后发现我们修改的内容不会被MCUXpresso  overwrite了。


    3.GCC 编译器对link file 定义的symbol 的引用


    因之前的代码是基于IAR 环境下的,IAR 通过  __section_begin("FSymTab"),__section_end("FSymTab") 可以引用symbol 的定义此语法在gcc 编译器下是无法编译通过的,一下是摘自“GNU链接器ld_v2.34”对代码引用link file 的symbol的描述。
    1. 3.5.5 Source Code Reference

    2. Accessing a linker script defined variable from source code is not intuitive. In particular a linker script symbol is not equivalent to a variable declaration in a high level language, it is instead a symbol that does not have a value.

    3. Before going further, it is important to note that compilers often transform names in the source code into different names when they are stored in the symbol table. For example, Fortran compilers commonly prepend or append an underscore, and C++ performs extensive ‘name mangling’. Therefore there might be a discrepancy between the name of a variable as it is used in source code and the name of the same variable as it is defined in a linker script. For example in C a linker script variable might be referred to as:

    4.   extern int foo;


    5. But in the linker script it might be defined as:

    6.   _foo = 1000;


    7. In the remaining examples however it is assumed that no name transformation has taken place.

    8. When a symbol is declared in a high level language such as C, two things happen. The first is that the compiler reserves enough space in the program’s memory to hold the value of the symbol. The second is that the compiler creates an entry in the program’s symbol table which holds the symbol’s address. ie the symbol table contains the address of the block of memory holding the symbol’s value. So for example the following C declaration, at file scope:

    9.   int foo = 1000;


    10. creates an entry called ‘foo’ in the symbol table. This entry holds the address of an ‘int’ sized block of memory where the number 1000 is initially stored.

    11. When a program references a symbol the compiler generates code that first accesses the symbol table to find the address of the symbol’s memory block and then code to read the value from that memory block. So:

    12.   foo = 1;


    13. looks up the symbol ‘foo’ in the symbol table, gets the address associated with this symbol and then writes the value 1 into that address. Whereas:

    14.   int * a = & foo;


    15. looks up the symbol ‘foo’ in the symbol table, gets its address and then copies this address into the block of memory associated with the variable ‘a’.

    16. Linker scripts symbol declarations, by contrast, create an entry in the symbol table but do not assign any memory to them. Thus they are an address without a value. So for example the linker script definition:

    17.   foo = 1000;


    18. creates an entry in the symbol table called ‘foo’ which holds the address of memory location 1000, but nothing special is stored at address 1000. This means that you cannot access the value of a linker script defined symbol - it has no value - all you can do is access the address of a linker script defined symbol.

    19. Hence when you are using a linker script defined symbol in source code you should always take the address of the symbol, and never attempt to use its value. For example suppose you want to copy the contents of a section of memory called .ROM into a section called .FLASH and the linker script contains these declarations:

    20.   start_of_ROM   = .ROM;
    21.   end_of_ROM     = .ROM + sizeof (.ROM);
    22.   start_of_FLASH = .FLASH;


    23. Then the C source code to perform the copy would be:

    24.   extern char start_of_ROM, end_of_ROM, start_of_FLASH;

    25.   memcpy (& start_of_FLASH, & start_of_ROM, & end_of_ROM - & start_of_ROM);


    26. Note the use of the ‘&’ operators. These are correct. Alternatively the symbols can be treated as the names of vectors or arrays and then the code will again work as expected:

    27.   extern char start_of_ROM[], end_of_ROM[], start_of_FLASH[];

    28.   memcpy (start_of_FLASH, start_of_ROM, end_of_ROM - start_of_ROM);


    29. Note how using this method does not require the use of ‘&’ operators.
    复制代码
    从这段描述中可知C代码引用了link file 的符号并不会未其分配地址,只是一个数值,c 代码中可以根据该符号是数值或者地址空间进行使用,shell中是作为地址空间进行引用的,所以在符号前加& 编译器将该符号作为指针变量处理。

    #if defined (__ICCARM__)
        littleshell_system_function_init(__section_begin("FSymTab"),
                                   __section_end("FSymTab"));
    #elif defined (__GNUC__)
        extern const int __fsymtab_start;
        extern const int __fsymtab_end;
        littleshell_system_function_init(&__fsymtab_start, &__fsymtab_end);

    #endif

    至此shell 的移植已经完成了,我们可以在代码中添加命令打印 __fsymtab_start  和 & __fsymtab_start 来验证这两种写法的区别,添加如下测试代码打印信息:
    1. unsigned int symbol(char argc,char ** argv)
    2. {
    3.     extern const int __fsymtab_start;
    4.     extern const int __fsymtab_end;

    5.     PRINTF("&__fsymtab_start = %p &__fsymtab_end = %p.\r\n",&__fsymtab_start,&__fsymtab_end);
    6.     PRINTF("__fsymtab_start = %x __fsymtab_end = %x.\r\n",__fsymtab_start,__fsymtab_end);
    7.     return 1;
    8. }
    9. LTSH_FUNCTION_EXPORT(symbol,"show symbol");
    复制代码
    打印结果如下:
    1. #symbol
    2. &__fsymtab_start = 47C0 &__fsymtab_end = 47E4.
    3. __fsymtab_start = 4120 __fsymtab_end = 10000024.
    复制代码

    根据编译的map 的FSymTab 下述信息可知&__fsymtab_start ,&__fsymtab_end  的地址信息和map 一致。
    1.                 0x000047c0                __fsymtab_start = .
    2. *(SORT_BY_ALIGNMENT(FSymTab))
    3. FSymTab        0x000047c0        0xc ./source/hello_world.o
    4.                 0x000047c0                __fsym_led
    5. FSymTab        0x000047cc       0x18 ./littleshell/littleshell.o
    6.                 0x000047cc                __fsym_hello
    7.                 0x000047d8                __fsym_symbol
    8.                 0x000047e4                __fsymtab_end = .
    复制代码

    __fsymtab_start = 4120 __fsymtab_end = 10000024 这个打印的内容是什么呢,从debug memory 窗口可知该内容是0x000047c0  和 0x000047e4 对应的地址空间的内容:


    section_value.png


    ==========代码路径==========



    ==========资料分割线==========
    GNU汇编器as_v2.34.pdf (1.39 MB, 下载次数: 0)
    该会员没有填写今日想说内容.
    回复

    使用道具 举报

  • TA的每日心情
    开心
    6 小时前
  • 签到天数: 1337 天

    [LV.10]以坛为家III

    88

    主题

    4294

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    9059
    最后登录
    2024-5-6
    发表于 2023-4-4 10:07:22 | 显示全部楼层
    这怎么还使用上了汇编了~~
    今天天气不错!签到!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    6 小时前
  • 签到天数: 598 天

    [LV.9]以坛为家II

    51

    主题

    2232

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    7113
    最后登录
    2024-5-6
     楼主| 发表于 2023-4-4 10:12:13 | 显示全部楼层
    jobszheng5 发表于 2023-4-4 10:07
    这怎么还使用上了汇编了~~

    没有使用汇编呀,只是在link file 里添加了个section ,c 代码里去使用对应的 link file  里的symbol
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    6 小时前
  • 签到天数: 1337 天

    [LV.10]以坛为家III

    88

    主题

    4294

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    9059
    最后登录
    2024-5-6
    发表于 2023-4-4 10:33:52 | 显示全部楼层
    不是汇编, 是linker文件的宏定义。

    不过,添加源文件,为什么需要手动修改linker文件?
    需要把shell放在固定的section上面吗?
    今天天气不错!签到!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    6 小时前
  • 签到天数: 598 天

    [LV.9]以坛为家II

    51

    主题

    2232

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    7113
    最后登录
    2024-5-6
     楼主| 发表于 2023-4-4 10:41:57 | 显示全部楼层
    jobszheng5 发表于 2023-4-4 10:33
    不是汇编, 是linker文件的宏定义。

    不过,添加源文件,为什么需要手动修改linker文件?

    添加源文件不用,只要加到local source里就行,应该是上面有张图有歧义,只是把shell 的命令结构体放到linker 文件的某一个section 种,方便程序按照对应的section 地址进行索引查找命令
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    6 小时前
  • 签到天数: 1337 天

    [LV.10]以坛为家III

    88

    主题

    4294

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    9059
    最后登录
    2024-5-6
    发表于 2023-4-4 17:21:41 | 显示全部楼层
    那就更不理解了。
    您不手动指定,程序也可以查询到啊!毕竟编译器会给其设定一下啊 ?
    今天天气不错!签到!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    6 小时前
  • 签到天数: 598 天

    [LV.9]以坛为家II

    51

    主题

    2232

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    7113
    最后登录
    2024-5-6
     楼主| 发表于 2023-4-5 15:05:46 | 显示全部楼层
    本帖最后由 andeyqi 于 2023-4-5 15:39 编辑
    jobszheng5 发表于 2023-4-4 17:21
    那就更不理解了。
    您不手动指定,程序也可以查询到啊!毕竟编译器会给其设定一下啊 ? ...

    只是实现方式上的差异,实现方式是参照的rttthread 的finsh 的实现方式,将命令定义到一个section里了,所以程序内部要知道对应的section 在编译链接后所在的地址进行查找,代码提交到git 了(https://github.com/andeyqi/LPC845BRK) ,大佬应该看下源码就能明白了。
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    6 小时前
  • 签到天数: 1337 天

    [LV.10]以坛为家III

    88

    主题

    4294

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    9059
    最后登录
    2024-5-6
    发表于 2023-4-5 15:42:47 | 显示全部楼层
    参照的rttthread 的finsh 的实现方式,将命令定义到一个section里

    原来是这个样子啊!
    谢谢耐心的回复,现在明白了
    今天天气不错!签到!
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-5-6 20:48 , Processed in 0.147914 second(s), 27 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表