本页内容发表于MCUZone,但不代表MCUZone的观点。
如果你对本页的技术内容有疑问,请到 MCUZone技术论坛 发帖。
如果你认为本页的内容侵害了你的权益,请与hotislandn@hotmail.com;hdapple_2000@hotmail.com 联系,我们会在确认后移除。

AT91SAM7S64: 代码在FLASH与RAM中运行的速度对比

问题提出

从AT91SAM7S64的数据手册看:

  1. 内部Flash最大只支持30MHz时的单周期访问
  2. 内部Flash有2个32-bit预取指缓冲,可优化Thumb指令的执行,具体可以浏览数据手册EFC(Embedded Flash Controller), Page 101,※20.2.2章节
  3. 内部RAM可以在最高速度下单周期访问

具体可以参考S64的完整数据手册:

由此,我们可以预测下面几种情况时,代码的执行速度:

代码运行模式 代码大小 执行速度
ARM in Flash
ARM in RAM 最快
Thumb in Flash 较快
Thumb in RAM

测试平台

  • 虚拟示波器
  • Jlink
  • Keil MDK3.04, RV 3.0工具链(b951)
  • 电脑一台:!:
  • SAM-BA DELETEME

测试方法及测试代码

测试方法

  • 在主循环中依次将PA0,PA1,PA2脚输出低电平,并延时一段时间,最后将这三个IO置高。使得外部led闪烁
  • 延时程序使用简单的常数递减方式
  • 启动代码始终使用ARM模式编译
  • ARM Core 工作于 47 923 200 Hz
  • 内部Flash设置为0等待
  • 使用RV 3.0的工具链编译,优化级别为默认
  • 使用fromelf工具从目标文件中获得代码大小
  • 使用虚拟示波器测量IO脚输出波形的频率来表征代码执行速度
  • 通过将main.c选择为thumb模式编译来进行thumb代码的相关测试

测试代码分析

s64_scl.sct

用于armlink的scatter loader file
main.c中包含的代码(测试代码)可以选择被放置于RAM或者Flash中。

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
 
LR_IROM1 0x00100000 0x00010000  
{  ; load region
    ER_IROM1 0x00100000  
    {       ; load address = execution address
        SAMboot.o (START, +First)
        SAMinit.o (+RO)
        ; unsmask the following two line to link code in Flash
        ; 打开下面两行将使得主要代码在Flash中运行
        Main.o (+RO)
        * (+RO)
    }
 
    RAM_VECT 0x00200000   
    {  ; RW data
        ram_vect.o (RAM_VECTOR, +First)
        ; unsmask the following two line to link code in RAM
        ; 打开下面两行将使得主要代码在RAM中运行
        ;Main.o (+RO)
        ;* (+RO)
    }
  
    RW_IRAM1 +0 
    {  ; RW data
        * (+RW +ZI)
    }
}

SAMboot.s

简单的启动代码:

  1. 异常向量除IRQ均为本地循环
  2. 初始化各模式堆栈
  3. 调用C的初始化代码,初始化时钟系统
  4. 复制RAM_VECT段的代码到RAM
    1. 使用armlink为相应段定义的变量进行复制
  5. 初始化RW及ZI区域
  6. 跳转到main
;/****************************************************************************************************************/
;/********************* Startup code for AT91SAM7S ***************************************************************/
;/ hotislandn@hotmail.com                                                                                         /
;/ WWW.MCUZONE.COM 
;/
;/ Rev 0.2                                                                                              /
;/****************************************************************************************************************/
 
MODUSR    EQU    0x10
MODSYS    EQU    0x1F 
MODSVC    EQU    0x13
MODABT    EQU    0x17
MODUDF    EQU    0x1B
MODIRQ    EQU    0x12
MODFIQ    EQU    0x11
 
IRQBIT    EQU    0x80 
FIQBIT    EQU    0x40
 
RAMEND    EQU    0x00204000  ; S64 : 16KB RAM
 
VECTSIZE  EQU    0x100       ; 驻留RAM的向量区域定为256字节
 
UsrStkSz    EQU   8          ; size of  USR  stack
SysStkSz    EQU   128        ; size of  SYS  stack
SvcStkSz    EQU   8          ; size of  SVC  stack
UdfStkSz    EQU   8          ; size of  UDF  stack
AbtStkSz    EQU   8          ; size of  ABT  stack
IrqStkSz    EQU   128        ; size of  IRQ  stack
FiqStkSz    EQU   16         ; size of  FIQ  stack
       
      PRESERVE8
      AREA  START, CODE, READONLY
      CODE32
      
      ENTRY
        EXPORT RESET  
        EXPORT SYSINIT          
