一乐电子

 找回密码
 请使用微信账号登录和注册会员

QQ登录

只需一步,快速开始

微信扫码登录

手机号码,快捷登录

手机号码,快捷登录

搜索
查看: 5505|回复: 1

转 ZYNQ "HELLO,WORLD!"背后的故事

[复制链接]
发表于 2019-1-6 20:41 | 显示全部楼层 |阅读模式
转载来自http://blog.chinaaet.com/detail/30143.html
( S- D+ }# p) N) J! F( o: E之前看到一篇比较好的博文,转载了。( L- S- L4 x. t8 O
我等电子爱好者拿到一块开发板当然首先就是让他输出HELLO,WORLD的啦。ZYNQ作为XILINX推出的最新的ALL PROGRAMME平台自然也无法逃离此等“厄运”。  & R+ ?: e  r+ {# p/ v
      让ZYNQ输出"HELLO,WORLD"非常简单,ZEDBOARD.ORG网站上已有ZedBoard_CTT_v14.1文档,大家按照文档中的步骤就能通过串行接口看到输出了。如果不太明白也可以到BAIDU上搜索ZEDBOARD,很多前辈已经把输出"HELLO,WORLD"的步骤图文并茂的一步一步给出了。图1就是main函数的截图,可以看到该函数非常的简单,首先初始化 平台,然后利用“重定向把printf”函数的输出定位到串口上。9 V, ~* J5 x# b+ _
1093260465066.jpg ' C1 U+ [, S3 G' n! u+ x
注意到代码中把init_platform和cleanup_platform函数已经被注释掉了,难道只有9 k8 J" r+ V" b4 R3 w) H) D; B
一个printf就能输出吗?当然,查看Init_platform()和cleanup_platform()源码我们会发现其实这两个函数和串口输出并没有任何关系,这两个函数的作用就是打开系统的CACHE和关闭系统CACHE(Init_platform函数中包含有init_uart函数,但仔细观察发现只有在系统中定义了STDOUT_IS_16550宏,init_uart函数中的语句才起作用,否则该函数就是一个空函数。而在本例子中我们是并没有用到STDOUT,所以根本没有定义 STDOUT_IS_16550宏 )。( q3 `' P; |1 Y0 F
     看到这个估计很多人都有个疑问:串口在使用之前不是应该初始化的吗?至少要设置一下波特率啥的吧?看代码中,似乎没有对ZYNQ的串口进行初始化就使用了,难道ZYNQ太先进了不用初始化?3 U; g7 Y% a+ u2 C* U+ T6 L
     在讨论这个问题之前让我们先来看看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继续对处理器进行配置直到系统完全完成初始化。
* Z3 @0 @$ O* ^( t5 H+ V( D, ]3 G7 S     既然在执行MAIN函数之前还有这么多步骤,那么这些代码到底写在哪里呢?打开工程目录下的SDKSDK_Exporthello_world_bsp_0ps7_cortexa9_0libsrcstandalone_v3_05_a路径下的SRC文件夹,里面有asm_vectors.S
0 {. P3 i0 C2 ~" Y  d$ I$ Pboot.S
6 ~( s) [$ k% ?+ s# Z3 X7 B/ Wcpu_init.S5 o9 v+ ?# y4 b- }7 V  Y. M# M3 F$ l
translation_table.s
1 H  D3 K5 z- D2 `6 F2 qxil-crt0.S等5个文件,它们都是.s结尾的汇编文件,根据以往的经验,这些都应该是最底层的,系统启动后首先执行的文件。我们知道ARM启动都是从0x00地址开始的,而在这个地址上放的应该是中断向量表,根据这个线索,我们首先查看asm_vectors.S文件,文件的最开头如图2所示:
. N  ]9 k; O& f5 c; {& A' ?! I( }7 G3 G 1093627843673.jpg
- {$ l6 \! f, ]$ m8 d) o* Y: w% ~& n0 J0 C/ k2 @
这里就是中断向量表,ZYNQ启动后所运行的第一句话就是B  _boot,这是一个跳转语句,直接跳转到boot.s文件中的  _boot  标号处开始执行后面的语句,由于后面的代码比较长,在此就不赘述了。boot.s中的代码主要完成MMU、CACHE的初始化以及ARM各种模式下栈基地址的配置,最后通过  C6 p8 K% Q+ G! V3 k$ I
b _start
7 j; j  u+ M/ D) x3 ?# \ 语句跳转到_start标号中继续执行。_start标号在xil-crt0.S文件中被实现,代码如下:; R. c# f3 q- z0 e; e

& t: G$ O+ z6 j9 s" v. {_start:6 K  g  V! c/ B+ @
bl      __cpu_init r0, #0) Q" {* B0 d& {+ l/ B

