版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
转载来自http://blog.chinaaet.com/detail/30143.html
) A! B7 M& K6 T之前看到一篇比较好的博文,转载了。
) T# C* N0 i5 s* z/ n我等电子爱好者拿到一块开发板当然首先就是让他输出HELLO,WORLD的啦。ZYNQ作为XILINX推出的最新的ALL PROGRAMME平台自然也无法逃离此等“厄运”。 4 n; B1 d! ^; s9 f3 E% J5 h, n" j
让ZYNQ输出"HELLO,WORLD"非常简单,ZEDBOARD.ORG网站上已有ZedBoard_CTT_v14.1文档,大家按照文档中的步骤就能通过串行接口看到输出了。如果不太明白也可以到BAIDU上搜索ZEDBOARD,很多前辈已经把输出"HELLO,WORLD"的步骤图文并茂的一步一步给出了。图1就是main函数的截图,可以看到该函数非常的简单,首先初始化 平台,然后利用“重定向把printf”函数的输出定位到串口上。
9 C& S3 I7 O9 R3 ~
- T% X( @& A- K' x0 m- Q" X5 n
注意到代码中把init_platform和cleanup_platform函数已经被注释掉了,难道只有
5 m: j; H0 @, b R" C4 `- x' H9 @ i' \4 l一个printf就能输出吗?当然,查看Init_platform()和cleanup_platform()源码我们会发现其实这两个函数和串口输出并没有任何关系,这两个函数的作用就是打开系统的CACHE和关闭系统CACHE(Init_platform函数中包含有init_uart函数,但仔细观察发现只有在系统中定义了STDOUT_IS_16550宏,init_uart函数中的语句才起作用,否则该函数就是一个空函数。而在本例子中我们是并没有用到STDOUT,所以根本没有定义 STDOUT_IS_16550宏 )。5 f8 l. i4 N' S+ P" }$ c' s h" L
看到这个估计很多人都有个疑问:串口在使用之前不是应该初始化的吗?至少要设置一下波特率啥的吧?看代码中,似乎没有对ZYNQ的串口进行初始化就使用了,难道ZYNQ太先进了不用初始化?
! D: l+ _$ v; F1 ~* W 在讨论这个问题之前让我们先来看看ZYNQ的结构以及ARM的启动过程。ZYNQ中包含有2个ARM CORTEX-A9的核(PS)和PL逻辑单元。ZYNQ支持多种启动模块,总的来说包括安全和不安全的两种引导方式。引导主要是通过PS部分来完成。对于安全引导来说,PL部分必须启动以便使用其中的安全模块,通过这些模块可以完成256位AES和SHA授权。在系统复位后,系统将会检测模式引脚上的电平状态,以此来决定从哪个设备(NOR,NAND,QUAD-SPI或JTAG)来引导系统。当然,JTAG方式只能工作在不安全引导方式下,通常该模式是用来调试系统的。在决定从哪个设备引导系统后,其中一个ARM A9的核开始执行片外ROM中的代码并拷贝第一阶段BOOT LOADER(FSBL)到OCM中。拷贝完成后,处理器开始执行FSBL,系统开始对PS部分进行初始化并可以开始配置PL模块(当然,你也可以不配置PL模块)。通常,在FSBL中需要包含对第二阶段代码(SSBL)的载入(如UBOOT)。SSBL继续对处理器进行配置直到系统完全完成初始化。+ B0 t( _) w5 I# g/ h# ^
既然在执行MAIN函数之前还有这么多步骤,那么这些代码到底写在哪里呢?打开工程目录下的SDKSDK_Exporthello_world_bsp_0ps7_cortexa9_0libsrcstandalone_v3_05_a路径下的SRC文件夹,里面有asm_vectors.S
" F5 `, w: k( n1 M3 z, @0 F0 h# `boot.S
, _: P1 W+ C! b# `# h1 h6 w% bcpu_init.S
2 a: h$ h. P# Ctranslation_table.s, b9 Y, [/ k, c6 |5 h
xil-crt0.S等5个文件,它们都是.s结尾的汇编文件,根据以往的经验,这些都应该是最底层的,系统启动后首先执行的文件。我们知道ARM启动都是从0x00地址开始的,而在这个地址上放的应该是中断向量表,根据这个线索,我们首先查看asm_vectors.S文件,文件的最开头如图2所示:3 }! [+ R6 O" _: `; b
5 X0 j4 A9 ~# I) u0 k- {( |. a. w8 L* W. S8 x+ O
这里就是中断向量表,ZYNQ启动后所运行的第一句话就是B _boot,这是一个跳转语句,直接跳转到boot.s文件中的 _boot 标号处开始执行后面的语句,由于后面的代码比较长,在此就不赘述了。boot.s中的代码主要完成MMU、CACHE的初始化以及ARM各种模式下栈基地址的配置,最后通过
3 e* h3 Z9 H0 b8 y5 ab _start
( C+ S- g3 C9 Z: q( ~# e1 V 语句跳转到_start标号中继续执行。_start标号在xil-crt0.S文件中被实现,代码如下:- d5 ?" R( Y9 e3 `
0 L5 J# E5 v7 Y: w1 Z0 k& Z_start:
Q6 I) T7 N3 Ubl __cpu_init r0, #05 u9 v; D8 P/ B2 i. Y. S
, B0 O1 `/ N+ E' u# R+ x/ A, D2 [
/* clear sbss */
! F& K2 ^' [ r% r5 ^1 o' e4 Pldr /* calculate beginning of the SBSS */4 k% B1 K0 G0 |' H9 ]+ u9 f
ldr /* calculate end of the SBSS */2 ~. t# ]# M. R9 G& L6 a/ S
& d; `+ t1 ~! M% S$ D* o
.Lloop_sbss:
- m/ H1 u. k' b Q, n6 z0 _cmp .Lenclsbss r0, [r1], #45 T1 k: y4 k% e
b r1,.Lbss_start r2,.Lbss_end r1,r25 x) q o1 p5 E1 E1 A4 L: }
bge /* If no BSS, no clearing required */
+ D2 l/ K2 H$ v9 [8 \# p$ V8 N' Sstr .Lloop_bss
+ a7 V: x/ i, u0 W$ ^3 [: V) w L5 a8 R
, S9 n& @1 {: o( w8 H.Lenclbss:3 f8 B( y- {: `9 _, E
( [; I. ?1 @ d9 K4 z7 ]
/* set stack pointer */# A5 V0 A. b) l. ~) ~ c
ldr /* stack address */! a; f5 W6 M* h% F/ H A
+ ~: q4 U* W+ }5 R/* Initialize STDOUT to 115200bps */
4 ^. X. X& C9 \) W% ^# |$ E" nldr Init_Uart9 d; S J$ S2 k6 G
#ifdef PROFILING _profile_init
* \; x# i9 L5 m7 {; d#endif /* PROFILING */; n- e# k" t$ l& U
3 [# y* _# h4 g5 U5 Q# V4 X) i( N, c7 u+ t; L& i: @6 }2 d2 d, I
/* Initialize the SMC interfaces for NOR and SRAM */
3 L6 l, a8 u1 L8 F6 z#ifndef USE_AMP
1 T' i _0 n: m. ^bl XSmc_SramInit& K; b1 K7 f# Z( }7 j, w
#endif" I) H' W2 d' I4 Y+ {1 R2 w
( p( T" M+ Y# w0 W( r i' m' t/* make sure argc and argv are valid */5 j/ K5 {* c) N0 e" x
mov r1, #02 A8 O: L% G* A B
4 ^" c0 p0 h' T* _0 m% p
/* Let her rip */
" p. P- H4 |8 e4 r; y/ @/ M$ N* Ebl main# U5 p$ d" k& R* r7 o
* Q% o" ~+ n- Y2 q! T" `
* W) L/ N1 s2 a; A! A( M. f; l* _% x0 N
代码首先通过语句bl __cpu_init Init_Uart跳转到UART.C文件中对串口进行初始化;通过语句bl XSmc_SramInit对NOR和SRAM存储器进行初始化,最后调用bl main跳转到我们的MAIN函数中开始执行用户的程序。+ G! u4 X0 r; L+ R5 v
这下我们就可以解释开始所提出的问题了,实际中ZYNQ的串口并不是不需要初始化,而是在进入main函数之前就已经被初始化过了,其所用的波特率在xil-crt0.S文件的顶头被宏定义过,默认为115200BPS。如果我们要用其它的波特率的话,理论上来说在该处更改即可。为什么说理论上呢?因为在实际中更改此处是不行的,究其原因我认为是由于xil-crt0.S是系统生成,如果我们更改后不编译该文件那么自然不能更改波特率;如果编译该文件则EDK会自动从系统中重新拷一个xil-crt0.S文件到项目中, 这也就把我们更改过的文件覆盖了。所以,如果我们需要更改串口的波特率的话最简便的方法就是在main函数中再调用一次Init_Uart函数,指定其输入参数为我们所需要的波特率,如9600.( m3 S7 @/ a0 [& J4 j, _5 e
) ]5 \0 s4 G+ H, j1 Z3 m
总结一下,其实ZYNQ中的ARM和我们平时所用的ARM启动方式上并没有任何差别,也是从中断向量表开始然后经过多次跳转最终调用用户程序的MAIN函数。用流程图表示如下所示:1 i! t8 Z- ]- s' j1 ?
1 ^" v, i- E5 c# C& @
7 h% z* f! y$ O
* H) p% L# g/ ^+ Z e* N
总结如下: 实际中ZYNQ的串口并不是不需要初始化,而是在进入main函数之前就已经被初始化过了,如果我们需要更改串口的波特率的话最简便的方法就是在main函数中再调用一次Init_Uart函数,指定其输入参数为我们所需要的波特率,如9600.
& Y% R# T$ l/ `# s# a# {8 G. n) U
[ u5 W: k" E% P( b$ h/ P* u; m+ M% X
|
|