版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
转载来自http://blog.chinaaet.com/detail/30143.html3 J& O/ @/ C# y0 g9 |: {
之前看到一篇比较好的博文,转载了。
0 x7 s3 l' J V0 x& H( \8 E我等电子爱好者拿到一块开发板当然首先就是让他输出HELLO,WORLD的啦。ZYNQ作为XILINX推出的最新的ALL PROGRAMME平台自然也无法逃离此等“厄运”。 3 s: {6 d1 v' G
让ZYNQ输出"HELLO,WORLD"非常简单,ZEDBOARD.ORG网站上已有ZedBoard_CTT_v14.1文档,大家按照文档中的步骤就能通过串行接口看到输出了。如果不太明白也可以到BAIDU上搜索ZEDBOARD,很多前辈已经把输出"HELLO,WORLD"的步骤图文并茂的一步一步给出了。图1就是main函数的截图,可以看到该函数非常的简单,首先初始化 平台,然后利用“重定向把printf”函数的输出定位到串口上。
* n/ h, G" S0 A; L9 c
$ K* C# C2 r5 O8 G
注意到代码中把init_platform和cleanup_platform函数已经被注释掉了,难道只有
1 t' u( |" H7 D0 g2 Z一个printf就能输出吗?当然,查看Init_platform()和cleanup_platform()源码我们会发现其实这两个函数和串口输出并没有任何关系,这两个函数的作用就是打开系统的CACHE和关闭系统CACHE(Init_platform函数中包含有init_uart函数,但仔细观察发现只有在系统中定义了STDOUT_IS_16550宏,init_uart函数中的语句才起作用,否则该函数就是一个空函数。而在本例子中我们是并没有用到STDOUT,所以根本没有定义 STDOUT_IS_16550宏 )。1 u- o8 h' i9 z! E4 f/ t: J, T
看到这个估计很多人都有个疑问:串口在使用之前不是应该初始化的吗?至少要设置一下波特率啥的吧?看代码中,似乎没有对ZYNQ的串口进行初始化就使用了,难道ZYNQ太先进了不用初始化?* c$ t0 B. R, @ Y: Z. y
在讨论这个问题之前让我们先来看看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继续对处理器进行配置直到系统完全完成初始化。
# r1 g" i% [# J; J 既然在执行MAIN函数之前还有这么多步骤,那么这些代码到底写在哪里呢?打开工程目录下的SDKSDK_Exporthello_world_bsp_0ps7_cortexa9_0libsrcstandalone_v3_05_a路径下的SRC文件夹,里面有asm_vectors.S( J1 b5 P& Z8 z2 Q6 N4 f+ t8 b
boot.S7 i9 S* Z# Q! P# _4 D" y8 G
cpu_init.S( ?" [9 P( n& m) O5 Q
translation_table.s
& P1 ~9 Q R; q0 bxil-crt0.S等5个文件,它们都是.s结尾的汇编文件,根据以往的经验,这些都应该是最底层的,系统启动后首先执行的文件。我们知道ARM启动都是从0x00地址开始的,而在这个地址上放的应该是中断向量表,根据这个线索,我们首先查看asm_vectors.S文件,文件的最开头如图2所示:+ w7 [! I: |( b) C! _3 ]9 s
9 C- e- w% U( V, @. u9 R( `
- c+ a0 X8 q- p) {
这里就是中断向量表,ZYNQ启动后所运行的第一句话就是B _boot,这是一个跳转语句,直接跳转到boot.s文件中的 _boot 标号处开始执行后面的语句,由于后面的代码比较长,在此就不赘述了。boot.s中的代码主要完成MMU、CACHE的初始化以及ARM各种模式下栈基地址的配置,最后通过8 Z x$ }% B! ^+ j. D9 A& m0 w
b _start
! C+ c) T& J' A# A9 o, ` 语句跳转到_start标号中继续执行。_start标号在xil-crt0.S文件中被实现,代码如下:
" \, C2 u. r# z$ @" U1 m4 Q( y4 f. _5 b6 X
_start:
! F) L! w! { j- K. Obl __cpu_init r0, #0/ i7 X/ p- ]9 d
: S- C s2 D8 v" o2 _/* clear sbss */
2 m" d" r2 i1 {. z6 Yldr /* calculate beginning of the SBSS */
@, q- K& g& _- \6 fldr /* calculate end of the SBSS */
" E. }- [6 K" Z% i# B- J7 v# u l: i+ }5 Q5 f# O1 o& o" Q- ?' u
.Lloop_sbss:
; E! X% Z- } B1 ~cmp .Lenclsbss r0, [r1], #4/ F; U d: |1 b1 T; P
b r1,.Lbss_start r2,.Lbss_end r1,r20 k' o3 y: u9 e6 M( k& }
bge /* If no BSS, no clearing required */1 x$ a _: B0 B" p6 [) I
str .Lloop_bss4 h6 e" ^4 ~% B. D X
h6 j G" f7 B9 `.Lenclbss:
. ~1 r1 d+ k3 \" y3 W5 }) n; E4 Y R9 m- O0 E2 A! }7 G) I
/* set stack pointer */
+ u" D ?' Y& z/ X3 Lldr /* stack address */
9 m) Z0 Z0 i! Q: J# {, Z/ ^0 C6 l9 I8 `
/* Initialize STDOUT to 115200bps */
7 ?$ b: q( c6 m% j D! o9 Zldr Init_Uart
: g4 G& `. y$ G1 V7 p#ifdef PROFILING _profile_init3 e" I, g! {/ C
#endif /* PROFILING */
/ d" t, Z0 y# s
" @0 Z) n) [' t$ Q7 B) I7 t" b
3 V9 i" f6 S. |6 d: [/* Initialize the SMC interfaces for NOR and SRAM */
+ f3 v5 ~/ {9 x8 {- d9 u' D#ifndef USE_AMP" }; t: f' v& q# g
bl XSmc_SramInit
3 t6 ]% S/ y0 M# b# M#endif
' c( f! `$ \3 H ]6 e" s) }/ x1 `1 i# [, k% |8 Q! q
/* make sure argc and argv are valid */ o/ _7 e) z- h4 k0 r
mov r1, #0( y4 i+ q, E; n3 q
" r" Q2 Q6 L: Q9 m; K# c1 B
/* Let her rip */
( B6 z( _* k, o; X) gbl main/ Z) ^2 E+ h; j8 Q& ^. e4 K% ?
/ s2 v. v+ U" f- g- @. T6 {' V/ J' \* ?, Q) h: H
: R5 l4 s0 r) {代码首先通过语句bl __cpu_init Init_Uart跳转到UART.C文件中对串口进行初始化;通过语句bl XSmc_SramInit对NOR和SRAM存储器进行初始化,最后调用bl main跳转到我们的MAIN函数中开始执行用户的程序。
- f4 T0 h+ P1 l 这下我们就可以解释开始所提出的问题了,实际中ZYNQ的串口并不是不需要初始化,而是在进入main函数之前就已经被初始化过了,其所用的波特率在xil-crt0.S文件的顶头被宏定义过,默认为115200BPS。如果我们要用其它的波特率的话,理论上来说在该处更改即可。为什么说理论上呢?因为在实际中更改此处是不行的,究其原因我认为是由于xil-crt0.S是系统生成,如果我们更改后不编译该文件那么自然不能更改波特率;如果编译该文件则EDK会自动从系统中重新拷一个xil-crt0.S文件到项目中, 这也就把我们更改过的文件覆盖了。所以,如果我们需要更改串口的波特率的话最简便的方法就是在main函数中再调用一次Init_Uart函数,指定其输入参数为我们所需要的波特率,如9600.* Q, X; a7 Y. R$ p2 s: x3 J
# L7 m3 A" h5 D4 z4 ~* L
总结一下,其实ZYNQ中的ARM和我们平时所用的ARM启动方式上并没有任何差别,也是从中断向量表开始然后经过多次跳转最终调用用户程序的MAIN函数。用流程图表示如下所示:3 S' y) e9 j8 ~7 ]; S E$ M
_( i6 {. g; B
! A( p- W( `$ O
! P- l+ x- S8 V6 _2 a$ g1 P* E总结如下: 实际中ZYNQ的串口并不是不需要初始化,而是在进入main函数之前就已经被初始化过了,如果我们需要更改串口的波特率的话最简便的方法就是在main函数中再调用一次Init_Uart函数,指定其输入参数为我们所需要的波特率,如9600.
0 m+ u7 ]! D; c+ p( D; M N) X! h. ^1 l. b# A8 f$ M Z
4 [6 @/ q' l0 P+ s e+ q
|
|