RESET
        LDR    PC,=SYSINIT         ; Reset
        B      UDFHANDLER          ; UNDEFINED
        B      SWIHANDLER          ; SWI
        B      PABTHANDLER         ; PREFETCH ABORT
        B      DABTHANDLER         ; DATA ABORT
        B      .                   ; RESERVED  
        LDR    PC,[PC,#-0xF20]
        B      .                   ; ADD FIQ CODE HERE
		       			
UDFHANDLER
        B      .
 
SWIHANDLER
        B      .
 
PABTHANDLER
        B      .
 
DABTHANDLER
        B      .
	
	
;/****************************************************************************************************************/
SYSINIT
                                ;设置各模式堆栈,关闭IRQ,FIQ
        MRS    R0,CPSR
        BIC    R0,R0,#0x1F
		
        MOV    R2,#RAMEND
        ORR    R1,R0,#(MODSVC :OR: IRQBIT :OR: FIQBIT)
        MSR    cpsr_cxsf,R1     ; ENTER SVC MODE
        MOV    sp,R2
        SUB    R2,R2,#SvcStkSz
		
        ORR    R1,R0,#(MODFIQ :OR: IRQBIT :OR: FIQBIT)
        MSR    CPSR_cxsf,R1     ; ENTER FIQ MODE
        MOV    sp,R2
        SUB    R2,R2,#FiqStkSz
 
        ORR    R1,R0,#(MODIRQ :OR: IRQBIT :OR: FIQBIT)
        MSR    CPSR_cxsf,R1     ; ENTER IRQ MODE
        MOV    sp,R2
        SUB    R2,R2,#IrqStkSz
 
        ORR    R1,R0,#(MODUDF :OR: IRQBIT :OR: FIQBIT)
        MSR    CPSR_cxsf,R1     ; ENTER UDF MODE
        MOV    sp,R2
        SUB    R2,R2,#UdfStkSz
 
        ORR    R1,R0,#(MODABT :OR: IRQBIT :OR: FIQBIT)
        MSR    CPSR_cxsf,R1     ; ENTER ABT MODE
        MOV    sp,R2
        SUB    R2,R2,#AbtStkSz
 
        ;ORR    R1,R0,#(MODUSR :OR: IRQBIT :OR: FIQBIT)
        ;MSR    CPSR_cxsf,R1    ; ENTER USR MODE
        ;MOV    sp,R2
        ;SUB    R2,R2,#UsrStkSz
 
        ORR    R1,R0,#(MODSYS :OR: IRQBIT :OR: FIQBIT)
        MSR    CPSR_cxsf,R1     ; ENTER SYS MODE
        MOV    SP,R2            ; 于SYS模式运行代码       
 
;/****************************************************************************************************************/        
        IMPORT S64Init
        LDR    R0,=S64Init
        MOV    LR,PC
        BX     R0
;/****************************************************************************************************************/
                                ; 复位后有16sec的时间禁止WDT
							   
;/****************************************************************************************************************/
        IMPORT |Image$$ER_IROM1$$Base|
        IMPORT |Image$$ER_IROM1$$Limit|
        IMPORT |Load$$RAM_VECT$$Base|
        IMPORT |Image$$RAM_VECT$$Base|
        IMPORT |Image$$RAM_VECT$$Limit|
        IMPORT |Image$$RAM_VECT$$Limit|
        IMPORT |Load$$RW_IRAM1$$Base|
        IMPORT |Image$$RW_IRAM1$$Base|
        IMPORT |Image$$RW_IRAM1$$Limit|
        IMPORT |Image$$RW_IRAM1$$ZI$$Base|
        IMPORT |Image$$RW_IRAM1$$ZI$$Limit|
 
;/****************************************************************************************************************/
                                ; copy vector to RAM 	
        LDR    R0,=|Load$$RAM_VECT$$Base|
        LDR    R1,=|Image$$RAM_VECT$$Base|
        LDR    R2,=|Image$$RAM_VECT$$Limit|
1       
        CMP    R1,R2
        LDRLO  R3,[R0],#4
        STRLO  R3,[R1],#4
        BLO    %B1  
				   
;/****************************************************************************************************************/
                                ; RW , ZI 初始化
 
         LDR    R0,=|Load$$RW_IRAM1$$Base|
        LDR    R1,=|Image$$RW_IRAM1$$Base|
        LDR    R2,=|Image$$RW_IRAM1$$ZI$$Base|
1       
        CMP    R1,R2
        LDRLO  R3,[R0],#4
        STRLO  R3,[R1],#4
        BLO    %B1  
 
        MOV    R3,#0
        LDR    R1,=|Image$$RW_IRAM1$$ZI$$Limit|
2
        CMP    R2,R1
        STRLO  R3,[R2],#4
        BLO    %B2    
 
;/****************************************************************************************************************/
 
;/****************************************************************************************************************/
                                ; 跳转到Main(!)
        IMPORT Main
        
        LDR    R0,=Main
        MOV    LR,PC
        BX     R0
 
loop_here
        B      .
 
;/****************************************************************************************************************/
 
__user_initial_stackheap
        LDR    r0, =|Image$$RW_IRAM1$$ZI$$Limit|
        ;MOV pc, lr
        BX     lr
 
      END

ram_vect.s

驻留在RAM中的异常向量。
:!:这个文件目前没有完全实现,因为本例中并没有使用。
:!:有些代码无意义

      PRESERVE8
      AREA  RAM_VECTOR, CODE, READONLY
      CODE32
      
        IMPORT SYSINIT            
VECTOR_IN_RAM
        LDR    PC,=SYSINIT ; Reset
        B      .           ; UNDEFINED
        B      .           ; SWI
        B      .           ; PREFETCH ABORT
        B      .           ; DATA ABORT
        B      .                   ; RESERVED  
        LDR    PC,[PC,#-0xF20]
        ;B      .                   ; ADD FIQ CODE HERE
 
        ADD    R0, R0,#1
        BX     LR
 
        END

SAMinit.c

标准的器件初始化代码:

  1. EFC初始化,包括Flash的等待周期
  2. 关闭wdt
  3. 配置主时钟,使能PLL,设置MCK为47923200Hz。:!:这个频率只是模板为了照顾USB设备时钟,而不是器件的极限频率

:!:这个文件中的代码不能设置到在RAM中运行,因为在启动代码中,对这段代码的调用出现在复制代码到RAM之前。

#include "AT91SAM7S64.h"
 
#include "SAMinit.h"
//#include "SAMIsr.h"
 
//unsigned int F_CPU=0;
 
void S64Init(void)
{
 AT91PS_PMC     pPMC = AT91C_BASE_PMC;
    //* Set Flash Waite sate
	//  Single Cycle Access at Up to 30 MHz, or 40
	//  if MCK = 47923200 I have 50 Cycle for 1 usecond ( flied MC_FMR->FMCN
	    AT91C_BASE_MC->MC_FMR = ((AT91C_MC_FMCN)&(48 <<16)) | AT91C_MC_FWS_0FWS ;
 
    //* Watchdog Disable
        AT91C_BASE_WDTC->WDTC_WDMR= AT91C_WDTC_WDDIS;
 
	//* Set MCK at 47 923 200
    // 1 Enabling the Main Oscillator:
        // SCK = 1/32768 = 30.51 uSecond
    	// Start up time = 8 * 6 / SCK = 56 * 30.51 = 1,46484375 ms
       pPMC->PMC_MOR = ( (AT91C_CKGR_OSCOUNT) & (0x06 <<8)) | AT91C_CKGR_MOSCEN ;
        // Wait the startup time
        while(!(pPMC->PMC_SR & AT91C_PMC_MOSCS));
	// 2 Checking the Main Oscillator Frequency (Optional)
	// 3 Setting PLL and divider:
		// - div by 5 Fin = 3,6864 =(18,432 / 5)
		// - Mul 25+1: Fout =	95,8464 =(3,6864 *26)
		// for 96 MHz the erroe is 0.16%
		// Field out NOT USED = 0
		// PLLCOUNT pll startup time estimate at : 0.844 ms
		// PLLCOUNT 28 = 0.000844 /(1/32768)
       pPMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x05) |
                         (AT91C_CKGR_PLLCOUNT & (28<<8)) |
                         (AT91C_CKGR_MUL & (25<<16)));
 
        // Wait the startup time
        while(!(pPMC->PMC_SR & AT91C_PMC_LOCK));
        while(!(pPMC->PMC_SR & AT91C_PMC_MCKRDY));
 	// 4. Selection of Master Clock and Processor Clock
 	// select the PLL clock divided by 2
 	    pPMC->PMC_MCKR =  AT91C_PMC_PRES_CLK_2 ;
 	    while(!(pPMC->PMC_SR & AT91C_PMC_MCKRDY));
 
 	    pPMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK  ;
 	    while(!(pPMC->PMC_SR & AT91C_PMC_MCKRDY));
 
	// 5. ReMap
	    //AT91C_BASE_MC->MC_RCR=1;
		
	// 6. IRQ
		  //SAMIsrInit();
		  
	//F_CPU=XTAL/CKGR_DIV*CKGR_MUL/PCK_DIV;	  
}

