打印

探讨一下ARM以及RVDS的使用

[复制链接]
13111|46
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
xinzha|  楼主 | 2009-9-6 10:21 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 xinzha 于 2009-9-6 10:54 编辑

最近太闲,想给自己找点事做,开个贴跟大家交流一下ARM和RVDS的使用,共同进步一下,说的不对的地方请多多指教。
   ARM是个充满争议的架构,有人说它是RISC,因为它基本做到了指令等长以及指令周期可预期(不考虑ARM/THUMB混用),并且指令数目远远少于CISC,可反对者认为它只是个类RISC,因为它的桶形移位器的存在,并且现在已经添加了非常多的多媒体指令。无论如何,这是一款优秀的cpu,因为它占有率第一。不知道MIPS改变市场策略之后能不能对ARM形成一定的冲击。
   首先讨论一下Remap的问题,由于ARM上电启动后的中断向量地址表的位置不能改变,必须是在物理0x0地址,这就产生了一个问题,如果你的代码有一定的速度要求,需要启动后在RAM中运行,那每次中断产生都要去ROM或者FLASH中查找中断向量,会对系统性能有点小影响(当然如果你的代码允许全程运行在FLash中就无所谓)。为了应对这个问题,ARM公司推荐了一种做法,就是上电复位的时候0x0指向NV memory,而在复位后马上执行一个重映射的设置,执行之后NV memory的地址变成另外一个地址,比如说0x4000 0000,而此时RAM的首地址变成了0x0,只需把包含中断向量表的镜像拷贝到ram的起始地址并做相应配置,pc跳转到0x0开始执行,一个崭新的世界开始了。
   由上面的问题又产生了另外一个问题,复位时的物理0x0地址应该放什么?一般的做法是一块rom或者Nor flash,里面是预先烧好的bootloader,执行这个bootloader的代码将运行镜像拷贝到相应的ram中(如果代码就在nv memory里执行,咱就把这步省了),所以这个bootloader中要包含相应的驱动程序,来从主机或者本板上读取文件。而前两天一位大侠(貌似叫itelectron)告诉我有些芯片可以把不能直接寻址的nand中的前4k代码拷贝到ram中,让我如梦初醒,又见到了一个美丽新世界。这种做法既免除了地址重映射的麻烦,又可以节省nor或者rom的开销,人才啊!不过这种做法已经不属于arm架构范畴,完全是芯片厂商的自由发挥。

相关帖子

沙发
xinzha|  楼主 | 2009-9-6 14:19 | 只看该作者
本帖最后由 xinzha 于 2009-9-6 14:21 编辑

一段汇编

; On reset, an aliased copy of ROM is at 0x0.
        LDR     pc, =Instruct_2
        
Instruct_2        

; Remap by setting Remap bit of the CM_ctl register
        LDR     r1, =CM_ctl_reg
        LDR     r0, [r1]
        ORR     r0, r0, #Remap_bit
        STR     r0, [r1]
        
; RAM is now at 0x0.
; The exception vectors (in vectors.s) must be copied from ROM to the RAM
; The copying is done later by the C library code inside __main

    ENDIF

;EXPORT  Reset_Handler

Reset_Handler

; --- Initialize stack pointer registers

; Enter each mode in turn and set up the stack pointer

        IMPORT  top_of_stacks       ; defined in stack.s and located by scatter file
        LDR     r0, =top_of_stacks

        MSR     CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit ; No interrupts
        SUB     sp, r0, #Offset_FIQ_Stack

        MSR     CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit ; No interrupts
        SUB     sp, r0, #Offset_IRQ_Stack

       MSR     CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit ; No interrupts
       SUB     sp, r0, #Offset_ABT_Stack

       MSR     CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit ; No interrupts
       SUB     sp, r0, #Offset_UND_Stack

        MSR     CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit ; No interrupts
        SUB     sp, r0, #Offset_SVC_Stack

