luoyiming1984 发表于 2011-11-27 19:13:54

测试:8051的CODE BANK切换,IAR和KEIL哪个更给力。(占个位置)

很多51单片机都有超过64KB的CODE区,就要涉及到BANK切换。
我无聊,我蛋疼,我就做了这个比较。
占个位置,端个板凳,排排坐吃果果。
KEIL平台:keil UV3,硬件:CEL ZIC241016MHz,软件:IEEE802.15.4 MAC
IAR平台:IAR 7.6,硬件:TI CC243032MHz,软件:TIMAC

先卖个关子,ZIC2410的MOVX指令和MOVC指令在1~2个周期,CC2430要3~4个周期。

先去吃饭……

继续:

8051的CODE BANK模式原理:
    CODE区有16位寻址,即64KB,被分成ROOT区和BANK区,通常都是对半分,0000~7FFF是ROOT区,8000~FFFF是BANK区,然后通过一个寄存器,来选择BANK号。
    由于8051的PC只有16位,所以像函数调度,中断函数都是放在ROOT区的,这样的好处是,无论当前程序运行在哪个BANK,他们都能寻址到同一个ROOT区。如果一个函数是在BANK1,它要调度BANK2中的函数,在keil和IAR中,BANK模式下查看一个函数的地址,我们可以看到一个函数的地址有3个字节,比如BANK1的函数fun1()在CODE中的地址是0x01EF23,BANK2中fun2()的地址是0x029145。如果fun1中要调度fun2,通常是要在ROOT区中建立一个fun2的入口,因为指向fun2的函数指针是16位,无法操作BANK切换寄存器。
    这个过程是fun1中调度一个fun2的入口函数,fun2的入口函数则相当于一条指令,位于ROOT区,用于将PC跳转到fun2的实际所在地址。

以上说了这么多,很多人还是觉得抽象,我们先给出IAR的例程,再给出keil的例程。我们今天的主题就是那个编译器在切换BANK的时候速度更快。

首先是IAR,硬件是CC2430,在IAR下,工程的options选项中General Options下,有个code bank的设置,这个是设置bank切换寄存器和bank起始地址,结束地址。CC2430的BANK是8000~FFFF。另外IAR工程中还有个XCL文件的配置,其中有关BANK的配置如下:


-D_CODE_START=0x0000         // Code size = 128k for CC2430-F128
-D_CODE_END=0x7FFF             // Last address for ROOT bank
//
-D_BANK1A=(10000+_CODE_END+1)// First address for BANK1

-D_NEAR_CODE_END=0x7FFF      // Last address for near code, near code segment is 32KB in banked code model.

-D_BANK1_START=_BANK1A
-D_BANK1_END=0x1FFFF
//
-D_BANK2_START=0x28000
-D_BANK2_END=0x2FFFF
//
-D_BANK3_START=0x38000
-D_BANK3_END=0x3DFFF
//
-P(CODE)BANK1=_BANK1_START-_BANK1_END
-P(CODE)BANK2=_BANK2_START-_BANK2_END
-P(CODE)BANK3=_BANK3_START-_BANK3_END
//-P(CODE)BANK3b=_BANK3b_START-_BANK3b_END

IAR的函数,是编译器自动分配BANK区。
下面我们来看看一个函数是怎么被调度的。
仿真模式下,可以看编译的代码
一个函数MSA_Delay,它的地址是0x19881,但是执行MSA_Delay的时候,是LCALL MSA_Delay::?relay指令。MSA_Delay::?relay又是什么东西?
MSA_Delay::?relay位于0x111D,显然是ROOT区,我们来看看,这里看到的代码是12 01 04 81 98 01,共6个字节,81 98 01就是函数的空间地址0x19881,12 01 04 就是一条跳转指令,跳到0x19881。
12是指令LCALL,调度 01 04地址的函数。
那我们来看看这个函数的真面目。
?BDISPATCH_FF:
000104D0 83         POP   DPH
000106D0 82         POP   DPL
000108C0 9F         PUSH?CBANK
00010AE4            CLR   A
00010B93            MOVCA,@A+DPTR
00010CC0 E0         PUSHA
00010E74 01         MOV   A,#0x01
00011093            MOVCA,@A+DPTR
000111C0 E0         PUSHA
00011374 02         MOV   A,#0x02
00011593            MOVCA,@A+DPTR
000116F5 9F         MOV   ?CBANK,A
00011822            RET
对了,还有个东西,叫SP寄存器,都忘了。
假设我们在一个BANK2的函数中,调度MSA_Delay,运行LCALL MSA_Delay::?relay的指令位于0x029813。假如此时SP值为0x23。
当取完这条指令后,由于是3个字节,PC应该变成0x9816,LCALL后,PC被压入堆栈,SP变成0x25。stack中地址0x24,0x25的值为0x16,0x98。
MSA_Delay::?relay位于0x111D,即PC变成0x111D,执行12 01 04指令,即LCALL ?BDISPATCH_FF:后,PC变成先变成0x1120压入堆栈,然后跳转0x0104,SP变成0x27,0x26的值是0x20,0x27的值是0x11。?BDISPATCH_FF:函数下stack中的值为0x16,0x98,0x20,0x11。然后POP DPH,POP DPL后,SP又变成了0x25,DPTR变成了0x1120。
再PUSH ?CBANK,当前BANK还是BANK2,所以SP就成了0x26,stack值为0x16,0x98,0x02,看看,这个是不是就是0x029816,就是函数执行完后需要回到的地方。

CLR   A,MOVCA,@A+DPTR,PUSHA,MOV   A,#0x01,MOVCA,@A+DPTR,PUSHA,我们看看这几条指令是干嘛的。我们知道0x1120开始的指令是81 98 01,而DPTR的值为0x1120,即对应值是0x81。于是,CLR   A,MOVCA,@A+DPTR,PUSHA后,SP就成了0x27,stack中就是0x16,0x98,0x02,0x81;MOV   A,#0x01,MOVCA,@A+DPTR,PUSHA,SP就成了0x28,stack中就是0x16,0x98,0x02,0x81,0x98,这时候, MOV   A,#0x02,MOVCA,@A+DPTR,MOV   ?CBANK,A,就是将BANK寄存器设置成0x01。然后再RET,PC就变成了0x9881,而BANK寄存器又是0x01,当然就是执行0x19881,即函数MSA_Delay。

对于在BANK区的函数,结束时,有个LJMP ?BRET_FF指令代替了原先RET指令的位置,跳转至POP ?CBANK,RET。我们来分析一些堆栈,进函数的时候,SP变成了0x26,stack中是0x16,0x98,0x02。POP ?CBANK后,BANK切换至BANK2,SP变成0x25,stack中是0x16,0x98,一旦RET,PC就会跳回0x29816这个地址,也就是刚才执行函数的地方。

对于CC2430来说,执行bank区的函数,要将近60个周期的跳转时间,如果加上XRAM堆栈保存,消耗的周期更多,所以32MHz的频率对CC2430来说并不算多。

明天我们来分析一下keil上是如何实现的。

rgwan 发表于 2011-11-27 21:15:55

sdcc撸过占沙发

xukai871105 发表于 2012-2-26 19:40:05

有点深奥啊,还是多谢楼主!
学习了!

uc_stm32f050 发表于 2012-2-27 09:46:07

站位

jackee 发表于 2012-2-27 18:38:59

也来占个位

uc_stm32f050 发表于 2012-3-2 08:59:35

期待后续

d-link2 发表于 2012-3-2 21:25:25

mask
页: [1]
查看完整版本: 测试:8051的CODE BANK切换,IAR和KEIL哪个更给力。(占个位置)