版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
转载来自http://blog.chinaaet.com/detail/30143.html# X& M5 i+ x4 Y1 r/ r: f( A
之前看到一篇比较好的博文,转载了。/ c' l8 h6 L3 M7 D( @; |1 J$ q- {
我等电子爱好者拿到一块开发板当然首先就是让他输出HELLO,WORLD的啦。ZYNQ作为XILINX推出的最新的ALL PROGRAMME平台自然也无法逃离此等“厄运”。 8 _6 s7 {+ d' n6 ?
让ZYNQ输出"HELLO,WORLD"非常简单,ZEDBOARD.ORG网站上已有ZedBoard_CTT_v14.1文档,大家按照文档中的步骤就能通过串行接口看到输出了。如果不太明白也可以到BAIDU上搜索ZEDBOARD,很多前辈已经把输出"HELLO,WORLD"的步骤图文并茂的一步一步给出了。图1就是main函数的截图,可以看到该函数非常的简单,首先初始化 平台,然后利用“重定向把printf”函数的输出定位到串口上。) c- [5 A! n% x- c
) t/ i; H7 j. o7 B% ~
注意到代码中把init_platform和cleanup_platform函数已经被注释掉了,难道只有
) v5 p. ^8 d0 A$ E6 U$ f i$ a; q+ M G一个printf就能输出吗?当然,查看Init_platform()和cleanup_platform()源码我们会发现其实这两个函数和串口输出并没有任何关系,这两个函数的作用就是打开系统的CACHE和关闭系统CACHE(Init_platform函数中包含有init_uart函数,但仔细观察发现只有在系统中定义了STDOUT_IS_16550宏,init_uart函数中的语句才起作用,否则该函数就是一个空函数。而在本例子中我们是并没有用到STDOUT,所以根本没有定义 STDOUT_IS_16550宏 )。0 P( G& D5 G+ t9 _2 O
看到这个估计很多人都有个疑问:串口在使用之前不是应该初始化的吗?至少要设置一下波特率啥的吧?看代码中,似乎没有对ZYNQ的串口进行初始化就使用了,难道ZYNQ太先进了不用初始化? B0 U( t* G* t
在讨论这个问题之前让我们先来看看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继续对处理器进行配置直到系统完全完成初始化。
4 @/ K7 d* l/ }) X9 i 既然在执行MAIN函数之前还有这么多步骤,那么这些代码到底写在哪里呢?打开工程目录下的SDKSDK_Exporthello_world_bsp_0ps7_cortexa9_0libsrcstandalone_v3_05_a路径下的SRC文件夹,里面有asm_vectors.S
: v" ?# S- R' o& _% ]. Uboot.S# o% ~+ B3 k! W% V! ~& P
cpu_init.S+ m1 |/ F: [" |0 X1 K4 H) n
translation_table.s" p4 V! p% O; f* [
xil-crt0.S等5个文件,它们都是.s结尾的汇编文件,根据以往的经验,这些都应该是最底层的,系统启动后首先执行的文件。我们知道ARM启动都是从0x00地址开始的,而在这个地址上放的应该是中断向量表,根据这个线索,我们首先查看asm_vectors.S文件,文件的最开头如图2所示:: i4 l8 K9 M, P
, J+ X$ `% X$ Z1 n. a5 |* w, Z6 c: @0 l9 b
这里就是中断向量表,ZYNQ启动后所运行的第一句话就是B _boot,这是一个跳转语句,直接跳转到boot.s文件中的 _boot 标号处开始执行后面的语句,由于后面的代码比较长,在此就不赘述了。boot.s中的代码主要完成MMU、CACHE的初始化以及ARM各种模式下栈基地址的配置,最后通过
# \& R( [5 { b, e0 pb _start) J: m. X' f5 p
语句跳转到_start标号中继续执行。_start标号在xil-crt0.S文件中被实现,代码如下:
# k ^; B/ S5 o6 R5 l$ P- r+ F; M
_start:
1 l; G& |2 X: gbl __cpu_init r0, #0
2 L/ w# g7 i) V. [9 i1 z1 U3 a5 Z
# b3 u) f# E/ T6 b/* clear sbss */1 j3 ]6 ~5 h3 i& ~/ y/ H) ?8 h
ldr /* calculate beginning of the SBSS *// z8 ^! c& Z) c4 P4 v0 R: X
ldr /* calculate end of the SBSS */) k' O4 I" S: w n, e/ d; B
2 L" ]+ o& I) @" y9 m2 s( {9 m0 C2 U
.Lloop_sbss:
4 l0 [( ^( ]. n6 F" W1 [+ {cmp .Lenclsbss r0, [r1], #4
: X7 h+ s$ b( S4 ab r1,.Lbss_start r2,.Lbss_end r1,r2
. Z9 H0 Q& R+ B, T- q( x0 \bge /* If no BSS, no clearing required */3 w6 [8 ]6 ?0 b- Y/ g) z' j+ j' Q
str .Lloop_bss8 z* L5 q0 D# i/ c" J( h
5 u+ k! L* Y4 _
.Lenclbss:
6 e5 R9 { [9 U8 O4 }; n; b* d! G6 a( k' {
/* set stack pointer */
9 ]: t/ q) i0 zldr /* stack address */
. S( y1 `' Q6 G) G/ y0 f$ z$ h$ Z" T% `+ T, _. y" P( N6 e1 x
/* Initialize STDOUT to 115200bps */
- \8 p: p$ |5 W e) B6 pldr Init_Uart
* e* _' n4 U/ T#ifdef PROFILING _profile_init
+ l$ c8 u0 g5 |#endif /* PROFILING */
7 p+ t' x4 k( o* g+ P* W% t" R' I, O, ~
" j& B, \0 q- M4 z7 i2 w: T; I. s/* Initialize the SMC interfaces for NOR and SRAM */
! I1 O* K* a0 y& I8 w: E7 w#ifndef USE_AMP9 f( m: P3 ]4 Y6 L! s- Z2 W! i( v; \
bl XSmc_SramInit& ~6 _" {' n6 I% S
#endif9 ]' Q: C: x. [! D, G7 U* j
* L. S1 C! A ?# w) x; ~
/* make sure argc and argv are valid */
|' _6 P( N0 }. C" l H( amov r1, #0
& L4 z x7 j1 {5 T. o& J2 z |; L% C; M
2 O8 f2 D$ N1 {( ]) B4 J8 `2 l4 W/* Let her rip */4 f/ C0 d. F# U
bl main! m! w9 H K& e3 P9 g
1 n6 F- n# Q, E; x, f
4 b% T }* a/ o( n9 ]% {1 B5 X' y Z) j
代码首先通过语句bl __cpu_init Init_Uart跳转到UART.C文件中对串口进行初始化;通过语句bl XSmc_SramInit对NOR和SRAM存储器进行初始化,最后调用bl main跳转到我们的MAIN函数中开始执行用户的程序。' h+ t1 m" e7 U* H @, Y. z9 r: e5 c
这下我们就可以解释开始所提出的问题了,实际中ZYNQ的串口并不是不需要初始化,而是在进入main函数之前就已经被初始化过了,其所用的波特率在xil-crt0.S文件的顶头被宏定义过,默认为115200BPS。如果我们要用其它的波特率的话,理论上来说在该处更改即可。为什么说理论上呢?因为在实际中更改此处是不行的,究其原因我认为是由于xil-crt0.S是系统生成,如果我们更改后不编译该文件那么自然不能更改波特率;如果编译该文件则EDK会自动从系统中重新拷一个xil-crt0.S文件到项目中, 这也就把我们更改过的文件覆盖了。所以,如果我们需要更改串口的波特率的话最简便的方法就是在main函数中再调用一次Init_Uart函数,指定其输入参数为我们所需要的波特率,如9600.
- E* R+ ]( T/ u+ E$ O
5 m3 i0 Q/ V( A! N# S' H' [总结一下,其实ZYNQ中的ARM和我们平时所用的ARM启动方式上并没有任何差别,也是从中断向量表开始然后经过多次跳转最终调用用户程序的MAIN函数。用流程图表示如下所示:
. N) j4 E9 a8 o4 T3 n* q
r8 i6 y$ P7 N0 i0 w7 ~
0 z5 E t5 s) E& R' _4 x7 [; Y
' ?* [) f7 T" f) N7 |3 v3 [+ n, P总结如下: 实际中ZYNQ的串口并不是不需要初始化,而是在进入main函数之前就已经被初始化过了,如果我们需要更改串口的波特率的话最简便的方法就是在main函数中再调用一次Init_Uart函数,指定其输入参数为我们所需要的波特率,如9600.
$ ~" S# d. K; Q% m- h! {1 |, l6 f/ x& G9 T) l* h; y, x
7 ]5 G3 n" ]2 H2 D
|
|