; --- Now change to User mode and set up User mode stack, if needed
        MSR     CPSR_c, #Mode_USR:OR:I_Bit:OR:F_Bit ; No interrupts
        SUB     sp, r0, #Offset_USR_Stack
               
        IMPORT  main

; --- Now enter C code
        B       main  ; note use B not BL, because an application will never return this way
这段代码一定都很熟悉,它存在于很多arm芯片的参考设计中,在这段代码中清晰地体现出来地址重映射以及栈初始化的过程, B main这句之后就跳转到大家更熟悉的c代码中。过去看书的时候经常看到这样一句“在进行cpu的初始化之后,就可以执行c代码”,我觉得这里应该多说两句,因为我当时死活想不明白,不都是编译成了跟机器码一一对应的汇编,咋对cpu来说c和汇编还有差别?后来才渐渐知道原来是因为c编译器在处理c代码的时候需要栈的协助,如果此时栈寄存器没有初始化,那么这里面的栈指针是野,后面的c肯定就会更加狂野不羁。写书的牛人少说两句话,像我这样的蠢人就得考虑好几年啊!

使用特权

评论回复
板凳
xinzha|  楼主 | 2009-9-8 20:50 | 只看该作者
再说一下对齐。
可能有的兄弟用一辈子arm也不会碰到地址对齐所导致的问题,不过我可以保证,如果你碰到过一次,今后你的代码中一定会处处考虑到对齐问题,因为不对齐造成的问题让人查起来很痛苦(当然如果有很多经验了就很快查出来),宁可多花点功夫,也不想碰到问题。
多数arm编译器是默认32位对齐的,除非有意地把这个选项去掉以适应某些对空间非常苛刻的场合。

typedef struct xd_struct {
   uint_8         MAX_PKTS_PER_UFRAME;
   void           (*p_fn_callback)(uchar_ptr);
   uint_8      CTL;           /* USB Endpoint Control byte */
   uint_8      NEXTODDEVEN;   /* Odd/Even of Next BDT to queue */
   uint_8      LASTODDEVEN;   /* Odd/Even of Last BDT used */
} XD_STRUCT, _PTR_ XD_STRUCT_PTR;
这个结构在x86上时没有任何问题的,而在32位risc上,就会出现两个互相矛盾的问题,要么是32位对齐编译模式,导致这个本该占用8字节的结构占了12字节,要么是非对齐编译模式,那个函数指针在一个非32位对齐的地址上,一旦应用到这个指针的时候,将会出现不可预估的错误。
在ARM v3中取指时,cpu自动将地址与0xfffffffc与,这样的话非对齐指针就不能正确取到指令,而在v4以上的指令集中,则干脆将非32位对齐的取指操作定义为“未定义”,如何实现就是看芯片厂商心情了。所以针对arm编程的时候,建议尽量做到32位对齐,如果只是靠编译器的话还是不安全,还是自己保证为好。

使用特权

评论回复
地板
xinjie1023| | 2009-9-20 18:40 | 只看该作者
学习了,但Mode_ABT:OR:I_Bit:OR:F_Bit 这里面的OR好像ARM没这指令??

使用特权

评论回复
5
xinzha|  楼主 | 2009-9-21 08:46 | 只看该作者
OR是数字表达式之间的按位或操作,供人类使用。而ORR操作是最终产生机器码的格式,由于桶形移位器的存在,显得非常诡异,相关内容可以看下arm体系结构与变成。

使用特权

评论回复
6
xinjie1023| | 2009-9-22 11:53 | 只看该作者
谢谢,不过ARM汇编用OR似乎不对吧

使用特权

评论回复
7
小嘿| | 2009-9-23 10:08 | 只看该作者
lz好帖,居然放了几个星期都没人顶
对齐问题
RVDS2.2有个伪操作来检查了:

    PRESERVE8            ; make RVCT happy..

使用特权

