版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
转载来自http://blog.chinaaet.com/detail/30143.html
+ H6 F5 }# u3 q之前看到一篇比较好的博文,转载了。; O9 S1 R8 s; `, g, e9 M7 R1 M
我等电子爱好者拿到一块开发板当然首先就是让他输出HELLO,WORLD的啦。ZYNQ作为XILINX推出的最新的ALL PROGRAMME平台自然也无法逃离此等“厄运”。
7 p' G- |9 g! W2 R9 m; r! L( d 让ZYNQ输出"HELLO,WORLD"非常简单,ZEDBOARD.ORG网站上已有ZedBoard_CTT_v14.1文档,大家按照文档中的步骤就能通过串行接口看到输出了。如果不太明白也可以到BAIDU上搜索ZEDBOARD,很多前辈已经把输出"HELLO,WORLD"的步骤图文并茂的一步一步给出了。图1就是main函数的截图,可以看到该函数非常的简单,首先初始化 平台,然后利用“重定向把printf”函数的输出定位到串口上。- g& ?' k D4 {( A
3 ^+ n- z6 A+ B% n; s. I' P
注意到代码中把init_platform和cleanup_platform函数已经被注释掉了,难道只有& z9 G2 l& w" Q# Z% Z7 l
一个printf就能输出吗?当然,查看Init_platform()和cleanup_platform()源码我们会发现其实这两个函数和串口输出并没有任何关系,这两个函数的作用就是打开系统的CACHE和关闭系统CACHE(Init_platform函数中包含有init_uart函数,但仔细观察发现只有在系统中定义了STDOUT_IS_16550宏,init_uart函数中的语句才起作用,否则该函数就是一个空函数。而在本例子中我们是并没有用到STDOUT,所以根本没有定义 STDOUT_IS_16550宏 )。
8 A4 q; p& L3 X# b# O8 x+ M 看到这个估计很多人都有个疑问:串口在使用之前不是应该初始化的吗?至少要设置一下波特率啥的吧?看代码中,似乎没有对ZYNQ的串口进行初始化就使用了,难道ZYNQ太先进了不用初始化?# k3 ?- l* Y N5 z' u
在讨论这个问题之前让我们先来看看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继续对处理器进行配置直到系统完全完成初始化。
" z1 O4 R4 z2 D# d( B* c. m 既然在执行MAIN函数之前还有这么多步骤,那么这些代码到底写在哪里呢?打开工程目录下的SDKSDK_Exporthello_world_bsp_0ps7_cortexa9_0libsrcstandalone_v3_05_a路径下的SRC文件夹,里面有asm_vectors.S, }+ v3 \. X s8 ]" ?2 Q
boot.S
2 |1 G9 K0 x; T7 z( [( T i. Rcpu_init.S: H, ^$ ~+ h/ a0 A& W/ x. o \
translation_table.s
4 S- p; ~. m5 i1 A' z* c- bxil-crt0.S等5个文件,它们都是.s结尾的汇编文件,根据以往的经验,这些都应该是最底层的,系统启动后首先执行的文件。我们知道ARM启动都是从0x00地址开始的,而在这个地址上放的应该是中断向量表,根据这个线索,我们首先查看asm_vectors.S文件,文件的最开头如图2所示:
1 C& j, D2 o0 g4 H5 K. q9 o$ f( Z
2 D# @; _( q$ e' E! N$ d9 h0 Z( w
这里就是中断向量表,ZYNQ启动后所运行的第一句话就是B _boot,这是一个跳转语句,直接跳转到boot.s文件中的 _boot 标号处开始执行后面的语句,由于后面的代码比较长,在此就不赘述了。boot.s中的代码主要完成MMU、CACHE的初始化以及ARM各种模式下栈基地址的配置,最后通过
/ J, q) r" z: O" ?7 Sb _start( x/ P4 a- q& E2 E) E
语句跳转到_start标号中继续执行。_start标号在xil-crt0.S文件中被实现,代码如下:2 ]7 A0 D9 g& u: A6 ~5 v
8 _( R' [2 X% M8 Z. ?) M: R: X+ y& r
_start:
; |9 z8 U# Q* @' @3 fbl __cpu_init r0, #0
8 F; K* M/ @( `" W: v( S9 L( f$ U* Y5 O. ^2 }3 W! m
/* clear sbss */
9 @8 R, c3 c( g' pldr /* calculate beginning of the SBSS */
- f0 s+ X: y: _ldr /* calculate end of the SBSS */
% v8 o4 _! ^ Q$ K- H5 d
& A" I0 B7 u7 [* \$ A& g! Y.Lloop_sbss:
0 ~9 P2 _" |' }# k& ccmp .Lenclsbss r0, [r1], #4. S; h$ u6 f: \: _, V# ]
b r1,.Lbss_start r2,.Lbss_end r1,r27 a4 v+ u" E* L8 W2 |4 `$ J
bge /* If no BSS, no clearing required */' v) s/ g) ], b9 n
str .Lloop_bss
. E7 a3 j D& |' ` m h! v9 X
. `$ A% a& i( d% K.Lenclbss:
3 g# E9 `! Q1 R0 w! z' h8 I# R! G' `, _* @5 b0 j7 v$ T7 d
/* set stack pointer */
c7 T4 D Q0 cldr /* stack address */( W5 ?3 o/ x7 ^* r& \! [, W
9 ~) p9 `! {# ]- T% y6 r/* Initialize STDOUT to 115200bps */
' J' F* X3 I" w; f/ v6 d. rldr Init_Uart
|: K: M) O E" m#ifdef PROFILING _profile_init5 U6 l" M) y1 r% E- V( a2 C
#endif /* PROFILING */
1 V* O* h% E F! T0 b1 S7 O, M" X2 n1 G1 ~
0 e S: |, l3 t- u3 U, r/* Initialize the SMC interfaces for NOR and SRAM */
' {: x, x. o! L& Q0 P0 o. _: H#ifndef USE_AMP
& a# u3 Q8 u/ z- O: n. C( Xbl XSmc_SramInit" U+ |* P( x) I* J
#endif- f2 \' r l& c* s, F/ g
5 M% K+ _/ @3 ~: j/* make sure argc and argv are valid */
6 u; Q' w# D i, q( _" [7 Nmov r1, #0
2 O3 _% A& d& w* J$ T8 q4 H$ r: x/ }; y. Y9 _
/* Let her rip */- D3 D0 h0 ?+ O0 v
bl main4 Z ?) h7 P3 {, @
7 C& G) |1 s0 F5 N+ \' L8 p
+ v8 `3 r: |' P4 L" }( |- ~
6 }) V" E j3 o: q3 @2 N5 i代码首先通过语句bl __cpu_init Init_Uart跳转到UART.C文件中对串口进行初始化;通过语句bl XSmc_SramInit对NOR和SRAM存储器进行初始化,最后调用bl main跳转到我们的MAIN函数中开始执行用户的程序。
) c) L7 g; m2 R6 n- I/ S' |% v! X 这下我们就可以解释开始所提出的问题了,实际中ZYNQ的串口并不是不需要初始化,而是在进入main函数之前就已经被初始化过了,其所用的波特率在xil-crt0.S文件的顶头被宏定义过,默认为115200BPS。如果我们要用其它的波特率的话,理论上来说在该处更改即可。为什么说理论上呢?因为在实际中更改此处是不行的,究其原因我认为是由于xil-crt0.S是系统生成,如果我们更改后不编译该文件那么自然不能更改波特率;如果编译该文件则EDK会自动从系统中重新拷一个xil-crt0.S文件到项目中, 这也就把我们更改过的文件覆盖了。所以,如果我们需要更改串口的波特率的话最简便的方法就是在main函数中再调用一次Init_Uart函数,指定其输入参数为我们所需要的波特率,如9600.
" d( Y( i |: |) i$ ^9 C
5 ~7 n- V! g6 M H$ ~+ a. z4 M, ]- q5 R总结一下,其实ZYNQ中的ARM和我们平时所用的ARM启动方式上并没有任何差别,也是从中断向量表开始然后经过多次跳转最终调用用户程序的MAIN函数。用流程图表示如下所示:
1 f0 }6 ?" F! z; |- I
/ I: Q9 t* @; u1 I# A
4 M! n7 h& Q ~4 l; V5 r0 ^) i3 V2 ?. ~9 N
总结如下: 实际中ZYNQ的串口并不是不需要初始化,而是在进入main函数之前就已经被初始化过了,如果我们需要更改串口的波特率的话最简便的方法就是在main函数中再调用一次Init_Uart函数,指定其输入参数为我们所需要的波特率,如9600. # a5 I7 Q! W- S2 E: {
& a! x. x' w- D, m8 p7 X
4 h, K* Z! x2 {5 s1 { |
|