嵌入式教程中流水灯以及程序教程中的”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位置都是可以正确执行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
; 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入口。

1
2
3
4
5
6
7
8
       SECTION .intvec:CODE:NOROOT(2)
       PUBLIC   __iar_program_start

       ARM
__iar_program_start
       EXTERN  main
       BLX main
       END

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
#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