main.c

简单的循环。

#include <AT91SAM7S64.h>
#include "lib_AT91SAM7S64.h"
 
void delay(unsigned int len)
{
    while(--len);
}
 
int Main(void)
{
    AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, 0x07);
    AT91F_PIO_SetOutput(AT91C_BASE_PIOA, 0x07);
 
    AT91C_BASE_RSTC->RSTC_RMR=0xA5000001; // enable user reset
 
    while(1)
    {
        delay(1000);
        AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, 0x01);
        delay(1000);
        AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, 0x02);
        delay(1000);
        AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, 0x04);
        delay(1000);
        AT91F_PIO_SetOutput(AT91C_BASE_PIOA, 0x07);
    }
}

ROMstart.ini

用于Keil的仿真器初始化文件。
开始debug时,PC位于0×00000000(复位后Flash被映射到0),而代码从0×100000开始link,因此只能看到反汇编,执行完第一条指令
LDR PC,=SYSINIT 后,PC就会跳到SYSINIT处(0×1000??,Flash的真实地址),即可看到源代码。

FUNC void RomStart(void)
{
 PC=0x000000;
}
 
LOAD .\output\s64_scl.axf INCREMENTAL
RomStart();
//g,Main

测试结果

代码运行模式 代码大小(Byte) IO输出信号频率(Hz)
ARM in Flash 612 1152
ARM in RAM 612 1585
Thumb in Flash 556 1185
Thumb in RAM 556 1470

