龙芯俱乐部开源社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 732|回复: 2

智龙裸机点亮LED

[复制链接]

5

主题

16

帖子

1261

积分

金牌会员

Rank: 6Rank: 6

积分
1261
发表于 2019-7-15 01:34:32 | 显示全部楼层 |阅读模式
本帖最后由 ahau 于 2019-7-27 22:38 编辑

一直有一个心愿,就是要弄清楚CPU上电后执行第一条指令到底是怎么玩的。这听起来简单,但是我发现真正要做到其实挺难的,要补很多知识点。
我们知道u-boot其实就是一个裸机程序,用汇编、C和shell写的,我觉得从u-boot开始是个不错的选择。我试过从pmon开始,我觉得pmon还是太复杂了。

如果你跟我一样对“第一条指令”比较感兴趣,希望本贴能帮到你。如果有写的不对的地方,希望大家指正,以免误导他人。

我用的是智龙V2.1开发板。先来张图看一下效果:
blink.gif

除了板子,我们还需要一个flash烧写器。我看大家都用的土豪金,我也买了一个,挺好用的。win10上驱动有点小问题,大家百度解决一下就好了。

土豪金正面

土豪金正面

土豪金背面

土豪金背面



  • 第一次从智龙板子上很难拔出来,可以用牙签把它撬出来,多弄几次,以后就像挑螺蛳一样轻松了。
  • 注意烧写的时候方向别弄反了,可以从背面的线框图的凹槽看出方向,25系列的在左边。
  • 烧写之前要先擦除,不擦除好像不行。先打开软件,再插入土豪金。
  • 智龙板子上电后,摸一摸flash芯片烫不烫,如果插反了,立马就会发烫,赶紧断电,我的差点就烧掉了


=== 再开一贴就得等10分钟 ===

我们需要一个start.S,注意S大写。
如果你去stackoverflow上搜bootloader代码怎么写,他们会告诉你第一条指令是 b reset。但是真正写的时候就得变成这样:
  1.     .set noreorder

  2.     .globl _start
  3.     .text

  4. _start:
  5.         b    reset
  6.         nop

  7. reset:
  8.         tc0    zero, CP0_WATCHLO
  9.         mtc0    zero, CP0_WATCHHI
复制代码



.set noreorder,禁止重排序。禁止谁重排序?编译器。重排序是个大话题,有编译器重排序和cpu重排序,cpu重排序好像是跟cpu的乱序发射有关,总之加上这句,指令会按我们期望的顺序执行。

b reset后面为什么要加一个nop?这涉及到跳转指令延迟槽(delay slot)的问题。本来编译器会自动在跳转指令后面加上nop的,这里已经设置noreorder了,所以必须手动加一个。

在reset这里是我们真正第一条有意义的指令。 那应该写什么呢? 据我所知通常都是关看门狗。这两句是关看门狗吗?
纠正一个错误,之前我想当然地以为是关看门狗。其实完全错了。

这两句代码的作用是:
清除硬件数据断点,防止产生调试断点,导致程序停止。芯片复位后,某些寄存器的内容也许是你想要的结果,但是谁知道呢,为了保证准确无误,最好还是重新进行手动初始化。

接下来是禁止全局中断;初始化异常寄存器,清除异常原因指示。


  1. mtc0    zero, CP0_STATUS   // 清零cp0 status寄存器 $12
  2. mtc0    zero, CP0_CAUSE    // 清零cp0 cause寄存器 $13
复制代码


到这里你可能会疑惑,这个CP0_STATUS在哪里定义的?它其实就是$12寄存器的别名,在u-boot的mipsregs.h里定义的。`#define CP0_STATUS $12`
直接用数字记不住寄存器的含义,就像我们记不住指令的机器码一样,所以我们使用汇编助记符。这里也一样,给每个寄存器定义一个别名。
u-boot在编译的时候会创建一个asm的软链接,我们不搞这么复杂,直接新建一个asm的文件夹,把用到的头文件放进来。

除了mipsregs.h我们还需要regdef.h,由于regdef.h包含了sgidefs.h,所以我们还需要把sgidefs.h放进来。  sgidefs.h在这里没什么用,只不过u-boot.lds里用到了里面的一个宏,我们暂且不修改u-boot.lds,所以这里直接引入就好了。这个头文件就几行代码,就定义了几个宏而已。

