ARM编译器的semihosting

出处:ARM编译器的semihosting

Semihosted环境(semihosting是针对ARM目标机的一种机制,它能够根据应用程序代码的输入/输出请求,与运行有调试功能的主机通讯。这种技术允许主机为通常没有输入和输出功能的目标硬件提供主机资源)
semlhosted环境下用来实现C库函数与目标相关的函数。

可以在你的Application Code中使用printfstand IO Function in C Library! 方便调试!更多的你可以参考ARM DUI 0058DDebug Target Guide!)

1ARM公司对Semihosting的中文解释是半主机机制。为什么叫半主机呢?主要是指应用程序的代码运行在目标系统上,当需要类似PC平台下的控制台输入输出时,会调用Semihosting去利用PC上的控制台输入输出设备:如打开关闭文件,PC显示器输出,键盘输入等等。

2SemihostingADS1.2的开发环境下,只能在以下调试代理上运行,ARMulator, RealMonitor, Multi-ICE以及Angle。这就是为什么周工的EasyJTAG不支持Semihosting的原理,因为它不属于上述四种调试代理中的任何一种。

3Semihosting是一段功能代码,这段功能代码主要运行在PC上,并由调试工具上的固件来激活调用。而周工的EasyJTAG没有实现这个激活调用功能。

4、对于开发用户来说,Semihosting是目标系统通过调用SWI 0x123456 SWI 0xAB来调用的。前者ARM状态下的专用操作号,后者是THUMB状态的专用操作号。当开发者的软硬件调试工具配置正确时,可以正确执行Semihosting功能。打个比方说,你在ARMulator下仿真指令
MOV R1 0x18
SWI 0x123456
ARMulator
会正确终止你的程序执行。
而在EasyJTAG下仿真时,却会跳入0x08的异常向量入口处。

quote from
http://www.yodao.com/cache?docid=606CB319C2037801&eterms=1PwOAO78n8Qagsx2n654vg%3D%3D&q=semihosted&url=http%3A%2F%2Fwww.dzsc.com%2Fdzbbs%2F20050512%2F2007651930330487.html

使用ARM标准C库进行嵌入式应用程序开发
http://blog.ednchina.com/jerryzhang8023/14409/Message.aspx

AXD中运行程序时,报告out of heap memory

不是程序的问题。因为ADS调用了semihosting,所以需要修改变量$top_of_memory

使用SEMIHOSTING时,SEMIHOSTING的设置一般不需要修改。主要的是设置TOP_OF_MEMORY,这个值指定的部分内存空间在SEMIHOSTING的时候需要用到。你要保证TOP_OF_MEMORY值知道的空间是可用的。而且,要使用SEMIHOSTING的话,你需要做些初始化的工作的,如果你没有用MAIN的话,需要自己添加,如果你有MAIN函数的话,编译器自己会添加。


SEMIHOSTING相关配置
AXD中:
1.OPTIONS -> CONFIGURE TARGET -> ARMULATE;
2.OPTIONS -> CONFIGRUE PROCESSOR -> SEMIHOSTING -> 
选中


quote from:
http://forum.eepw.com.cn/forum/main?url=http%3A%2F%2Fbbs.edw.com.cn%2Fthread%2F61698%2F1

SEMIHOSTING主要是针对I/O操作的,在嵌入式开发过程当中,通过SEMIHOSTING,可以把输入输出定向到HOST上,利用HOST的输入和输出。

从用户的角度来看,printf好像和普通的一样,关键的区别在于printf的实现。一般的调试器都提供两个版本的IO库,一个式标准的库,另外一个是支持semihosting的库。其实现有区别。下面以printf举例说明其原理:支持semihostingprintf的实现和标准的printf不同,支持semihostingprintfSWI指令来通知仿真器。仿真器在地址0x8处设置断点,但SWI指令执行后,仿真器可以捕获到该SWI指令。根据SWInumber来判断这个SWI是不是SEMIHOSTING请求,如果是,再根据具体的semihosting number响应用户的semihosting请求,完成用户的semihosting请求后,返回到SWI的后面一条指令,继续执行。所以,对用户来说,这是透明的。

ADS下面,默认的好像是支持SEMIHOSTING的,你自己写一个简单的程序,用printf输出,应该能在console看到输出。注意几点: 1. AXD里面semihosting必须要打开; 2. 仿真器必须支持semihosting

 

 

 

 
    随着对高处理能力、实时多任务、超低功耗等方面需求的增长,高端嵌入式处理器已经进入了国内开发人员的视野,并在国内得到了普遍的重视和应用。ARM是目前嵌入式领域应用最广泛的RISC微处理器结构,凭借低成本、低功耗、高性能等优点占据了嵌入式系统应用领域的领先地位。ADSARM公司推出的ARM集成开发环境,提供了对CC++的支持,是目前开发ARM的主要工具。本文针对日益缩短的嵌入式开发周期,结合ARM系统开发调试经验,对使用ARM标准库进行应用程序开发作了比较系统的分析。

1 ARM标准库介绍
    ADS提供了ANSI CC++标准库,本文仅讨论ANSI C库,该库包含下面几个部分:
    ◇IS0 C库标准所定义的函数;
    ◇在semlhosted环境下用来实现C库函数与目标相关的函数;
    ◇CC++编译器要使用的heIper函数。

    该库提供的诸如文件输入输出之类的设备,使用了标准的ARM semihosted执行环境(semihosting是针对ARM目标机的一种机制,它能够根据应用程序代码的输入/输出请求,与运行有调度功能的主机通信,这种技术允许主机为通常没有输入和输出功能的目标硬件提供主机资源)ARMulatorAngelMulti-lCE都支持这个环境,可以使用ADs中提供的开发工具开发应用程序,然后在ARMulator或者是开发板上运行和调试该程序。如果要使应用系统独立于这个环境,则必须重新实现C库中依赖于这个环境的相关函数,根据用户系统的运行环境对C库进行适当的裁减。

    使用ANSI标准C库进行程序开发,不仅可以提高开发效率而且可以增强程序的可移植性。在程序中使用库函数,必须先建立一个库函数可以执行的环境,这些工作都由库中的函数完成。当应用程序链接了C库中的函数时,C库中的函数将完成:
    ◇创建C程序所需的执行环境(建立栈,如果需要创建一个堆,初始化程序使用的部分库)
    ◇调用main()函数开始执行C程序;
    ◇支持程序使用的Is0定义的函数;
    ◇捕获运行时的错误和信号,如果需要,根据错误终止执行或程序退出。

裁减ARM标准C函数库
    
标准库中包含了部分依赖于ARM semihosted执行环境的函数,这部分函数的函数名中包含有单个或两个下划线“-”,需要重新实现这部分函数。如果在程序中定义这些函数,则编译器就会使用新定义的函数,这个过程称为库函数的裁减。一般情况下,只需要重新定义很少的几个函数就可以使用C库。

    ARM应用系统开始执行用户应用程序,必须先将应用程序加载到执行域,建立应用程序的执行环境。使用C库时,这些繁琐的工作就大部分由c函数来完成了。汇编程序完成系统初始化后,跳转到C程序的人口_main()(注意:不是main(),当C程序中定义了main()主函数时,编译器就会生成_main代码)。由_main()引导库函数完成C执行环境的初始化,具体过程如下:
    ◇将非启动代码的RORW执行域代码从加载域地址复制到执行域地址;
    ◇将ZI域清零;
    ◇跳转到_rt_entry

    调用_main()将大大简化汇编启动代码的编写,汇编代码仅需完成系统硬件的初始化,而没有必要将代码从加载域地址复制到执行域地址,以及ZI域清零等工作。特别是当使用分布式加载时_main()的作用就更加明显了。但是_main()并没有建立C库运行必须的环境,这项工作由_rt_entry()完成,主要调用过程为:
    ◇调用_rt_stackheap_init()建立堆和栈;
    ◇调用_rt_lib_init()初始化引用的库函数;如果需要,建立main()函数的参数argcargv等;
    ◇调用main()函数,执行应用程序,可以应用库函数;
    ◇用main()函数的返回值作参数调用exit()  

    _rt_entry
并不是C函数,它是用ARM C库编程的起始点。_rt_entry不能用C语言宴现,因为这时候堆栈还没有建立,堆栈由_ rt_stackheap_init()来建立。

    上面简单介绍了C程序使用库函数时的调用过程,由_rt—stackheap_init()建立C库使用的内存模型--堆和栈。因为ARM库是建立在semihosted执行环境的,它实现的内存模型是基于这个环境的,所以必须修改这个内存模型建立机制。表1列出了需要重新实现的函数,实现了这些函数,应用程序就可以脱离宿主机环境独立运行了。其中,必须重新实现的是_user initial_stackheap(),因为默认的实现是基于semihosted执行环境的,该函数被_n_stackheap_init()调用创建内存模型,其他两个函数没有默认的实现。

 

   


    实现该函数,必须满足下面的条件:
    ◇使用不超过96字节的栈空间;
    ◇除了R12(ip)外不要污染其他寄存器;
    ◇将堆基址、栈基址、堆边界和栈边界分别存在ROR3作为返回参数;
    ◇堆必须保持8个字节对齐。
    实现例程如下:

    
    
为了提高应用程序开发效率和可移植性,希望在目标系统上使用ARM库提供的标准输人输出库函数。

    高层输入输出函数是不依赖于目标系统环境的,但是高层输入输出函数必须调用依赖于目标系统的底层函数,才能实现应用系统的输入输出。依据目标系统硬件环境重新定义这些底层函数,就可以使用库提供的标准inputoutput库函数了。下面以裁减ARM标准库提供的printf系列输出函数为例来作说明。

    标准I/O库中最常用的是printf系列函数,包括_printf()printf()_fprintf()fprintf()vprintf()vfprintf()。所有这些函数非透明地使用_FILE,并且仅依赖于fputc()ferror()两个函数。函数_printf()_fprintf()printf()fprintf()的区别仅在于前两个函数不能格式化浮点值。只要定义了自己的_FILE版本和fputc()ferror()函数,外加定义一个具有FILE类型的_stdout变量,就可以不作任何修改地使用printf系列、fwrite()fputs()puts()函数了。

    下面给出了具体实现的模板,可以根据实际需要修改。
    #include<stdioh>
    struct__FILE
    {
    int handle

    /*用户需要的任何代码(如果使用文件仅是为了调试使用prinft在标准输出端输出信息,则不需要任何文件处理代码)*/
}

    FlLE_stdout/*FILEstdio.h中定义为:typedef struct_
    FILE FILE
*/
    int fputc(int ch
FILE*f){
    /*
用户实现的fpute代码。输出一个字符,可以根据需要实现*/
return ch

}
    int ferror(FILE*f){
    /*
用户实现的ferror代码*/
    return EOF

    }


