一乐电子

一乐电子百科

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

QQ登录

只需一步,快速开始

快捷登录

手机号码,快捷登录

搜索
查看: 4534|回复: 1
收起左侧

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

[复制链接]
发表于 2019-1-6 20:41 | 显示全部楼层 |阅读模式
转载来自http://blog.chinaaet.com/detail/30143.html4 |4 t9 l; K" v4 N; N3 t) {8 }, U
之前看到一篇比较好的博文,转载了。7 H! z4 D- q9 D4 I
我等电子爱好者拿到一块开发板当然首先就是让他输出HELLO,WORLD的啦。ZYNQ作为XILINX推出的最新的ALL PROGRAMME平台自然也无法逃离此等“厄运”。  
3 ^( p3 B3 X4 ^3 Q      让ZYNQ输出"HELLO,WORLD"非常简单,ZEDBOARD.ORG网站上已有ZedBoard_CTT_v14.1文档,大家按照文档中的步骤就能通过串行接口看到输出了。如果不太明白也可以到BAIDU上搜索ZEDBOARD,很多前辈已经把输出"HELLO,WORLD"的步骤图文并茂的一步一步给出了。图1就是main函数的截图,可以看到该函数非常的简单,首先初始化 平台,然后利用“重定向把printf”函数的输出定位到串口上。
8 ?4 `) Q" T4 O7 F5 ^$ H 1093260465066.jpg 2 D, j' j; z3 Q- R
注意到代码中把init_platform和cleanup_platform函数已经被注释掉了,难道只有: y7 [6 H9 `$ p; a2 e3 i8 E
一个printf就能输出吗?当然,查看Init_platform()和cleanup_platform()源码我们会发现其实这两个函数和串口输出并没有任何关系,这两个函数的作用就是打开系统的CACHE和关闭系统CACHE(Init_platform函数中包含有init_uart函数,但仔细观察发现只有在系统中定义了STDOUT_IS_16550宏,init_uart函数中的语句才起作用,否则该函数就是一个空函数。而在本例子中我们是并没有用到STDOUT,所以根本没有定义 STDOUT_IS_16550宏 )。
$ d7 _' K' x7 P( t. M6 P     看到这个估计很多人都有个疑问:串口在使用之前不是应该初始化的吗?至少要设置一下波特率啥的吧?看代码中,似乎没有对ZYNQ的串口进行初始化就使用了,难道ZYNQ太先进了不用初始化?. c& U4 E: B- `8 Y/ 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继续对处理器进行配置直到系统完全完成初始化。0 R1 G# k2 Q6 g1 H* I( |! ?, ^
     既然在执行MAIN函数之前还有这么多步骤,那么这些代码到底写在哪里呢?打开工程目录下的SDKSDK_Exporthello_world_bsp_0ps7_cortexa9_0libsrcstandalone_v3_05_a路径下的SRC文件夹,里面有asm_vectors.S
# E, n0 F8 W% U2 Bboot.S- x% S# o. J" d  N" ~1 B1 M3 u
cpu_init.S" Y' [. M5 Y1 ^+ d' r% u' }' ]
translation_table.s
& k3 z6 p* u4 b' \xil-crt0.S等5个文件,它们都是.s结尾的汇编文件,根据以往的经验,这些都应该是最底层的,系统启动后首先执行的文件。我们知道ARM启动都是从0x00地址开始的,而在这个地址上放的应该是中断向量表,根据这个线索,我们首先查看asm_vectors.S文件,文件的最开头如图2所示:$ ^2 a/ d! R8 `
1093627843673.jpg   S$ v: Q8 o" t6 ^; h: Z7 \7 a

6 s! |5 X$ W+ b1 N9 I/ v  {! z这里就是中断向量表,ZYNQ启动后所运行的第一句话就是B  _boot,这是一个跳转语句,直接跳转到boot.s文件中的  _boot  标号处开始执行后面的语句,由于后面的代码比较长,在此就不赘述了。boot.s中的代码主要完成MMU、CACHE的初始化以及ARM各种模式下栈基地址的配置,最后通过4 D0 n( C4 n3 l2 O& z' P1 d& z
b _start5 B4 U9 s0 i$ M* G1 K- o# I
语句跳转到_start标号中继续执行。_start标号在xil-crt0.S文件中被实现,代码如下:; P4 [0 s$ B& w" C  @! s, o
6 [$ ]$ A  e" D( l
_start:
4 J+ ]) q: \9 F$ @! h9 ^+ P2 Qbl      __cpu_init r0, #09 ]3 Q; }( _. x5 @7 Z3 q% R" k
* K1 a' s9 L, O4 g) f* n/ l% q
/* clear sbss */
: [6 g4 C) K1 O& P  N6 M. H; Rldr  /* calculate beginning of the SBSS */
: e: ^0 \. `' D5 j; W: K- Sldr /* calculate end of the SBSS */
3 O& L( ], p. ^* O/ A/ H3 l) \2 C5 L: X5 r, r- w0 I, l8 G5 m+ M
.Lloop_sbss:- B0 @* k/ t0 b) t) Q1 T; r
cmp .Lenclsbss r0, [r1], #4
6 R: N7 X, U% r* V* \, lb r1,.Lbss_start r2,.Lbss_end r1,r2" U7 ~) J6 L. B6 a) I& Y4 D, O4 p
bge /* If no BSS, no clearing required */
3 E: V9 t' F+ [5 e! R! estr .Lloop_bss
9 w( l: ]9 V4 ]+ b5 B! D' s' j2 p% o- o' ?2 y
.Lenclbss:
& ~" S' Y2 |6 B- b: z8 g; S. s2 g+ C* \2 m' i! M2 ?6 K0 z- a! m
/* set stack pointer */" D( e1 T& s" V) Y
ldr /* stack address */$ J" ^# q1 I3 J9 J1 ]5 n4 y- R
" R& W! g! L* A- u/ C' O+ _
/* Initialize STDOUT to 115200bps */7 d: |, K- b, z1 z/ U% l5 o# a
ldr Init_Uart
# Z9 b. i$ n& J5 x4 i8 F) q#ifdef PROFILING _profile_init
8 N, D% D$ I: R3 i# T; B' W#endif /* PROFILING */8 M! l, ]5 e# K! w: Z& y9 g, v& p

; \, h! h$ O- ]+ G
+ b# f& s4 A4 B/* Initialize the SMC interfaces for NOR and SRAM */
7 m$ y3 n, a6 b) y2 R#ifndef USE_AMP
  `, l5 k3 |5 f, tbl XSmc_SramInit
4 {! U+ x! \, Z3 L; ^! [#endif
3 {& [4 {% R; l
+ \- \3 z+ H  x+ y/* make sure argc and argv are valid */3 j1 r' _6 c1 p$ d
mov r1, #0
" U* @/ W) a7 l& f, y/ n) u$ r% C2 [) m% Z4 e( L
/* Let her rip */6 C7 i5 J+ J3 D) M" B
bl main3 h' n) g. }/ a! F- Y1 E- m
1 h3 ?% U' p7 H
3 b4 l  Z. n: A6 Q; X6 W

. d4 H: Z  I" j6 z$ I9 E; v代码首先通过语句bl      __cpu_init Init_Uart跳转到UART.C文件中对串口进行初始化;通过语句bl XSmc_SramInit对NOR和SRAM存储器进行初始化,最后调用bl main跳转到我们的MAIN函数中开始执行用户的程序。) T7 n, _( W- L4 B0 ~! m! E
        这下我们就可以解释开始所提出的问题了,实际中ZYNQ的串口并不是不需要初始化,而是在进入main函数之前就已经被初始化过了,其所用的波特率在xil-crt0.S文件的顶头被宏定义过,默认为115200BPS。如果我们要用其它的波特率的话,理论上来说在该处更改即可。为什么说理论上呢?因为在实际中更改此处是不行的,究其原因我认为是由于xil-crt0.S是系统生成,如果我们更改后不编译该文件那么自然不能更改波特率;如果编译该文件则EDK会自动从系统中重新拷一个xil-crt0.S文件到项目中, 这也就把我们更改过的文件覆盖了。所以,如果我们需要更改串口的波特率的话最简便的方法就是在main函数中再调用一次Init_Uart函数,指定其输入参数为我们所需要的波特率,如9600.0 _3 q* B. P* c; p+ P3 Y

9 o* K, s9 n' K. n0 @$ u- R/ N7 ^) C总结一下,其实ZYNQ中的ARM和我们平时所用的ARM启动方式上并没有任何差别,也是从中断向量表开始然后经过多次跳转最终调用用户程序的MAIN函数。用流程图表示如下所示:
/ `0 {' M+ G" l+ G5 {) J8 k 1105578308231.jpg
* `7 i* q/ }1 u/ G, z' e0 b
0 k  p6 v$ @" c' t# ^5 z3 Z9 G
3 ]  l1 O! H1 L1 U1 N8 t! ]2 c

总结如下:

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


+ {$ E1 x# k1 b3 W2 y  ]. B

& \# Y! C9 R8 J' w5 v# N; c. f' U2 J0 l
# R0 \2 M) L5 c. z' ]& y! a
发表于 2019-1-9 18:53 | 显示全部楼层
链接中间少了

本版积分规则

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

GMT+8, 2025-4-28 08:27 , Processed in 0.048124 second(s), 27 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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