mipsregs.h和regdef.h没什么神秘的,就是给寄存器定义一个别名而已,我们自己也可以写。既然已经有人写好了,我们就不重复自己了。但是这里有一个问题。 两个头文件里都给$12寄存器定义别名了,这不乱了吗?
`#define CP0_STATUS $12`
`#define t4        $12`
不会的,仔细看操作CP0_STATUS的时候虽然也是$12,但是用的指令不是普通的指令,而是mtc0。mtc0操作的是协处理0的$12寄存器,其他指令操作操作的$12是CPU的寄存器。



CPU刚上电的时候只有kseg1能用,kseg0需要走缓存,我们还没设置缓存呢。所以只能用kseg1。这个我是从勤为本的博客上看到的,感谢。如果你跟我一样不知道为什么和怎么设置kseg1,请看他的博客,说的很清楚。这是个非常重要的知识点,必须掌握。
设置kseg1:

  1. li      t0, SR_BOOT_EXC_VEC //设置启动异常向量入口地址为ROM地址(0xbfc00000) 将寄存器cp0 status的BEV置1,
  2. mtc0    t0, CP0_STATUS       // 使CPU采用ROM(kseg1)空间的异常入口点
复制代码


下面就是初始化SPI了,直接粘贴过来。

  1. /* initialize spi */
  2. li  t0, 0xbfe80000      //地址0xbfe80000为SPI0的寄存器基地址
  3. //li  t1, 0x1b         
  4. li  t1, 0x17            // div 4, fast_read + burst_en + memory_en double I/O 模式 部分SPI flash可能不支持
  5. sb  t1, 0x4(t0)            // 设置寄存器sfc_param
  6. li  t1, 0x05
  7. sb  t1, 0x6(t0)         // 设置寄存器sfc_timing
复制代码

之前没有仔细研究初始化SPI的代码。仔细研究了一下,这一块挺有意思的。根据用户手册Loongson_1C300_user.pdf,
SPI控制器除了有若干IO寄存器外还有一段映射到SPI Flash的只读memory空间。如果将这段memory空间分配在0xbfc00000,复位后不需要软件干预就可以直接访问,从而支持处理器从SPI Flash启动。

我们的链接命令已经将代码链接到了0xbfc000000这个地址,所以其实我们不需要这段代码,CPU就能读flash了。我试过将这一段代码注释掉,确实可以。但是有一个问题,因为默认不是fastread模式,如果不设置快速读模式,程序会跑的非常慢。怎么知道的?因为延迟程序跑的非常慢,所以灯闪的特别慢!是不是很有意思,这就跟没启用缓存,程序跑的非常慢一个道理。有意思吧?

默认不是fastread模式

默认不是fastread模式


所以这里我们要设置SPI的参数寄存器,设置为0x17。即分频系数为4, 非双I/O模式,快速读模式,连续读,使能SPI flash。
因为双I/O模式的优先级高于快速读,所以设置为0x17是非常准确的。


到这里必要的初始化都已经完成,可以点灯了。 至于PLL,定时器,缓存什么的暂时都不用管,点亮灯之后我们可以一点一点的加上去。

前面啰嗦了那么多,真正点灯的程序却是很轻松的。拢共分三步(LS1C开发板用户手册_V0.60.pdf有详细说明):
配置GPIO为普通IO,不复用
配置GPIO为输出模式
GPIO设置为低电平,注意看电路图,设置低电平时为点亮。

  1. /*config gpio50 pad is gpio func*/
  2.     li t1, 0xbfd010c0
  3.     //lw t2, 0x4(t1)
  4.     //li t3, (0x1<<18)
  5.     //or t2, t2, t3
  6.     li t2, 0xffffffff
  7.     sw t2, 0x4(t1)

  8.     /*config gpio50 direction is output*/
  9.     //lw t2, 0x4(t1)
  10.     //li t3, ~(0x1<<18)
  11.     //and t2, t2, t3
  12.     li t1, 0xbfd010d0
  13.     li t2, 0x0
  14.     sw t2, 0x4(t1)

  15. blink:

  16.     /*config gpio50 output low*/
  17.     li t1, 0xbfd010f0
  18.     li t2, 0x0
  19.     sw t2, 0x4(t1)
  20.     DELAY(20000)

  21.     /*config gpio50 output high*/
  22.     li t1, 0xbfd010f0
  23.     li t2, 0xffffffff
  24.     sw t2, 0x4(t1)
  25.     DELAY(20000)
  26.     b blink
  27.     nop