循环代码的反汇编

0x00200054  E3A00FFA  MOV       R0,#0x000003E8
     6:     while(--len); 
     7: } 
     8:  
     9: int Main(void) 
    10: { 
    11:     AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, 0x07); 
    12:     AT91F_PIO_SetOutput(AT91C_BASE_PIOA, 0x07); 
    13:   
    14:     AT91C_BASE_RSTC->RSTC_RMR=0xA5000001; // enable user reset 
    15:  
    16:     while(1) 
    17:     { 
    18:         delay(1000); 
    19:         AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, 0x01); 
0x00200058  E2500001  SUBS      R0,R0,#0x00000001
0x0020005C  1AFFFFFD  BNE       0x00200058
0x00200060  E3A00001  MOV       R0,#0x00000001
0x00200064  E5810034  STR       R0,[R1,#0x0034]
    20:         delay(1000); 
    21:         AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, 0x02); 
0x00200068  E3A00FFA  MOV       R0,#0x000003E8
0x0020006C  E2500001  SUBS      R0,R0,#0x00000001
0x00200070  1AFFFFFD  BNE       0x0020006C
0x00200074  E3A00002  MOV       R0,#0x00000002
0x00200078  E5810034  STR       R0,[R1,#0x0034]
    22:         delay(1000); 
    23:         AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, 0x04); 
0x0020007C  E3A00FFA  MOV       R0,#0x000003E8
     6:     while(--len); 
     7: } 
     8:  
     9: int Main(void) 
    10: { 
    11:     AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, 0x07); 
    12:     AT91F_PIO_SetOutput(AT91C_BASE_PIOA, 0x07); 
    13:   
    14:     AT91C_BASE_RSTC->RSTC_RMR=0xA5000001; // enable user reset 
    15:  
    16:     while(1) 
    17:     { 
    18:         delay(1000); 
    19:         AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, 0x01); 
    20:         delay(1000); 
    21:         AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, 0x02); 
    22:         delay(1000); 
    23:         AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, 0x04); 
0x00200080  E2500001  SUBS      R0,R0,#0x00000001
0x00200084  1AFFFFFD  BNE       0x00200080
0x00200088  E3A00004  MOV       R0,#0x00000004
0x0020008C  E5810034  STR       R0,[R1,#0x0034]
    24:         delay(1000); 
0x00200090  E3A00FFA  MOV       R0,#0x000003E8
0x00200094  E2500001  SUBS      R0,R0,#0x00000001
0x00200098  1AFFFFFD  BNE       0x00200094
0x0020009C  E5812030  STR       R2,[R1,#0x0030]
    16:     while(1) 
0x002000A0  EAFFFFEB  B         0x00200054

测试结论

从测试结果看:

  • 测试结果和预测一致
  • 由于程序中的主要耗时操作是延时,而那个程序段最终会编译为一个比较与跳转,因此,针对顺序代码优化的预取指缓冲并没有什么优势
  • 程序在RAM中执行优势大

由此在实际应用中:

  • 向量可以放在RAM中,以改善中断延时
  • 一些对时间严格的代码可以放到RAM中运行,当RAM尺寸有限时,可以合理编译为thumb代码
  • 过多的程序分支对thumb的执行有影响,无法利用Flash读取的优化

注意事项

  1. 本测试结果仅供参考:!:
  2. 测试用的代码太简单,实际中的情况更加复杂
  3. 实际应用中需要根据需要合理分布代码

参考资料

 
user\dracula\s64_rom_ram.txt · 最后更改: 2007/02/01 10:15 由 dracula
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki