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

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

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x
    * q/ r. `7 E3 W
    经过前面千辛万苦、爬山涉水、纠结了好久才弄好的环境,现在我们终于可以把FPGA当成个单片机使了,稍微比单片机猛一点,但是绝对比单片机贵一个数量级,FPGA现在能完成由CPU做的事,我们就把它当成一个大价钱买回来的单片机,不过相比于S3C24XX的片子来说,那么贵的一个FPGA板卡跑40M的一个CPU绝对的奢侈,不过仅限于理解和学习嵌入式的过程,管他的。
    ; i1 J* x. G8 h$ _: r9 ~        但是如果是这样呢,把or1200资源优化好,例化两个或者多个CPU,是不是可以做成SMP架构?又或者说现有的产品上有FPGA芯片,但是里面的资源还足以容得下一个or1200,是不是可以做个协处理器在里面?我想应该可以做的事情还很多的······吧。( i' u/ q# f" r! [/ M
    , F* A" m1 z0 t0 l' [8 T
            按照惯例,硬件代码第一个无非是点亮LED灯;软件代码无非是写个hello world,有同感木有。* \3 [2 ~1 P/ C, w0 w

    ! o  @- N- K: T5 S; P) a4 _/ W8 k+ {/ r  a        但是想想,我们现在的SOC只有什么?CPU、RAM、Debug、UART、BUS,我们没有把GPIO外设加进去,点个灯貌似行不通了,但是能操作的毕竟外设蛮多的,但是又想看到有现象输出的,那我们只好选择去写个UART的代码咯。. b& k# t& q3 h4 F% p

    ' {' m. n* S% _! h% Z! B        我们在后面完善or1200_basic_soc时把其他外设添加进去后,上了linux系统后,在编写GPIO字符驱动时再去把灯点亮吧,现在我们就当FPGA是个单片机去编写所谓的“裸机”程序。
    9 c6 h# M) o4 G* P0 Y1 M, J0 c7 x& W1 Z5 Z
            我们现在手头上有两本书对吧,《开源软核处理器OPENRISC的SOPC设计》、《CPU源代码分析与芯片设计及Linux移植》,那我们就先看看接下来要写的UART16550外设的说明文档先咯,uart16550_latest.tar.gz源码包里的doc文件夹有该ipcore的说明文档,其实这就是相当于看芯片手册一样了,是不是很熟悉,那就不继续吐槽了,或者《开源软核处理器OPENRISC的SOPC设计》里面第7章也有UART16550的中文翻译版本,感觉这本书就是opencores上面大堆文档的翻译大杂烩,算了,自己写不出的书还是不吐槽人家的了。
    ) c0 ?% z  G2 K& B6 N( S
    2 q, T9 N; o" \# z1 y6 ^        大概浏览一下UART16550的结构后我们就来咯,敲代码。% m# m- I' {6 ]# F3 x. ^  Q; K

    , t$ y7 k6 {: D& y7 r# o/ _        在敲代码之前先搞清楚一些流程,当内嵌在FPGA的or1200上电复位时,它在做些什么,在硬件环境搭建时,我们把CPU的复位地址设置为0x100,这个地址可以在or1200_defines.v中任意指定的,什么是0x100呢,我们在CPU体系手册《openrisc-arch-1.0-rev0.pdf》中16章操作系统接口有所描述,0x100是复位后取指令的第一个地址,虽然是这样说,但是复位地址可以是任意指定的,以后我们固化代码在Flash时,会将这个复位地址设置为Flash的起始地址,目前我们先指定在0x100,所以,CPU复位时就会从0x100取出第一条执行的指令,OK。* ?! z" x, x  E4 T3 f# f) @0 O
    $ @7 w  P3 i2 ]# S
    9 c$ t+ R6 k8 P+ y2 N! A' l
    2 p% J. J! G% _4 C
            对于其他向量地址来说,是CPU设计时规定好的异常向量地址,好比0x100是复位异常向量,我们在写C51时熟悉的对比一下看还有什么?0x500是否熟悉?只是时钟中断异常向量。还有什么咧?0x800,外中断异常向量,所以,当我们在CPU将计数器中断enable后,当时钟计满中断时,就会跳转到0x500执行指令。对于外中断而言,前面我们在修改or1200_defines.v把PIC的中断个数设置为20个,所以,当这20个外中断enable后,中断源触发后,CPU就跳转到0x800地址来执行指令啦,至于其他的异常向量都是类似的,可能以前我们接触得比较少,但在对于上了操作系统后,这每个异常向量都会有对应的处理过程。现在对于or1200_basic_soc来说,只使用了复位、时钟、中断异常向量。' A" G# O. j+ ]+ X" y

    & F+ v0 v+ X4 i- c        所以我们敲代码的第一步就是编写这些异常向量的处理程序,即异常向量表,因为刚上电时处理器的C环境还没有建立好,所以这些代码都是要用汇编去写的,我们现在至少要用到reset复位异常向量,硬着头皮码吧。
    1 V" f+ s" Y/ j3 Q& c2 ^1 f
    ) C2 e2 J- l1 [* k        我理解的reset复位异常向量是通常说的启动代码,在用Keil MDK创建S3C2440的工程时也有对应的启动代码文件s3c2440.S,看起来蛮多的,1k多行,但是往下看,800多行的宏定义,那启动代码就200多行了。再来吐槽下or1200在linux系统下的关于启动代码的文件entry.S、head.S,这两个文件加起来2k5多行,那我们手动去敲那么多代码么,开玩笑吧。
    : D! u0 Q7 H. O/ C8 |; o0 n. i  i2 G* G4 r) z/ ^
            好在我们现在用到的外设不多,就一个UART,而且我们现在还不需要用到时钟计数,还没需要到中断,所以复位向量用到的代码大概只有50行,50的汇编对于我这种级别        来说简直小菜一碟,好,码字。: O! f+ `7 x& [

    1 ^6 u6 m) U: e) y        至于这个简单的UART代码我都打包好在附件中了,那我们简单分析一下这50多行reset向量需要的代码。
    % K: q6 @7 A8 E2 O5 i' y( ]- q8 v2 z* h1 M
            把工程都扔进sourceinsight,方便查看嘛,打开reset.S,对于前面的宏定义先跳过,用到的时候再说吧。( g9 [1 i8 z( P

    & h3 Q$ G$ |, \, J
    3 f( C! \5 ]" E8 ]% b        先看0x100的异常向量,3行代码,是不是很有成就感。
    # |+ |9 r) j1 l- ^6 y: j
    ( Y! K9 L5 h! R/ D5 `
    5 V1 Y' I% |) j7 L: g& m- m' e
    3 J' @$ j6 x& @7 U/ h; z) c. u/ z. ]: X* P. s! K
            LOAD_SYMBOL_2_GPR看名字就知道意思,作用是将标号的地址存入CPU的寄存器。
    ; w7 E+ g1 c  R" [- g! x3 h0 S9 w( q: D
    : Z- E, j, J+ n) {- v 0 Y5 {) B/ N) o, N. A
    + H0 V0 D' `- D; r6 |. z& o! ]; y
    5 [! k1 f) n; A; R  O# E3 ?0 Z
            18行l.movhi是or1200处理器的逻辑指令,作用是把立即数零拓展左移16bit
    $ \! ?7 ?" Z& K& {+ z* S5 \1 X
    / K8 o) {' B) il.movhi rD,K) W7 A& a$ l' l5 V

    : c# l- ~. N& K3 o1 z- jrD[31:0] < -extz(Immediate) < < 16$ W+ V. l3 f3 g8 U, K5 B; k: O

    8 r# H. c2 A3 N( e4 \        所以18行将symbol的高16位存入gpr中。8 |3 O' B! o% K: L. D5 N
    . @1 M3 b1 g, f9 B8 m$ o2 ]
            l.or是or1200处理器的逻辑指令按位或,作用不用讲了吧,# D% q1 E5 p) F3 w8 A2 k, [3 }" o

    ( e9 D$ g; l  `7 ?. E) Wl.or rD,rA,rB
    - Z6 k6 {9 l  d" j* F: g1 Y0 r4 d$ m. w' V; a
    rD[31:0] < -rA[31:0] OR rB[31:0]
    ; ~, U6 W4 ?9 ~. x
    # r" S( _0 a: k3 {8 q        所以19行将symbol的低16位存入gpr中。/ D! i* x( w* P' Q$ r6 |  ~
    4 V$ Z( I/ c3 Z
            继续reset向量,l.jr r3,即跳转到r3寄存器的地址上,所以reset向量前3行就是跳转到_reset标号地址上。
    7 s% R/ H! a0 F* a8 Z% j' l5 w3 u+ h) t. N! A8 c7 O1 Y& f
            貌似55行还有个l.nop指令,只是or1200的空操作指令。跳转指令之后紧接着的指令称为延时槽,在or1200体系中,在跳转指令之后的紧接着的一条指令是规定、必须、肯定会执行的,所以在现在or1200的gcc编译器中,在跳转指令之后都会自动补上一个nop指令,貌似在新版本的编译器会在跳转指令后执行别的有效指令代替nop操作,提高效率。
    : G. t( Q$ m" [2 \3 W& u! F8 Q" ~; B, S$ {- Q
            好,continue······  [( M0 G4 V" A  w) F) i
    6 S2 b% G* J6 @/ P& V5 M$ G
    - u: Y- b) s/ H' x/ B' E
            Sourceinsight中一跳,转到_reset标号上
    # I' O( T0 H8 D: ~& K( d8 t. R3 t" m5 \; }0 s
    & S2 Q5 y7 n2 p+ G; C/ s9 @0 [  X$ x
    ) q( W4 A. q! C2 G, u
    ) @0 t9 m9 o8 `5 X. z* E: s+ W
            l.movhi,l.ori很熟悉啊,目的将r0寄存器清零。* x7 u/ o) C3 L* C: e6 N

    : ]5 [+ G3 S6 N- @- g6 o        l.addi作用是将r0和SPR_SR_SM相加后存入r3中,至于SPR_SR_SM又是什么?跟踪下就可以发现在spr-defs.h有其定义。8 {/ e. z5 ^+ d% S( M

    & L& C! {0 Y" B+ a . y0 g  a- w" _4 P* N! M

    9 `% c# \2 {7 C# H  x2 k' e" D- F0 T* ^: c0 {6 E. q& D/ m: M
            这是supervisor模式,这个linux的内核态和用户态概念很像,or1200存在两种模式,supervisor和user,区别是操作CPU内部寄存器组时权限不同。9 b$ U! `) J4 c" D
    4 Z$ `8 v1 i0 |
    继续,l.mtspr就是操作or1200内特殊寄存器组的专用指令,专用的,将r0和r3的寄存器的值相加后送入SPR_SR寄存器内! J. D( F9 g* t" d& \# K  e
    - ^0 M* a: U: [& w: c
    ; k7 w) Q; A# m( {

    / g( E( ^9 `; Z7 x0 G
    $ M& a8 ]- N* s0 C1 |" m% R2 j        在《openrisc-arch-1.0-rev0.pdf》中可以看到,SPR_SR的first bit就是SM位
    . W: c+ H! l  M1 Z, w3 y. u6 D9 m. G0 G3 q
            为1时就是Supervisor Mode
      S: K8 u3 G& D; Q& q
    1 U' |/ l7 D% N% U( O0 p        继续往下看,有个CLEAR_GPR的宏,目的是将gpr设置成r0的值  Q+ ]5 |7 l, Z, L" `

    3 T) i. U7 Z6 l" N0 h- s 2 R$ E' w; T( i" n/ x" N

    . X% I  b; e0 L$ o, g: X
    $ y% Q2 n: `* W. Y  \        在or1200中,r0被设置成常数0,做与这个宏的作用就是清零5 r4 p' _- x1 P4 r' [$ K  |

    ! C: x% A- Z2 Q0 Q* O2 m0 c( k4 o好,继续1 y, i' t0 @/ I; Z+ P
    9 C1 v8 i: a) O" M+ A6 U! e
    & X# E& q2 U9 n! u
    & q$ t" s( Q4 c3 T7 y) p2 K5 Z
    - o  B9 W' O' U

    9 N( _) X4 E8 {( S8 x1 g  }: p        SPR_TTMR是关于or1200内Timer的寄存器,将r0的值赋入SPR_TTMR中,至于SPR_TTMR的解释如下表。
    : A; X2 g/ T$ Z+ _2 |+ _
    # z- F/ S' ]7 |* p' r
    . @+ [; w3 |  o* P2 K8 m4 C) J, r; d% C, x6 |7 |
    * ^4 \/ r% S( R7 p9 O$ j- b% d# g
            目的就是把执行计数单元(简称定时器吧)的中断和使能都禁用了。2 b1 O8 j' D4 F% M& ?
    ; S' |9 S+ _1 ?. S8 X. U
            好,继续······貌似完了,200行吧_stack的值赋给r1,_stack是CPU堆栈的值,_stack这个值在lds文件内定义,这个文件稍后再解释。
    . _' H' J2 Y) x( Q- K/ x0 m+ l5 v
    9 ?6 Y  R6 m% i- P; F  u        第203,204行是不是很熟悉,跳转到main函数执行,堆栈建立好之后下面main函数就可以用C来码了。
    7 o4 z# F9 v  P
    % w- Y' ?1 W1 k' s9 v% f/ U0 h        Reset向量就需要这些代码了,至于reset.S剩下的代码是关于中断处理的,比如在4 r7 a+ ~* j4 f

    % U; s- ]; ]1 B 9 \2 E4 y+ I* h7 F- N

    ; P0 e  [1 K: W. N) B/ N! m& C) l( E/ ~/ F3 [; X* D  @  Y; }; u
            在定时器中断和外中断会先执行EXCEPTION_HANDLER宏,目的是执行中断处理之前的现场保护,跳转至中断服务函数,执行完中断服务函数之后的现场恢复,中断的概念不用多解释了,应该是很熟悉的了。- w8 f/ q" R) t

    # f, Q6 j  X, _/ C        关于or1200中断的流程可以参考《CPU源代码分析与芯片设计及Linux移植》,里面讲得蛮详细的,虽然大部分也是翻译手册的,至于附件的代码关于reset.S剩余部分不是特别难,就不解释了,不解释咯······) C+ O2 S4 Y9 L5 G, H
    6 ~. v# P" G! a3 V: ^' I
            突然发现,reset的异常向量我们就敲完了,在reset向量里会跳转至main函数,那就开始了,main函数熟悉了吧,* b# m4 \# _1 {4 Z' [& b& c% w/ b5 D( s

    5 m1 @, K" \1 a: J: W2 M, Y! Y! \        附件中的uart.c和uart.h关于UART16550的串口驱动和头文件,程序不难,对比着uart16550_latest.tar.gz的说明文档、《开源软核处理器OPENRISC的SOPC设计》和源码的注释5分钟就可以看懂了,不详细说了,因为大家都有C语言功底嘛。
    4 l" O* X8 r& `+ V  b9 t. N0 {' a6 v: S) g  K3 H( U
             至于附件driver和include的其他文件可以先不管,因为现在还用不上,有兴趣可以自行翻阅下,是关于中断二级向量表的实现和timer的驱动,大部分都是参考u-boot下openrisc架构修修改改过来的,所以最好还是稍微浏览下,然后在下次移植u-boot时能快速上手。
    # @5 m. y" B  p  p' Q( X4 ^" F
    - x" T) i$ a; b% {" w& \        Main函数更不用多说了,手痒自己随意打的,随便修改吧,至于里面的函数用sourceinsight追一下就可以了,都是比较简单的东西了。
    . n5 R* j$ k- {+ o% |* ?' z0 l2 _3 P( ?: M8 l
            UART的代码到这里我们就敲完了,最后就是把程序下到我们的SDRAM中运行了,好,在下到SDRAM之前呢,还需要编写一个连接脚本文件,告诉编译器编译出来的可执行程序各个段的存储位置,入口地址,在学校学汇编和计算机原理的时候都应该讲过这些的了,就是编译源码后得到的可执行bin文件的在RAM的存储位置,例如在link.lds文件中0 @% V3 f, {" ?! `5 H) u
    ) j/ ^2 p7 I# P& X' q! e* n8 e
    % U& W, @1 I/ ^

    # e5 k  W/ i+ p- h( f0 P6 ^; J* d6 ~" \4 ?7 I
            vectors段,即我们之前编写的异常向量表的存放位置,从0x0开始到0x2000,除or1200结构本身定义的异常向量表之外,还保留空的异常向量允许自定义操作。/ B4 D4 N0 V* r3 b# b3 Z" e

    ; M! Z, i7 b2 C" v  J  o/ ~
    & `* u* f6 T% m% q2 A8 [3 n        ram段就是开始存放我们uart的程序代码啦,继续看······
    ! P" u' R$ P5 l; o/ z; P# i9 I% ?0 h& b4 e/ f2 o8 L

    . O7 W. K6 [; [2 _# {1 A- Q4 \; n/ C. |

    6 C; y+ q4 `: I% E0 e6 A2 h        这里定义了一个_min_stack的变量,大小为8K,记得我们在写reset向量时设置CPU堆栈的值吗?' t0 M8 ?/ y9 w# D
    / @- q- `5 X, V& G0 O+ _6 T6 S
    1 Q, `, v" F2 x* N
    1 g9 F9 A+ a8 G8 [
    ! W7 P+ I9 K  S) n4 A
            在这里_stack的值在这里取出,这里我说的不是很明白,关于lds连接文件附件中的ARM79出品-u-boot移植手册.pdf的附录一有详细说明,参考参考很容易就明白啦,或者可以google一下,再或者看看别人的技术博客就行了,这里不再详细解释了,因为码字是在是很累人的一件事。
    9 ^* ?/ o; n: r( J" Q* ?2 a
    - l( B" r6 ~$ M5 |2 X; n3 o% c! Q3 E% f' n, I1 ?# z8 @
            一切OK准备就绪,但是在linux环境下编译我们还要写一个makefile,哇天啊,好吧,认栽吧,写吧。
    ' b& E2 T/ J( z) P
    . E% O3 p+ e7 Y6 v- H3 S        ······
    , y8 i) e, W! c) T+ t4 x; b& v7 p& n5 l$ K
             现在可以打开virtualbox,先浏览一下放在桌面的那些文件,比如$ O5 b8 ~' K# Q5 q8 |
    $ r3 w2 c2 T6 r. D% f6 J' V
    6 H" _7 S% o4 N/ y
    1 E6 c" [) G2 A% N; G

    9 t3 F% [! E, T% N        可大概看看这个环境可以做些什么东西。3 |% y8 F3 h" O9 s( B

    ; B- ]/ M# U$ n9 A- w7 Y6 I" P0 ^9 C% i4 G7 T$ L8 ~
            然后打开home folder,点啊点,点啊点) Y. w* s0 p! T' F

    0 v0 H3 E$ Y% j+ p
    & x7 z! W9 z- b) T0 y& ?4 ]& S0 T
    4 C  K8 a3 V1 h9 Y
    6 v" D+ z; N0 k3 N+ e( }        在orpmon文件夹中包含一个opencores社区针对or1200做的bootloader,而且,我在写makefile的时候都是参考这个bootloader的makefile写的,至于怎么去写一个makefile,参考《跟我一起写 Makefile.pdf》和《GNU+makefile中文手册.pdf》这两个手册咯。0 h: `6 W. {' W& L

    该用户从未签到

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

    该用户从未签到

    3#
    发表于 2021-4-23 18:06 | 只看该作者
    现在我们终于可以把FPGA当成个单片机使了,稍微比单片机猛一点,但是绝对比单片机贵一个数量级,FPGA现在能完成由CPU做的事
    & `, O0 _% X' [* J+ ^; F4 y
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

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

    EDA365公众号

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

    GMT+8, 2025-6-23 13:52 , Processed in 0.109375 second(s), 26 queries , Gzip On.

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

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

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