复制代码


这里的DELAY是我们定义的一个宏。这个宏在u-boot的lowlevel_init.S里有定义,直接拷过来用。
我还想再啰嗦一句, li, sw不是MIPS的指令,从指令手册上是找不到的,它是汇编编译器的宏指令。到gnu as的文档里才能查到。我之前就是这个写错了,结果灯老是不亮。

到此咱们的代码就完成了,怎么编译,怎么链接呢? 为了方便,我们写一个简单的Makefile来做这个工作。不然每次重编译的时候很麻烦。

  1. start.o:
  2.         echo "compiling u-boot..."
  3.         mipsel-linux-gcc -D__ASSEMBLY__ -g -Os -I/mnt/hgfs/CentOS_Share/chapter03 -ffreestanding -nostdinc -isystem /opt/gcc-4.3-ls232/bin/../lib/gcc/mipsel-linux/4.3.0/include -pipe  -G 0 -mabicalls -fpic -EL -msoft-float -march=mips32 -mabi=32 -o start.o start.S -c
  4.         echo "echo "linking u-boot...""
  5.         mipsel-linux-ld -G 0 -static -n -nostdlib -EL -m elf32ltsmip -T u-boot.lds --gc-sections -pie -Bstatic -Ttext 0xbfc00000 start.o -L /opt/gcc-4.3-ls232/bin/../lib/gcc/mipsel-linux/4.3.0 -lgcc -Map u-boot.map -o u-boot
  6.         echo "generating u-boot.bin..."
  7.         mipsel-linux-objcopy --remove-section=.dynsym --gap-fill=0xff -O binary u-boot u-boot.bin
  8.         echo "done!"

  9. .PHONY : clean

  10. clean:
  11.         rm -f start.o u-boot u-boot.map u-boot.bin u-boot.dis u-boot.s
复制代码


这里的编译命令是我从u-boot里找到的,我去掉了一些优化的参数。每个参数的含义大家可以查看gnu的文档,这里不展开了。

再啰嗦几句:

  • 虽然只有汇编代码,但却是用gcc编译的,头文件了用到了__ASSEMBLY__这个宏,所以要加一个`-D__ASSEMBLY__`
  • 这里将代码编译成了位置无关码。如果你不了解,可以看看这篇博客:http://blog.chinaunix.net/uid-20528014-id-4445271.html,这是个非常重要的知识点,涉及到重定位问题。
  • 链接脚本直接使用u-boot.lds,运行内存地址(也叫VMA,gnu ld文档里好像只把它称作VMA)一定要链接到0xbfc00000。
  • 编译完了,一定要mipsel-linux-objdump -D u-boot查看一下VMA和LMA对不对。
  • 因为我们编译成了位置无关码(PIC),要注意看看输出文件里得有一个.rel.dyn这个section。要特别注意section之间缝隙不要太大,否则objcopy的时候生成的.bin文件会非常大。
  • 不用链接脚本,直接一个-Ttext 0xbfc00000行不行?我试了很多遍都没成功,好像必须得明确设置每个section的地址,否则objdump会报out of bounds的错,.text段也根本没有生成。


到这里就告一段落了。 chapter03.zip (14.57 KB, 下载次数: 2)

55

主题

379

帖子

43万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
437801
发表于 2019-7-17 16:15:07 | 显示全部楼层
做的很好 有文章有代码还有图

5

主题

16

帖子

1261

积分

金牌会员

Rank: 6Rank: 6

积分
1261
 楼主| 发表于 2019-7-17 21:05:55 | 显示全部楼层
sn2015ol 发表于 2019-7-17 16:15
做的很好 有文章有代码还有图

感谢,加精是对楼主最大的肯定。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|龙芯俱乐部开源社区  

GMT+8, 2020-3-28 22:14 , Processed in 0.234710 second(s), 39 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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