评论回复
8
xinzha|  楼主 | 2009-9-23 15:09 | 只看该作者
没问题的,这个就是ARM公司提供的标准参考代码,你写的汇编代码只要你的汇编器可以识别就可以。
哈哈,谢楼上。

使用特权

评论回复
9
xinzha|  楼主 | 2009-9-24 20:31 | 只看该作者
本帖最后由 xinzha 于 2009-9-25 11:29 编辑

再来讨论一下PC。
某些比较极端的情况下,需要用JATG去抓pc的值,然后根据pc值以及反汇编结果来分析程序(或者硬件)问题,可这时候大家会发现根据pc值分析的结果和实际结果不相符,为什么会发生状况无法理顺。而其实这个时候你可能犯了一个小小的错误,当发生错误时pc所指向的地址并不是当前语句,而是当前语句加8个字节,也就是2条指令,那么你根据当前所分析的结果就是完全错误的了(忍不住赞一下MIPS,会有个EPC寄存器,让开发者清楚地知道哪里出错,ARM接触太少,还没见过类似的东西)。
  那这个PC为什么不反应当前指令呢?因为ARM是个类risc的cpu,在当前指令执行的时候,流水线上自然就有其他指令处于取指,译码或者运算或者xx的其他阶段,而这个时候pc所指向的就是正处于取指阶段的指令,根据流水线级数的不同可能会对当前执行指令有不一样的偏移。
  说到了流水线自然就要提起cache,ARM的cache只有过一次接触,至于架构和操作还没有任何概念,待鄙人找些资料和代码看看再来讨论。

使用特权

评论回复
10
alex74| | 2009-9-25 17:00 | 只看该作者
arm的cache很恶心,配置要通过cp15来转一下,难用

使用特权

评论回复
11
xinzha|  楼主 | 2009-9-29 09:29 | 只看该作者
昨天一同事又遇到了ARM的字节对齐问题。
我们的芯片内部集成了ARM7-TDMI的ip core,按照ARM手册对ARM v3指令集的解释,在对数据类型与地址边界不匹配的情况下,会强行将取数据地址边界与地址匹配(说起来有点拗口,但是想解释起来很麻烦,有兴趣的可以交流)。由于我们的硬件连线失误,导致dma数据错位两个byte,这样最后在包尾填写magic number之类数据的时候一样也要移位两个字节,组里一兄弟开始准备直接用指针+2操作,可是结果是修改失败,最后一查,是由于magic number定义为uint32, 而+2之后就是只能对齐uint16,cpu在操作的时候自动将低位地址与0xfc与,最终导致问题出现。
看来地址对齐问题还是会偶尔出现的啊。

使用特权

评论回复
12
xinzha|  楼主 | 2009-12-13 12:14 | 只看该作者
由于要对内核进行评估,突击学习了一下arm7的架构,指令集以及amba的东西,现在又可以胡诌一点东西了。
这期间发生了一些有意思的事,一一记录下来。
第一件比较崩溃的就是,由于项目中有一个任务要占到50~60 MIPS的工作,然后还有视频传输相关,所以就要对带宽,内存和cpu进行详细的论证,以保证最坏情况下系统依然能够保证运行,同时为了追求die面积的最小化,ic的兄弟们还是无比倾向于使用arm7的软核。
好了,开始论证性能,内存没问题,cpu频率也没问题,软核可以到216MHz,然后讨论带宽,讨论结果就是:崩溃,然后再崩溃。arm7tdmi是一款没有cache的处理器,这就意味着每个指令周期,辛苦的cpu要去内存中取一次指令...(也有例外,就是跳转不断发生在流水线内的三条指令间,通俗点说就是死循环了),这样就产生了一个问题,如果cpu跑在100M,就算只有50%的时间需要更新流水线,那么需要的带宽就是50M*32Bits,再考虑到总线仲裁以及并不是所有访问都是burst的问题,预留个7,80*32bits的带宽是正常的,而AHB的效率也远不能达到100%,这样问题就出来了,要嘛整个总线都给cpu用,数据传输歇菜,要嘛数据不传输了,cpu爱咋玩就咋玩,整个系统也就没用了...。最后这个结论得到了也曾经做过类似项目的同志的确定,大伙儿歇了吧。
兄弟们在选型的时候需要注意,arm7(不包括720t)真的只是个mcu,如果系统工作繁重,数据量较大的时候还是换个别的吧,比如说MIPS,哈哈,为我喜欢的MIPS做个小广告。当然也有例外,ic设计者可能会做一下手脚,比如说总线隔离一类的,但毕竟只是规避了问题,而没有解决问题。

