嵌入式教程中流水灯以及程序教程中的”Hello world”都有其特殊的意义,意味着入门。笔者此处也不例外,分别以汇编、c语言在IAR下点流水灯作为S5PV210入门程序。点流水灯之前必须对芯片有基本的认识,包括其指令集、流水线等内核架构,基本的启动流程,基本的编译器开发特性等,只有这样点亮的流水灯才算实现其意义。

1. 指令集

S5PV210是Cortex-A8内核,该内核为ARMv7-A架构,支持两个最主要的指令集:32位ARM指令集以及16/32位Thumb-2指令集。ARM指令集每条指令采用32位长度,具有最高的效率,但也需要更多的代码空间,ARM指令集是向后兼容的,即ARMv7-A的处理器几乎可以直接执行ARMv4架构的ARM指令集代码(如ARM7的应用代码)。Thumb-2是Thumb的扩展指令集,在ARMv6架构前,Thumb作为16位指令集,是作为ARM指令集的子集,它是为了减小代码量而提出的,并不完整,只支持通用功能,无法脱离ARM指令集。在ARMv7架构中,Thumb-2作为必备指令集,它支持16/32混合指令模式,几乎实现了所有的ARM指令集功能,并且效率接近ARM指令集,代码密度接近Thumb指令集。Thumb-2指令集的引入意味着程序存储器可以更小,在一些Cache应用中,相同容量的指令Cache可以缓存更多的指令,提高了指令Cache命中率,间接提升了内核性能。例如,对于Cortex-M内核,更是只支持Thumb-2指令集,因此一般无特殊情况,对于ARMv7内核,也可以直接采用Thumb-2指令集。

2. 流水线

S5PV210内核Cortex-A8配置了先进的超标量体系结构管线,能够同时执行多条指令,提供超过2.0 DMIPS/MHZ,集成了32k/32k的指令/数据一级缓存以及512k的二级缓存,从而达到最快的读取速度和最大的吞吐量,使用了先进的分支预测技术,并且具有专用的NEON整形和浮点型管线进行媒体和信号处理。

Cortex-A8流水线架构基于双对称、顺序发射的13级流水线,硬件上具有I/D Cache、分支预测结构,因此指令在流水线的流入流出过程变得不明确,但仍可以通过统计分析其大概的过程。

S5PV210在上电启动后,最先执行内部BL0代码,代码只初始化并开启I Cache,其它D Cache、L2 Cache、分支预测均是关闭的,设置了CPU主频为400M,最后是跳转到用户的BL1代码。我们在BL1中的流水灯代码根据以上信息,可以设计一个较精确的软件延时函数,每次访问I Cache均会命中,每次访问D Cache均从主存读取,需要相应周期的等待延时,每次跳转均会分支预测失败,清流水线需额外13个CPU时钟。在一个实用的系统中,I/D Cache、分支预测等硬件功能必须开启,不然CPU性能大大打折扣。

3. 汇编实现

汇编代码中有两点需要注意:

1) ARMv7-A架构推荐基本用Thumb-2指令集,有相当好的效率以及较好的代码密度,BL0跳转到BL1时是ARM状态,因此BL1流水灯第一条指令为ARM指令,切换到Thumb状态后开始执行Thumb-2指令。

2) 此处避开链接器功能,不使用链接文件,编写的流水灯代码应该是位置无关的,即代码加载进任意RAM位置都是可以正确执行的。

; IO port for controlingLEDs

GPB_BASE        EQU    0xE0200040  ; GPB Base Address

GPH0_BASE   EQU      0xE0200C00       ; GPH0 Base Address

GPCON_OFS     EQU    0x00        ; Control RegisterOffset

GPDAT_OFS     EQU    0x04        ; Data Register Offset



GPB0_LED2          EQU              0     ; GPB0->LED2

GPB2_LED3          EQU              2     ; GPB2->LED3

GPB1_LED4          EQU              1     ; GPB1->LED4

GPB3_LED5          EQU              3     ; GPB3->LED5

GPH07_LED6              EQU              7     ;GPH07->LED6

GPH05_LED7              EQU              5     ;GPH05->LED7



       SECTION RESET:CODE:NOROOT(2)

       PUBLIC   __iar_program_start



       ARM

__iar_program_start

       BL   Reset



       THUMB

Reset

       BL   Gpio_Init



