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

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

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x

    & ?" k; A& n  B+ R3 ?; z: [经过前面千辛万苦、爬山涉水、纠结了好久才弄好的环境,现在我们终于可以把FPGA当成个单片机使了,稍微比单片机猛一点,但是绝对比单片机贵一个数量级,FPGA现在能完成由CPU做的事,我们就把它当成一个大价钱买回来的单片机,不过相比于S3C24XX的片子来说,那么贵的一个FPGA板卡跑40M的一个CPU绝对的奢侈,不过仅限于理解和学习嵌入式的过程,管他的。
    / C$ G, t: D3 C2 F7 J( U% b2 [        但是如果是这样呢,把or1200资源优化好,例化两个或者多个CPU,是不是可以做成SMP架构?又或者说现有的产品上有FPGA芯片,但是里面的资源还足以容得下一个or1200,是不是可以做个协处理器在里面?我想应该可以做的事情还很多的······吧。
    # d, j5 X( _7 c; L, N, P5 J% s" ]1 Y  D9 w  B
            按照惯例,硬件代码第一个无非是点亮LED灯;软件代码无非是写个hello world,有同感木有。
    0 m+ d  ?: {+ a' h' Y5 x* ?, Y7 s3 Q/ r
            但是想想,我们现在的SOC只有什么?CPU、RAM、Debug、UART、BUS,我们没有把GPIO外设加进去,点个灯貌似行不通了,但是能操作的毕竟外设蛮多的,但是又想看到有现象输出的,那我们只好选择去写个UART的代码咯。
    * h& k% @( J! H/ S
    , O, ?$ Y  C( n6 z        我们在后面完善or1200_basic_soc时把其他外设添加进去后,上了linux系统后,在编写GPIO字符驱动时再去把灯点亮吧,现在我们就当FPGA是个单片机去编写所谓的“裸机”程序。% |+ O8 Z1 M# S8 j* X
    & j5 Z7 B, F% Z9 c5 d
            我们现在手头上有两本书对吧,《开源软核处理器OPENRISC的SOPC设计》、《CPU源代码分析与芯片设计及Linux移植》,那我们就先看看接下来要写的UART16550外设的说明文档先咯,uart16550_latest.tar.gz源码包里的doc文件夹有该ipcore的说明文档,其实这就是相当于看芯片手册一样了,是不是很熟悉,那就不继续吐槽了,或者《开源软核处理器OPENRISC的SOPC设计》里面第7章也有UART16550的中文翻译版本,感觉这本书就是opencores上面大堆文档的翻译大杂烩,算了,自己写不出的书还是不吐槽人家的了。: M' g9 t- i) K6 l
    3 ^2 H- A. c2 p
            大概浏览一下UART16550的结构后我们就来咯,敲代码。
    8 F! m. \0 j; B0 A1 P: `& w9 \; V1 C0 e$ B6 @8 _; j
            在敲代码之前先搞清楚一些流程,当内嵌在FPGA的or1200上电复位时,它在做些什么,在硬件环境搭建时,我们把CPU的复位地址设置为0x100,这个地址可以在or1200_defines.v中任意指定的,什么是0x100呢,我们在CPU体系手册《openrisc-arch-1.0-rev0.pdf》中16章操作系统接口有所描述,0x100是复位后取指令的第一个地址,虽然是这样说,但是复位地址可以是任意指定的,以后我们固化代码在Flash时,会将这个复位地址设置为Flash的起始地址,目前我们先指定在0x100,所以,CPU复位时就会从0x100取出第一条执行的指令,OK。
    # b' q# Z" a, j4 C5 m, L# Z' @& B* \) [+ p! K9 `
    " _. A- H6 b) D' ~+ t9 Q7 e: m5 T5 i
    6 Z3 D0 H9 S* \+ A9 e0 L
            对于其他向量地址来说,是CPU设计时规定好的异常向量地址,好比0x100是复位异常向量,我们在写C51时熟悉的对比一下看还有什么?0x500是否熟悉?只是时钟中断异常向量。还有什么咧?0x800,外中断异常向量,所以,当我们在CPU将计数器中断enable后,当时钟计满中断时,就会跳转到0x500执行指令。对于外中断而言,前面我们在修改or1200_defines.v把PIC的中断个数设置为20个,所以,当这20个外中断enable后,中断源触发后,CPU就跳转到0x800地址来执行指令啦,至于其他的异常向量都是类似的,可能以前我们接触得比较少,但在对于上了操作系统后,这每个异常向量都会有对应的处理过程。现在对于or1200_basic_soc来说,只使用了复位、时钟、中断异常向量。* W- i0 I6 t8 Y( D( c, ~' |8 Q6 m

    4 _+ W8 ]& |" `! V        所以我们敲代码的第一步就是编写这些异常向量的处理程序,即异常向量表,因为刚上电时处理器的C环境还没有建立好,所以这些代码都是要用汇编去写的,我们现在至少要用到reset复位异常向量,硬着头皮码吧。% e7 B2 X/ p* u9 X% N' Q# G& t

    ! X  i& N2 Z1 {3 L  N$ Q' V# p        我理解的reset复位异常向量是通常说的启动代码,在用Keil MDK创建S3C2440的工程时也有对应的启动代码文件s3c2440.S,看起来蛮多的,1k多行,但是往下看,800多行的宏定义,那启动代码就200多行了。再来吐槽下or1200在linux系统下的关于启动代码的文件entry.S、head.S,这两个文件加起来2k5多行,那我们手动去敲那么多代码么,开玩笑吧。
    9 S2 j* M9 h6 ~+ H. x# M! m& D2 J) E4 d+ q
            好在我们现在用到的外设不多,就一个UART,而且我们现在还不需要用到时钟计数,还没需要到中断,所以复位向量用到的代码大概只有50行,50的汇编对于我这种级别        来说简直小菜一碟,好,码字。
    4 z* H: t0 y. [% t7 f0 p# v4 u$ \
            至于这个简单的UART代码我都打包好在附件中了,那我们简单分析一下这50多行reset向量需要的代码。) Z: G( k# M, ^; |& C; Q4 X

    4 a  w7 G  k' L' V1 x' E        把工程都扔进sourceinsight,方便查看嘛,打开reset.S,对于前面的宏定义先跳过,用到的时候再说吧。
    $ i8 s! J! h2 d: G2 Q
    , P. D+ k- P, J& J. s( S* H8 h* K0 R$ |0 S0 }6 n: G& D
            先看0x100的异常向量,3行代码,是不是很有成就感。
    6 X' Y  N  x- o% p. w+ v/ `5 J, g4 M3 }& H3 p  x/ ]0 w0 M* M
    " |1 z, T( z0 p9 b% \7 v, s
    3 r4 H- Z& G6 n) U" y* Y1 T
    6 E: [& @3 g& R5 r  b
            LOAD_SYMBOL_2_GPR看名字就知道意思,作用是将标号的地址存入CPU的寄存器。! F; h7 g. d  e6 R( F4 W
    & `( I: r( [. D! W* s

    & n5 P& J; f3 k) ]3 L- T/ R& e! u$ {9 X4 o) z: M% N

    ; y- R: \. _# H! I        18行l.movhi是or1200处理器的逻辑指令,作用是把立即数零拓展左移16bit
    ! V% w  A, d2 X6 e% @  |8 X7 _2 w, P6 ?5 _  q7 _- U% ?
    l.movhi rD,K7 G; F# A. M7 X0 ^$ H  r4 m* l
      B" M/ x! ^* Q% Y
    rD[31:0] < -extz(Immediate) < < 16
    3 |+ ?. e1 n7 _5 u* A
    6 z$ y- h) \. ]  o" r        所以18行将symbol的高16位存入gpr中。
    2 T/ N. i; N6 n% Z5 Y" q+ O# ?9 `: i6 h) g
            l.or是or1200处理器的逻辑指令按位或,作用不用讲了吧,
    ( D3 d; `' V3 h$ f  N9 M  X" j7 K) ~. N1 [1 I; E, s: Z) f, I
    l.or rD,rA,rB: a5 W! Q. c. R, K# v& p8 |

    ( ?7 c8 m! r- arD[31:0] < -rA[31:0] OR rB[31:0]/ w; q, r4 ^) q) `* y& G( o

    1 B2 {9 Z0 b2 ]& P4 Z        所以19行将symbol的低16位存入gpr中。( Y' n+ F+ e2 B2 c

    3 G0 P! T4 J! v4 y        继续reset向量,l.jr r3,即跳转到r3寄存器的地址上,所以reset向量前3行就是跳转到_reset标号地址上。
    ' Y# ]+ A8 g4 T8 o3 x0 j7 R0 Z( P7 M" x6 D" H$ @0 e* y* L
            貌似55行还有个l.nop指令,只是or1200的空操作指令。跳转指令之后紧接着的指令称为延时槽,在or1200体系中,在跳转指令之后的紧接着的一条指令是规定、必须、肯定会执行的,所以在现在or1200的gcc编译器中,在跳转指令之后都会自动补上一个nop指令,貌似在新版本的编译器会在跳转指令后执行别的有效指令代替nop操作,提高效率。
    ! N3 w/ F( K- ?, O4 O3 O6 i
    5 q, d: v  T2 n) t3 H/ d: ~% a+ a, U8 ^8 b# _        好,continue······$ ], {- d. ?" R
    + q, I0 J& m2 e
    " ?! K  p, g; l8 R( S" b
            Sourceinsight中一跳,转到_reset标号上, I% f8 g! f. n8 U2 {

    1 J- c: I" d. C0 E; @0 j" H9 S 2 z( U. p  X3 C2 P

    ! a2 a+ o) k. [8 p0 e1 Y6 g
    3 k$ @* C- G+ u, \        l.movhi,l.ori很熟悉啊,目的将r0寄存器清零。; e& M9 ^' u" x" t

    # g" v: T! a% D" R% e& J        l.addi作用是将r0和SPR_SR_SM相加后存入r3中,至于SPR_SR_SM又是什么?跟踪下就可以发现在spr-defs.h有其定义。& y( n: |' A6 Z% {+ [, S9 V
    9 C/ P  r& d8 V* X4 b; M

    / l" d* S" H4 Q/ \! p
    6 l; Y' j  f5 R" C" _4 Q) |  N7 C. A; Z6 F7 v
            这是supervisor模式,这个linux的内核态和用户态概念很像,or1200存在两种模式,supervisor和user,区别是操作CPU内部寄存器组时权限不同。* R+ s7 u" ]( p9 q

    & e* u: q8 w: S& S8 J) ~4 P继续,l.mtspr就是操作or1200内特殊寄存器组的专用指令,专用的,将r0和r3的寄存器的值相加后送入SPR_SR寄存器内7 n! F* m" k( w8 y- W* v9 y

    8 m; e& D' f; z/ C& `' N # \7 Q6 q6 x% d6 T

    , s0 Z# `# G  }: ]3 C5 S# ]
    % i% u( [  {, e/ A' n! L+ d7 Y        在《openrisc-arch-1.0-rev0.pdf》中可以看到,SPR_SR的first bit就是SM位
    ( B: p- M$ Z  V3 f
    & a( ^+ D; h6 H( o: P        为1时就是Supervisor Mode0 s; W" v! l: u# l  U
    + T% ~, ^5 i, Q
            继续往下看,有个CLEAR_GPR的宏,目的是将gpr设置成r0的值
    : X0 e. }' p0 e8 G7 ~+ ~  C2 V5 P5 S0 T' J+ B5 V; u

    / e6 @% C: R8 @& p# W/ G2 }; @( X% J! b& Y# w' X$ x( Z
    * \7 ]4 L5 z% u) n+ V8 p5 x5 n
            在or1200中,r0被设置成常数0,做与这个宏的作用就是清零$ c$ s6 f6 `0 z* q

    2 w/ g9 r& L4 R7 f$ r6 }+ G好,继续) K$ m3 u3 n$ W5 E/ z! Z

    3 I6 e* V. I# a8 L+ S0 [9 Y
    # o5 }( C! x/ B$ d- f  B/ N3 g
    2 t: K* {# w' c: Q; H# i( R' R7 I) m. ]+ E9 h& F4 B

    ) {# [* l, [& ^0 m7 C1 a        SPR_TTMR是关于or1200内Timer的寄存器,将r0的值赋入SPR_TTMR中,至于SPR_TTMR的解释如下表。
    0 S0 x6 V" r: W0 ]0 }4 J  @. x5 x9 b8 ?

    : g7 r( k4 V- F: R3 V5 t
    7 G" M# x, l$ J8 K- i# e* E2 P9 w  s. Y
            目的就是把执行计数单元(简称定时器吧)的中断和使能都禁用了。' T+ N* a8 h* l- j6 u  t3 r8 e% L

    8 n  M8 x+ r; c6 m        好,继续······貌似完了,200行吧_stack的值赋给r1,_stack是CPU堆栈的值,_stack这个值在lds文件内定义,这个文件稍后再解释。% ?& h; J3 z& ], U4 A+ h) o6 B

    2 t7 |, X9 i6 B$ }* S$ r8 L        第203,204行是不是很熟悉,跳转到main函数执行,堆栈建立好之后下面main函数就可以用C来码了。: C3 \+ H, a6 E3 I5 w9 h9 j5 k9 J8 o
    ( N- b- _6 B' I8 g8 ~& H/ B
            Reset向量就需要这些代码了,至于reset.S剩下的代码是关于中断处理的,比如在0 s* U. j: [) q. u! l, @: F

      q4 [3 \) s1 a( J- ]/ p , y2 @: L$ `, \, h# t
    & @  @. h+ X- H, a7 O
    6 V* P8 }- ^4 V3 H6 I
            在定时器中断和外中断会先执行EXCEPTION_HANDLER宏,目的是执行中断处理之前的现场保护,跳转至中断服务函数,执行完中断服务函数之后的现场恢复,中断的概念不用多解释了,应该是很熟悉的了。9 x5 n2 r% k' F! A$ P( e, c
    7 N! O- I) d. m  S5 Q7 @
            关于or1200中断的流程可以参考《CPU源代码分析与芯片设计及Linux移植》,里面讲得蛮详细的,虽然大部分也是翻译手册的,至于附件的代码关于reset.S剩余部分不是特别难,就不解释了,不解释咯······
    0 {5 g4 |. p0 o
    " W; E2 ]* X3 b. G+ x4 s+ P        突然发现,reset的异常向量我们就敲完了,在reset向量里会跳转至main函数,那就开始了,main函数熟悉了吧,2 V/ w& _( N/ W: N  X
    0 F" y7 l: ?! K
            附件中的uart.c和uart.h关于UART16550的串口驱动和头文件,程序不难,对比着uart16550_latest.tar.gz的说明文档、《开源软核处理器OPENRISC的SOPC设计》和源码的注释5分钟就可以看懂了,不详细说了,因为大家都有C语言功底嘛。4 z) }" M; I( M; m# |
    ' Z' K2 P! o+ _4 N7 C, `7 E8 c
             至于附件driver和include的其他文件可以先不管,因为现在还用不上,有兴趣可以自行翻阅下,是关于中断二级向量表的实现和timer的驱动,大部分都是参考u-boot下openrisc架构修修改改过来的,所以最好还是稍微浏览下,然后在下次移植u-boot时能快速上手。
    # n9 a, n! {) g* L' [% [0 t( r% p# r" G3 r# \, A
            Main函数更不用多说了,手痒自己随意打的,随便修改吧,至于里面的函数用sourceinsight追一下就可以了,都是比较简单的东西了。
    ! v2 c0 w5 N% a- K6 \& W) U: x7 H, H& k  t2 \
            UART的代码到这里我们就敲完了,最后就是把程序下到我们的SDRAM中运行了,好,在下到SDRAM之前呢,还需要编写一个连接脚本文件,告诉编译器编译出来的可执行程序各个段的存储位置,入口地址,在学校学汇编和计算机原理的时候都应该讲过这些的了,就是编译源码后得到的可执行bin文件的在RAM的存储位置,例如在link.lds文件中
    * S+ P2 E! m2 c( B% q# `% Q6 m& r
    : Q7 f% h5 [4 l9 b' K, Z 3 G6 d0 Q4 @: z/ x( O0 b
    # }1 g# A0 T: ]" R: k( B& R

    % V& j, m- H- z* z- j2 _        vectors段,即我们之前编写的异常向量表的存放位置,从0x0开始到0x2000,除or1200结构本身定义的异常向量表之外,还保留空的异常向量允许自定义操作。
    ! j4 P# g# J$ ~3 ^% N7 V6 T- ^4 ], R( N  E1 Q) `

    + a( k% R; x8 R+ u/ C1 J) S, _        ram段就是开始存放我们uart的程序代码啦,继续看······& O. `1 j: i6 }6 q; b& g

    7 W, H6 U6 f+ E
    6 H  ]" u$ w6 U3 R: N7 ^, M: X8 S8 b0 a+ v6 c
    ' `/ E0 u0 A! T9 @( e
            这里定义了一个_min_stack的变量,大小为8K,记得我们在写reset向量时设置CPU堆栈的值吗?% x2 F  ?; x; J9 w* a

    4 J. S8 o; p6 s" w" K: ? 5 U1 D0 _0 C0 ?

    # q  B. R8 [) m2 ^7 L( g+ ^  b4 R3 @- v
    8 f# B- J* G5 i+ I- j; y' N4 N9 M        在这里_stack的值在这里取出,这里我说的不是很明白,关于lds连接文件附件中的ARM79出品-u-boot移植手册.pdf的附录一有详细说明,参考参考很容易就明白啦,或者可以google一下,再或者看看别人的技术博客就行了,这里不再详细解释了,因为码字是在是很累人的一件事。
    9 w5 k6 T! s0 D) p( }0 t
    & P  u, Y( s" b7 f
    % w+ r1 A& l* C* j  h( S8 w        一切OK准备就绪,但是在linux环境下编译我们还要写一个makefile,哇天啊,好吧,认栽吧,写吧。
    7 h1 f& w% n0 S8 H
    - E8 s- d; U0 {7 |; G0 R: G1 o        ······
    - l  U+ k) |) W5 I+ R, M8 @9 B( d9 E. h- m
             现在可以打开virtualbox,先浏览一下放在桌面的那些文件,比如' d  v+ ?6 T0 ?- [& o3 o$ Y8 o
    . z1 P6 E  d! U2 U2 T% n' s/ n
    . I  D+ S$ Q0 W+ F+ u! g  s

    0 d/ E6 P- T8 f! O# H1 J* ]. p- T
    % s) _/ d; I' C' \- t9 I        可大概看看这个环境可以做些什么东西。
    / R* u& k9 L8 f4 W3 ?8 s( E" H2 B) V* j) {! R( I
    8 _9 H! b/ e8 ]+ l( l8 W+ o
            然后打开home folder,点啊点,点啊点, j; e5 Z7 n( C
    + E1 x, k- s9 ^; Y
    5 Q1 t+ b+ H( q9 Z

      {' k/ e8 ~  V4 e
    % s9 |/ C$ i8 N: d        在orpmon文件夹中包含一个opencores社区针对or1200做的bootloader,而且,我在写makefile的时候都是参考这个bootloader的makefile写的,至于怎么去写一个makefile,参考《跟我一起写 Makefile.pdf》和《GNU+makefile中文手册.pdf》这两个手册咯。
    : I2 F' W  W% I

    该用户从未签到

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

    该用户从未签到

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

    本版积分规则

    关闭

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

    EDA365公众号

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

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

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

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

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