结语
    本文分析了ARM标准库的工作机理,给出了裁减C库进行程序开发的关键步骤。实际应用时需要根据具体的硬件环境和应用要求裁减C库,提高代码执行效率。

 

https://blog.csdn.net/fantastikman/article/details/70664983

printf重定向

半主机是用于 ARM 目标的一种机制,可将来自应用程序代码的输入/输出请求传送至运行调试器的主机。 例如,使用此机制可以启用 C 库中的函数,如 printf() 和 scanf(),来使用主机的屏幕和键盘,而不是在目标系统上配备屏幕和键盘。

这种机制很有用,因为开发时使用的硬件通常没有最终系统的所有输入和输出设备。 半主机可让主机来提供这些设备。

半主机是通过一组定义好的软件指令(如 SVC)来实现的,这些指令通过程序控制生成异常。 应用程序调用相应的半主机调用,然后调试代理处理该异常。 调试代理提供与主机之间的必需通信。

半主机接口对 ARM 公司提供的所有调试代理都是通用的。 在无需移植的情况下使用 RealView ARMulator® ISS、指令集系统模型 (ISSM)、实时系统模型 (RTSM)、RealView ICE 或 RealMonitor 时,会执行半主机操作。

标准库使用半主机模式,半主机是通过一组定义好的软件指令 (如 SVC)SVC 指令 (以前称为 SWI 指令)来实现的,这些指令通过程序控制生成异常。 应用程序调用相应的半主机调用,然后调试代理处理该异常。调试代理(这里的调试代理是仿真器)提供与主机之间的必需通信。也就是说使用半主机模式必须使用仿真器调试。

ARMv7 之前的 ARM 处理器使用 SVC 指令 (以前称为 SWI 指令)进行半主机调

用。 但是,如果要为 ARMv6-M 或 ARMv7-M (如 Cortex™-M1 或 Cortex-M3 处

理器)进行编译,请使用 BKPT 指令来实现半主机。
简单的来说,半主机模式就是通过仿真器实现开发板在电脑上的输入和输出。和半主机模式功能相同的是ITM调试机制。
有关ITM调试机制可以参考这里http://www.douban.com/note/248637026/

       上面介绍的半主机和ITM功能相当,他们都是调试机制,开发板均借助仿真器与电脑连接,实现单片机利用主机的屏幕键盘的输入输出。

这两种机制的运行均需要仿真器,否则无法运行。

       开发式一般单片机需要独立运行,开发者应去掉仿真器,把printf函数通过单片机的外设来实现,例如通过开发板的串口,lcd或者sd卡。

      MDK中通常使用以下两种方法:

方法1.使用微库,因为使用微库的话,不会使用半主机模式.

int fputc(int ch, FILE *f)
{      
while((USART1->SR&0X40)==0);
    USART1->DR = (u8) ch;      
return ch;

}  

方法2.仍然使用标准库,在主程序添加下面代码:
​#pragma import(__use_no_semihosting) 
​struct __FILE 

int handle; 
}; 
FILE __stdout;       
_sys_exit(int x) 

x = x; 

int fputc(int ch, FILE *f)
{      
while((USART1->SR&0X40)==0);//Ñ»··¢ËÍ,Ö±µ½·¢ËÍÍê±Ï   
    USART1->DR = (u8) ch;      
return ch;
}

关于 microlib

microlib 是缺省 C 库的备选库。 它用于必须在极少量内存环境下运行的深层嵌入式应用程序。 这些应用程序不在操作系统中运行。microlib 不会尝试成为符合标准的 ISO C 库。

microlib 进行了高度优化以使代码变得很小。 它的功能比缺省 C 库少,并且根本不具备某些 ISO C 特性。某些库函数的运行速度也比较慢,例如,memcpy()。

上面给出了正确的方法,我在测试的过程中发现方法二中注释掉#pragma import(__use_no_semihosting) 程序依然运行正确,这个很让人费解。

也有使用#pragma import(__use_no_semihosting_swi)的。这几个的区别我没弄懂。

如果使用微库或者禁用半主机,没有重定义fputc函数,程序可以运行,但不知道结果打印到主机的哪个地方去了。既没有禁用半主机也没有重定义fputc函数,程序将一直停在中断处,如图:

 

网上的大部分资料都是禁用了半主机模式,没有使用过半主机模式借助主机的键盘输入参数,看了很多资料,都是讲半主机的,但讲的内容太浅显,

我没有学会使用半主机,更多的是学会了禁用半主机模式,我们好像忽略了半主机模式的意义。

 

另外,我们可以查到的大部分资料讲解的大同小异,它们更多的告诉我们正确的步骤,而不是让我们从源头了解一个问题,当然,大部分人看重的是实用,问题解决了,也就不再去思考背后的原理。

