标签 ARM 下的文章
CPU:STM32F429IGT6对于其他的stm32芯片或者其他ARM Cortex-M芯片,其实解决方法都相通。建议先完整阅读了本文之后,再对照着你所遇到问题的现象进行调试。1.基础知识在ARM Cortex-M系列处理器中,有若干个系统异常专用于 fault 处理。 CM3 中的 Faults 可分为以下几类:(1).总线 faults;(2).存储器管理 faults;(3).用法 faults;(4).硬 fault;1.1.总线 faults当 AHB 接口上正在传送数据时,如果回复了一个错误信号(error response),则会产生总线faults,产生的场合可以是:(1).取指,通常被称作“预取流产”(prefetch abort);(2).数据读/写,通常被称作“数据流产”(data abort);在 CM3 中,执行如下动作时,如果地址有误,亦会触发总线异常:(1).中断处理起始阶段的堆栈 PUSH 动作。此时若发生总线 fault,则称为“入栈错误”;(2).中断处理收尾阶段的堆栈 POP 动作。此时若发生总线 fault,则称为“出栈错误”;(3).在处理器启动中断服务序列(sequence)后读取向量时。这是一种极度罕见的特殊情况,被归类为硬 fault。总线 fault 状态寄存器(BFSR),地址:0xE000_ED29,BFSR的各个位的定义如下:1.2.存储器管理 faults存储器管理 faults 多与 MPU 有关,其诱因常常是某次访问触犯了 MPU 设置的保护规范。另外,某些非法访问,例如,在不可执行的存储器区域试图取指,也会触发一个 MemManage fault,而且在这种场合下,即使没有 MPU 也会触发 MemMange fault。MemManage faults 的常见诱因如下所示:(1).访问了所有 MPU regions 覆盖范围之外的地址;(2).访问了没有存储器与之对应的空地址;(3).往只读 region 写数据;(4).用户级下访问了只允许在特权级下访问的地址;存储器管理 fault 状态寄存器(MFSR),地址:0xE000_ED28,MFSR的各个位的定义如下:1.3.用法 faults用法 faults 发生的场合可以是:(1).执行了协处理器指令。 Cortex-M3 本身并不支持协处理器,但是通过 fault 异常机制,可以建立一套“软件模拟”的机制,来执行一段程序模拟协处理器的功能,从而可以方便地在其它 Cortex 处理器间移植。(2).执行了未定义的指令。同上一点的道理,亦可以软件模拟未定义指令的功能。(3).尝试进入 ARM 状态。因为 CM3 不支持 ARM 状态,所以用法 fault 会在切换时产生。软件可以利用此机制来测试某处理器是否支持 ARM 状态。(4).无效的中断返回(LR 中包含了无效/错误的值);(5).使用多重加载/存储指令时,地址没有对齐。另外,如果需要严格要求程序的质量,还可以让 CM3 在遇到除数为零的时候,以及遇到未对齐访问的时候也产生用法 fault。在 NVIC 中有两个控制位分别与它们对应。通过设置这两个控制位,就可以激活它们。用法 fault 状态寄存器(UFSR),地址:0xE000_ED2A,UFSR的各个位的定义如下:1.4.硬 fault硬 fault 是上文讨论的总线 fault、存储器管理 fault 以及用法 fault 上访的结果。如果这些 fault 的服务例程无法执行,它们就会成为“硬伤” ——上访( escalation)成硬 fault。另外,在取向量(异常处理时对异常向量表的读取)时产生的总线 fault 也按硬 fault 处理。在 NVIC中有一个硬 fault 状态寄存器(HFSR),它指出产生硬 fault 的原因。如果不是由于取向量造成的,则硬 fault 服务例程必须检查其它的 fault 状态寄存器,以最终决定是谁上访的。硬 fault 状态寄存器(HFSR),地址:0xE000_ED2C,HFSR的各个位的定义如下:2.UsageFault INVPC置1解决过程最近在使用RTOS增加DMA驱动时,在对内存到设备和设备到内存的DMA传输测试时,出现了UsageFault,并且UFSR中的INVPC置1了。最开始,单独测试DMA发送是没有问题的,但是DMA发送和接收一起测试时,就会出现UsageFault(INVPC置1)。这个异常不太好定位出现问题的具体位置,因此就检查DMA驱动,并且逐步调试吧。最终,DMA驱动检查和修改好了,仍然出现UsageFault,实在没法了,还是从为什么会出现UsageFault(INVPC置1)开始分析吧。2.1.出现UsageFault(INVPC置1)的原因如果LR中的EXC_RETURN不是合法的值(合法值见下图,包括企图返回ARM状态),则引起用法fault。如果用法fault被除能,也上访成硬fault。此时,用法Fault状态寄存器(UFSR,地址: 0xE000_ED2A)中的INVPC位(位偏移: 2),或者是INVSTATE位(位偏移: 1)置位。上面就是出现该异常的文字分析了。2.2.UsageFault(INVPC置1)的解决过程因为该异常是异常响应期间才可能出现的异常(<<Cortex-M3权威指南>> 9.8节介绍了下,在9.8.4节进行文字说明),因此,只要在异常或中断的返回处打断点,执行下一步就有可能进入UsageFault异常。(当然了这个方法,是比较笨的,不过在缩小了异常出现的范围之后,可以在每次异常或中断的返回处打断点,然后执行下一步,就有可能进入UsageFault异常)我这里是每次开始DMA测试之后,就进入UsageFault异常,并且我的系统中目前就打开了SysTick,PendSV,DMA1_STREAM5,DMA1_STREAM6,NMI,HardFault,MemFault,BusFault,UsageFault这些异常和中断。因此我在开始DMA测试的时候打一个断点,在程序运行到DMA测试开始的断点处时,再在DMA1_STREAM5,DMA1_STREAM6,NMI,HardFault,MemFault,BusFault,UsageFault这些函数的入口打断点,在PendSV的返回处打断点,SysTick就暂时先不管。然后全力运行,发现每次都会在PendSV的异常返回断点处停留(因为任务切换嘛)总共在PendSV的断点处停留了大概7到8次,就进入到了UsageFault。有了上面步骤的铺垫,先去除中断和异常中的断点,还是先在DMA测试开始处打断点,等运行到DMA测试开始处,再在上述的中断和异常相关位置打断点。接下来我就慢慢的调试,在开始DMA测试之后,全速运行,在退出PendSV异常时,执行单步运行到下一步,重复7到8次,从PendSV就进入了UsageFault,在这7到8次中,我看在退出PendSV时,LR寄存器中的值都是0xFFFFFFFD,是合法的啊,当时仔细一想,有可能是退出异常时硬件再将堆栈中的PC赋值给PC时出问题,导致进入了UsageFault。果不其然,在PenSV退出之前,我查看每个PSP(0xFFFFFFFD:返回线程模式,并使用线程堆栈)对应内存数值,能够正常退出PendSV的寄存器和PSP堆栈内容如下图所示:进入UsageFault异常之前的寄存器和PSP堆栈内容如下图所示:此处堆栈中的内容,明显的0x20003D94堆栈中的PC值是有问题的,我的程序是烧写到flash中的PC地址应该是08xxxxxx,然而现在堆栈中的PC地址是0x20003DA8,这个地址是SRAM中的地址,SRAM存储的数据而不是代码,出现这个问题的原因,猜想一下,应该就是任务堆栈溢出导致,当我增加任务堆栈的大小之后,哈哈,程序正常运行,世界是如此美好。3.调试小结3.1.解决过程小结其实上面的步骤2,是我自己的一个调试解决问题的过程,这里给大家提供一个比较直接的解决方式。在开始运行程序之前,直接在UsageFault异常入口函数中打一个断点,然后全速运行程序,等程序停止在UsageFault异常函数的断点处时,需要注意以下几点:(1).如果LR是合法值,那么根据LR判断退出异常时使用的堆栈,然后在Memory查看窗口中,查看堆栈中R0,R1,R2,R3,R12,LR,PC,xPSR这些寄存器的值,根据这些寄存器的值,判断是否是堆栈溢出导致该异常发生;如果不是堆栈溢出导致该异常发生,那么就要根据PC值,在汇编窗口中跳转到PC值对应的代码处,分析导致异常发生的原因;(2).如果LR不是合法值,就要分析下你的代码中,有哪些地方修改过LR的值,确保修改的值要是合法的。3.2.关于UsageFault 如何才能让INVPC置1(1).在退出异常或中断时,执行BX LR时,LR的值是非法的,此时就会触发UsageFault异常,并且INVPC置1。(2).在退出异常或中断时,执行BX LR时,LR的值是合法的,但是退出异常之后要使用的堆栈中,堆栈里面的PC值是有问题的,此时就有可能触发UsageFault异常,并且INVPC置1。对于上面的两点的模拟其实也比较好做,在PendSV或者其他异常的退出的地方打一个断点,然后手动修改LR或者堆栈中PC的值,就能触发UsageFault异常,并且INVPC置1。这里注意一下,修改堆栈中PC的值,我这里测试时候,设置PC值为其他值可能引起其他的异常,貌似修改PC的值为RAM中数据区的地址才会出现该异常,不太清楚为什么会这样,可能是数据区是没有执行代码的权限,因此出异常吧,不太确定,有知道的朋友,欢迎留言讲解。当然了,博主的知识水平有限,对于异常的一些说明可能会有误,如果有误,欢迎指正,谢谢!
1.浮点运算指令浮点运算指令用于FPU单元的单精度浮点运算。浮点运算指令都是用V开头的汇编指令。只有在FPU开启的状态下,才能运行这些指令。如果在FPU没有开启的状态下,执行了浮点运算指令,系统会产生一个硬fault异常。2.为什么FPU需要Lazy Stacking当Cortex-M系列的芯片多了对浮点运算的支持之后,在中断响应和退出时会增加对FPU扩展寄存器的保护。入栈浮点寄存器组所带来的影响有如下几方面:a.增加stack frame所占的存储区域;b.增加中断响应延迟;c.在OS环境下,增加上下文切换时间;为了减少中断响应延迟和OS环境下的上下文切换时间,引入了FPU的Lazy Stacking机制。3.FPU Lazy Stacking简述Lazy Stacking机制在下面的情况下,会跳过对浮点寄存器组的入栈操作(仅预留浮点寄存器组S0~S15和FPSCR的存储空间),以避免中断延迟的增加:a.中断处理函数不使用FPU;b.被中断的程序未曾用到FPU;被中断函数使用了FPU,如果在执行中断处理函数时也用到了FPU,在执行到中断函数的第一条浮点指令时,内核会暂停,然后硬件自动将先前的浮点寄存器内容(S0~S15,FPSCR)压入预留的存储空间中。Lazy Stacking是可以通过软件使能和关闭的,如下述操作:a.使能Lazy Stacking必须同时置位FPCCR寄存器的LSPEN位和ASPEN位;b.清除FPCCR寄存器的LSPEN位可以关闭Lazy Stacking;关于FPCCR寄存器的LSPEN位和ASPEN位的组合情况有如下说明:FPCCR.ASPENFPCCR.LSPEN说明00取消自动状态保存。中断响应时不入栈FPU寄存器。应用场景:1. 应用中没有用到OS或者多任务调度,如果没有任何中断异常用到FPU。2. 在应用程序代码中只有一个中断用到FPU。如果有多个中断用到FPU,那么中断嵌套必须被禁止。可以通过把所有的中断优先级设置位相同优先级实现。10关闭Lazy Stacking,仅打开自动状态保存。如果用到FPU,CONTROL.FPCA位自动置1。中断响应时,硬件自动入栈S0~S15和FPSCR寄存器11打开Lazy Stacking,打开自动状态保存。如果用到FPU,CONTROL.FPCA位自动置1。如果响应中断时,CONTROL.FPCA为1,处理器在堆栈中预留FPU寄存器的空间,同时将FPCCR.LSPACT位置1。但是PFU寄存器并没有马上入栈,直到在中断处理函数中用到FPU时再入栈。01非法配置4.FPU Lazy Stacking用到的几个重要标志浮点寄存器入栈和出栈过程中用到的标志位:标志位说明CONTROL.FPCA0 = 当前上下文中没有用到FPU1 = 当前上下文中用到FPU这里的上下文,即表示普通的任务上下文,也表示中断上下文。FPCCR.LSPACT0 = 退出Lazy状态(实际入栈后被硬件清零或者中断返回时硬件清零)1 = 进入Lazy状态(栈帧中预留了FPU寄存器的空间,但没有实际入栈)EXC_RETURN[4]0 = 栈帧中包括了FPU寄存器空间1 = 栈帧中不包括FPU寄存器空间5.FPU Lazy Stacking使用实例下面的例子都是在使能了FPU的Lazy Stacking机制下进行说明的。(1).被中断的程序和中断程序中都没有使用到FPU,其示意图如下所示:(2).被中断的程序用到了FPU,中断程序中没有使用到FPU,其示意图如下所示:(3).被中断的程序和中断程序中都用到了FPU,其示意图如下所示:6.RTOS使用Lazy Stacking首先肯定是要使能FPU的Lazy Stacking,然后在PendSV切换任务的时候做下面的处理:(1).上文保存阶段:检测 EXC_RETURN 的 bit4(通过LR访问),如果该位为零,就入栈剩下的S16-S31 即可;如果该位为 1,则无需保存 FPU 寄存器,因为该任务未曾使用过 FPU。当然了这里有一个标志数据,表示当前任务是否使用了FPU,便于下次任务恢复时,确定是否需要从堆栈弹出内容到S16-S31。标志数据也压入当前任务的堆栈进行保存。(2).下文恢复阶段:从堆栈中弹出标志数据,判断待恢复的任务是否使用了FPU,如果使用了FPU,那么从堆栈中弹出内容到S16-S31;如果没有使用FPU,则不需要恢复FPU寄存器。接下来需要修改当前LR寄存器的内容,如果使用了FPU,LR(EXC_RETURN)的bit4需要清零,来保证在退出PendSV时将先前压入该任务堆栈的S0-S15和FPSCR寄存器出栈;如果没有使用FPU,LR(EXC_RETURN)的bit4需要置1,来告诉CPU该任务栈帧中不包括FPU寄存器内容,不需要进行出栈操作。对于S0-S15和FPSCR寄存器,如果任务使用了FPU,在进入到PendSV之前,就在任务堆栈中预留了存储空间,当执行上文保存阶段的S16-S31的入栈操作时,会首先将S0-S15和FPSCR寄存器压入先前堆栈预留的存储空间中,接着再将S16-S31压入堆栈。如果这篇文章对你有帮助,记得点赞和关注博主就行了^_^。
1.问题描述最近进行ARM嵌入式系统开发过程中遇到一个问题,就是打印浮点数据不正确。这里的打印函数在其他文件定义的,在main.c中调用了打印函数,但是并没有include打印函数的头文件,编译能够正确的编译过去,但是打印浮点数据时浮点数据的内容始终不正确,比如kprintf("float_num:%f\r\n", 12.06);实际显示的内容可能是:float_num:0.0000。最开始以为浮点的堆栈处理问题,后来检查浮点的入栈和出栈并没有什么问题,后来调试发现kprintf("float_num:%f\r\n", 12.06);这句代码的汇编格式使用d0在保存浮点数据,正常来说ARM传递参数使用的是r0,r1,r2,r3寄存器或者堆栈,这明显就不对,采用的貌似是编译器的通用参数处理方式。当然了导致这个问题的原因就是kprintf这个函数并未声明,因为kprintf函数未声明,编译器在编译当前文件时,并不知道kprintf函数的参数及顺序,因此采用的貌似是编译器的通用参数处理方式。kprintf函数未声明时,kprintf("float_num:%f\r\n", 12.06);对应的汇编代码为:vldr d0, [pc, #188]ldr r0, [pc, #200]bl kprintfkprintf函数在main.c文件中声明了时,kprintf("float_num:%f\r\n", 12.06);对应的汇编代码为:add r3, pc, #252ldrd r2, r3, [r3]ldr r0, [pc, #188]bl kprintf函数未声明除了造成上述问题之外(参数传入的不正确导致结果出错),也可能导致结果正确,但是返回的结果不正确(比如一个函数返回double型的结果,如果函数未声明就使用,可能会返回4字节的结果,导致结果返回错误)。函数未声明时,kprintf("int_num:%d\r\n", 15);能够正确的显示,因为此时15这个值能够通过普通寄存器(r0/r1/r2/r3)传递,因此不会出现打印浮点数的问题。如果传递的参数或者返回的值,不能通过普通寄存器(r0/r1/r2/r3)传递时,就可能出现奇怪的问题了。2.问题解决方法解决这个问题的方法自然是,在使用到kprintf的文件中include打印函数kprintf的头文件。3.小结对于开发过程中,如果编译时提示"warning: implicit declaration of function 'xxx'"这类的信息,一定还是加上这些函数的声明。如果不添加函数声明,编译虽然能够通过,但是遇到我上面提及的怪异问题,调试可能都不知如何下手,谨记吧。如果这篇文章对你有帮助,记得点赞和关注博主就行了^_^。
为了描述方便,将ARM Compiler 5简称为AC5,将ARM Compiler 6.12简称AC6.12。目前,我自己的工程已经可以成功的使用AC6.12进行编译和调试了。在适配工程的尾声,也遇到了两个问题,在这里记录下来,以便后面查证。我这里使用的开发环境如下:CPU:LPC55S69Keil:5.27ARM Compiler:AC6.121、Heap region was used, but no heap region was defined工程中,我使用的是自己的分散加载文件,并且没有定义ARM_LIB_STACKHEAP,ARM_LIB_STACK,ARM_LIB_HEAP这些符号,因为我自己要重新定义堆栈,就没有使用这些符号,因此在C代码中加入:__asm(".global __use_no_heap"); //不使用ARM提供的堆函数,但是编译的时候还是报错:Error: L6915E: Library reports error: Heap region was used, but no heap region was defined。最初我以为是分散加载文件的问题,后来查看分析,觉得分散加载没问题,于是就换个方向思考。因为我在C代码中添加了不使用ARM提供的堆函数的声明,然而ARM_LIB_STACKHEAP,ARM_LIB_STACK,ARM_LIB_HEAP这些符号会在ARM官方代码的堆栈初始化函数中进行使用,那么我只要分析出是谁在调用ARM官方的堆栈初始化函数,就能够解决这个问题了。后来发现我的启动代码中少写了一个函数__rt_entry,这个函数的作用就是一些初始化工作,当然也就包括初始化堆栈了。这个函数在ARM官方库中已经实现,由于我没有自定义__rt_entry函数,因此在启动时会调用ARM官方的__rt_entry函数,也就自然会调用ARM库中的堆栈初始化函数,在链接的过程中,当发现分散加载文件没有定义ARM_LIB_STACKHEAP,ARM_LIB_STACK,ARM_LIB_HEAP这些符号就报错:Error: L6915E: Library reports error: Heap region was used, but no heap region was defined。解决方法:自然是在我的启动文件中自定义一个__rt_entry函数,该函数会调用main()函数。2、启动过程中出现总线异常在解决了上面第1个问题后,工程能够成功的编译和链接,但是程序运行时会出现总线错误,而且有时候代码量减小又不会出现这个问题。最开始分析这个问题,花费了不少时间,也没有摸着门道。后来也是在我组长的指点下才解决的。首先分析下为什么这个总线异常问题有时会出现,有时不会出现。这就得说说分散加载文件中加载域和执行域中的一个标志:NOCOMPRESS,AC6.12的armlink文档中有对这个标志的描述:RW data compression is enabled by default. The NOCOMPRESS keyword enables you to specify that RW data in an execution region must not be compressed in the final image.大致意思,默认情况下RW数据会被压缩放置在image中,只有在分散加载文件中相关的加载域与执行域中使用标志NOCOMPRESS时,才不会对RW数据进行压缩。因为我没有使用NOCOMPRESS标志,因此会对RW数据进行压缩,因此在image中的RW数据会比实际的长度小一些。由于是我自己在启动代码中初始化data和bss,因此在将flash中的RW data拷贝到RAM中时,RW data的长度我仍然使用的是未压缩的数据长度,该长度大于压缩数据的长度,在将flash中的RW data拷贝到RAM中时,当长度超过压缩数据的长度,flash就会产生保护,从而触发一个总线异常,而且异常的地址存储在BFAR中。解决方法:这里有两种解决方法。方法1:最简单的就是在分散加载文件中RW data相关的加载域和执行域使用NOCOMPRESS标志,当然了这个方法的缺点就是产生的image文件会大一些。方法2:可以在启动代码中配置好sp,然后就不用去对RW data和bss进行处理(屏蔽掉自己写的RW data和bss的处理代码),这些工作ARM官方库会自行处理的(压缩的RW data在拷贝到RAM中时,ARM官方库会进行解压缩的,这些ARM库代码在编译和链接的时候会自动的加入到image中)。如果觉得文章写的不错,对你有帮助,欢迎点赞,关注博主哟!
为了描述方便,将ARM Compiler 5简称为AC5,将ARM Compiler 6.12简称AC6.12。 ARM官方的迁移文档,下载地址:点此下载 ,密钥:0iaf。1、armasm编译汇编代码,链接失败的问题这里新开一篇博文,专门讲讲针对ARM格式的汇编代码,使用AC6.12应该如何处理。下述内容大多来自文档《migration_and_compatibility_guide_100068_0612_00_en.pdf》文档的 3.3 Command-line options for preprocessing assembly source code。我在我自己的工程中遇到过使用AC6.12编译汇编代码成功,但是链接会失败。提示内容大致是:xxx.scf Error: L6236E: No section matches selector - no section to be FIRST/LAST.对于这个问题,我起初以为是分散加载文件(*.scf)有问题,看了半天的分散加载文件内容,也没发现分散加载文件有问题。于是我转换方向,使用命令去手动编译startup_LPC55S69_cm33_core0.s,然后再手动链接,发现还是提示xxx.scf Error: L6236E: No section matches selector - no section to be FIRST/LAST.错误。于是我再次查阅ARM官方的升级与适配手册《migration_and_compatibility_guide_100068_0612_00_en.pdf》,发现armasm使用--cpreproc和--cpreproc_opts选项编译汇编代码时,输入源代码的后缀名是.S(大写)。然而我的工程的ARM格式的汇编代码文件的后缀名均为小写,导致了armasm处理出错。下面是文档中3.3 Command-line options for preprocessing assembly source code对--cpreproc和--cpreproc_opts的描述:If you are using armasm to assemble source code that requires the use of the preprocessor, you must use both the --cpreproc and --cpreproc_opts options together. Also:• As a minimum, you must include the armclang options --target and either -mcpu or -march in --cpreproc_opts.• The input assembly source must have an upper-case extension .S.其中第2点,对汇编源文件后缀名为大写.S作了一个说明吧。为了处理汇编源文件后缀是小写.s的情况,文档的下面也提供了一个操作的说明吧:If you have existing source files, which require preprocessing, and that have the lower-case extension .s, then to avoid having to rename the files:1. Perform the preprocessing step separately using the armclang -x assembler-with-cpp option.2. Assemble the preprocessed file without using the --cpreproc and --cpreproc_opts options.上面这段英文的内容给出了汇编源代码文件后缀是小写.s,但是又不想修改源代码后缀的方法,首先使用armclang (带编译选项-x assembler-with-cpp)去预处理*.s汇编代码,生成一个过程*.s文件,接着再使用armasm(不带编译选项--cpreproc和--cpreproc_opts)去编译这个过程*.s文件。这里提供一个例子如下:(1).armclang --target=arm-arm-none-eabi -mcpu=cortex-m33 -x assembler-with-cpp -E test.s -o test_preproc.s(2).armasm --cpu=Cortex-M33 --fpu=FPv5-SP test_preproc.s2、总结ARM Compiler 6.12对于ARM格式的汇编处理,这里总结下吧,分两种情况:(1).如果ARM格式汇编代码源文件的后缀名是大写的.S,那么直接使用armasm 带编译选项--cpreproc和--cpreproc_opts进行编译即可,例如:armasm --cpu=cortex-m33 --cpreproc --cpreproc_opts=--target=arm-arm-none-eabi,-mcpu=cortex-m33 startup_LPC55S69_cm33_core0.S(2).如果ARM格式汇编代码源文件的后缀名是小写的.s,这里就需要特殊处理了,有两种方法:a.将ARM格式汇编代码源文件的后缀名改为大写.S,然后按照步骤(1)进行处理即可。b.首先使用armclang (带编译选项-x assembler-with-cpp)去预处理*.s汇编代码,生成一个过程*.s文件,接着再使用armasm(不带编译选项--cpreproc和--cpreproc_opts)去编译这个过程*.s文件。这里提供一个例子如下:(1).armclang --target=arm-arm-none-eabi -mcpu=cortex-m33 -x assembler-with-cpp -E test.s -o test_preproc.s(2).armasm --cpu=Cortex-M33 --fpu=FPv5-SP test_preproc.s如果觉得文章写的不错,对你有帮助,欢迎点赞,关注博主哟!
为了描述方便,将ARM Compiler 5简称为AC5,将ARM Compiler 6.12简称AC6.12。 ARM官方的迁移文档,下载地址:点此下载,密钥:0iaf。1、为什么要进行ARM编译器版本的更换目前,AC5基本处于停止更新状态,如果想要使用AC5编译器编译新的ARM架构或者内核的代码,基本是不可能了,因此自然要使用ARM Compiler 6来代替之前的ARM Compiler 5。其实,我自己觉得还有一个比较重要的原因,那就是AC6支持使用armclang编译GNU语法格式的汇编代码,这样在以后的项目开发中,可以只编写GNU语法格式的汇编代码(不必像以前一样还要再写一份符合ARM语法格式的汇编代码),这样既可以使用ARM-GCC编译工具链编译这个项目,也可以使用ARM Compiler编译这个项目,汇编代码维护更加方便了。2、AC6.12的组成(1).armclang:armclang编译器替代了AC5的armcc,并且具有如下优点:基于LLVM和Clang技术;支持编译GNU语法的汇编代码;高度兼容当初为GCC编译的源代码;实现包括ANSI/ISO C和C++,用于Arm架构的ABI,用于64位Arm架构的ABI以及Arm C语言扩展(ACLE)等规范。(2).armlink:功能丰富的专用嵌入式链接器,能够将对象和库组合在一起以生成可执行文件。(3).fromelf:镜像文件转换和反汇编功能。(4).armar:压缩程序。(5).armasm:ARM语法的汇编代码编译器。(6).ARM C和C++库:ARM C库经过ARM公司的优化有很好的的性能和代码密度;ARM C++库基于LLVM libc++项目。下图展示了ARM Compiler 6.12编译工具链的整体结构:3、ARM Compiler 5和ARM Compiler 6编译工具链的差异AC5和AC6的主要差异是AC6使用armclang代替了armcc,因此在AC6中就没有armcc这个编译工具了。并且armclang的编译参数相对于之前的armcc的编译参数也有许多不同。下表列出了ARM Compiler 5和ARM Compiler 6之间各个工具的功能:4、编译工具链升级过程其实将AC5更换为AC6.12编译工具链,无非就是找出这两个工具链的编译参数的差异进行修改即可。当然了,基本上编译参数变化还是挺大的,这里推荐大家参考如下手册进行编译工具链升级。参考手册:《migration_and_compatibility_guide_100068_0612_00_en.pdf》。因为AC5和AC6.12的差异主要体现在ARM Compiler 6使用armclang代替了armcc,因此这篇文章,可以重点关注Chapter 2和Chapter 3,当然了也可以通过搜索一些编译参数来快速定位。5、我在升级过程遇到的问题我在迁移一个项目使用AC6.12的过程中,主要还是参考《migration_and_compatibility_guide_100068_0612_00_en.pdf》文档进行参数修改,起初还算比较顺利,但是涉及到汇编代码的编译过程中死活编译会有问题。这里简单的记录下吧。我这里仍然使用armasm来编译之前的ARM语法格式的汇编代码。修改汇编的编译参数时,一定要注意编译选项--cpreproc,该编译选项在AC5的含义是命令armasm调用armcc预处理输入的汇编源代码;在AC6的含义是命令armasm调用armclang预处理输入的汇编源代码。我的工程,在使用AC5编译工具链时,在汇编过程中,使用的编译参数配置和armcc的编译参数配置是一样的,并且也添加了编译选项--cpreproc。升级到AC6.12之后,因为armclang的编译参数和armcc的差异比较大,自然的就不能够直接用于armasm的配置,因此我按照AC5的armasm配置作为AC6.12armasm的配置,此时能够进行汇编,但是提示:armclang: fatal error: no target architecture given; use --target=arm-arm-none-eabi or --target=aarch64-arm-none-eabi。当时我就在想,为什么我使用armasm编译汇编代码怎么会调用armclang呢,还以为编译工程的脚本有问题,检查了编译脚本也没发现问题,后来还是查看《migration_and_compatibility_guide_100068_0612_00_en.pdf》文档才有所收获。原来在汇编过程中调用armclang是armasm的编译选项--cpreproc在作怪,该编译选项是命令armasm调用armclang预处理输入的汇编源代码。ARM Compile 6中armasm需要另一个编译选项--cpreproc_opts,用于填写armclang预处理汇编代码时的一些配置参数。由于先前没有配置--cpreproc_opts,当然的在使用armclang进行预处理时会提示armclang: fatal error: no target architecture given; use --target=arm-arm-none-eabi or --target=aarch64-arm-none-eabi。问题解决办法:对armasm新增编译选项--cpreproc_opts,并且填写上armclang预处理汇编代码需要用到的一些配置信息。例如:armasm --cpu=cortex-a9 --cpreproc --cpreproc_opts=--target=arm-arm-none-eabi,-mcpu=cortexa9,-D,DEF1,-D,DEF2 -I /path/to/includes1 -I /path/to/includes2 input.S 对于--cpreproc和--cpreproc_opts编译选项,可参考《migration_and_compatibility_guide_100068_0612_00_en.pdf》文档的 3.3 Command-line options for preprocessing assembly source code。6、下面简单列举一些编译参数的差异(1).对于armcc和armclang之间,编译参数存在的差异如下:AC5 OptionAC6 Option描述--cpu=Cortex-M4--target=arm-arm-none-eabi -mcpu=cortex-m4Cortex-M4处理器的修改,其他处理器可能不一样,请参考我这里提供的官方手册.--thumb-mthumb支持thumb指令集--fpu=fpv5_sp_d16-mfloat-abi=hard -mfpu=fpv5_sp_d16支持硬件浮点--fpu=softvfp-mfloat-abi=soft -mfpu=none软件浮点--unaligned_access-munaligned-access设定处理器可以生成地址非对齐的数据--apcs=interwork没有对应的编译选项在AC6中总是允许ARM指令和Thumb指令一起使用,因此没有对应的编译选项.--split_sections-ffunction-sections生成的函数在自己的段中--debug/-g-g生成调试信息--c99 --gnu-xc -std=gnu99允许编译器编译带有GNU扩展的C99代码--cpp --gnu-xc++ -std=gnu++03允许编译器编译带有GNU扩展的C++03代码--no_exceptions-fno-exceptions禁止生成需要支持C++异常的代码--no_rtti-fno-rtti [ALPHA]禁止生成需要支持C++ Run Time Type Information(RTTI)特征的代码-Otime默认支持减少执行时间的优化,代价就是执行文件的大小会增加-O3 -Otime-OmaxHighest optimization for performance-O3 -Ospace-OzHighest optimization for code size(2).对于AC6使用armasm需要注意的事项:使用AC6编译汇编代码时,编译选项和AC5基本差不多,这里再强调一下编译选项:--cpreproc。在AC6中如果使用了编译选项--cpreproc,那么就必须附带的使用编译选项--cpreproc_opts,并且填写上armclang预处理汇编代码需要用到的一些配置信息,例如:armasm --cpu=cortex-a9 --cpreproc --cpreproc_opts=--target=arm-arm-none-eabi,-mcpu=cortexa9,-D,DEF1,-D,DEF2 -I /path/to/includes1 -I /path/to/includes2 input.S好了,我在这里简单的记录了下ARM Compiler 5升级到ARM Compiler 6.12的过程,给以后有需要的朋友留作参考吧。如果觉得文章写的不错,对你有帮助,欢迎点赞,关注博主哟!
之前,写过两篇文章,介绍了在Windows下搭建ARM11的裸机开发环境,以及使用Eclipse创建ARM11的裸机程序管理工程,需要的朋友可以过去看看。这里贴出链接吧:《Windows下搭建ARM11裸机开发环境(1):工具安装》,《Windows下搭建ARM11裸机开发环境(2):Eclipse创建工程》,欢迎关注,支持,喜欢的点个赞,留个言吧。 下面涉及的代码我已经分享到网盘了,有需要的朋友可以自行下载。链接:点此下载 密钥:40ok。网盘里也分享了我使用的Eclipse的绿色版本,后面的文章将会讲讲如何制作Eclipse的绿色版本,方便随时拷贝到其他电脑使用,敬请关注吧。系统环境:Windows 7 64位ARM11:Tiny6410Eclipse:2019-03 (4.11.0)这篇文章,讲讲如何在Eclipse开发环境中使用JLink来调试ARM11的裸机程序。需要的朋友可以往下面进行阅读,欢迎各位朋友收藏我的博客,博客内容也会不定时更新,总会有你想要的内容。1、打开Makefile工程首先,打开上一篇文章创建的Makefile工程,这里以上一篇文章创建的MakefileProject为例进行说明,如下图:2、创建Debug配置项选中MakefileProject,然后点击鼠标右键,在弹出的菜单中选择Debug As -> Debug Configurations...:在弹出的Debug配置菜单中,选中GDB SEGGER J-Link Debugging,然后鼠标双击GDB SEGGER J-Link Debugging,即可创建一个新的Debug配置项,Debug配置项的名字这里默认的是MakefileProject Default:3、配置Debug配置项接下来就是对上一步创建的Debug配置项进行具体的配置了,在Main tab页中,C/C++ Application输入框中输入led.elf,当然了你也可以使用下面的Browse..按钮来定位led.elf文件或者你自己的可执行程序(当然进行这一步的时候,请确保已经编译过工程了,不然没有生成对应的可执行程序,你也没法进行选择),下面选中Disable auto build,这一个页面算是配置完成了:在Debugger tab页中,取消Start the J-Link GDB server locally的勾选,在Executable输入框中,修改为具体的GDB调试工具名,这是填写的是arm-none-eabi-gdb,在Other options输入框中,填入内容:--command=s3c6410_init.gdb(s3c6410_init.gdb这个文件我已经上传到上面的链接了),当然该页面其他一些配置,可参考下图:在Startup tab页中,取消Initial Reset and Halt的勾选,取消Enable semihosting的勾选,勾选上Ram application(reload after each reset/restart),取消Pre-run/Restart reset的勾选,该页面其他一些配置,可参考下图:对于Source tab页,这里并没有进行什么配置,也就不作什么说明了,在Common tab页中,在Display in favorites menu的两个选项,都可以勾选上,配置完之后记住点击Apply按钮保存配置,最后点击Close退出配置对话框:4、调试程序使用JLink连接好开发板,然后给ARM11开发板上电,将JLink连接到电脑(请确保你之前已经安装了JLink的驱动程序,我这里安装的是Setup_JLinkARM_V440.exe,你根据自己JLink的版本安装对应的驱动程序就好了),然后进入路径:C:\Program Files (x86)\SEGGER\JLinkARM_V440,打开JLinkGDBServer.exe(不同版本的驱动程序名字可能会不怎么一样,但是GDBServer应该是不会变的),当JLink成功的和板子建立连接之后,会出现如下的提示信息:当JLink和开发板建立好连接之后,在想要停留的代码处添加一个断点(对于调试汇编程序,这是必要的,不然程序就直接运行了),我这里在第15行添加了一个断点(选中第15行,按CTRL+shift+B可以快速的添加或者删除本行的断点):点击工具栏的调试按钮,然后选择刚才配置好的Debug配置项:MakefileProject Default,接下来就会自动下载程序,并且运行至断点处了:好了,接下来就是调试自己的代码了。如果喜欢,觉得对各位有帮助的话点个赞吧,也欢迎关注我的博客,留个言吧。
上一篇文章介绍了在Windows下搭建ARM11的裸机开发环境,需要的朋友可以过去看看。这里贴出链接吧:《Windows下搭建ARM11裸机开发环境(1):工具安装》,《Windows下搭建ARM11裸机开发环境(3):Eclipse+JLink调试代码》,欢迎关注,支持,喜欢的点个赞,留个言吧。 下面涉及的工具我已经分享到网盘了,有需要的朋友可以自行下载。链接:点此下载 密钥:40ok。网盘里也分享了我使用的Eclipse的绿色版本,后面的文章将会讲讲如何制作Eclipse的绿色版本,方便随时拷贝到其他电脑使用,敬请关注吧。系统环境:Windows 7 64位ARM11:Tiny6410Eclipse:2019-03 (4.11.0)1、创建Makefile工程这里我介绍的是创建Makefile工程,使用自己的Makefile来编译代码,当然了你也可以创建其他类型的工程,结果都是一样的,都是为了编译出正确可执行程序。打开Eclipse,点击File -> New -> Project…,在弹出窗口中的C/C++下选择C Project,然后点击Next,在接下来的窗口中,Project name输入框中输入:MakefileProject,下面选择 Makefile project中的Empty Project,然后点击Finish完成工程的创建。2、添加代码到工程可以将上一篇文章的led程序作为这个Makefile工程的代码文件,首先将start.S和Makefile文件拷贝到刚才创建的工程目录中,然后在Eclipse的工程区刷新一下,就可以看到刚刚添加的文件了。3、编译程序右键MakefileProject,点击Properties,在弹出的窗口中选中C/C++ Build,选择Behavior选项卡,将Build(Incremental build)输入框中的参数all删除掉,接着点击Apply and close:点击Project -> Build All编译程序(或者按快捷键CTRL+B):将编译好的led.bin文件通过友善之臂提供的MiniTools下载到ARM11板子中,就可以看到核心板的4个小灯在闪烁了。下一节将讲述如何利用Eclipse+JLink下载程序到ARM11开发板中进行调试,喜欢的点个赞,留个言吧。
之前写过文章,描述在Linux环境下搭建ARM11的开发环境,有需要的可以在ARM11标签中进行查找。目前在学习ARM11的裸机编程,不想用虚拟机跑Linux来进行开发,因为平时用的笔记本电脑的配置跑虚拟机还是有点困难(我的台式机跑虚拟机还是没问题的),现在讲述下如何在Windows中搭建ARM11的裸机开发环境,方便没法使用虚拟机的朋友也能够进行ARM11的开发。 下面涉及的工具我已经分享到网盘了,有需要的朋友可以自行下载。链接:点此下载 密钥:40ok。系统环境:Windows 7 64位ARM11:Tiny64101、交叉编译工具 - GNU Tools ARM Embedded官方网址:https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm。使用这个交叉编译工具就可以将程序编译成能在ARM11上运行的文件。这里将GNU Tools ARM Embedded的bin目录添加到系统的环境变量中,然后打开控制台输入:arm-none-eabi-gcc -v,查看环境变量配置是否成功,如果环境变量配置成功了,那么会正确的显示arm-none-eabi-gcc的版本号:2、MSYSMSYS是一个小型的GNU环境,包括基本的bash,make等等。对裸机程序通过Makefile文件进行管理与编译,并且通过MSYS的make来执行Makefile文件的内容。将msys/1.0/bin目录下的路径添加到系统的环境变量中,然后打开控制台输入:make -v,查看环境变量配置是否成功,如果环境变量配置成功了,那么会正确的显示make的版本号:3、编译程序在LED程序文件中,打开控制终端,然后执行make命令来编译LED程序:将编译好的led.bin文件通过友善之臂提供的MiniTools下载到ARM11板子中,就可以看到核心板的4个小灯在闪烁了。接下来,我将写两篇文章:《Windows下搭建ARM11裸机开发环境(2):Eclipse创建工程》,《Windows下搭建ARM11裸机开发环境(3):Eclipse+JLink调试代码》,欢迎关注,支持,喜欢的点个赞,留个言吧。
- 1
本站信息
目前本站共被浏览 138472 次
目前本站已经运行 2943 天
目前本站共有 150 篇文章
目前本站共有 6 条评论信息
目前本站共有 100 个标签
目前本站共有 0 条留言信息
网站创建时间: 2015年03月01日
最近更新时间: 2022年12月04日
目前本站已经运行 2943 天
目前本站共有 150 篇文章
目前本站共有 6 条评论信息
目前本站共有 100 个标签
目前本站共有 0 条留言信息
网站创建时间: 2015年03月01日
最近更新时间: 2022年12月04日
JLink V9掉固件修复(灯不亮) 3Zephyr笔记2 - 在STM32F429上运行HelloWorld 2计算NandFlash要传入的行地址和列地址 1C语言-const char*,char const*,char *const理解 0Fedora-14源配置 0文件传输-FTP使用简介 0
最新评论
标签云
Linux嵌入式实用技巧内核学习问题集合ARMCC++编程语言阅读笔记汇编Linux内核完全注释Windows驱动开发计算机基础ARM11STM32IDESublime操作系统OfficeAPUEgccRTOS中断Ubuntu漫游世界随笔感悟开发工具软件VMWare应用编程VsCodearmccarmclang编译器ZephyrSPIJLink网卡驱动安装各种芯片库函数NFSμCOS内核sambaFlashUnix命令与脚本输入法Linux内核设计与实现gitRIFFWAVJATGar8161安装centos有线上网μCGUI字库工程建立右键菜单网络文件系统Firefox百度NTFS文件系统CodeBlocksCentOS数据结构算法PhotoShop51KeilQTUltraEditscanfglibc宏定义UIDGID优先级娱乐天地SourceInsight磁盘扇区总线I2CPDFBComparePythonI2SFPUMakefileSWDCPUARP软件推荐FTPFileZilla