- T1 W* U5 q# k3 `0 C" m8 Q) _/ A/* clear sbss */
8 w+ V# ?: V' d. p" X+ A. `ldr  /* calculate beginning of the SBSS */
: k% ~. w3 M9 L  Sldr /* calculate end of the SBSS */
1 U% E& ~* F" T' D' W3 P: ]9 U8 E8 d8 e; T7 Y
.Lloop_sbss:1 [, i  K* E7 H3 d+ Q. t  \
cmp .Lenclsbss r0, [r1], #4
% n( w/ i2 g7 Ib r1,.Lbss_start r2,.Lbss_end r1,r2
3 t$ m( ~' L" O' w2 W! `bge /* If no BSS, no clearing required */2 {5 _8 w% N' e3 |3 y  d
str .Lloop_bss
- J$ P+ S# v4 y, w$ Q
- k  b: S3 X% W# H8 a" ?.Lenclbss:5 x. V3 V, d2 m! @$ g" k& q
2 a* G' h1 o7 U. a' m5 l6 u
/* set stack pointer */, }- X* \' O6 v3 P* P$ V
ldr /* stack address */2 h- |+ y5 c; |

& ?! A) j+ [4 s% L7 F1 Z/* Initialize STDOUT to 115200bps */$ M' j, [4 v* J( |0 c( V# d
ldr Init_Uart5 N( F! Y, ~$ s3 U
#ifdef PROFILING _profile_init
/ F: T) G" w! z7 Y3 e; x5 S+ r! j7 }#endif /* PROFILING */
4 [1 u7 I  o' D3 `0 n
% n) N, c1 v' u6 r1 ]) L/ c  B4 X
3 d6 b* _$ P+ l. E+ I$ @  N# O/* Initialize the SMC interfaces for NOR and SRAM */* r9 n6 q8 e) R! c& H
#ifndef USE_AMP
) |& g5 S- c" e9 g) i! {6 J' xbl XSmc_SramInit$ }) Z- p3 V% K3 I- I3 f& `
#endif; h% }% \. \: [% a1 U, e
" G2 ~3 V1 _. O
/* make sure argc and argv are valid *// R6 [5 |; m+ P
mov r1, #0
. o% f2 V9 {' G# R+ A& E
0 t$ p, L& S: L# m/* Let her rip */- a8 `) l. s/ r, q. O
bl main
8 l# q$ o( c: x& v0 Y

  K" i; H$ x% I. Y* g  ]- e
7 v0 F+ }  ?! Y7 h, W, h
. }* f  Z  I0 ^) Y% w6 h* R代码首先通过语句bl      __cpu_init Init_Uart跳转到UART.C文件中对串口进行初始化;通过语句bl XSmc_SramInit对NOR和SRAM存储器进行初始化,最后调用bl main跳转到我们的MAIN函数中开始执行用户的程序。
9 }: _) W/ [  ~5 M& F        这下我们就可以解释开始所提出的问题了,实际中ZYNQ的串口并不是不需要初始化,而是在进入main函数之前就已经被初始化过了,其所用的波特率在xil-crt0.S文件的顶头被宏定义过,默认为115200BPS。如果我们要用其它的波特率的话,理论上来说在该处更改即可。为什么说理论上呢?因为在实际中更改此处是不行的,究其原因我认为是由于xil-crt0.S是系统生成,如果我们更改后不编译该文件那么自然不能更改波特率;如果编译该文件则EDK会自动从系统中重新拷一个xil-crt0.S文件到项目中, 这也就把我们更改过的文件覆盖了。所以,如果我们需要更改串口的波特率的话最简便的方法就是在main函数中再调用一次Init_Uart函数,指定其输入参数为我们所需要的波特率,如9600.# q; m0 \4 n: {' _: L! C4 u/ E
6 N' ~+ r' G. J9 O4 M
总结一下,其实ZYNQ中的ARM和我们平时所用的ARM启动方式上并没有任何差别,也是从中断向量表开始然后经过多次跳转最终调用用户程序的MAIN函数。用流程图表示如下所示:
  y0 Q  v/ X5 }( C/ y 1105578308231.jpg 2 U8 G$ z" U% k  i  v, y$ m+ U
$ z$ U* S8 q8 J8 ?3 Z2 L) [
/ M- X) J$ [  O' R

总结如下:

实际中ZYNQ的串口并不是不需要初始化,而是在进入main函数之前就已经被初始化过了,如果我们需要更改串口的波特率的话最简便的方法就是在main函数中再调用一次Init_Uart函数,指定其输入参数为我们所需要的波特率,如9600.

% I4 m) Y- D( w; Y+ i: `
7 w. C9 h0 s! |4 Q6 m8 B! e
1 m2 B7 E2 Q7 K  E
发表于 2019-1-9 18:53 | 显示全部楼层
链接中间少了
回复

使用道具 举报

本版积分规则

QQ|一淘宝店|手机版|商店|一乐电子 ( 粤ICP备09076165号 ) 公安备案粤公网安备 44522102000183号

GMT+8, 2025-10-27 06:44 , Processed in 0.032760 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表