最有效最直接最权威的资料是mdk官方给出的资料,但是资料内容太多,看起来太多,不易懂,大部分人没有看下去的欲望。我们应该让使用手册看起来更直观,更易懂,更方便查阅,更高效地查阅。

第一个给出此类问题的解决方法的人,他们以通俗易懂的方式告诉了广大开发者,他们阅读了这些晦涩的手册,他们是真的勇士!

 

 

 MDK软件中半主机模式的几点理解 

看了原子哥的usart部分printf函数重定向的问题,自己动手试了试,单片机可以通过串口打印信息,printf函数重定向后使用十分方便。

所谓重定向是指修改printf的底层函数,使printf打印到单片机的外设中。

还有一个概念是半主机。

---参考   RealView 编译工具开发指南   8.1.1. 什么是半主机?
RealView 编译工具开发指南我已上传,大家可以下载研究研究。

 RealView编译工具开发指南.pdf (967.71 KB, 下载次数: 833)

 

这里也给出没打开、中c库和c+库的使用规范

     

 ARM Developer Suite Compilers and Libraries Guide.pdf (1.54 MB, 下载次数: 634)

有兴趣的可以看看,内容有点多,看起来不很好理解。


半主机是用于 ARM 目标的一种机制,可将来自应用程序代码的输入/输出请求传送至运行调试器的主机。 例如,使用此机制可以启用 C 库中的函数,如 printf() 和 scanf(),来使用主机的屏幕和键盘,而不是在目标系统上配备屏幕和键盘。

 

这种机制很有用,因为开发时使用的硬件通常没有最终系统的所有输入和输出设备。 半主机可让主机来提供这些设备。

 

半主机是通过一组定义好的软件指令(如 SVC)来实现的,这些指令通过程序控制生成异常。 应用程序调用相应的半主机调用,然后调试代理处理该异常。 调试代理提供与主机之间的必需通信。

 

半主机接口对 ARM 公司提供的所有调试代理都是通用的。 在无需移植的情况下使用 RealView ARMulator® ISS、指令集系统模型 (ISSM)、实时系统模型 (RTSM)、RealView ICE 或 RealMonitor 时,会执行半主机操作。

 

标准库使用半主机模式,半主机是通过一组定义好的软件指令 (如 SVC)SVC 指令 (以前称为 SWI 指令)来实现的,这些指令通过程序控制生成异常。 应用程序调用相应的半主机调用,然后调试代理处理该异常。调试代理(这里的调试代理是仿真器)提供与主机之间的必需通信。也就是说使用半主机模式必须使用仿真器调试。

 

ARMv7 之前的 ARM 处理器使用 SVC 指令 (以前称为 SWI 指令)进行半主机调用。 但是,如果要为 ARMv6-M 或 ARMv7-M (如 Cortex™-M1 或 Cortex-M3 处理器)进行编译,请使用 BKPT 指令来实现半主机。

 

简单的来说,半主机模式就是通过仿真器实现开发板在电脑上的输入和输出。和半主机模式功能相同的是ITM调试机制。
有关ITM调试机制可以参考这里:
http://www.douban.com/note/248637026/

 

上面介绍的半主机和ITM功能相当,他们都是调试机制,开发板均借助仿真器与电脑连接,实现单片机利用主机的屏幕键盘的输入输出。

这两种机制的运行均需要仿真器,否则无法运行。

 

开发式一般单片机需要独立运行,开发者应去掉仿真器,把printf函数通过单片机的外设来实现,例如通过开发板的串口,lcd或者sd卡。

 

MDK中通常使用以下两种方法:

方法1.使用微库,因为使用微库的话,不会使用半主机模式.

  1. int fputc(int ch, FILE *f)
  2. {      
  3. while((USART1->SR&0X40)==0);
  4.     USART1->DR = (u8) ch;      
  5. return ch;
  6. }  
复制代码

方法2.仍然使用标准库,在主程序添加下面代码:

  1. #pragma import(__use_no_semihosting) 
  2. ​struct __FILE 
  3. int handle; 
  4. }; 
  5. FILE __stdout;       
  6. _sys_exit(int x) 
  7. x = x; 
  8. int fputc(int ch, FILE *f)
  9. {      
  10. while((USART1->SR&0X40)==0);//Ñ»··¢ËÍ,Ö±µ½·¢ËÍÍê±Ï   
  11.     USART1->DR = (u8) ch;      
  12. return ch;
  13. }
复制代码

关于 microlib

microlib 是缺省 C 库的备选库。 它用于必须在极少量内存环境下运行的深层嵌入式应用程序。 这些应用程序不在操作系统中运行。microlib 不会尝试成为符合标准的 ISO C 库。

microlib 进行了高度优化以使代码变得很小。 它的功能比缺省 C 库少,并且根本不具备某些 ISO C 特性。某些库函数的运行速度也比较慢,例如,memcpy()。

 

上面给出了正确的方法,我在测试的过程中发现方法二中注释掉#pragma import(__use_no_semihosting) 程序依然运行正确,这个很让人费解。

也有使用#pragma import(__use_no_semihosting_swi)的。这几个的区别我没弄懂。

如果使用微库或者禁用半主机,没有重定义fputc函数,程序可以运行,但不知道结果打印到主机的哪个地方去了。既没有禁用半主机也没有重定义fputc函数,程序将一直停在中断处,如图:

 