Loop

       LDR R0, =GPB_BASE

       LDR R1, [R0,#GPDAT_OFS]

       ORR R1, R1,#(1<<GPB0_LED2)

       STR R1, [R0, #GPDAT_OFS]; LED2 on

       LDR R7, =1000 ; delay 1s

       BL   Delay_ms

       BIC  R1, R1,#(1<<GPB0_LED2)

       STR R1, [R0, #GPDAT_OFS]



       LDR R1, [R0, #GPDAT_OFS]

       ORR R1, R1,#(1<<GPB2_LED3)

       STR R1, [R0, #GPDAT_OFS]; LED3 on

       LDR R7, =1000 ; delay 1s

       BL   Delay_ms

       BIC  R1, R1,#(1<<GPB2_LED3)

       STR R1, [R0, #GPDAT_OFS]     



       LDR  R1, [R0,#GPDAT_OFS]

       ORR R1, R1,#(1<<GPB1_LED4)

       STR R1, [R0, #GPDAT_OFS] ;LED4 on

       LDR R7, =1000 ; delay 1s

       BL   Delay_ms

       BIC  R1, R1,#(1<<GPB1_LED4)

       STR R1, [R0, #GPDAT_OFS]     



       LDR  R1, [R0,#GPDAT_OFS]

       ORR R1, R1,#(1<<GPB3_LED5)

       STR R1, [R0, #GPDAT_OFS]; LED5 on

       LDR R7, =1000 ; delay 1s

       BL   Delay_ms

       BIC  R1, R1, #(1<<GPB3_LED5)

       STR R1, [R0, #GPDAT_OFS]     



       LDR R0, =GPH0_BASE

       LDR R1, [R0, #GPDAT_OFS]

       ORR R1, R1,#(1<<GPH07_LED6)

       STR R1, [R0, #GPDAT_OFS]; LED6 on

       LDR R7, =1000 ; delay 1s

       BL   Delay_ms

       BIC  R1, R1,#(1<<GPH07_LED6)

       STR R1, [R0, #GPDAT_OFS]



       LDR R1, [R0, #GPDAT_OFS]

       ORR  R1, R1,#(1<<GPH05_LED7)

       STR R1, [R0, #GPDAT_OFS]; LED7 on

       LDR R7, =1000 ; delay 1s

       BL   Delay_ms

       BIC  R1, R1,#(1<<GPH05_LED7)

       STR R1, [R0, #GPDAT_OFS]



       B     Loop



Gpio_Init

       LDR R0, =GPB_BASE

       LDR R1, [R0, #GPCON_OFS]

       BIC  R1, R1,#(0xf<<(GPB0_LED2<<2))

       ORR  R1, R1,#(0x1<<(GPB0_LED2<<2)) ; GPB0 output led2

       STR R1, [R0, #GPCON_OFS]

       LDR R1, [R0, #GPDAT_OFS]

       BIC  R1, R1,#(1<<GPB0_LED2)

       STR R1, [R0, #GPDAT_OFS]; LED2 off



       LDR R1, [R0, #GPCON_OFS]

       BIC  R1, R1,#(0xf<<(GPB2_LED3<<2))

       ORR  R1, R1,#(0x1<<(GPB2_LED3<<2)) ; GPB2 output led3

       STR R1, [R0, #GPCON_OFS]    

       LDR R1, [R0, #GPDAT_OFS]

       BIC  R1, R1,#(1<<GPB2_LED3)

       STR R1, [R0, #GPDAT_OFS]; LED3 off  



       LDR R1, [R0, #GPCON_OFS]

       BIC  R1, R1,#(0xf<<(GPB1_LED4<<2))

       ORR  R1, R1,#(0x1<<(GPB1_LED4<<2)) ; GPB1 output led4

       STR R1, [R0, #GPCON_OFS]    

       LDR R1, [R0, #GPDAT_OFS]

       BIC  R1, R1,#(1<<GPB1_LED4)

       STR R1, [R0, #GPDAT_OFS]; LED4 off  



       LDR R1, [R0, #GPCON_OFS]

       BIC  R1, R1,#(0xf<<(GPB3_LED5<<2))

       ORR  R1, R1,#(0x1<<(GPB3_LED5<<2)) ; GPB3 output led5

       STR R1, [R0, #GPCON_OFS]           

       LDR R1, [R0, #GPDAT_OFS]

       BIC  R1, R1,#(1<<GPB3_LED5)

       STR R1, [R0, #GPDAT_OFS]; LED5 off  



       LDR R0, =GPH0_BASE

       LDR R1, [R0, #GPCON_OFS]

       BIC  R1, R1,#(0xf<<(GPH07_LED6<<2))

       ORR  R1, R1, #(0x1<<(GPH07_LED6<<2)); GPH07 output led6

       STR R1, [R0, #GPCON_OFS]           

       LDR R1, [R0, #GPDAT_OFS]

       BIC  R1, R1,#(1<<GPH07_LED6)

       STR R1, [R0, #GPDAT_OFS]; LED6 off         



       LDR R1, [R0, #GPCON_OFS]

       BIC  R1, R1,#(0xf<<(GPH05_LED7<<2))

       ORR  R1, R1,#(0x1<<(GPH05_LED7<<2)) ; GPH05 output led7

       STR R1, [R0, #GPCON_OFS]           

       LDR R1, [R0, #GPDAT_OFS]

       BIC  R1, R1,#(1<<GPH05_LED7)

       STR R1, [R0, #GPDAT_OFS]; LED7 off  



       BX   LR



; ARM CLOCK 400M

Delay_ms     

       LDR  R6, =13333 ; 延时1ms

Delay2                 

       SUBS R6, R6, #1 ; 单发射cycle 1

       ; 跳转清流水线,以下指令均只用作填充流水线

       MOV R0, R0 ; 双发射 cycle 1

       MOV R0, R0 ; 单发射 cycle 2

       MOV R0, R0 ; 单发射 cycle 3

       MOV R0, R0 ; 单发射 cycle 4

       MOV R0, R0 ; 单发射 cycle 5

       MOV R0, R0 ; 单发射 cycle 6

       MOV R0, R0 ; 单发射 cycle 7

       MOV R0, R0 ; 单发射 cycle 8

       MOV R0, R0 ; 单发射 cycle 9

       MOV R0, R0 ; 单发射 cycle 10

       MOV R0, R0 ; 单发射 cycle 11

       MOV R0, R0 ; 单发射 cycle 12

       MOV R0, R0 ; 单发射 cycle 13

       MOV R0, R0 ; 单发射 cycle 14

       MOV R0, R0 ; 单发射 cycle 15

       MOV R0, R0 ; 单发射 cycle 16

       MOV R6, R6 ; 单发射 cycle 17

       BNE  Delay2 ; 跳转会清流水线,13个ARMCLOCK,cycle 30



       SUBS R7, R7, #1

       BNE Delay_ms

       BX   LR



       END

4. C实现

C代码中需要注意两点:

1) 需要汇编指令跳转到c函数,链接器默认链接.intvec段做为代码的开头,需一条跳转汇编指令链接到代码起启位置,用来跳转到c入口。

       SECTION .intvec:CODE:NOROOT(2)
       PUBLIC   __iar_program_start

       ARM
__iar_program_start
       EXTERN  main
       BLX main
       END

2)c文件中不要尝试使用c库以及使用全局变量、静态变量等,因为此处避开链接器功能,不使用链接文件,编写的流水灯代码c运行环境都是BL0初始化的,是位置无关的,只有栈是有效的。

#include"stdint.h"



// IO port for controlingLEDs

#define GPB_BASE     0xE0200040  // GPB Base Address

#define GPH0_BASE   0xE0200C00 // GPH0 Base Address

#define GPCON_OFS  0x00        // Control Register Offset

#define GPDAT_OFS  0x04        // Data Register Offset



#define GPBCON_REG       (*(volatile uint32_t *)(GPB_BASE+GPCON_OFS))

#define GPBDAT_REG       (*(volatile uint32_t *)(GPB_BASE+GPDAT_OFS))

#define GPH0CON_REG  (*(volatile uint32_t *)(GPH0_BASE+GPCON_OFS))

#define GPH0DAT_REG     (*(volatile uint32_t *)(GPH0_BASE+GPDAT_OFS))



#define GPB0_LED2   0     // GPB0->LED2

#define GPB2_LED3   2     // GPB2->LED3

#define GPB1_LED4   1     // GPB1->LED4

#define GPB3_LED5   3     // GPB3->LED5

#define GPH07_LED6       7     // GPH07->LED6

#define GPH05_LED7       5     // GPH05->LED7



void Delay_ms(uint32_tCount)

{
       //延时1ms,共延时nCountms

       // Arm clock为400M,循环体每次30个Armclock

       int32_t temp1 = 13333;

       int32_t temp2 = 0;

       asm volatile (

              "Delay_ms_0:\n"

              "mov  %0,%2\n" 

              "Delay_ms_1:\n"

              "subs  %0,%0, #1\n" // 单发射 cycle 1

              // 跳转清流水线,以下指令均只用作填充流水线

              "mov %1, %1\n" // 双发射 cycle 1

              "mov %1, %1\n" // 单发射 cycle 2

              "mov %1, %1\n" // 单发射 cycle 3

              "mov %1, %1\n" // 单发射 cycle 4       

              "mov %1, %1\n" // 单发射 cycle 5       

              "mov %1, %1\n" // 单发射 cycle 6

              "mov %1, %1\n" // 单发射 cycle 7

              "mov %1, %1\n" // 单发射 cycle 8

              "mov %1, %1\n" // 单发射 cycle 9

              "mov %1, %1\n" // 单发射 cycle 10

              "mov %1, %1\n" // 单发射 cycle 11

              "mov %1, %1\n" // 单发射 cycle 12

              "mov %1, %1\n" // 单发射 cycle 13

              "mov %1, %1\n" // 单发射 cycle 14

              "mov %1, %1\n" // 单发射 cycle 15

              "mov %1, %1\n" // 单发射 cycle 16

              "mov %0, %0\n" // 单发射 cycle 17

              "bne.w Delay_ms_1\n" // 跳转会清流水线,13级流水线,cycle30

              "subs  %1,%1, #1\n"   // 调用者确保nCount不为0

              "bne.w Delay_ms_0\n"

              : "+r"(temp2), "+r"(Count):"r"(temp1): "cc"

       );    

}



void LED2(uint8_t On)

{
       if (On) {
              GPBDAT_REG |= (1<<GPB0_LED2);

       } else {
              GPBDAT_REG &= ~(1<<GPB0_LED2);

       }

}



void LED3(uint8_t On)

{
       if (On) {
              GPBDAT_REG |= (1<<GPB2_LED3);

       } else {
              GPBDAT_REG &= ~(1<<GPB2_LED3);

       }

}



void LED4(uint8_t On)

{
       if (On) {
              GPBDAT_REG |= (1<<GPB1_LED4);

       } else {
              GPBDAT_REG &= ~(1<<GPB1_LED4);

       }

}



void LED5(uint8_t On)

{
       if (On) {
              GPBDAT_REG |= (1<<GPB3_LED5);

       } else {
              GPBDAT_REG &= ~(1<<GPB3_LED5);

       }

}



void LED6(uint8_t On)

{
       if (On) {
              GPH0DAT_REG |= (1<<GPH07_LED6);

       } else {
              GPH0DAT_REG &= ~(1<<GPH07_LED6);

       }

}



void LED7(uint8_t On)

{
       if (On) {
              GPH0DAT_REG |= (1<<GPH05_LED7);

       } else {
              GPH0DAT_REG &= ~(1<<GPH05_LED7);

       }

}



void Gpio_Init(void)

{
      //GPB0 output led2, off

       GPBCON_REG = (GPBCON_REG &(~(0xf<<(GPB0_LED2<<2)))) | (0x1<<(GPB0_LED2<<2)); 

       GPBDAT_REG &= ~(1<<GPB0_LED2);

      //GPB2 output led3, off

       GPBCON_REG = (GPBCON_REG &(~(0xf<<(GPB2_LED3<<2)))) | (0x1<<(GPB2_LED3<<2)); 

       GPBDAT_REG &= ~(1<<GPB2_LED3);

      //GPB1 output led4, off

       GPBCON_REG = (GPBCON_REG &(~(0xf<<(GPB1_LED4<<2)))) | (0x1<<(GPB1_LED4<<2)); 

       GPBDAT_REG &= ~(1<<GPB1_LED4);

      //GPB3 output led5, off

       GPBCON_REG = (GPBCON_REG &(~(0xf<<(GPB3_LED5<<2)))) | (0x1<<(GPB3_LED5<<2)); 

       GPBDAT_REG &= ~(1<<GPB3_LED5);

      //GPH07 output led6, off

       GPH0CON_REG = (GPH0CON_REG &(~(0xf<<(GPH07_LED6<<2)))) |(0x1<<(GPH07_LED6<<2)); 

       GPH0DAT_REG &= ~(1<<GPH07_LED6);

      //GPH05 output led7, off

       GPH0CON_REG = (GPH0CON_REG &(~(0xf<<(GPH05_LED7<<2)))) |(0x1<<(GPH05_LED7<<2)); 

       GPH0DAT_REG &= ~(1<<GPH05_LED7);

}



void main(void)

{
       Gpio_Init();

       while (1) {    

              LED2(1);

              Delay_ms(1000);

              LED2(0);

              LED3(1);

              Delay_ms(1000);

              LED3(0);

              LED4(1);

              Delay_ms(1000);

              LED4(0);

              LED5(1);

              Delay_ms(1000);

              LED5(0);

              LED6(1);

              Delay_ms(1000);

              LED6(0);

              LED7(1);

              Delay_ms(1000);

              LED7(0);

       }

}

5. 流水灯运行

编译器直接编译生成的二进制代码是不满足相应的启动格式的,需要通过SdBoot.exe生成相应的sd/mmc烧录代码,再通过SdBoot.exe把带”_1”后缀的sd/mmc烧录文件烧录进sd/mmc卡,设置目标板从sd/mmc卡启动,即可运行sd/mmc卡里面的流水灯程序。

6. 附录

IAR下S5PV210汇编流水灯工程例程以及C流水灯工程例程,SdBoot相关工具: http://pan.baidu.com/s/1c0dcSU0