使用特权

评论回复
评分
参与人数 1威望 +6 收起 理由
McuPlayer + 6
13
desert_hawk| | 2009-12-13 12:42 | 只看该作者
ARM7TDMI确实不适合楼上的应用,因为无cache,而且总线是冯.诺依曼结构的。cortex-m3可能会好些,哈弗结构,在片内flash里运行程序可以比较快的。数据量较大而且要求主频较高的应用直接用ARM9就好了。

使用特权

评论回复
14
xinzha|  楼主 | 2009-12-13 17:05 | 只看该作者
主要是处于成本的考虑,arm9的die面积太大,这样晶圆和封装的钱都要成倍增长,而且arm9的license费用远高于arm7,为了能给公司省点钱(也为了项目能有个合理预算,促使老大们批准...),ic的兄弟们还是觉得用arm7更划算一些。我们并不是想做一款通用的cpu,而只是想让自己的芯片有个更强劲点的功能。

使用特权

评论回复
15
xinzha|  楼主 | 2009-12-14 13:19 | 只看该作者
本帖最后由 xinzha 于 2010-5-20 13:35 编辑


在调试中碰到了一个很诡异的问题,就是硬件复位之后,timer可以跑得好好的,可是一旦进入断点之后再重新运行,timer中断就再也进不去了,而从Jtag监视看到的timer中断实际是产生了,而且cpsr中的中断使能也都开着,可就是无法产生异常,最终查到的问题居然是在断点处,由于cpu停转,但是timer依然运行,此时RVDS接管了中断,cpu再运行之后,这个中断也没有上报给cpu,于是timer的IP始终处于中断置位状态,而cpu却并不知道这一点,也不会去清理timer的中断状态。
而要避免这个问题,就要在相应的ICE配置中去掉软件中断管理的选项,这样就可以保证重新运行之后能够正确上报中断并处理了。
  现在这个问题终于有了最终的结论,实际上不是RVDS乱了,而是ARM的Interrupt Controller的状态机乱了,看了下ARM的Interrupt Controller的ip,里面在对简单中断流程做处理的时候,如果来了一个中断并且cpu没有处理的时候,如果总线上出现了对ICVectorAddr这个寄存器的读操作,就会导致IP误进入向量中断处理模式,从而内部状态机发生混乱,再也不能处理后续中断。
  至于在进入断点后产生读寄存器操作的原因是因为RVDS的窗口中打开了对中断处理模块的监视,这样cpu断点后,RVDS就会通过JTAG把所有寄存器刷新一遍,于是乎产生了一次对寄存器的读操作。
  这个问题的规避,要么是关闭那个窗口,要么是在做Integration的时候,如果使用简单中断处理模式,就直接把这个寄存器干掉。

使用特权

评论回复
16
xinzha|  楼主 | 2009-12-14 20:43 | 只看该作者
关于arm的中断机制,还要补充一些。
对于ARMv4以下的版本,arm的异常向量表地址固定为0(习惯性地,把cpu内部激发的称为异常,外部条件激发的称为中断);ARMv4及其以上的版本,ARM异常向量表的地址受协处理器CP15的c1寄存器(control register)中V位(bit[13])的控制,如果V=0,则异常向量表的地址为0x00000000~0x0000001C;如果V=1,则为:0xffff0000~0xffff001C。
而加了后面这个设置,猜测是因为很多操作系统是把内核代码放置在程序空间的高地址,比如linux的内核空间就占据了0xc0000000以上,这样的话就保证了异常发生的时候异常地址落在了内核地址范围内,同时满足了安全性,小范围跳转等需求。
同时ARM还提供了一个VIC这个IP模块(似乎叫vectored interrupt control),这个模块实现了更快的中断响应,以及中断分级等机制。举个例子说,如果他有16个优先级别的中断源,然后我在相应寄存器中配置了这些中断的服务地址,那么当这其中的某个或者某几个中断产生的时候,VIC模块会自动进行优先级排序,将产生的最高级中断的服务例程地址写入某一固定地址的寄存器,同时将中断上报,激发cpu异常,这时cpu只需要将此寄存器读出赋给pc,就可以跳转到相应服务,大大简化了中断响应的流程。
lpc22xx似乎是实现了以上提到的VIC机制,有兴趣的兄弟可以看看,一起讨论一下。

使用特权

评论回复
17
xinzha|  楼主 | 2009-12-15 22:22 | 只看该作者
本帖最后由 xinzha 于 2009-12-15 22:39 编辑

一直想要找个ARM的全局反汇编工具,就是那种能把整个bin文件反汇编出来,并且有全局跳转的那种,arm本身提供的工具中没有找到这项功能,只找到了每个文件的反汇编,如果果真如此,那就是无形中增大了嵌入式软件工程师的工作难度,因为在实际工作过程中,同系统挂死打交道是不可避免,而RVDS和ADS这两个调试软件的功能实在值得怀疑,给出的调用栈深度有限,如果能有个整体的反汇编文件,直接进行内存里面的反向堆栈分析,就可以在实在没有其他办法的时候,解决很多的疑难问题。
另外MIPS提供了一个寄存器叫做EPC,记录cpu上一次发生异常时的pc值,以利于问题的追踪,而我在接触ARM的这接近一年时间内始终没有找到类似的寄存器。
上面的两个信息不知道哪位能提供一下,不胜感激!

使用特权

评论回复
18
desert_hawk| | 2009-12-16 08:20 | 只看该作者
16# xinzha
ARM7和ARM9的VIC貌似是由各个芯片设计厂家制定的吧,并没有统一的标准,各个厂家都不尽相同。ARMv7-M版本的cortex-m3内核倒是制定了一个“标准”的中断向量控制器和存储器空间范围。
17# xinzha
ARM的每种异常模式都有自己的LR,而这个LR减去一定的偏移量就是发生异常时PC的值,这个在ARM内核的技术手册上有描述的。

使用特权

评论回复
19
xinzha|  楼主 | 2009-12-16 08:54 | 只看该作者
总感觉ARM提供的那个LR无法像MIPS精确定位,可能是还不熟悉,几次跟踪undefine instruction,LR中的数据都是无意义的。
ARM提供了VIC模块,但是并没有强制要求,至于是否使用或者放到什么地址就是厂家自己决定了,几天前看了下LPC的手册,跟这个vic模块非常一致,估计LPC用的是这种机制。ARM提供了很多模块,timer,int什么的都有,有些是免费的例程,有些成熟的是要付费的。

使用特权

评论回复
20
desert_hawk| | 2009-12-16 09:30 | 只看该作者
LR中的数据无意义,这个不应该吧。先排除是否是调试工具的问题,即观察到的LR是否正确,我记得以前用山寨版的J-LINK-V7的时候,调试时发现有的寄存器值明显是错的。如果调试工具没问题,那就可能是你还没有想到它的意义。进入UNDEFINED模式之后,LR中的数值减4才是发生异常的指令。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

10

主题

950

帖子

7

粉丝