找回密码
 注册
关于网站域名变更的通知
查看: 597|回复: 2
打印 上一主题 下一主题

linux学习之路_or1200第一个裸机程序(上)

[复制链接]
  • TA的每日心情

    2019-11-20 15:22
  • 签到天数: 2 天

    [LV.1]初来乍到

    跳转到指定楼层
    1#
    发表于 2021-4-22 17:38 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

    EDA365欢迎您登录!

    您需要 登录 才可以下载或查看,没有帐号?注册

    x
    * ?" p! x0 b4 e& z7 P4 W
    经过前面千辛万苦、爬山涉水、纠结了好久才弄好的环境,现在我们终于可以把FPGA当成个单片机使了,稍微比单片机猛一点,但是绝对比单片机贵一个数量级,FPGA现在能完成由CPU做的事,我们就把它当成一个大价钱买回来的单片机,不过相比于S3C24XX的片子来说,那么贵的一个FPGA板卡跑40M的一个CPU绝对的奢侈,不过仅限于理解和学习嵌入式的过程,管他的。
    ; m- O% K$ J/ t' r: C8 |( {; Y        但是如果是这样呢,把or1200资源优化好,例化两个或者多个CPU,是不是可以做成SMP架构?又或者说现有的产品上有FPGA芯片,但是里面的资源还足以容得下一个or1200,是不是可以做个协处理器在里面?我想应该可以做的事情还很多的······吧。
    # k6 s* `% K# F: h
    - B# `* e0 @$ R! ^        按照惯例,硬件代码第一个无非是点亮LED灯;软件代码无非是写个hello world,有同感木有。
    , s1 v* o% l" S5 b, _. x, ?5 [+ V7 [/ Q& B. y; E% f9 c/ l
            但是想想,我们现在的SOC只有什么?CPU、RAM、Debug、UART、BUS,我们没有把GPIO外设加进去,点个灯貌似行不通了,但是能操作的毕竟外设蛮多的,但是又想看到有现象输出的,那我们只好选择去写个UART的代码咯。
    0 e$ l6 N& n, Y7 h1 S. _: v# o" y
            我们在后面完善or1200_basic_soc时把其他外设添加进去后,上了linux系统后,在编写GPIO字符驱动时再去把灯点亮吧,现在我们就当FPGA是个单片机去编写所谓的“裸机”程序。8 l% b) e1 I6 \% m7 Y, [; F1 n

    * S6 u2 {; A2 ^        我们现在手头上有两本书对吧,《开源软核处理器OPENRISC的SOPC设计》、《CPU源代码分析与芯片设计及Linux移植》,那我们就先看看接下来要写的UART16550外设的说明文档先咯,uart16550_latest.tar.gz源码包里的doc文件夹有该ipcore的说明文档,其实这就是相当于看芯片手册一样了,是不是很熟悉,那就不继续吐槽了,或者《开源软核处理器OPENRISC的SOPC设计》里面第7章也有UART16550的中文翻译版本,感觉这本书就是opencores上面大堆文档的翻译大杂烩,算了,自己写不出的书还是不吐槽人家的了。# P& m- C: x9 }( |  `, @: N* ?

      P! k/ E' z7 D) O7 o        大概浏览一下UART16550的结构后我们就来咯,敲代码。1 z# F, o7 x0 j
    " C" }7 F  N$ I9 h. V2 M% X1 a- N
            在敲代码之前先搞清楚一些流程,当内嵌在FPGA的or1200上电复位时,它在做些什么,在硬件环境搭建时,我们把CPU的复位地址设置为0x100,这个地址可以在or1200_defines.v中任意指定的,什么是0x100呢,我们在CPU体系手册《openrisc-arch-1.0-rev0.pdf》中16章操作系统接口有所描述,0x100是复位后取指令的第一个地址,虽然是这样说,但是复位地址可以是任意指定的,以后我们固化代码在Flash时,会将这个复位地址设置为Flash的起始地址,目前我们先指定在0x100,所以,CPU复位时就会从0x100取出第一条执行的指令,OK。, l! o3 M' r2 M% K
      I0 A0 N; a) w* \
    % G0 d) F" Q4 S* {
    - f2 Y/ h- F+ c' u! ~
            对于其他向量地址来说,是CPU设计时规定好的异常向量地址,好比0x100是复位异常向量,我们在写C51时熟悉的对比一下看还有什么?0x500是否熟悉?只是时钟中断异常向量。还有什么咧?0x800,外中断异常向量,所以,当我们在CPU将计数器中断enable后,当时钟计满中断时,就会跳转到0x500执行指令。对于外中断而言,前面我们在修改or1200_defines.v把PIC的中断个数设置为20个,所以,当这20个外中断enable后,中断源触发后,CPU就跳转到0x800地址来执行指令啦,至于其他的异常向量都是类似的,可能以前我们接触得比较少,但在对于上了操作系统后,这每个异常向量都会有对应的处理过程。现在对于or1200_basic_soc来说,只使用了复位、时钟、中断异常向量。* w) @2 z4 j% b3 `
    $ F) z, R8 w. n* S9 |: {/ }
            所以我们敲代码的第一步就是编写这些异常向量的处理程序,即异常向量表,因为刚上电时处理器的C环境还没有建立好,所以这些代码都是要用汇编去写的,我们现在至少要用到reset复位异常向量,硬着头皮码吧。
    & s) f* R" k, f* l3 y: S
    4 s2 Z+ z# f+ a0 a        我理解的reset复位异常向量是通常说的启动代码,在用Keil MDK创建S3C2440的工程时也有对应的启动代码文件s3c2440.S,看起来蛮多的,1k多行,但是往下看,800多行的宏定义,那启动代码就200多行了。再来吐槽下or1200在linux系统下的关于启动代码的文件entry.S、head.S,这两个文件加起来2k5多行,那我们手动去敲那么多代码么,开玩笑吧。
    ' b. _( h: s- R  v; C9 d' H( m& \' h! [+ N, V( b2 ^8 ?) i+ [
            好在我们现在用到的外设不多,就一个UART,而且我们现在还不需要用到时钟计数,还没需要到中断,所以复位向量用到的代码大概只有50行,50的汇编对于我这种级别        来说简直小菜一碟,好,码字。
    4 H2 m4 a' T* h0 M. c8 ?. g7 O6 E. g2 J$ d2 p# C
            至于这个简单的UART代码我都打包好在附件中了,那我们简单分析一下这50多行reset向量需要的代码。
    ; ?5 V$ A" Z# e- W7 n* }" C
    / d1 n- E# |( d5 I  j        把工程都扔进sourceinsight,方便查看嘛,打开reset.S,对于前面的宏定义先跳过,用到的时候再说吧。
    - ~# O! S! w8 }3 f" S
    2 j" x2 ^# \# P% M0 l
    & H8 T5 k( n; U+ _6 |/ P! t        先看0x100的异常向量,3行代码,是不是很有成就感。. B" |2 ^+ i" R5 G& }6 r

    & Y2 I' P: J; A, p
    , V4 B- _" Q1 a# k( S# I! ]4 w
    5 ^# z* }4 j$ {0 i0 j* x1 Z
    0 l6 V7 {; t4 k& l        LOAD_SYMBOL_2_GPR看名字就知道意思,作用是将标号的地址存入CPU的寄存器。  ^9 `9 P1 i/ `
    $ a; {1 J/ D; M  y
    ! ~$ ~! O/ x. ?4 Q

    9 @7 C) P% q+ @) B( O- y0 ~6 e5 m" V6 b  }3 j4 X
            18行l.movhi是or1200处理器的逻辑指令,作用是把立即数零拓展左移16bit6 X- k# T+ P- y' _/ P4 T
    6 S: ]' P# o$ l
    l.movhi rD,K% ~$ n# V" t, A, ^0 V( o
    : Z& q: p2 ]3 L% c3 C
    rD[31:0] < -extz(Immediate) < < 167 q1 o  ^" `8 q! p- C

    * i1 a9 L% G& |" w' z1 A; `3 u0 ?        所以18行将symbol的高16位存入gpr中。8 k1 D5 u7 I7 s9 w8 K, o

    6 H2 Y+ Q9 C+ ]* C8 V. ?. a        l.or是or1200处理器的逻辑指令按位或,作用不用讲了吧,
    3 Z! L5 p# H/ V* s: f$ l$ l: V& C4 x! W8 l9 w, [, b1 E
    l.or rD,rA,rB" X: _6 m8 I& T4 g

    ' p/ e/ p* \- NrD[31:0] < -rA[31:0] OR rB[31:0]- e; R5 O; ~+ s* S6 U$ u1 N

    . G/ ]) h% w# z# i. a. N( J        所以19行将symbol的低16位存入gpr中。6 w6 s% n0 d8 v. _1 ^+ m. d  g7 I
    ; t6 k; }! N2 I' G, |% d
            继续reset向量,l.jr r3,即跳转到r3寄存器的地址上,所以reset向量前3行就是跳转到_reset标号地址上。
    # O- ~9 D& v6 O7 q2 O9 C( r( Z9 Y/ H% {/ P% x
            貌似55行还有个l.nop指令,只是or1200的空操作指令。跳转指令之后紧接着的指令称为延时槽,在or1200体系中,在跳转指令之后的紧接着的一条指令是规定、必须、肯定会执行的,所以在现在or1200的gcc编译器中,在跳转指令之后都会自动补上一个nop指令,貌似在新版本的编译器会在跳转指令后执行别的有效指令代替nop操作,提高效率。# c9 ?# K5 C8 n! j6 Y  b( l* h, `
    - c5 c' j0 {# X! Z
            好,continue······
    ; V' ~# X9 M  w8 G+ v) W4 F
    ' Q' N' H& x2 S; y- B
    9 o5 P% r" ~8 \* ~% M& ~        Sourceinsight中一跳,转到_reset标号上
    % H# B: I- q, m; G* I; s3 x* G& f* d+ O( s- E" D

    & C- {, K5 a% }' X$ S2 Y4 ^) r% l$ K, \$ k/ {) g# m

    1 U) Q, y  g& ?8 M9 K" I        l.movhi,l.ori很熟悉啊,目的将r0寄存器清零。1 [9 D; F+ W; C; m5 |

    . Q6 m6 u2 {1 J2 q7 S& ~/ L        l.addi作用是将r0和SPR_SR_SM相加后存入r3中,至于SPR_SR_SM又是什么?跟踪下就可以发现在spr-defs.h有其定义。4 c& D, r  B! E1 y# p: Q) \

    ; b, ]# M, E4 p0 W1 S ; f3 ~5 Y6 |9 A

    3 l! I. Y/ }1 D1 ~$ V* y2 D5 K8 [4 g0 e1 w
            这是supervisor模式,这个linux的内核态和用户态概念很像,or1200存在两种模式,supervisor和user,区别是操作CPU内部寄存器组时权限不同。( _- J- C. L# z. B
    0 D! T7 K. v$ k$ c/ k+ M5 z
    继续,l.mtspr就是操作or1200内特殊寄存器组的专用指令,专用的,将r0和r3的寄存器的值相加后送入SPR_SR寄存器内
    / ^6 A, b- u& a* e. F/ U5 M; z  ]% e  W) m0 `+ Y$ p9 l- L

    6 E" D' S4 ^/ z+ r' P3 N6 ?3 {  p8 {8 F
    8 L' S: o! F4 E, o5 d! f$ t
            在《openrisc-arch-1.0-rev0.pdf》中可以看到,SPR_SR的first bit就是SM位
    + w  S' g+ e0 a; ~& D1 S1 _% O) G$ v; H4 W/ u2 F
            为1时就是Supervisor Mode
    ( r; Q, o! S5 _8 M7 V2 r- o. ?: X
            继续往下看,有个CLEAR_GPR的宏,目的是将gpr设置成r0的值
    4 R- h& n. Z" L
    " ?3 F4 Z7 L1 @, ^6 D8 t: v
    5 x% j$ x. j" d/ w$ n6 o
    4 q. J* ?7 }) p7 ~' N
    3 n6 u* [- ~0 _2 ~3 D0 g        在or1200中,r0被设置成常数0,做与这个宏的作用就是清零; m$ O: m1 V$ ^8 v# \. k3 `

    0 l2 i& l8 p2 @: Y5 \( d' w$ K5 l好,继续7 A4 |2 {( a! Q

    1 k" y) Z% |) b9 Y' }% Y0 e/ u 2 R* N& a1 N  W; d

    : p$ D; r+ q% H- c
    . }6 p/ [+ W- i* C* A% V% {$ W" A: b) J, h7 b( ^" X# ^% s, Y! @- R
            SPR_TTMR是关于or1200内Timer的寄存器,将r0的值赋入SPR_TTMR中,至于SPR_TTMR的解释如下表。
    2 P2 k* k0 _! S4 w
    . n8 q' B" `2 Q5 @  l- ^ 9 X) y9 b6 e* d& z6 ~+ F: ]
    0 k* T! r; ~3 a2 }% w' Z

    & W; _# T  L, b9 d- o  {0 T        目的就是把执行计数单元(简称定时器吧)的中断和使能都禁用了。, Q7 Q5 N5 p' }# Q# B5 J
    # H9 i) r% A. `$ A
            好,继续······貌似完了,200行吧_stack的值赋给r1,_stack是CPU堆栈的值,_stack这个值在lds文件内定义,这个文件稍后再解释。
    . m1 J6 o- ^: b5 i4 b$ T* m, j9 }( Y
            第203,204行是不是很熟悉,跳转到main函数执行,堆栈建立好之后下面main函数就可以用C来码了。8 M: r8 b: ^5 h+ b6 a+ f7 p" f
    2 g& e  Q% R4 P
            Reset向量就需要这些代码了,至于reset.S剩下的代码是关于中断处理的,比如在
    " y/ t  o" }5 v  d7 I8 W' a1 {% G
    ! l' C2 G2 l& w6 I  |* p) D# H

    + X' A7 B3 w( {* @0 A
    ( h3 ]# p' r. a+ n" R        在定时器中断和外中断会先执行EXCEPTION_HANDLER宏,目的是执行中断处理之前的现场保护,跳转至中断服务函数,执行完中断服务函数之后的现场恢复,中断的概念不用多解释了,应该是很熟悉的了。  m2 n/ o/ h9 @3 d

    ; w7 a, F, R$ |  x6 [( R( L% c: I( W        关于or1200中断的流程可以参考《CPU源代码分析与芯片设计及Linux移植》,里面讲得蛮详细的,虽然大部分也是翻译手册的,至于附件的代码关于reset.S剩余部分不是特别难,就不解释了,不解释咯······
    ( S6 m# f3 W* ]1 f3 ~3 G& s. s+ {, o# y2 d/ s: d: V" Z
            突然发现,reset的异常向量我们就敲完了,在reset向量里会跳转至main函数,那就开始了,main函数熟悉了吧,
    ' S. G$ }! e: m" t! E5 k; n
    0 c' `; x: |9 `4 U3 ~5 [        附件中的uart.c和uart.h关于UART16550的串口驱动和头文件,程序不难,对比着uart16550_latest.tar.gz的说明文档、《开源软核处理器OPENRISC的SOPC设计》和源码的注释5分钟就可以看懂了,不详细说了,因为大家都有C语言功底嘛。
    . R$ J5 f5 l  X+ o
    4 |3 a& g% u( N  G4 V4 _         至于附件driver和include的其他文件可以先不管,因为现在还用不上,有兴趣可以自行翻阅下,是关于中断二级向量表的实现和timer的驱动,大部分都是参考u-boot下openrisc架构修修改改过来的,所以最好还是稍微浏览下,然后在下次移植u-boot时能快速上手。
    . T7 u( Q+ I( @" m5 B
    6 y8 {( M& ?1 P) i' r+ L( X        Main函数更不用多说了,手痒自己随意打的,随便修改吧,至于里面的函数用sourceinsight追一下就可以了,都是比较简单的东西了。
    8 G: P4 l6 y; j' {5 k- \
    - c9 @1 \( ?8 p% |! A8 r$ M* t        UART的代码到这里我们就敲完了,最后就是把程序下到我们的SDRAM中运行了,好,在下到SDRAM之前呢,还需要编写一个连接脚本文件,告诉编译器编译出来的可执行程序各个段的存储位置,入口地址,在学校学汇编和计算机原理的时候都应该讲过这些的了,就是编译源码后得到的可执行bin文件的在RAM的存储位置,例如在link.lds文件中
    $ e! @. H/ x; M
    2 B+ s3 C4 u) _5 b* R# ?% e
    / Z( n( C# `4 R5 l8 B) J* P' |; p/ x' ?1 E3 j0 M# l# [5 Q
    : s/ o7 g0 m! `5 j  t
            vectors段,即我们之前编写的异常向量表的存放位置,从0x0开始到0x2000,除or1200结构本身定义的异常向量表之外,还保留空的异常向量允许自定义操作。4 K3 j( H7 I7 d  d
    1 @! s8 q) F' [" n1 n
    ) g$ o# y$ U. I# y9 @8 X
            ram段就是开始存放我们uart的程序代码啦,继续看······6 Q) l+ [2 E* r" u9 B4 L

    * |5 I1 |  x, Z. V$ O
    * A( q2 p* m# n6 W3 {8 {
    % F9 o' \! P1 _5 {3 V: i8 G& j
    ) B1 F, D/ z, D8 G        这里定义了一个_min_stack的变量,大小为8K,记得我们在写reset向量时设置CPU堆栈的值吗?8 [9 _8 [- L; r1 ^; W

    + C( g3 I  f' Y# G7 |) V2 y
    " \: ]& K: Y! u" j/ {
    4 ]- U' I4 D# w9 H, y/ J/ ?; T, @) ]1 T
            在这里_stack的值在这里取出,这里我说的不是很明白,关于lds连接文件附件中的ARM79出品-u-boot移植手册.pdf的附录一有详细说明,参考参考很容易就明白啦,或者可以google一下,再或者看看别人的技术博客就行了,这里不再详细解释了,因为码字是在是很累人的一件事。3 q* Q3 _% `& [; Y( S) W
    2 V; u6 H2 i1 \

    : W1 u' U5 W2 K# k3 j$ F        一切OK准备就绪,但是在linux环境下编译我们还要写一个makefile,哇天啊,好吧,认栽吧,写吧。
    - Z  @. ^  H. Q, g/ j
    1 y+ H# O# w8 ~3 S        ······8 f8 y# V3 Z1 F
    4 y7 m- O/ L( i/ w! b$ @
             现在可以打开virtualbox,先浏览一下放在桌面的那些文件,比如8 K" X/ z, Q, u4 p! v  u; ]- X

    + a1 K( z- Y* y0 y 9 W/ B( U) d8 ~3 E' W7 T( G
    . c2 [% J2 x% O

    # b( [. c5 j& F9 X  a: e; Q- d% g0 f        可大概看看这个环境可以做些什么东西。" {5 f) a3 _3 x
    % g% v: x4 o( x2 w
    1 z) f7 r8 `; d7 A9 h7 Q4 u3 y3 d
            然后打开home folder,点啊点,点啊点% ]$ A8 n0 {1 D7 g2 o) ]
    8 k6 J  F4 l" ], l
    - ~  k3 w6 V* U5 h( R3 z
    * n* a1 g: ^% K

    - U3 d4 d$ I- P- R! }  r        在orpmon文件夹中包含一个opencores社区针对or1200做的bootloader,而且,我在写makefile的时候都是参考这个bootloader的makefile写的,至于怎么去写一个makefile,参考《跟我一起写 Makefile.pdf》和《GNU+makefile中文手册.pdf》这两个手册咯。3 a4 j6 P! Q( I3 l  Y% V

    该用户从未签到

    2#
    发表于 2021-4-22 18:31 | 只看该作者
    or1200第一个裸机程序(上)

    该用户从未签到

    3#
    发表于 2021-4-23 18:06 | 只看该作者
    现在我们终于可以把FPGA当成个单片机使了,稍微比单片机猛一点,但是绝对比单片机贵一个数量级,FPGA现在能完成由CPU做的事0 e. t- W. ]  e
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-24 16:37 , Processed in 0.218750 second(s), 26 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

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