网上的大部分资料都是禁用了半主机模式,没有使用过半主机模式借助主机的键盘输入参数,看了很多资料,都是讲半主机的,但讲的内容太浅显,

我没有学会使用半主机,更多的是学会了禁用半主机模式,我们好像忽略了半主机模式的意义。

 

另外,我们可以查到的大部分资料讲解的大同小异,它们更多的告诉我们正确的步骤,而不是让我们从源头了解一个问题,当然,大部分人看重的是实用,问题解决了,也就不再去思考背后的原理。

 

最有效最直接最权威的资料是mdk官方给出的资料,但是资料内容太多,看起来太多,不易懂,大部分人没有看下去的欲望。我们应该让使用手册看起来更直观,更易懂,更方便查阅,更高效地查阅。

 

第一个给出此类问题的解决方法的人,他们以通俗易懂的方式告诉了广大开发者,他们阅读了这些晦涩的手册,他们是真的勇士!

MDK

实时事件触发实时调度问题--使用一个子函数来触发结果无法返回了

1、对比了两种写法:

第一种写法在任务task1里直接由内嵌汇编swi来触发实现任务调度,swi之后的指令还能正常工作。其它任务调度逻辑是正常的。
第二种写法在任务task1里通过调用taskDelay(1)函数,在该函数里使用内嵌汇编swi来触发任务调度,那么taskDelay(1)函数之后的指令无法再下一次调用该任务时还原到此指令执行,原因用户栈不在了,使用任务栈保存的参数无法在复原了。

小结:

第一种写法在任务task1里直接由内嵌汇编swi来触发实现任务调度,swi之后的指令还能正常工作。其它任务调度逻辑是正常的。
第二种写法在任务task1里通过调用taskDelay(1)函数,在该函数里使用内嵌汇编swi来触发任务调度,那么taskDelay(1)函数之后下一条指令printf无法执行,分析原因用户栈被破坏了,保存的局部参数无法在复原了。
修改了任务栈保存信息,把当前任务的R0-R15都给保存到任务栈里,还是不行。

目前关于寄存器值保存和恢复的做法:

A、每次进入中断和svc模式,保存用户态的所有寄存器到当前任务栈
B、每次将要运行的任务的SP值不恢复,直接跳过,其它寄存器值都恢复。这样避免了用户态SP值得改变,从一个任务到另外一个任务是连续的,各个任务调度是正常的。这种方式只适合上面第一种写法,而不适合第二种写法。
C、每次将要运行的任务的SP值也恢复,那么通过中断产生任务调度再次运行该任务时会重启了,不能继续运行。

最后解决方法:

对比了两种写法的汇编,第二种写法的sp被调用下一个任务sp给占用了,所以在此问题导致了。第一个任务的sp虽然恢复了,但是sp里的局部参数(改参数是返回到任务的地址)被下一个任务的sp给占用了,所以第一个任务sp在切换前应该保存一段空间,用来隔开第二个任务的sp即可。

第一种写法代码如下:

int TEST_TestTask1(void)
{
    printf("\n\rTask1 start...\n\r");
    printf("user mode ...      SP 0x%x\n\r",sp_value());
    printf("CPSR==> 0x%x\n\r",cpsr_value());
    while(1){
    printf("\n\rTask1 running again...\n\r");

    #if  1
        tickVal = 1;
        printf("start taskDelay %ds ..... now!\n\r",tickVal);
        __asm__(
            " swi #255 \n\t"                /* 
        );
    #endif
        printf("Task1 Delay 1s over...\n\r");

    }
    printf("Task1 is running over!\n\r"); 

    return 0;
}


对应汇编:

第一种写法汇编
30001894 <TEST_TestTask1>:
30001894:   e92d4030    stmdb   sp!, {r4, r5, lr}
30001898:   e59f0050    ldr r0, [pc, #80]   ; 300018f0 <.text+0x18f0>
3000189c:   eb00009e    bl  30001b1c <printf>
300018a0:   ebffff72    bl  30001670 <sp_value>
300018a4:   e1a01000    mov r1, r0
300018a8:   e59f0044    ldr r0, [pc, #68]   ; 300018f4 <.text+0x18f4>
300018ac:   eb00009a    bl  30001b1c <printf>
300018b0:   ebffff67    bl  30001654 <cpsr_value>
300018b4:   e1a01000    mov r1, r0
300018b8:   e59f0038    ldr r0, [pc, #56]   ; 300018f8 <.text+0x18f8>
300018bc:   eb000096    bl  30001b1c <printf>
300018c0:   e59f5034    ldr r5, [pc, #52]   ; 300018fc <.text+0x18fc>
300018c4:   e3a04001    mov r4, #1  ; 0x1
300018c8:   e59f0030    ldr r0, [pc, #48]   ; 30001900 <.text+0x1900>
300018cc:   eb000092    bl  30001b1c <printf>
300018d0:   e1a01004    mov r1, r4
300018d4:   e59f0028    ldr r0, [pc, #40]   ; 30001904 <.text+0x1904>
300018d8:   e5854000    str r4, [r5]
300018dc:   eb00008e    bl  30001b1c <printf>
##300018e0: ef0000ff    swi 0x000000ff
##300018e4: e59f001c    ldr r0, [pc, #28]   ; 30001908 <.text+0x1908>
##300018e8: eb00008b    bl  30001b1c <printf>
300018ec:   eafffff5    b   300018c8 <TEST_TestTask1+0x34>
300018f0:   3000411c    andcc   r4, r0, ip, lsl r1
300018f4:   30004084    andcc   r4, r0, r4, lsl #1
300018f8:   30003978    andcc   r3, r0, r8, ror r9
300018fc:   30005bfc    strccd  r5, [r0], -ip
30001900:   30004130    andcc   r4, r0, r0, lsr r1
30001904:   30003c04    andcc   r3, r0, r4, lsl #24
30001908:   3000414c    andcc   r4, r0, ip, asr #2

第二种写法代码如下:

int TEST_TestTask1(void)
{
    printf("\n\rTask1 start...\n\r");
    printf("user mode ...      SP 0x%x\n\r",sp_value());
    printf("CPSR==> 0x%x\n\r",cpsr_value());
    while(1){
      printf("\n\rTask1 running again...\n\r");
          taskDelay(1);
      printf("Task1 Delay 1s over...\n\r");

    }
    printf("Task1 is running over!\n\r"); 
    return 0;
}


对应汇编

30001894 <TEST_TestTask1>:
30001894:   e52de004    str lr, [sp, #-4]!
30001898:   e59f003c    ldr r0, [pc, #60]   ; 300018dc <.text+0x18dc>
3000189c:   eb000097    bl  30001b00 <printf>
300018a0:   ebffff72    bl  30001670 <sp_value>
300018a4:   e1a01000    mov r1, r0
300018a8:   e59f0030    ldr r0, [pc, #48]   ; 300018e0 <.text+0x18e0>
300018ac:   eb000093    bl  30001b00 <printf>
300018b0:   ebffff67    bl  30001654 <cpsr_value>
300018b4:   e1a01000    mov r1, r0
300018b8:   e59f0024    ldr r0, [pc, #36]   ; 300018e4 <.text+0x18e4>
300018bc:   eb00008f    bl  30001b00 <printf>
300018c0:   e59f0020    ldr r0, [pc, #32]   ; 300018e8 <.text+0x18e8>
300018c4:   eb00008d    bl  30001b00 <printf>
300018c8:   e3a00001    mov r0, #1  ; 0x1
##300018cc: ebfffced    bl  30000c88 <taskDelay>
300018d0:   e59f0014    ldr r0, [pc, #20]   ; 300018ec <.text+0x18ec>
300018d4:   eb000089    bl  30001b00 <printf>
300018d8:   eafffff8    b   300018c0 <TEST_TestTask1+0x2c>

30000c88 <taskDelay>:
30000c88:   e59f201c    ldr r2, [pc, #28]   ; 30000cac <.text+0xcac>
30000c8c:   e1a03000    mov r3, r0
##30000c90: e52de004    str lr, [sp, #-4]!
30000c94:   e1a01000    mov r1, r0
30000c98:   e59f0010    ldr r0, [pc, #16]   ; 30000cb0 <.text+0xcb0>
30000c9c:   e5823000    str r3, [r2]
30000ca0:   eb000396    bl  30001b00 <printf>
##30000ca4: ef0000ff    swi 0x000000ff
##30000ca8: e49df004    ldr pc, [sp], #4
30000cac:   30005bdc    ldrccd  r5, [r0], -ip
30000cb0:   30003be4    andcc   r3, r0, r4, ror #23


 



作者:yuanyuanxingliu
链接:https://www.jianshu.com/p/f9b10cb11ff8
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

 

展开阅读全文

ADS1.2(ARM编译器)

07-28

ADS全称为 ARM Developer Suite 。是 ARM 公司推出的新一代 ARM 集成开发工具。现在 ADS 的最新版本是 1.2 ,它取代了早期的 ADS1.1 和 ADS1.0 。它除了可以安装在 Windows NT4 , Windows 2000 , Windows 98 和 Windows 95 操作系统下,还支持 Windows XP 和 Windows Me 操作系统。 ADS 由命令行开发工具, ARM 时实库, GUI 开发环境 (Code Warrior 和 AXD) ,实用程序和支持软件组成。有了这些部件,用户就可以为 ARM 系列的 RISC 处理器编写和调试自己的开发应用程序了。rnrn下面就详细介绍一下 ADS 的各个组成部分。rnrn命令行开发工具:这些工具完成将源代码编译,链接成可执行代码的功能。rnrnADS 提供下面的命令行开发工具: armcc armcc 是 ARM C 编译器。这个编译器通过了 Plum Hall C Validation Suite 为 ANSI C 的一致性测试。 armcc 用于将用 ANSI C 编写的程序编译成 32 位 ARM 指令代码。 因为 armcc 是我们最常用的编译器,所以对此作一个详细的介绍。rnrn在命令控制台环境下,输入命令: armcc – help 可以查看 armcc 的语法格式以及最常用的一些操作选项. armcc 最基本的用法为: armcc [options] file1 file2 ... filen 这里的 option 是编译器所需要的选项, fiel1,file2…filen 是相关的文件名。这里简单介绍一些最常用的操作选项。 -c :表示只进行编译不链接文件; -C : ( 注意:这是大写的 C) 禁止预编译器将注释行移走; -D :定义预处理宏,相当于在源程序开头使用了宏定义语句 #define symbol ,这里 symbol 默认为 1 ; -E :仅仅是对 C 源代码进行预处理就停止; -g :指定是否在生成的目标文件中包含调试信息表; -I :将 directory 所指的路径添加到 #i nclude 的搜索路径列表中去; -J :用directory 所指的路径代替默认的对#i nclude 的搜索路径; -o :指定编译器最终生成的输出文件名。 -O0 :不优化; -O1 :这是控制代码优化的编译选项,大写字母 O 后面跟的数字不同,表示的优化级别就不同, -O1 关闭了影响调试结果的优化功能; -O2 :该优化级别提供了最大的优化功能; -S :对源程序进行预处理和编译,自动生成汇编文件而不是目标文件; -U :取消预处理宏名,相当于在源文件开头,使用语句 #undef symbol; -W :关闭所有的或被选择的警告信息; 有关更详细的选项说明,读者可查看 ADS 软件的在线帮助文件。rnrnarmcpp armcpp 是 ARM C++ 编译器。它将 ISO C++ 或 EC++ 编译成 32 位 ARM 指令代码。rnrntcc tcc 是 Thumb C 编译器。该编译器通过了 Plum Hall C Validation Suite 为 ANSI 一致性的测试。 tcc 将 ANSI C 源代码编译成 16 位的 Thumb 指令代码。rnrntcpp tcpp 是 Thumb C++ 编译器。 它将 ISO C++ 和 EC++ 源码编译成 16 位 Thumb 指令代码。rnrnarmasm armasm 是 ARM 和 Thumb 的汇编器 . 它对用 ARM 汇编语言和 Thumb 汇编语言写的源代码进 行汇编。rnrnarmlink armlink 是 ARM 连接器。该命令既可以将编译得到的一个或多个目标文件和相关的一个或多个库文件进行链接,生成一个可执行文件,也可以将多个目标文件部分链接成一个目标文件,以供进一步的链接。 ARM 链接器生成的是 ELF 格式的可执行映像文件。rnrnarmsd armsd 是 ARM 和 Thumb 的符号调试器。它能够进行源码级的程序调试。用户可以在用 C 或汇编语言写的代码中进行单步调试,设置断点,查看变量值和内存单元的内容。rnrnGUI 开发环境 (Code Warrior 和 AXD) CodeWarrior 集成开发环境 CodeWarrior for ARM 是一套完整的集成开发工具,充分发挥了 ARM RISC 的优势 , 使产品开发人员能够很好的应用尖端的片上系统技术 . 该工具是专为基于 ARM RISC 的处理器而设计的 , 它可加速并简化嵌入式开发过程中的每一个环节,使得开发人员只需通过一个集成软件开发环境就能研制出 ARM 产品,在整个开发周期中 , 开发人员无需离开 CodeWarrior 开发环境 , 因此节省了在操做工具上花的时间 , 使得开发人员有更多的精力投入到代码编写上来, CodeWarrior 集成开发环境 (IDE) 为管理和开发项目提供了简单多样化的图形用户界面。rnrn用户可以使用 ADS 的 CodeWarrior IDE 为 ARM 和 Thumb 处理器开发用 C , C++ ,或 ARM 汇编语言的程序代码。通过提供下面的功能, CodeWarrior IDE 缩短了用户开发项目代码的周期。rnrn1. 全面的项目管理功能;rnrn2. 子函数的代码导航功能,使得用户迅速找到程序中的子函数。 可以在 CodeWarrior IDE 为 ARM 配置在 后面介绍的各种命令工具,实现对工程代码的编译, 汇编和链接。 在 CodeWarrior IDE 中所涉及到的 target 有两种不同的语义。 目标系统 (Target system) 是特指代码要运行的环境,是基于 ARM 的硬件。比如,要为 ARM 开发板上编写要运行在它上面的程序,这个开发板就是目标系统。生成目标 (Build target) 是指用于生成特定的目标文件的选项设置 ( 包括汇编选项,编译选项,链接选项以及链接后的处理选项 ) 和所用的文件的集合。 CodeWarrior IDE 能够让用户将源代码文件,库文件还有其他相关的文件以及配置设置等放在一个工程中。每个工程可以创建和管理生成目标设置的多个配置。例如,要编译一个包含调试信息的生成目标和一个基于 ARM7TDMI 的硬件优化生成目标,生成目标可以在同一个工程中共享文件,同时使用各自的设置。rnrnCodeWarrior IDE 为用户提供下面的功能: 源代码编辑器,它集成在 CodeWarrior IDE 的浏览器中,能够根据语法格式,使用不同的颜色显示代码;源代码浏览器,它保存了在源码中定义的所有符号,能够使用户在源码中快速方便的跳转;查找和替换功能,用户可以在多个文件中,利用字符串通配符,进行字符串的搜索和替换;文件比较功能,可以使用户比较路径中的不同文本文件的内容。rnrnADS 的 CodeWarrior IDE 是基于 Metrowerks CodeWarrior IDE 4.2 版本的。它经过适当的裁剪以支持 ADS 工具链。 针对 ARM 的配置面板为用户提供了在 CodeWarrior IDE 集成环境下配置各种 ARM 开发工具的能力,这样用户可以不用在命令控制台下就能够使用在后面介绍的各种命令。 以 ARM 为目标平台的工程创建向导,可以使用户以此为基础,快速创建 ARM 和 Thumb 工程。 尽管大多数的 ARM 工具链已经集成在 CodeWarrior IDE ,但是仍有许多功能在该集成环境中没有实现,这些功能大多数是和调试相关的,因为 ARM 的调试器没有集成到 CodeWarrior IDE 中。 由于 ARM 调试器 (AXD) 没有集成在 CodeWarrior IDE 中,这就意味着,用户不能在 CodeWarrior IDE 中进行断点调试和查看变量。 对于熟悉 CodeWarrior IDE 的用户会发现,有许多的功能已经从 CodeWarrior IDE For ARM 中移走,比如快速应用程序开发模板等。rnrn在 CodeWarrior IDE For ARM 中有很多的菜单或子菜单是不能使用的。下面介绍一下这些不能使用的选项。 1. View 菜单下不能使用的菜单选项有: Processes , Expressions , Global Variable , Breakpoints , Registers 。rnrn2. Project 菜单不能使用的菜单选项: Precompile 子菜单。因为 ARM 编译器不支持预编译的头文件。rnrn3. Debug 菜单 该菜单中没有一个子菜单是可以使用的。rnrn4. Browser 菜单中不能使用的菜单选项: New Property , New Method 和 New Event Set 。rnrn5. Help menu 中不能用于 ADS 的菜单选项有: CodeWarrior Help , Index , Search 和 Online Manuals 。 有关 CodeWarrior IDE 中一些常用菜单的使用,将在后面的举例中具体说明的,在此,不在赘述。rnrnADS 调试器 调试器本身是一个软件,用户通过这个软件使用 debug agent 可以对包含有调试信息的,正在运行的可执行代码进行比如变量的查看,断点的控制等调试操作。 ADS 中包含有 3 个调试器: AXD(ARM eXtended Debugger) : ARM 扩展调试器; armsd(ARM Symbolic Debugger) : ARM 符号调试器; 与老版本兼容的 Windows 或 Unix 下的 ARM 调试工具, ADW/ADU(Application Debugger Windows/Unix) 。下面对在调试映像文件中所涉及到的一些术语做一个简单的介绍。 Debug target 在软件开发的最初阶段,可能还没有具体的硬件设备。如果要测试所开发的软件是否达到了预期的效果,这可以由软件仿真来完成。即使调试器和要测试的软件运行在同一台 PC 上,也可以把目标当作一个独立的硬件来看待。当然,也可以搭建一个 PCB 板,这个板上可以包含一个或多个处理器,在这个板上可以运行和调试应用软件。只有当通过硬件或者是软件仿真所得到的结果达到了预期的效果,才算是完成了应用程序的编写工作。 调试器能够发送以下指令:rnrn1. 装载映像文件到目标内存;rnrn2. 启动或停止程序的执行;rnrn3. 显示内存,寄存器或变量的值;rnrn4. 允许用户改变存储的变量值。rnrnDebug agent Debug agent 执行调试器发出的命令动作,比如:设置断点,从存储器中读数据,把数据写到存储器等。 Debug agent 既不是被调试的程序,也不是调试器。在 ARM 体系中,它有这几种方式: Multi-ICE(Multi-processor in-circuit emulator) , ARMulator 和 Angel 。其中 Multi-ICE 是一个独立的产品,是 ARM 公司自己的 JTAG 在线仿真器,不是由 ADS 提供的。 AXD 可以在 Windows 和 UNIX 下,进行程序的调试。它为用 C , C++ ,和汇编语言编写的源代码提供了一个全面的 Windows 和 UNIX 环境。 实用程序 ADS 提供以下的实用工具来配合前面介绍的命令行开发工具的使用 fromELF 这是 ARM 映像文件转换工具。该命令将 ELF 格式的文件作为输入文件,将该格式转换为各种输出格式的文件,包括 plain binary(BIN 格式映像文件 ), Motorola 32-bit S-record format(Motorola 32位 S 格式映像文件 ), Intel Hex 32 format(Intel 32 位格式映像文件 ) ,和 Verilog-like hex format(Verilog 16 进制文件 ) 。 FromELF 命令也能够为输入映像文件产生文本信息,例如代码和数据长度。 armar ARM 库函数生成器将一系列 ELF 格式的目标文件以库函数的形式集合在一起,用户可以把一个库传递给一个链接器以代替几个 ELF 文件。 Flash downloader 用于把二进制映像文件下载到 ARM 开发板上的 Flash 存储器的工具 支持的软件 ADS 为用户提供下面的软件,使用户可以在软件仿真的环境下或者在基于 ARM 的硬件环境调试用户应用程序。 ARMulator 这是一个 ARM 指令集仿真器,集成在 ARM 的调试器 AXD 中,它提供对 ARM 处理器的指令集的仿真,为 ARM 和 Thumb 提供精确的模拟。用户可以在硬件尚未做好的情况下,开发程序代码。rn 论坛

没有更多推荐了,返回首页