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

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

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x
    6 ?8 x/ j' ]6 {3 f3 v
    经过前面千辛万苦、爬山涉水、纠结了好久才弄好的环境,现在我们终于可以把FPGA当成个单片机使了,稍微比单片机猛一点,但是绝对比单片机贵一个数量级,FPGA现在能完成由CPU做的事,我们就把它当成一个大价钱买回来的单片机,不过相比于S3C24XX的片子来说,那么贵的一个FPGA板卡跑40M的一个CPU绝对的奢侈,不过仅限于理解和学习嵌入式的过程,管他的。) x  K/ m' W/ ]: k
            但是如果是这样呢,把or1200资源优化好,例化两个或者多个CPU,是不是可以做成SMP架构?又或者说现有的产品上有FPGA芯片,但是里面的资源还足以容得下一个or1200,是不是可以做个协处理器在里面?我想应该可以做的事情还很多的······吧。
    0 c* Y  @* C2 h, r
    3 H/ U5 N* T7 L  d( ]        按照惯例,硬件代码第一个无非是点亮LED灯;软件代码无非是写个hello world,有同感木有。
    0 v4 i, M2 \8 r8 d
      u; w) E3 q& ]! I3 E) ?$ b! R+ d        但是想想,我们现在的SOC只有什么?CPU、RAM、Debug、UART、BUS,我们没有把GPIO外设加进去,点个灯貌似行不通了,但是能操作的毕竟外设蛮多的,但是又想看到有现象输出的,那我们只好选择去写个UART的代码咯。% b' L1 S  g* C' v+ G* v
    - O) B2 V: |3 z# d
            我们在后面完善or1200_basic_soc时把其他外设添加进去后,上了linux系统后,在编写GPIO字符驱动时再去把灯点亮吧,现在我们就当FPGA是个单片机去编写所谓的“裸机”程序。' c5 g2 i" e: r# l8 _' Q9 f

    ) W, F  M: G7 c        我们现在手头上有两本书对吧,《开源软核处理器OPENRISC的SOPC设计》、《CPU源代码分析与芯片设计及Linux移植》,那我们就先看看接下来要写的UART16550外设的说明文档先咯,uart16550_latest.tar.gz源码包里的doc文件夹有该ipcore的说明文档,其实这就是相当于看芯片手册一样了,是不是很熟悉,那就不继续吐槽了,或者《开源软核处理器OPENRISC的SOPC设计》里面第7章也有UART16550的中文翻译版本,感觉这本书就是opencores上面大堆文档的翻译大杂烩,算了,自己写不出的书还是不吐槽人家的了。5 m9 U% \9 X# Z1 a% u, k

    # z  J1 y2 i1 }* n- o& x        大概浏览一下UART16550的结构后我们就来咯,敲代码。+ |) v! l; R, _$ O0 o
    . x& Y! w, t0 {  _
            在敲代码之前先搞清楚一些流程,当内嵌在FPGA的or1200上电复位时,它在做些什么,在硬件环境搭建时,我们把CPU的复位地址设置为0x100,这个地址可以在or1200_defines.v中任意指定的,什么是0x100呢,我们在CPU体系手册《openrisc-arch-1.0-rev0.pdf》中16章操作系统接口有所描述,0x100是复位后取指令的第一个地址,虽然是这样说,但是复位地址可以是任意指定的,以后我们固化代码在Flash时,会将这个复位地址设置为Flash的起始地址,目前我们先指定在0x100,所以,CPU复位时就会从0x100取出第一条执行的指令,OK。
    , u- p' q. v. G2 {# |3 f7 h+ N: U9 t3 @" @/ [
    4 V7 g( C- X) K" R
    . ~$ N& \$ l  ^5 O" j) D
            对于其他向量地址来说,是CPU设计时规定好的异常向量地址,好比0x100是复位异常向量,我们在写C51时熟悉的对比一下看还有什么?0x500是否熟悉?只是时钟中断异常向量。还有什么咧?0x800,外中断异常向量,所以,当我们在CPU将计数器中断enable后,当时钟计满中断时,就会跳转到0x500执行指令。对于外中断而言,前面我们在修改or1200_defines.v把PIC的中断个数设置为20个,所以,当这20个外中断enable后,中断源触发后,CPU就跳转到0x800地址来执行指令啦,至于其他的异常向量都是类似的,可能以前我们接触得比较少,但在对于上了操作系统后,这每个异常向量都会有对应的处理过程。现在对于or1200_basic_soc来说,只使用了复位、时钟、中断异常向量。+ d# P. a: [6 J- q2 n" u# H: Y8 \

    1 }6 B7 l; `2 V- F# ^1 x        所以我们敲代码的第一步就是编写这些异常向量的处理程序,即异常向量表,因为刚上电时处理器的C环境还没有建立好,所以这些代码都是要用汇编去写的,我们现在至少要用到reset复位异常向量,硬着头皮码吧。
    ( `* S1 i1 s8 z/ r9 w$ o
    5 G. j' w) b+ m4 d        我理解的reset复位异常向量是通常说的启动代码,在用Keil MDK创建S3C2440的工程时也有对应的启动代码文件s3c2440.S,看起来蛮多的,1k多行,但是往下看,800多行的宏定义,那启动代码就200多行了。再来吐槽下or1200在linux系统下的关于启动代码的文件entry.S、head.S,这两个文件加起来2k5多行,那我们手动去敲那么多代码么,开玩笑吧。# ?/ f- i( p+ D# C

    ! R4 I0 o1 i5 P) d2 n        好在我们现在用到的外设不多,就一个UART,而且我们现在还不需要用到时钟计数,还没需要到中断,所以复位向量用到的代码大概只有50行,50的汇编对于我这种级别        来说简直小菜一碟,好,码字。
    # l3 M( J8 v5 F8 ]0 [& r0 \# [; u3 x4 S2 r, ~
            至于这个简单的UART代码我都打包好在附件中了,那我们简单分析一下这50多行reset向量需要的代码。
    - b  M! A& T/ F' y# p5 _+ [) A; y1 \9 u0 [% L$ o( _. C- d
            把工程都扔进sourceinsight,方便查看嘛,打开reset.S,对于前面的宏定义先跳过,用到的时候再说吧。
    % o) c8 m5 e) @$ Q# {; X4 D% {
      K! g  ^4 f: k9 ]9 d% X  _9 ]& ~" _7 ]- F! w5 H
            先看0x100的异常向量,3行代码,是不是很有成就感。$ L% u  C7 ]4 X3 N' ~( ^

    & @3 U3 J( {2 E9 ]$ T# ^. o 3 f$ u4 n6 z. e/ b( g- o; X

    # ?4 @/ Z: G: D- U: V1 F0 d! N/ x# U
            LOAD_SYMBOL_2_GPR看名字就知道意思,作用是将标号的地址存入CPU的寄存器。* `( O; x3 |* t- {
    ( u+ }% j# y1 f5 K, e
    8 ]0 z7 z. P6 a- s( f% z2 k
    / }; q" o" k7 d) G: E
    1 B3 A! e, L+ X+ i
            18行l.movhi是or1200处理器的逻辑指令,作用是把立即数零拓展左移16bit
    ; S1 ~1 ~0 Y, l" S  _! h+ g+ I
    , @2 A4 N- Z- d5 S+ ul.movhi rD,K
    5 t1 `3 g/ A' r, S6 n
    ; M+ _; O# \; T2 c$ e% T9 w9 prD[31:0] < -extz(Immediate) < < 16
    4 Y0 Q. z5 p2 e- d
    : q% _) K, [4 `1 s# f        所以18行将symbol的高16位存入gpr中。
    ; w8 }. z! [5 m% S: `5 v% j; P
    , B4 p( X0 C7 w& g; [+ z% a        l.or是or1200处理器的逻辑指令按位或,作用不用讲了吧,# t" {/ w, g- q4 ~) J4 A# A" ]

    8 |. R0 g" g3 E1 ?l.or rD,rA,rB+ @1 U. j) q6 N/ u/ E1 T
    + f' e6 b/ n( J: X! T
    rD[31:0] < -rA[31:0] OR rB[31:0]  g2 E3 n) c4 j3 D# o$ g; F
    ; C- Z: Y, \, [: o
            所以19行将symbol的低16位存入gpr中。
    . J! t9 l/ ?9 V+ A1 V7 D. v
    0 O5 }7 V( ]7 t7 C        继续reset向量,l.jr r3,即跳转到r3寄存器的地址上,所以reset向量前3行就是跳转到_reset标号地址上。
    $ ?7 l0 i8 d: c; S* e& |, V: k: Z7 k5 `  \! v
            貌似55行还有个l.nop指令,只是or1200的空操作指令。跳转指令之后紧接着的指令称为延时槽,在or1200体系中,在跳转指令之后的紧接着的一条指令是规定、必须、肯定会执行的,所以在现在or1200的gcc编译器中,在跳转指令之后都会自动补上一个nop指令,貌似在新版本的编译器会在跳转指令后执行别的有效指令代替nop操作,提高效率。
    . G5 U; K6 J+ I3 C
    9 w& j) P; q4 B4 x; `8 O        好,continue······
    ' w3 P$ R; x8 ~+ T
    5 w8 a! [8 Y' Y5 H
    ! E* T( D. z% y" ^8 m7 c4 B6 N        Sourceinsight中一跳,转到_reset标号上
    . @8 r3 g% m+ h% }/ O. M6 g) U+ Q7 l
    : g$ F6 S, H. D* R$ Z# u$ A
    # r" Y. j# d. j1 i
    ) \4 X" Z2 i: r, b& E$ Y/ i* g$ D; i# f' D
            l.movhi,l.ori很熟悉啊,目的将r0寄存器清零。
    2 M" D" p/ n3 z$ ^0 h
    " R( i2 z" U3 G8 ?" |: E        l.addi作用是将r0和SPR_SR_SM相加后存入r3中,至于SPR_SR_SM又是什么?跟踪下就可以发现在spr-defs.h有其定义。
    9 ^, S9 r4 l! y9 {0 r9 y- d0 y/ W: H

    $ ^* w& u3 R0 P1 N
    1 A8 E6 K* Q2 _8 V- h$ L5 f& I
            这是supervisor模式,这个linux的内核态和用户态概念很像,or1200存在两种模式,supervisor和user,区别是操作CPU内部寄存器组时权限不同。6 p( _8 k2 M) i3 ~+ s

    0 v( X4 z6 |2 t6 w7 ^0 l+ N; c继续,l.mtspr就是操作or1200内特殊寄存器组的专用指令,专用的,将r0和r3的寄存器的值相加后送入SPR_SR寄存器内
    1 Z, p# o: l$ |3 N
    3 p. k6 W% Q# m
    8 q' g! u: i  x: r, ]/ ^
    2 Q# N( y! q  x' Y
    & G3 l" c  Q$ w/ V        在《openrisc-arch-1.0-rev0.pdf》中可以看到,SPR_SR的first bit就是SM位
    & k* `: H( l3 F& c1 ]  M% B! M# B& `
            为1时就是Supervisor Mode
    % z9 ^2 c1 g+ `' G2 ]
    0 J2 @( d6 I. _/ _: ]9 g1 v        继续往下看,有个CLEAR_GPR的宏,目的是将gpr设置成r0的值
    4 x; K6 `) H: N6 s0 [2 U9 ]
    ! J7 g* _) F) E: c" k5 i - @6 j2 I! p3 |5 u. `( C* u
    - L/ o4 K' ]2 Q

    + K2 q5 N( j2 v) N& Z: ~        在or1200中,r0被设置成常数0,做与这个宏的作用就是清零
    * P  n; g2 Y, X; k% H+ ]
    ( d. V6 V2 `0 X3 t% q% b好,继续
    # V! `5 T& n* @* X& V- w0 Y# q8 U' g# {9 l
    6 {8 }7 M7 Q* [/ q: {

    5 _5 `6 ]2 A/ p( l9 Z, p) n
    4 P/ Q1 Q5 [" |5 a% _/ O$ ]
    2 X+ i2 y" ^/ ~+ K7 D0 X1 N) Y        SPR_TTMR是关于or1200内Timer的寄存器,将r0的值赋入SPR_TTMR中,至于SPR_TTMR的解释如下表。
    7 ?& A8 n% m1 |- J
    : w9 d" M# F6 d6 k
    1 ^! r3 q; h, I: W5 N2 q# s
    8 D; D- y4 s& \( Z8 M3 U$ U$ f3 B
            目的就是把执行计数单元(简称定时器吧)的中断和使能都禁用了。
    4 {7 t3 U+ Z, o3 B1 D6 ~& M0 N" L8 R: \
            好,继续······貌似完了,200行吧_stack的值赋给r1,_stack是CPU堆栈的值,_stack这个值在lds文件内定义,这个文件稍后再解释。
    3 O2 Y- P. Q( I6 @
    + M* Z5 R+ ?1 J* e( j- p        第203,204行是不是很熟悉,跳转到main函数执行,堆栈建立好之后下面main函数就可以用C来码了。7 m) j: L5 o8 U: u' F
    ! ~. w0 d* Y: T' _7 `7 [4 s: N
            Reset向量就需要这些代码了,至于reset.S剩下的代码是关于中断处理的,比如在
    6 ^7 h: ?$ G- m; s' B$ W3 e. s: s8 v9 n! y  M! D+ a

    5 C! @# b& E6 g; [- w. p  I# H* A3 o: b. b1 Q3 E3 ]9 U

    . {9 ~- @' A/ U2 @' s! j$ K9 g. ?        在定时器中断和外中断会先执行EXCEPTION_HANDLER宏,目的是执行中断处理之前的现场保护,跳转至中断服务函数,执行完中断服务函数之后的现场恢复,中断的概念不用多解释了,应该是很熟悉的了。
    0 `) @9 B9 h- _, X( M. R
    # f# U$ \2 x% w* F        关于or1200中断的流程可以参考《CPU源代码分析与芯片设计及Linux移植》,里面讲得蛮详细的,虽然大部分也是翻译手册的,至于附件的代码关于reset.S剩余部分不是特别难,就不解释了,不解释咯······
    ! O& x) I7 W  _+ _2 w; j
    8 h8 r, q1 G8 M8 ]        突然发现,reset的异常向量我们就敲完了,在reset向量里会跳转至main函数,那就开始了,main函数熟悉了吧,
    9 l3 z0 H$ {. ]$ |& Z& m5 h9 Y
    " B- a' n& p( g# |/ I# r        附件中的uart.c和uart.h关于UART16550的串口驱动和头文件,程序不难,对比着uart16550_latest.tar.gz的说明文档、《开源软核处理器OPENRISC的SOPC设计》和源码的注释5分钟就可以看懂了,不详细说了,因为大家都有C语言功底嘛。
    ; s# n5 i0 P" ^5 X6 `  O
    . p. m7 d) G. ]- o         至于附件driver和include的其他文件可以先不管,因为现在还用不上,有兴趣可以自行翻阅下,是关于中断二级向量表的实现和timer的驱动,大部分都是参考u-boot下openrisc架构修修改改过来的,所以最好还是稍微浏览下,然后在下次移植u-boot时能快速上手。
    & T8 P! X% h+ k, ~$ Z6 }
    4 F5 N# ^7 _* w8 m9 r8 s  a        Main函数更不用多说了,手痒自己随意打的,随便修改吧,至于里面的函数用sourceinsight追一下就可以了,都是比较简单的东西了。5 }6 x& R4 r) H
    # d- Z4 U* s4 F& W" c
            UART的代码到这里我们就敲完了,最后就是把程序下到我们的SDRAM中运行了,好,在下到SDRAM之前呢,还需要编写一个连接脚本文件,告诉编译器编译出来的可执行程序各个段的存储位置,入口地址,在学校学汇编和计算机原理的时候都应该讲过这些的了,就是编译源码后得到的可执行bin文件的在RAM的存储位置,例如在link.lds文件中
    " V0 I1 r% ?) x4 t9 P$ A: E
    * s9 B8 l( _) `3 K 1 j9 n1 T5 I* M2 d
    + [9 |) W8 J! r( ^/ }7 }
      Q" p3 `. w1 G. k6 a/ m
            vectors段,即我们之前编写的异常向量表的存放位置,从0x0开始到0x2000,除or1200结构本身定义的异常向量表之外,还保留空的异常向量允许自定义操作。
    $ y4 R6 \# z5 b; `2 k( x( ^# T' D! {% A. X' s3 g5 ~. K+ |
    $ M; Z, J- B# b) Z) w3 ~0 @
            ram段就是开始存放我们uart的程序代码啦,继续看······
    ) U# h8 [5 J" \$ v- O2 R% `$ A* Z1 ~; a0 C+ z% X

      F5 H! X* D. H4 Y' q9 [- A$ N& ]

    , x5 F9 s4 N' Q+ e: W        这里定义了一个_min_stack的变量,大小为8K,记得我们在写reset向量时设置CPU堆栈的值吗?+ o4 N3 ~- I! `3 B  W9 {. J0 N
    & s; @- w2 A6 n* u8 H
    # [+ P* G- V) e$ b

    5 r% l+ ]9 Q+ ]# ~$ T% x6 `. G$ r
            在这里_stack的值在这里取出,这里我说的不是很明白,关于lds连接文件附件中的ARM79出品-u-boot移植手册.pdf的附录一有详细说明,参考参考很容易就明白啦,或者可以google一下,再或者看看别人的技术博客就行了,这里不再详细解释了,因为码字是在是很累人的一件事。% ], K! U2 _# e  E3 m9 I0 A* h" E

    : n; b& n6 d0 W6 o9 S: `
    1 }6 B; [6 f2 R; Z) y9 O6 U6 t        一切OK准备就绪,但是在linux环境下编译我们还要写一个makefile,哇天啊,好吧,认栽吧,写吧。
    # Q; W. A+ O# Z; [3 x8 H
    ! s: x; z* h3 J* `3 ]& O        ······
    / }8 |7 ~& W! X7 c. h6 z& I2 g6 \3 O
    " F* n) E" s/ {; H         现在可以打开virtualbox,先浏览一下放在桌面的那些文件,比如2 R$ I) ]7 J& ?, z' A& j' ~
    : B# u$ ]) a2 }1 f4 R

    8 I5 h: h) d( d; W7 z: W" V, W* T- X# W
    " N$ b; P9 W6 R1 k
            可大概看看这个环境可以做些什么东西。: T1 H) P7 s9 }  k8 w
    6 `, d9 W4 I" {* r6 N6 a
    6 ]7 C& Z5 l$ D5 e$ [3 k
            然后打开home folder,点啊点,点啊点0 {1 q! H# D' ^* y0 U

      L1 s: `9 W  p3 \1 h 6 G; }6 _- M! ^/ w
    1 m; E5 V- B8 z6 v$ m% e5 i2 X

    $ |8 `) t. a" i! k0 n* ]        在orpmon文件夹中包含一个opencores社区针对or1200做的bootloader,而且,我在写makefile的时候都是参考这个bootloader的makefile写的,至于怎么去写一个makefile,参考《跟我一起写 Makefile.pdf》和《GNU+makefile中文手册.pdf》这两个手册咯。- Q7 X1 w+ _& }

    该用户从未签到

    3#
    发表于 2021-4-23 18:06 | 只看该作者
    现在我们终于可以把FPGA当成个单片机使了,稍微比单片机猛一点,但是绝对比单片机贵一个数量级,FPGA现在能完成由CPU做的事9 _- z& X# {8 d3 Z

    该用户从未签到

    2#
    发表于 2021-4-22 18:31 | 只看该作者
    or1200第一个裸机程序(上)
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

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

    EDA365公众号

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

    GMT+8, 2025-11-24 17:15 , Processed in 0.218750 second(s), 27 queries , Gzip On.

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

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

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