一乐电子

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

QQ登录

只需一步,快速开始

微信扫码登录

搜索
查看: 5243|回复: 1

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

[复制链接]
发表于 2019-1-6 20:41 | 显示全部楼层 |阅读模式
转载来自http://blog.chinaaet.com/detail/30143.html9 f  {* D" G$ m7 i" [5 [! a' f
之前看到一篇比较好的博文,转载了。
' X( d% i( C8 _6 c6 K! V我等电子爱好者拿到一块开发板当然首先就是让他输出HELLO,WORLD的啦。ZYNQ作为XILINX推出的最新的ALL PROGRAMME平台自然也无法逃离此等“厄运”。  . s$ {' \+ F- ~, d4 Z8 c( e! |
      让ZYNQ输出"HELLO,WORLD"非常简单,ZEDBOARD.ORG网站上已有ZedBoard_CTT_v14.1文档,大家按照文档中的步骤就能通过串行接口看到输出了。如果不太明白也可以到BAIDU上搜索ZEDBOARD,很多前辈已经把输出"HELLO,WORLD"的步骤图文并茂的一步一步给出了。图1就是main函数的截图,可以看到该函数非常的简单,首先初始化 平台,然后利用“重定向把printf”函数的输出定位到串口上。
2 G6 v+ ?0 ]& u, w7 ^3 e 1093260465066.jpg
$ \5 G/ R% U* G8 f! ~4 j1 e注意到代码中把init_platform和cleanup_platform函数已经被注释掉了,难道只有: Q5 z0 j, k8 ~6 V
一个printf就能输出吗?当然,查看Init_platform()和cleanup_platform()源码我们会发现其实这两个函数和串口输出并没有任何关系,这两个函数的作用就是打开系统的CACHE和关闭系统CACHE(Init_platform函数中包含有init_uart函数,但仔细观察发现只有在系统中定义了STDOUT_IS_16550宏,init_uart函数中的语句才起作用,否则该函数就是一个空函数。而在本例子中我们是并没有用到STDOUT,所以根本没有定义 STDOUT_IS_16550宏 )。* u8 y8 r9 m: r- B
     看到这个估计很多人都有个疑问:串口在使用之前不是应该初始化的吗?至少要设置一下波特率啥的吧?看代码中,似乎没有对ZYNQ的串口进行初始化就使用了,难道ZYNQ太先进了不用初始化?
7 E; M) R" ^4 c; y+ Z) J/ f8 |     在讨论这个问题之前让我们先来看看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继续对处理器进行配置直到系统完全完成初始化。
7 {8 u- H/ T3 v3 H     既然在执行MAIN函数之前还有这么多步骤,那么这些代码到底写在哪里呢?打开工程目录下的SDKSDK_Exporthello_world_bsp_0ps7_cortexa9_0libsrcstandalone_v3_05_a路径下的SRC文件夹,里面有asm_vectors.S
. T  r4 }1 C* ]2 b6 pboot.S( L5 I7 j* K+ u0 p; f
cpu_init.S
1 L1 i: Q! j6 n0 }9 wtranslation_table.s; X. S$ U; A/ q( n- r
xil-crt0.S等5个文件,它们都是.s结尾的汇编文件,根据以往的经验,这些都应该是最底层的,系统启动后首先执行的文件。我们知道ARM启动都是从0x00地址开始的,而在这个地址上放的应该是中断向量表,根据这个线索,我们首先查看asm_vectors.S文件,文件的最开头如图2所示:9 A) ~# e( @1 g
1093627843673.jpg
  C, `' c# r7 X" _+ L3 e: Q9 Q" a3 T; x0 K" M" g
这里就是中断向量表,ZYNQ启动后所运行的第一句话就是B  _boot,这是一个跳转语句,直接跳转到boot.s文件中的  _boot  标号处开始执行后面的语句,由于后面的代码比较长,在此就不赘述了。boot.s中的代码主要完成MMU、CACHE的初始化以及ARM各种模式下栈基地址的配置,最后通过: U% y) n5 L' l  [
b _start
6 H& i, z( G4 I4 p. O 语句跳转到_start标号中继续执行。_start标号在xil-crt0.S文件中被实现,代码如下:
1 L8 C  N  i, l+ c' }  l
; \/ A! i+ e6 m' R_start:
( B$ p3 ^1 I9 O, ybl      __cpu_init r0, #03 ]: \+ j0 i. I5 x3 S4 |
1 ?1 c8 Z6 Z" {9 x' r
/* clear sbss */
4 U  u/ E: [& A3 t2 y6 X; pldr  /* calculate beginning of the SBSS */% K; {7 E7 J  g  F+ k. j
ldr /* calculate end of the SBSS */! h3 A+ q! @" Z) [9 C- ~/ G8 ]9 V

. O% s+ M: V: |4 C. y1 s5 D.Lloop_sbss:
- R% D; O/ b" E0 d! V$ T- scmp .Lenclsbss r0, [r1], #4- _4 `  J, |. ~, Q% I5 R0 q
b r1,.Lbss_start r2,.Lbss_end r1,r2
$ T. n$ K/ L4 \" ?bge /* If no BSS, no clearing required */  L' p0 U' t$ L! T$ G- J
str .Lloop_bss! X( s3 \# N7 u, l
0 \- w# V9 A+ ?- X- c9 f  a
.Lenclbss:8 A8 F: k8 V6 q

. e; `) l3 T7 }/* set stack pointer */
2 s- q! e- f. y; U' Y  Cldr /* stack address */
" c$ M0 Z+ s/ e6 m0 j
( C8 [+ F1 I; F0 D/* Initialize STDOUT to 115200bps */
6 w5 L- `: o' `ldr Init_Uart" m$ ^7 z/ Y3 o# S5 n4 U
#ifdef PROFILING _profile_init) o0 e: s! H7 N0 H
#endif /* PROFILING */9 m1 |+ N( T3 j: L" c
* C7 @! g/ v' d. M0 r

& {4 y, p3 a+ F: b/* Initialize the SMC interfaces for NOR and SRAM */  S( P# K* l& I# ^# {! y7 a1 d
#ifndef USE_AMP* t* F6 C, B4 o( l
bl XSmc_SramInit9 @3 }+ W, W/ M( \  z4 y5 L
#endif; D! A4 w) V4 l' M1 V
. N9 e4 C; Z! k/ B/ L! |0 h
/* make sure argc and argv are valid */* Z8 N+ C, x2 A
mov r1, #0; Y  z6 q4 ^- F' U! Z0 C7 d
7 u3 j- f3 K) E: N. v
/* Let her rip */' r, R$ {6 ~1 j( e! A, }
bl main
& `3 @/ V# F4 Y7 V5 ]1 |* Y% Q
' @* U- N1 x( h
+ d" s! |! Q2 \8 h7 d

# ]! w4 x% G7 U5 T9 {5 O8 N8 F; i代码首先通过语句bl      __cpu_init Init_Uart跳转到UART.C文件中对串口进行初始化;通过语句bl XSmc_SramInit对NOR和SRAM存储器进行初始化,最后调用bl main跳转到我们的MAIN函数中开始执行用户的程序。" F8 N) J- z! m, D. U1 e7 @% [  C) e
        这下我们就可以解释开始所提出的问题了,实际中ZYNQ的串口并不是不需要初始化,而是在进入main函数之前就已经被初始化过了,其所用的波特率在xil-crt0.S文件的顶头被宏定义过,默认为115200BPS。如果我们要用其它的波特率的话,理论上来说在该处更改即可。为什么说理论上呢?因为在实际中更改此处是不行的,究其原因我认为是由于xil-crt0.S是系统生成,如果我们更改后不编译该文件那么自然不能更改波特率;如果编译该文件则EDK会自动从系统中重新拷一个xil-crt0.S文件到项目中, 这也就把我们更改过的文件覆盖了。所以,如果我们需要更改串口的波特率的话最简便的方法就是在main函数中再调用一次Init_Uart函数,指定其输入参数为我们所需要的波特率,如9600.2 M2 {, t4 T- U5 D3 H: t2 F2 V
1 O$ a' \* z( @+ m" q( k
总结一下,其实ZYNQ中的ARM和我们平时所用的ARM启动方式上并没有任何差别,也是从中断向量表开始然后经过多次跳转最终调用用户程序的MAIN函数。用流程图表示如下所示:, L) N  D6 y. j9 C1 J1 A$ l9 J
1105578308231.jpg . A  I  ^. u3 C. T( C: g' c

4 v# y6 D4 P5 {  J  Q% V' a9 g' w, K

总结如下:

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


) @3 }" n, w% L& o
: s' z  F1 }2 X; d8 u$ W# e) v& V

' t- f) N0 [2 `$ v: q4 i
发表于 2019-1-9 18:53 | 显示全部楼层
链接中间少了
回复

使用道具 举报

本版积分规则

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

GMT+8, 2025-8-20 09:06 , Processed in 0.038526 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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